Ejemplo n.º 1
0
 def map_params_to_state(self, params):
     # decode the signed params and add them to whatever params we have
     params = params.copy()
     signed_params = params["signed_params"]
     del params["signed_params"]
     params.update(unsign(signed_params.encode("ascii", errors="ignore")))
     return params
Ejemplo n.º 2
0
    def handle(self, request: Request, signed_params: str) -> Response:
        try:
            params = unsign(signed_params)
        except (SignatureExpired, BadSignature):
            return render_to_response(
                "sentry/integrations/slack/expired-link.html",
                request=request,
            )

        organization, integration, idp = get_identity_or_404(
            ExternalProviders.SLACK,
            request.user,
            integration_id=params["integration_id"],
        )

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-unlink-identity.html",
                request=request,
                context={"organization": organization, "provider": integration.get_provider()},
            )

        try:
            Identity.objects.filter(idp=idp, external_id=params["slack_id"]).delete()
        except IntegrityError as e:
            logger.error("slack.unlink.integrity-error", extra=e)
            raise Http404

        send_slack_response(integration, SUCCESS_UNLINKED_MESSAGE, params, command="unlink")

        return render_to_response(
            "sentry/integrations/slack/unlinked.html",
            request=request,
            context={"channel_id": params["channel_id"], "team_id": integration.external_id},
        )
 def map_params_to_state(self, params):
     # decode the signed params and add them to whatever params we have
     params = params.copy()
     signed_params = params["signed_params"]
     del params["signed_params"]
     params.update(unsign(signed_params, max_age=INSTALL_EXPIRATION_TIME,))
     return params
Ejemplo n.º 4
0
    def handle(self, request: Request, signed_params: str) -> Response:
        try:
            params = unsign(signed_params)
        except (SignatureExpired, BadSignature):
            return render_to_response(
                "sentry/integrations/slack/expired-link.html",
                request=request,
            )

        organization, integration, idp = get_identity_or_404(
            ExternalProviders.SLACK,
            request.user,
            integration_id=params["integration_id"],
            organization_id=params["organization_id"],
        )

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-link-identity.html",
                request=request,
                context={
                    "organization": organization,
                    "provider": integration.get_provider()
                },
            )

        # TODO(epurkhiser): We could do some fancy slack querying here to
        # render a nice linking page with info about the user their linking.

        # Link the user with the identity. Handle the case where the user is linked to a
        # different identity or the identity is linked to a different user.
        defaults = {
            "status": IdentityStatus.VALID,
            "date_verified": timezone.now()
        }
        try:
            identity, created = Identity.objects.get_or_create(
                idp=idp,
                user=request.user,
                external_id=params["slack_id"],
                defaults=defaults)
            if not created:
                identity.update(**defaults)
        except IntegrityError:
            Identity.objects.reattach(idp, params["slack_id"], request.user,
                                      defaults)

        send_slack_response(integration,
                            SUCCESS_LINKED_MESSAGE,
                            params,
                            command="link")

        return render_to_response(
            "sentry/integrations/slack/linked.html",
            request=request,
            context={
                "channel_id": params["channel_id"],
                "team_id": integration.external_id
            },
        )
Ejemplo n.º 5
0
    def handle(self, request, signed_params):
        params = unsign(signed_params)

        organization, integration, idp = get_identity(
            request.user, params["organization_id"], params["integration_id"]
        )

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-link-identity.html",
                request=request,
                context={"organization": organization, "provider": integration.get_provider()},
            )

        defaults = {"status": IdentityStatus.VALID, "date_verified": timezone.now()}
        try:
            identity, created = Identity.objects.get_or_create(
                idp=idp, user=request.user, external_id=params["teams_user_id"], defaults=defaults
            )
            if not created:
                identity.update(**defaults)
        except IntegrityError:
            Identity.reattach(idp, params["teams_user_id"], request.user, defaults)

        card = build_linked_card()
        client = MsTeamsClient(integration)
        user_conversation_id = client.get_user_conversation_id(
            params["teams_user_id"], params["tenant_id"]
        )
        client.send_card(user_conversation_id, card)

        return render_to_response(
            "sentry/msteams-linked.html", request=request, context={"team_id": params["team_id"]}
        )
Ejemplo n.º 6
0
    def handle(self, request, signed_params):
        params = unsign(signed_params)

        if request.method != "POST":
            return render_to_response(
                "sentry/integrations/msteams-unlink-identity.html",
                request=request,
                context={},
            )

        # find the identities linked to this team user and sentry user
        identity_list = Identity.objects.filter(
            external_id=params["teams_user_id"], user=request.user)
        # if no identities, tell the user that
        if not identity_list:
            return render_to_response(
                "sentry/integrations/msteams-no-identity.html",
                request=request,
                context={},
            )

        # otherwise, delete the identities, send message to the user, and render a success screen
        identity_list.delete()
        client = get_preinstall_client(params["service_url"])
        card = build_unlinked_card()
        client.send_card(params["conversation_id"], card)

        return render_to_response(
            "sentry/integrations/msteams-unlinked.html",
            request=request,
            context={},
        )
Ejemplo n.º 7
0
    def handle(self, request: Request, signed_params: str) -> Response:
        params = unsign(signed_params)

        organization, integration, idp = get_identity(
            request.user, params["organization_id"], params["integration_id"])
        channel_name = params["channel_name"]
        channel_id = params["channel_id"]

        external_teams = ExternalActor.objects.filter(
            organization=organization,
            integration=integration,
            provider=ExternalProviders.SLACK.value,
            external_name=channel_name,
            external_id=channel_id,
        )
        if len(external_teams) == 0:
            logger.error("slack.team.unlink.no_external_teams")
            raise Http404

        teams = Team.objects.filter(actor__in=[
            external_team.actor for external_team in external_teams
        ])
        if request.method != "POST":
            return render_to_response(
                "sentry/integrations/slack-unlink-team.html",
                request=request,
                context={
                    "team": teams[0],
                    "channel_name": channel_name,
                    "provider": integration.get_provider(),
                },
            )

        try:
            idp = IdentityProvider.objects.get(
                type="slack", external_id=integration.external_id)
        except IdentityProvider.DoesNotExist:
            logger.error("slack.action.invalid-team-id",
                         extra={"slack_id": integration.external_id})
            return render_error_page(request,
                                     body_text="HTTP 403: Invalid team ID")

        if not Identity.objects.filter(
                idp=idp, external_id=params["slack_id"]).exists():
            return render_error_page(
                request, body_text="HTTP 403: User identity does not exist")
        for external_team in external_teams:
            external_team.delete()
        for team in teams:
            NotificationSetting.objects.remove_for_team(
                team, ExternalProviders.SLACK)
        return send_confirmation(
            integration,
            channel_id,
            SUCCESS_UNLINKED_TITLE,
            SUCCESS_UNLINKED_MESSAGE.format(team=team.slug),
            "sentry/integrations/slack-unlinked-team.html",
            request,
        )
Ejemplo n.º 8
0
    def handle(self, request: Request, signed_params: str) -> Response:
        try:
            params = unsign(signed_params)
        except (SignatureExpired, BadSignature):
            return render_to_response(
                "sentry/integrations/slack/expired-link.html",
                request=request,
            )

        organization, integration, idp = get_identity_or_404(
            ExternalProviders.SLACK,
            request.user,
            integration_id=params["integration_id"],
            organization_id=params["organization_id"],
        )
        channel_name = params["channel_name"]
        channel_id = params["channel_id"]

        external_teams = ExternalActor.objects.filter(
            organization=organization,
            integration=integration,
            provider=ExternalProviders.SLACK.value,
            external_name=channel_name,
            external_id=channel_id,
        )
        if len(external_teams) == 0:
            return render_error_page(request,
                                     body_text="HTTP 404: Team not found")

        team = external_teams[0].actor.resolve()

        if request.method != "POST":
            return render_to_response(
                "sentry/integrations/slack/unlink-team.html",
                request=request,
                context={
                    "team": team,
                    "channel_name": channel_name,
                    "provider": integration.get_provider(),
                },
            )

        if not Identity.objects.filter(
                idp=idp, external_id=params["slack_id"]).exists():
            return render_error_page(
                request, body_text="HTTP 403: User identity does not exist")

        # Someone may have accidentally added multiple teams so unlink them all.
        for external_team in external_teams:
            external_team.delete()

        return send_confirmation(
            integration,
            channel_id,
            SUCCESS_UNLINKED_TITLE,
            SUCCESS_UNLINKED_MESSAGE.format(team=team.slug),
            "sentry/integrations/slack/unlinked-team.html",
            request,
        )
Ejemplo n.º 9
0
    def handle(self, request, signed_params):
        params = unsign(signed_params.encode("ascii", errors="ignore"))

        organization, integration, idp = get_identity(
            request.user, params["organization_id"], params["integration_id"])

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-unlink-identity.html",
                request=request,
                context={
                    "organization": organization,
                    "provider": integration.get_provider()
                },
            )

        # Delete the wrong slack identity.
        try:
            identity = Identity.objects.get(idp=idp,
                                            external_id=params["slack_id"])
            identity.delete()
        except IntegrityError as e:
            logger.error("slack.unlink.integrity-error", extra=e)
            raise Http404

        payload = {
            "replace_original":
            False,
            "response_type":
            "ephemeral",
            "text":
            "Your Slack identity has been unlinked from your Sentry account.",
        }

        client = SlackClient()
        try:
            client.post(params["response_url"], data=payload, json=True)
        except ApiError as e:
            message = six.text_type(e)
            # If the user took their time to link their slack account, we may no
            # longer be able to respond, and we're not guaranteed able to post into
            # the channel. Ignore Expired url errors.
            #
            # XXX(epurkhiser): Yes the error string has a space in it.
            if message != "Expired url":
                logger.error("slack.unlink-notify.response-error",
                             extra={"error": message})

        return render_to_response(
            "sentry/slack-unlinked.html",
            request=request,
            context={
                "channel_id": params["channel_id"],
                "team_id": integration.external_id
            },
        )
Ejemplo n.º 10
0
    def handle(self, request, signed_params):
        params = unsign(signed_params)

        organization, integration, idp = get_identity(
            request.user, params["organization_id"], params["integration_id"]
        )

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-link-identity.html",
                request=request,
                context={"organization": organization, "provider": integration.get_provider()},
            )

        # TODO(epurkhiser): We could do some fancy slack querying here to
        # render a nice linking page with info about the user their linking.

        # Link the user with the identity. Handle the case where the user is linked to a
        # different identity or the identity is linked to a different user.
        defaults = {"status": IdentityStatus.VALID, "date_verified": timezone.now()}
        try:
            identity, created = Identity.objects.get_or_create(
                idp=idp, user=request.user, external_id=params["slack_id"], defaults=defaults
            )
            if not created:
                identity.update(**defaults)
        except IntegrityError:
            Identity.reattach(idp, params["slack_id"], request.user, defaults)

        payload = {
            "replace_original": False,
            "response_type": "ephemeral",
            "text": "Your Slack identity has been linked to your Sentry account. You're good to go!",
        }

        client = SlackClient()
        try:
            client.post(params["response_url"], data=payload, json=True)
        except ApiError as e:
            message = six.text_type(e)
            # If the user took their time to link their slack account, we may no
            # longer be able to respond, and we're not guaranteed able to post into
            # the channel. Ignore Expired url errors.
            #
            # XXX(epurkhiser): Yes the error string has a space in it.
            if message != "Expired url":
                logger.error("slack.link-notify.response-error", extra={"error": message})

        return render_to_response(
            "sentry/slack-linked.html",
            request=request,
            context={"channel_id": params["channel_id"], "team_id": integration.external_id},
        )
Ejemplo n.º 11
0
    def handle(self, request: Request, signed_params: str) -> Response:
        try:
            params = unsign(signed_params)
        except (SignatureExpired, BadSignature):
            return render_to_response(
                "sentry/integrations/slack/expired-link.html",
                request=request,
            )

        organization, integration, idp = get_identity_or_404(
            ExternalProviders.SLACK,
            request.user,
            integration_id=params["integration_id"],
        )

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-link-identity.html",
                request=request,
                context={
                    "organization": organization,
                    "provider": integration.get_provider()
                },
            )

        Identity.objects.link_identity(user=request.user,
                                       idp=idp,
                                       external_id=params["slack_id"])

        send_slack_response(integration,
                            SUCCESS_LINKED_MESSAGE,
                            params,
                            command="link")
        if not NotificationSetting.objects.has_any_provider_settings(
                request.user, ExternalProviders.SLACK):
            IntegrationNudgeNotification(organization, request.user,
                                         ExternalProviders.SLACK).send()

        # TODO(epurkhiser): We could do some fancy slack querying here to
        #  render a nice linking page with info about the user their linking.
        return render_to_response(
            "sentry/integrations/slack/linked.html",
            request=request,
            context={
                "channel_id": params["channel_id"],
                "team_id": integration.external_id
            },
        )
Ejemplo n.º 12
0
    def get_validated_data(self, data, user):
        try:
            data = unsign(force_str(data))
        except SignatureExpired:
            raise InvalidPayload('Project transfer link has expired.')
        except BadSignature:
            raise InvalidPayload('Could not approve transfer, please make sure link is valid.')

        if data['user_id'] != user.id:
            raise InvalidPayload('Invalid permissions')

        try:
            project = Project.objects.get(
                id=data['project_id'], organization_id=data['from_organization_id'],
            )
        except Project.DoesNotExist:
            raise InvalidPayload('Project no longer exists')

        return data, project
    def get_validated_data(self, data, user):
        try:
            data = unsign(force_str(data))
        except SignatureExpired:
            raise InvalidPayload("Project transfer link has expired.")
        except BadSignature:
            raise InvalidPayload("Could not approve transfer, please make sure link is valid.")

        if data["user_id"] != user.id:
            raise InvalidPayload("Invalid permissions")

        try:
            project = Project.objects.get(
                id=data["project_id"], organization_id=data["from_organization_id"]
            )
        except Project.DoesNotExist:
            raise InvalidPayload("Project no longer exists")

        return data, project
Ejemplo n.º 14
0
    def handle(self, request: Request, signed_params) -> Response:
        try:
            params = unsign(signed_params)
        except (SignatureExpired, BadSignature):
            return render_to_response(
                "sentry/integrations/msteams/expired-link.html",
                request=request,
            )

        organization, integration, idp = get_identity_or_404(
            ExternalProviders.MSTEAMS,
            request.user,
            integration_id=params["integration_id"],
            organization_id=params["organization_id"],
        )

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-link-identity.html",
                request=request,
                context={
                    "organization": organization,
                    "provider": integration.get_provider()
                },
            )

        Identity.objects.link_identity(user=request.user,
                                       idp=idp,
                                       external_id=params["teams_user_id"])

        card = build_linked_card()
        client = MsTeamsClient(integration)
        user_conversation_id = client.get_user_conversation_id(
            params["teams_user_id"], params["tenant_id"])
        client.send_card(user_conversation_id, card)

        return render_to_response(
            "sentry/integrations/msteams/linked.html",
            request=request,
            context={"team_id": params["team_id"]},
        )
Ejemplo n.º 15
0
    def test_basic_flow(self):
        teams_user_id = "my-teams-user-id"
        Identity.objects.create(user=self.user1,
                                idp=self.idp,
                                external_id=teams_user_id,
                                status=IdentityStatus.VALID)

        unlink_url = build_unlinking_url(
            self.conversation_id, "https://smba.trafficmanager.net/amer",
            teams_user_id)

        signed_params = unlink_url.split("/")[-2]
        params = unsign(signed_params)
        assert params == {
            "conversation_id": self.conversation_id,
            "service_url": "https://smba.trafficmanager.net/amer",
            "teams_user_id": teams_user_id,
        }

        resp = self.client.get(unlink_url)

        assert resp.status_code == 200
        self.assertTemplateUsed(
            resp, "sentry/integrations/msteams-unlink-identity.html")

        # Unlink identity of user
        resp = self.client.post(unlink_url)

        assert resp.status_code == 200
        self.assertTemplateUsed(resp,
                                "sentry/integrations/msteams-unlinked.html")

        identity = Identity.objects.filter(external_id=teams_user_id,
                                           user=self.user1)

        assert len(identity) == 0
        assert (
            "Your Microsoft Teams identity has been unlinked to your Sentry account."
            in responses.calls[1].request.body.decode("utf-8"))
        assert len(responses.calls) == 2
Ejemplo n.º 16
0
    def handle(self, request: Request, signed_params: str) -> Response:
        params = unsign(signed_params)

        organization, integration, idp = get_identity(
            request.user, params["organization_id"], params["integration_id"])

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-unlink-identity.html",
                request=request,
                context={
                    "organization": organization,
                    "provider": integration.get_provider()
                },
            )

        # Delete the wrong slack identity.
        try:
            identity = Identity.objects.get(idp=idp,
                                            external_id=params["slack_id"])
            identity.delete()
        except IntegrityError as e:
            logger.error("slack.unlink.integrity-error", extra=e)
            raise Http404

        send_slack_response(integration,
                            SUCCESS_UNLINKED_MESSAGE,
                            params,
                            command="unlink")

        return render_to_response(
            "sentry/integrations/slack-unlinked.html",
            request=request,
            context={
                "channel_id": params["channel_id"],
                "team_id": integration.external_id
            },
        )
    def handle(self, request, *args, **kwargs):
        try:
            d = request.GET['data']
        except KeyError:
            raise Http404

        try:
            data = unsign(force_str(d))
        except BadSignature:
            messages.add_message(
                request, messages.ERROR,
                _(u'Could not approve transfer, please make sure link is valid.')
            )
            return HttpResponseRedirect(
                reverse('sentry')
            )
        except SignatureExpired:
            messages.add_message(
                request, messages.ERROR,
                _(u'Project transfer link has expired!')
            )
            return HttpResponseRedirect(
                reverse('sentry')
            )

        project_id = data['project_id']
        user_id = data['user_id']
        transaction_id = data['transaction_id']
        from_organization_id = data['from_organization_id']
        if user_id != request.user.id:
            messages.add_message(
                request, messages.ERROR,
                _(u'Invalid permissions!')
            )
            return HttpResponseRedirect(
                reverse('sentry')
            )

        # check if user is still an owner
        if not OrganizationMember.objects.filter(
            role=roles.get_top_dog().id,
            user__is_active=True,
            user_id=user_id,
        ).exists():
            return HttpResponseRedirect(
                reverse('sentry')
            )

        try:
            project = Project.objects.get(id=project_id, organization_id=from_organization_id)
        except Project.DoesNotExist:
            messages.add_message(
                request, messages.ERROR,
                _(u'Project no longer exists')
            )
            return HttpResponseRedirect(
                reverse('sentry')
            )

        form = self.get_form(request)
        if form.is_valid():
            # transfer the project
            team_id = form.cleaned_data.get('team')
            new_team = Team.objects.get(id=team_id)
            project.transfer_to(new_team)

            self.create_audit_entry(
                request=request,
                organization=project.organization,
                target_object=project_id,
                event=AuditLogEntryEvent.PROJECT_ACCEPT_TRANSFER,
                data=project.get_audit_log_data(),
                transaction_id=transaction_id,
            )

            return HttpResponseRedirect(
                reverse('sentry-organization-home', args=[new_team.organization.slug])
            )

        context = {
            'project': project,
            'form': form,
        }
        return self.respond('sentry/projects/accept_project_transfer.html', context)
Ejemplo n.º 18
0
    def handle(self, request, signed_params):
        params = unsign(signed_params.encode('ascii', errors='ignore'))

        try:
            organization = Organization.objects.get(
                id__in=request.user.get_orgs(),
                id=params['organization_id'],
            )
        except Organization.DoesNotExist:
            raise Http404

        try:
            integration = Integration.objects.get(
                id=params['integration_id'],
                organizations=organization,
            )
        except Integration.DoesNotExist:
            raise Http404

        try:
            idp = IdentityProvider.objects.get(
                Q(external_id=integration.external_id) | Q(external_id=None),
                type='slack',
                organization=organization,
            )
        except Integration.DoesNotExist:
            raise Http404

        if request.method != 'POST':
            return render_to_response('sentry/auth-link-identity.html', request=request, context={
                'organization': organization,
                'provider': integration.get_provider(),
            })

        # TODO(epurkhiser): We could do some fancy slack querying here to
        # render a nice linking page with info about the user their linking.

        identity, created = Identity.objects.get_or_create(
            user=request.user,
            idp=idp,
            defaults={
                'external_id': params['slack_id'],
                'status': IdentityStatus.VALID,
            },
        )

        if not created:
            # TODO(epurkhiser): In this case we probably want to prompt and
            # warn them that they had a previous identity linked to slack.
            identity.update(
                external_id=params['slack_id'],
                status=IdentityStatus.VALID
            )

        payload = {
            'replace_original': False,
            'response_type': 'ephemeral',
            'text': "Your Slack identity has been linked to your Sentry account. You're good to go!"
        }

        session = http.build_session()
        req = session.post(params['response_url'], json=payload)
        resp = req.json()

        # If the user took their time to link their slack account, we may no
        # longer be able to respond, and we're not guaranteed able to post into
        # the channel. Ignore Expired url errors.
        #
        # XXX(epurkhiser): Yes the error string has a space in it.
        if not resp.get('ok') and resp.get('error') != 'Expired url':
            logger.error('slack.link-notify.response-error', extra={'response': resp})

        return render_to_response('sentry/slack-linked.html', request=request, context={
            'channel_id': params['channel_id'],
            'team_id': integration.external_id,
        })
Ejemplo n.º 19
0
    def handle(self, request, signed_params):
        params = unsign(signed_params.encode("ascii", errors="ignore"))

        try:
            organization = Organization.objects.get(
                id__in=request.user.get_orgs(), id=params["organization_id"])
        except Organization.DoesNotExist:
            raise Http404

        try:
            integration = Integration.objects.get(id=params["integration_id"],
                                                  organizations=organization)
        except Integration.DoesNotExist:
            raise Http404

        try:
            idp = IdentityProvider.objects.get(
                external_id=integration.external_id, type="slack")
        except IdentityProvider.DoesNotExist:
            raise Http404

        if request.method != "POST":
            return render_to_response(
                "sentry/auth-link-identity.html",
                request=request,
                context={
                    "organization": organization,
                    "provider": integration.get_provider()
                },
            )

        # TODO(epurkhiser): We could do some fancy slack querying here to
        # render a nice linking page with info about the user their linking.

        # Link the user with the identity. Handle the case where the user is linked to a
        # different identity or the identity is linked to a different user.
        defaults = {
            "status": IdentityStatus.VALID,
            "date_verified": timezone.now()
        }
        try:
            identity, created = Identity.objects.get_or_create(
                idp=idp,
                user=request.user,
                external_id=params["slack_id"],
                defaults=defaults)
            if not created:
                identity.update(**defaults)
        except IntegrityError:
            Identity.reattach(idp, params["slack_id"], request.user, defaults)

        payload = {
            "replace_original":
            False,
            "response_type":
            "ephemeral",
            "text":
            "Your Slack identity has been linked to your Sentry account. You're good to go!",
        }

        session = http.build_session()
        req = session.post(params["response_url"], json=payload)
        status_code = req.status_code
        resp = req.json()

        # If the user took their time to link their slack account, we may no
        # longer be able to respond, and we're not guaranteed able to post into
        # the channel. Ignore Expired url errors.
        #
        # XXX(epurkhiser): Yes the error string has a space in it.
        if not resp.get("ok") and resp.get("error") != "Expired url":
            logger.error("slack.link-notify.response-error",
                         extra={"response": resp})
        track_response_code(status_code, resp.get("ok"))

        return render_to_response(
            "sentry/slack-linked.html",
            request=request,
            context={
                "channel_id": params["channel_id"],
                "team_id": integration.external_id
            },
        )
Ejemplo n.º 20
0
    def handle(self, request: Request, signed_params: str) -> HttpResponse:
        if request.method not in ALLOWED_METHODS:
            return render_error_page(request,
                                     body_text="HTTP 405: Method not allowed")

        try:
            params = unsign(signed_params)
        except (SignatureExpired, BadSignature):
            return render_to_response(
                "sentry/integrations/slack/expired-link.html",
                request=request,
            )

        integration = Integration.objects.get(id=params["integration_id"])
        organization_memberships = OrganizationMember.objects.get_for_integration(
            integration, request.user)
        # Filter to organizations where we have sufficient role.
        organizations = [
            organization_membership.organization
            for organization_membership in organization_memberships
            if is_valid_role(organization_membership)
        ]

        teams_by_id = {
            team.id: team
            for organization in organizations
            for team in Team.objects.get_for_user(organization, request.user)
        }

        channel_name = params["channel_name"]
        channel_id = params["channel_id"]
        form = SelectTeamForm(list(teams_by_id.values()), request.POST or None)

        if request.method == "GET":
            return self.respond(
                "sentry/integrations/slack/link-team.html",
                {
                    "form": form,
                    "teams": teams_by_id.values(),
                    "channel_name": channel_name,
                    "provider": integration.get_provider(),
                },
            )

        if not form.is_valid():
            return render_error_page(request,
                                     body_text="HTTP 400: Bad request")

        team_id = int(form.cleaned_data["team"])
        team = teams_by_id.get(team_id)
        if not team:
            return render_error_page(request,
                                     body_text="HTTP 404: Team does not exist")

        try:
            idp = IdentityProvider.objects.get(
                type="slack", external_id=integration.external_id)
        except IdentityProvider.DoesNotExist:
            logger.error("slack.action.invalid-team-id",
                         extra={"slack_id": integration.external_id})
            return render_error_page(request,
                                     body_text="HTTP 403: Invalid team ID")

        if not Identity.objects.filter(
                idp=idp, external_id=params["slack_id"]).exists():
            return render_error_page(
                request, body_text="HTTP 403: User identity does not exist")

        install = integration.get_installation(team.organization.id)
        external_team, created = ExternalActor.objects.get_or_create(
            actor_id=team.actor_id,
            organization=team.organization,
            integration=integration,
            provider=ExternalProviders.SLACK.value,
            defaults=dict(
                external_name=channel_name,
                external_id=channel_id,
            ),
        )

        analytics.record(
            "integrations.identity_linked",
            provider="slack",
            actor_id=team.actor_id,
            actor_type="team",
        )

        if not created:
            message = ALREADY_LINKED_MESSAGE.format(slug=team.slug)
            install.send_message(channel_id=channel_id, message=message)
            return render_to_response(
                "sentry/integrations/slack/post-linked-team.html",
                request=request,
                context={
                    "heading_text": ALREADY_LINKED_TITLE,
                    "body_text": message,
                    "channel_id": channel_id,
                    "team_id": integration.external_id,
                },
            )

        # Turn on notifications for all of a team's projects.
        NotificationSetting.objects.update_settings(
            ExternalProviders.SLACK,
            NotificationSettingTypes.ISSUE_ALERTS,
            NotificationSettingOptionValues.ALWAYS,
            team=team,
        )
        message = SUCCESS_LINKED_MESSAGE.format(slug=team.slug,
                                                channel_name=channel_name)
        install.send_message(channel_id=channel_id, message=message)
        return render_to_response(
            "sentry/integrations/slack/post-linked-team.html",
            request=request,
            context={
                "heading_text": SUCCESS_LINKED_TITLE,
                "body_text": message,
                "channel_id": channel_id,
                "team_id": integration.external_id,
            },
        )
    def handle(self, request, *args, **kwargs):
        try:
            d = request.GET['data']
        except KeyError:
            raise Http404

        try:
            data = unsign(force_str(d))
        except BadSignature:
            messages.add_message(
                request, messages.ERROR,
                _(u'Could not approve transfer, please make sure link is valid.'
                  ))
            return HttpResponseRedirect(reverse('sentry'))
        except SignatureExpired:
            messages.add_message(request, messages.ERROR,
                                 _(u'Project transfer link has expired!'))
            return HttpResponseRedirect(reverse('sentry'))

        project_id = data['project_id']
        user_id = data['user_id']
        transaction_id = data['transaction_id']
        from_organization_id = data['from_organization_id']
        if user_id != request.user.id:
            messages.add_message(request, messages.ERROR,
                                 _(u'Invalid permissions!'))
            return HttpResponseRedirect(reverse('sentry'))

        # check if user is still an owner
        if not OrganizationMember.objects.filter(
                role=roles.get_top_dog().id,
                user__is_active=True,
                user_id=user_id,
        ).exists():
            return HttpResponseRedirect(reverse('sentry'))

        try:
            project = Project.objects.get(id=project_id,
                                          organization_id=from_organization_id)
        except Project.DoesNotExist:
            messages.add_message(request, messages.ERROR,
                                 _(u'Project no longer exists'))
            return HttpResponseRedirect(reverse('sentry'))

        form = self.get_form(request)
        if form.is_valid():
            # transfer the project
            team_id = form.cleaned_data.get('team')
            new_team = Team.objects.get(id=team_id)
            project.transfer_to(new_team)

            self.create_audit_entry(
                request=request,
                organization=project.organization,
                target_object=project_id,
                event=AuditLogEntryEvent.PROJECT_ACCEPT_TRANSFER,
                data=project.get_audit_log_data(),
                transaction_id=transaction_id,
            )

            return HttpResponseRedirect(
                reverse('sentry-organization-home',
                        args=[new_team.organization.slug]))

        context = {
            'project': project,
            'form': form,
        }
        return self.respond('sentry/projects/accept_project_transfer.html',
                            context)
Ejemplo n.º 22
0
    def handle(self, request, signed_params):
        params = unsign(signed_params.encode('ascii', errors='ignore'))

        try:
            organization = Organization.objects.get(
                id__in=request.user.get_orgs(),
                id=params['organization_id'],
            )
        except Organization.DoesNotExist:
            raise Http404

        try:
            integration = Integration.objects.get(
                id=params['integration_id'],
                organizations=organization,
            )
        except Integration.DoesNotExist:
            raise Http404

        try:
            idp = IdentityProvider.objects.get(
                type='slack',
                organization=organization,
            )
        except Integration.DoesNotExist:
            raise Http404

        if request.method != 'POST':
            return render_to_response('sentry/auth-link-identity.html',
                                      request=request,
                                      context={
                                          'organization': organization,
                                          'provider':
                                          integration.get_provider(),
                                      })

        # TODO(epurkhiser): We could do some fancy slack querying here to
        # render a nice linking page with info about the user their linking.

        Identity.objects.get_or_create(
            external_id=params['slack_id'],
            user=request.user,
            idp=idp,
            status=IdentityStatus.VALID,
        )

        payload = {
            'replace_original':
            False,
            'response_type':
            'ephemeral',
            'text':
            "Your Slack identity has been linked to your Sentry account. You're good to go!"
        }

        session = http.build_session()
        req = session.post(params['response_url'], json=payload)
        resp = req.json()
        if not resp.get('ok'):
            logger.error('slack.link-notify.response-error',
                         extra={
                             'error': resp.get('error'),
                         })

        return render_to_response('sentry/slack-linked.html',
                                  request=request,
                                  context={
                                      'channel_id': params['channel_id'],
                                      'team_id': integration.external_id,
                                  })
Ejemplo n.º 23
0
    def handle(self, request: Request, signed_params: str) -> HttpResponse:
        if request.method not in ALLOWED_METHODS:
            return render_error_page(request,
                                     body_text="HTTP 405: Method not allowed")

        params = unsign(signed_params)
        integration = Integration.objects.get(id=params["integration_id"])
        organization = integration.organizations.all()[0]
        teams = Team.objects.get_for_user(organization, request.user)
        channel_name = params["channel_name"]
        channel_id = params["channel_id"]
        form = SelectTeamForm(teams, request.POST or None)

        if request.method == "GET":
            return self.respond(
                "sentry/integrations/slack-link-team.html",
                {
                    "form": form,
                    "teams": teams,
                    "channel_name": channel_name,
                    "provider": integration.get_provider(),
                },
            )

        if not form.is_valid():
            return render_error_page(request,
                                     body_text="HTTP 400: Bad request")

        team_id = form.cleaned_data["team"]
        try:
            team = Team.objects.get(id=team_id, organization=organization)
        except Team.DoesNotExist:
            return render_error_page(request,
                                     body_text="HTTP 404: Team does not exist")

        try:
            idp = IdentityProvider.objects.get(
                type="slack", external_id=integration.external_id)
        except IdentityProvider.DoesNotExist:
            logger.error("slack.action.invalid-team-id",
                         extra={"slack_id": integration.external_id})
            return render_error_page(request,
                                     body_text="HTTP 403: Invalid team ID")

        try:
            identity = Identity.objects.select_related("user").get(
                idp=idp, external_id=params["slack_id"])
        except Identity.DoesNotExist:
            logger.error("slack.action.missing-identity",
                         extra={"slack_id": integration.external_id})
            return render_error_page(
                request, body_text="HTTP 403: User identity does not exist")

        org_member = OrganizationMember.objects.get(user=identity.user,
                                                    organization=organization)

        if not is_valid_role(org_member, team, organization):
            return send_confirmation(
                integration,
                channel_id,
                INSUFFICIENT_ROLE_TITLE,
                INSUFFICIENT_ROLE_MESSAGE,
                "sentry/integrations/slack-post-linked-team.html",
                request,
            )
        external_team, created = ExternalActor.objects.get_or_create(
            actor_id=team.actor_id,
            organization=organization,
            integration=integration,
            provider=ExternalProviders.SLACK.value,
            defaults=dict(
                external_name=channel_name,
                external_id=channel_id,
            ),
        )

        if not created:
            return send_confirmation(
                integration,
                channel_id,
                ALREADY_LINKED_TITLE,
                ALREADY_LINKED_MESSAGE.format(slug=team.slug),
                "sentry/integrations/slack-post-linked-team.html",
                request,
            )

        # Turn on notifications for all of a team's projects.
        NotificationSetting.objects.update_settings(
            ExternalProviders.SLACK,
            NotificationSettingTypes.ISSUE_ALERTS,
            NotificationSettingOptionValues.ALWAYS,
            team=team,
        )
        return send_confirmation(
            integration,
            channel_id,
            SUCCESS_LINKED_TITLE,
            SUCCESS_LINKED_MESSAGE.format(slug=team.slug,
                                          channel_name=channel_name),
            "sentry/integrations/slack-post-linked-team.html",
            request,
        )
Ejemplo n.º 24
0
    def handle(self, request, signed_params):
        params = unsign(signed_params.encode('ascii', errors='ignore'))

        try:
            organization = Organization.objects.get(
                id__in=request.user.get_orgs(),
                id=params['organization_id'],
            )
        except Organization.DoesNotExist:
            raise Http404

        try:
            integration = Integration.objects.get(
                id=params['integration_id'],
                organizations=organization,
            )
        except Integration.DoesNotExist:
            raise Http404

        try:
            idp = IdentityProvider.objects.get(
                external_id=integration.external_id,
                type='slack',
            )
        except IdentityProvider.DoesNotExist:
            raise Http404

        if request.method != 'POST':
            return render_to_response('sentry/auth-link-identity.html',
                                      request=request,
                                      context={
                                          'organization': organization,
                                          'provider':
                                          integration.get_provider(),
                                      })

        # TODO(epurkhiser): We could do some fancy slack querying here to
        # render a nice linking page with info about the user their linking.

        # Link the user with the identity. Handle the case where the user is linked to a
        # different identity or the identity is linked to a different user.
        try:
            id_by_user = Identity.objects.get(user=request.user, idp=idp)
        except Identity.DoesNotExist:
            id_by_user = None
        try:
            id_by_external_id = Identity.objects.get(
                external_id=params['slack_id'], idp=idp)
        except Identity.DoesNotExist:
            id_by_external_id = None

        if not id_by_user and not id_by_external_id:
            Identity.objects.create(
                user=request.user,
                external_id=params['slack_id'],
                idp=idp,
                status=IdentityStatus.VALID,
            )
        elif id_by_user and not id_by_external_id:
            # TODO(epurkhiser): In this case we probably want to prompt and
            # warn them that they had a previous identity linked to slack.
            id_by_user.update(
                external_id=params['slack_id'],
                status=IdentityStatus.VALID,
            )
        elif id_by_external_id and not id_by_user:
            id_by_external_id.update(
                user=request.user,
                status=IdentityStatus.VALID,
            )
        else:
            updates = {'status': IdentityStatus.VALID}
            if id_by_user != id_by_external_id:
                id_by_external_id.delete()
                updates['external_id'] = params['slack_id']
            id_by_user.update(**updates)

        payload = {
            'replace_original':
            False,
            'response_type':
            'ephemeral',
            'text':
            "Your Slack identity has been linked to your Sentry account. You're good to go!"
        }

        session = http.build_session()
        req = session.post(params['response_url'], json=payload)
        resp = req.json()

        # If the user took their time to link their slack account, we may no
        # longer be able to respond, and we're not guaranteed able to post into
        # the channel. Ignore Expired url errors.
        #
        # XXX(epurkhiser): Yes the error string has a space in it.
        if not resp.get('ok') and resp.get('error') != 'Expired url':
            logger.error('slack.link-notify.response-error',
                         extra={'response': resp})

        return render_to_response('sentry/slack-linked.html',
                                  request=request,
                                  context={
                                      'channel_id': params['channel_id'],
                                      'team_id': integration.external_id,
                                  })
Ejemplo n.º 25
0
    def handle(self, request: Request, signed_params: str) -> Response:
        params = unsign(signed_params)

        organization, integration, idp = get_identity(
            request.user, params["organization_id"], params["integration_id"]
        )
        channel_name = params["channel_name"]
        channel_id = params["channel_id"]

        try:
            external_team = ExternalActor.objects.get(
                organization=organization,
                integration=integration,
                provider=ExternalProviders.SLACK.value,
                external_name=channel_name,
                external_id=channel_id,
            )
        except IntegrityError as e:
            logger.error("slack.team.unlink.integrity-error", extra=e)
            raise Http404

        team = Team.objects.get(actor=external_team.actor)
        if request.method != "POST":
            return render_to_response(
                "sentry/integrations/slack-unlink-team.html",
                request=request,
                context={
                    "team": team,
                    "channel_name": channel_name,
                    "provider": integration.get_provider(),
                },
            )

        try:
            idp = IdentityProvider.objects.get(type="slack", external_id=integration.external_id)
        except IdentityProvider.DoesNotExist:
            logger.error(
                "slack.action.invalid-team-id", extra={"slack_id": integration.external_id}
            )
            return render_error_page(request, body_text="HTTP 403: Invalid team ID")

        try:
            identity = Identity.objects.select_related("user").get(
                idp=idp, external_id=params["slack_id"]
            )
        except Identity.DoesNotExist:
            logger.error(
                "slack.action.missing-identity", extra={"slack_id": integration.external_id}
            )
            return render_error_page(request, body_text="HTTP 403: User identity does not exist")

        org_member = OrganizationMember.objects.get(user=identity.user, organization=organization)

        if not is_valid_role(org_member, team, organization):
            return send_confirmation(
                integration,
                channel_id,
                INSUFFICIENT_ROLE_TITLE,
                INSUFFICIENT_ROLE_MESSAGE,
                "sentry/integrations/slack-post-linked-team.html",
                request,
            )

        external_team.delete()
        NotificationSetting.objects.remove_for_team(team, ExternalProviders.SLACK)

        return send_confirmation(
            integration,
            channel_id,
            SUCCESS_UNLINKED_TITLE,
            SUCCESS_UNLINKED_MESSAGE.format(team=team.slug),
            "sentry/integrations/slack-unlinked-team.html",
            request,
        )