예제 #1
0
    def post(self, request, organization):
        # TODO: confirm mixed case emails get converted to lowercase
        serializer = OrganizationMemberSerializer(
            data={
                "email": request.data.get("userName"),
                "role": roles.get(organization.default_role).id,
            },
            context={
                "organization": organization,
                "allowed_roles": [roles.get(organization.default_role)],
                "allow_existing_invite_request": True,
            },
        )

        if not serializer.is_valid():
            if "email" in serializer.errors and any(
                ("is already a member" in error)
                    for error in serializer.errors["email"]):
                # we include conflict logic in the serializer, check to see if that was
                # our error and if so, return a 409 so the scim IDP knows how to handle
                raise ConflictError(detail=SCIM_409_USER_EXISTS)
            return Response(serializer.errors, status=400)

        result = serializer.validated_data
        with transaction.atomic():
            member = OrganizationMember(
                organization=organization,
                email=result["email"],
                role=result["role"],
                inviter=request.user,
            )
            self.create_audit_entry(
                request=request,
                organization_id=organization.id,
                target_object=member.id,
                data=member.get_audit_log_data(),
                event=AuditLogEntryEvent.MEMBER_INVITE
                if settings.SENTRY_ENABLE_INVITES else
                AuditLogEntryEvent.MEMBER_ADD,
            )
            # TODO: are invite tokens needed for SAML orgs?
            if settings.SENTRY_ENABLE_INVITES:
                member.token = member.generate_token()
            member.save()

        if settings.SENTRY_ENABLE_INVITES and result.get("sendInvite"):
            member.send_invite_email()
            member_invited.send_robust(
                member=member,
                user=request.user,
                sender=self,
                referrer=request.data.get("referrer"),
            )

        context = serialize(member,
                            serializer=OrganizationMemberSCIMSerializer())
        return Response(context, status=201)
예제 #2
0
 def test_token_generation_unicode_key(self):
     member = OrganizationMember(id=1,
                                 organization_id=1,
                                 email='*****@*****.**')
     with self.settings(
             SECRET_KEY=
             "\xfc]C\x8a\xd2\x93\x04\x00\x81\xeak\x94\x02H\x1d\xcc&P'q\x12\xa2\xc0\xf2v\x7f\xbb*lX"
     ):
         assert member.token == 'df41d9dfd4ba25d745321e654e15b5d0'
예제 #3
0
    def test_token_expires_at_set_on_save(self):
        organization = self.create_organization()
        member = OrganizationMember(organization=organization, email="*****@*****.**")
        member.token = member.generate_token()
        member.save()

        expires_at = timezone.now() + timedelta(days=INVITE_DAYS_VALID)
        assert member.token_expires_at
        assert member.token_expires_at.date() == expires_at.date()
def sentry_update_organization_member(organization, user, role):
    model = sentry_find_organization_member(organization, user)
    existing = model is not None
    if not existing:
        model = OrganizationMember()
        model.user = user
        model.organization = organization
        model.role = role
        model.save()

    return model, existing
    def test_send_invite_email(self):
        organization = self.create_organization()
        member = OrganizationMember(id=1, organization=organization, email='*****@*****.**')
        with self.options({'system.url-prefix': 'http://example.com'}), self.tasks():
            member.send_invite_email()

        assert len(mail.outbox) == 1

        msg = mail.outbox[0]

        assert msg.to == ['*****@*****.**']
예제 #6
0
    def test_regenerate_token(self):
        organization = self.create_organization()
        member = OrganizationMember(organization=organization, email="*****@*****.**")
        assert member.token is None
        assert member.token_expires_at is None

        member.regenerate_token()
        assert member.token
        assert member.token_expires_at
        expires_at = timezone.now() + timedelta(days=INVITE_DAYS_VALID)
        assert member.token_expires_at.date() == expires_at.date()
예제 #7
0
    def get(self, request: Request) -> Response:
        org = Organization(id=1, slug="petal", name="Petal")
        project = Project(id=1,
                          slug="nodejs",
                          name="Node.js",
                          organization=org)
        user = User(name="Nisanthan")
        OrganizationMember(organization=org, user=user, role="admin")
        notification = AutoSyncNotification(project)

        return render_preview_email_for_notification(notification, user)
예제 #8
0
 def get(self, request):
     org = Organization(id=1, slug="organization", name="sentry corp")
     member = OrganizationMember(id=1,
                                 organization=org,
                                 email="*****@*****.**")
     context = {"url": member.get_invite_link(), "organization": org}
     return MailPreview(
         html_template="sentry/emails/setup_2fa.html",
         text_template="sentry/emails/setup_2fa.txt",
         context=context,
     ).render(request)
예제 #9
0
    def test_send_sso_link_email(self):
        organization = self.create_organization()
        member = OrganizationMember(id=1, organization=organization, email="*****@*****.**")
        with self.options({"system.url-prefix": "http://example.com"}), self.tasks():
            member.send_invite_email()

        assert len(mail.outbox) == 1

        msg = mail.outbox[0]

        assert msg.to == ["*****@*****.**"]
예제 #10
0
    def test_token_expiration(self):
        organization = self.create_organization()
        member = OrganizationMember(organization=organization, email="*****@*****.**")
        member.token = member.generate_token()
        member.save()

        assert member.is_pending
        assert member.token_expired is False

        member.token_expires_at = timezone.now() - timedelta(minutes=1)
        assert member.token_expired
예제 #11
0
    def test_set_user(self):
        organization = self.create_organization()
        member = OrganizationMember(organization=organization, email="*****@*****.**")
        member.token = member.generate_token()
        member.save()

        user = self.create_user(email="*****@*****.**")
        member.set_user(user)

        assert member.is_pending is False
        assert member.token_expires_at is None
        assert member.token is None
        assert member.email is None
예제 #12
0
    def get(self, request: Request) -> Response:
        requester_name = request.GET.get("requester_name", "Requester")
        recipient_name = request.GET.get("recipient_name", "Recipient")

        org = Organization(id=1, slug="petal", name="Petal")
        project = Project(id=1, slug="nodejs", name="Node.js", organization=org)
        user = User(name=recipient_name)
        member = OrganizationMember(organization=org, user=user, role="admin")
        preview = MailPreviewAdapter(
            **get_codeowners_request_builder_args(project, member, requester_name)
        )

        return render_to_response("sentry/debug/mail/preview.html", {"preview": preview})
예제 #13
0
    def test_send_sso_link_email(self):
        organization = self.create_organization()
        member = OrganizationMember(id=1,
                                    organization=organization,
                                    email='*****@*****.**')
        with self.settings(SENTRY_URL_PREFIX='http://example.com'):
            member.send_invite_email()

            assert len(mail.outbox) == 1

            msg = mail.outbox[0]

            assert msg.to == ['*****@*****.**']
예제 #14
0
def invitation(request):
    org = Organization(id=1, slug="example", name="Example")
    om = OrganizationMember(id=1, email="*****@*****.**", organization=org)

    return MailPreview(
        html_template="sentry/emails/member-invite.html",
        text_template="sentry/emails/member-invite.txt",
        context={
            "email": "*****@*****.**",
            "organization": org,
            "url": absolute_uri(
                reverse("sentry-accept-invite", kwargs={"member_id": om.id, "token": om.token})
            ),
        },
    ).render(request)
예제 #15
0
 def get(self, request):
     org = Organization(
         id=1,
         slug='organization',
         name='sentry corp',
     )
     member = OrganizationMember(
         id=1,
         organization=org,
         email='*****@*****.**')
     context = {
         'url': member.get_invite_link(),
         'organization': org
     }
     return MailPreview(
         html_template='sentry/emails/setup_2fa.html',
         text_template='sentry/emails/setup_2fa.txt',
         context=context,
     ).render(request)
예제 #16
0
def invitation(request):
    org = Organization(
        id=1,
        slug='example',
        name='Example',
    )
    om = OrganizationMember(
        id=1,
        email='*****@*****.**',
        organization=org,
    )

    return MailPreview(
        html_template='sentry/emails/member-invite.html',
        text_template='sentry/emails/member-invite.txt',
        context={
            'email': '*****@*****.**',
            'organization': org,
            'url': absolute_uri(reverse('sentry-accept-invite', kwargs={
                'member_id': om.id,
                'token': om.token,
            })),
        },
    ).render(request)
    def post(self, request, organization):
        """
        Add a Member to Organization
        ````````````````````````````

        Invite a member to the organization.

        :pparam string organization_slug: the slug of the organization the member will belong to
        :param string email: the email address to invite
        :param string role: the role of the new member
        :param array teams: the slugs of the teams the member should belong to.

        :auth: required
        """
        # TODO: If the member already exists, should this still update the role and team?
        # For now, it doesn't, but simply returns the existing object

        if not features.has('organizations:invite-members',
                            organization,
                            actor=request.user):
            return Response(
                {
                    'organization':
                    'Your organization is not allowed to invite members'
                },
                status=403)

        serializer = OrganizationMemberSerializer(data=request.DATA)

        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        result = serializer.object

        _, allowed_roles = get_allowed_roles(request, organization)

        # ensure listed teams are real teams
        teams = list(
            Team.objects.filter(
                organization=organization,
                status=TeamStatus.VISIBLE,
                slug__in=result['teams'],
            ))

        if len(set(result['teams'])) != len(teams):
            return Response({'teams': 'Invalid team'}, 400)

        if not result['role'] in {r.id for r in allowed_roles}:
            return Response(
                {'role': 'You do not have permission to invite that role.'},
                403)

        # This is needed because `email` field is case sensitive, but from a user perspective,
        # Sentry treats email as case-insensitive ([email protected] equals [email protected]).

        existing = OrganizationMember.objects.filter(
            organization=organization,
            user__email__iexact=result['email'],
            user__is_active=True,
        ).exists()

        if existing:
            return Response(
                {'email': 'The user %s is already a member' % result['email']},
                409)

        om = OrganizationMember(organization=organization,
                                email=result['email'],
                                role=result['role'])

        if settings.SENTRY_ENABLE_INVITES:
            om.token = om.generate_token()

        try:
            with transaction.atomic():
                om.save()
        except IntegrityError:
            return Response(
                {'email': 'The user %s is already a member' % result['email']},
                409)

        lock = locks.get(u'org:member:{}'.format(om.id), duration=5)
        with TimedRetryPolicy(10)(lock.acquire):
            self.save_team_assignments(om, teams)

        if settings.SENTRY_ENABLE_INVITES:
            om.send_invite_email()
            member_invited.send_robust(member=om,
                                       user=request.user,
                                       sender=self,
                                       referrer=request.DATA.get('referrer'))

        self.create_audit_entry(
            request=request,
            organization_id=organization.id,
            target_object=om.id,
            data=om.get_audit_log_data(),
            event=AuditLogEntryEvent.MEMBER_INVITE if
            settings.SENTRY_ENABLE_INVITES else AuditLogEntryEvent.MEMBER_ADD,
        )

        return Response(serialize(om), status=201)
예제 #18
0
    def post(self, request: Request, organization) -> Response:
        """
        Create a new Organization Member via a SCIM Users POST Request.
        - `userName` should be set to the SAML field used for email, and active should be set to `true`.
        - Sentry's SCIM API doesn't currently support setting users to inactive,
        and the member will be deleted if inactive is set to `false`.
        - The API also does not support setting secondary emails.
        """

        serializer = OrganizationMemberSerializer(
            data={
                "email": request.data.get("userName"),
                "role": roles.get(organization.default_role).id,
            },
            context={
                "organization": organization,
                "allowed_roles": [roles.get(organization.default_role)],
                "allow_existing_invite_request": True,
            },
        )

        if not serializer.is_valid():
            if "email" in serializer.errors and any(
                ("is already a member" in error)
                    for error in serializer.errors["email"]):
                # we include conflict logic in the serializer, check to see if that was
                # our error and if so, return a 409 so the scim IDP knows how to handle
                raise ConflictError(detail=SCIM_409_USER_EXISTS)
            return Response(serializer.errors, status=400)

        result = serializer.validated_data
        with transaction.atomic():
            member = OrganizationMember(
                organization=organization,
                email=result["email"],
                role=result["role"],
                inviter=request.user,
            )

            # TODO: are invite tokens needed for SAML orgs?
            if settings.SENTRY_ENABLE_INVITES:
                member.token = member.generate_token()
            member.save()

        self.create_audit_entry(
            request=request,
            organization_id=organization.id,
            target_object=member.id,
            data=member.get_audit_log_data(),
            event=AuditLogEntryEvent.MEMBER_INVITE if
            settings.SENTRY_ENABLE_INVITES else AuditLogEntryEvent.MEMBER_ADD,
        )

        if settings.SENTRY_ENABLE_INVITES and result.get("sendInvite"):
            member.send_invite_email()
            member_invited.send_robust(
                member=member,
                user=request.user,
                sender=self,
                referrer=request.data.get("referrer"),
            )

        context = serialize(
            member,
            serializer=_scim_member_serializer_with_expansion(organization),
        )
        return Response(context, status=201)
예제 #19
0
 def test_legacy_token_generation(self):
     member = OrganizationMember(id=1,
                                 organization_id=1,
                                 email="*****@*****.**")
     with self.settings(SECRET_KEY="a"):
         assert member.legacy_token == "f3f2aa3e57f4b936dfd4f42c38db003e"
    def post(self, request, organization):
        """
        Add a Member to Organization
        ````````````````````````````

        Invite a member to the organization.

        :pparam string organization_slug: the slug of the organization the member will belong to
        :param string email: the email address to invite
        :param string role: the role of the new member
        :param array teams: the slugs of the teams the member should belong to.

        :auth: required
        """
        if not features.has("organizations:invite-members",
                            organization,
                            actor=request.user):
            return Response(
                {
                    "organization":
                    "Your organization is not allowed to invite members"
                },
                status=403)

        _, allowed_roles = get_allowed_roles(request, organization)

        serializer = OrganizationMemberSerializer(
            data=request.data,
            context={
                "organization": organization,
                "allowed_roles": allowed_roles,
                "allow_existing_invite_request": True,
            },
        )

        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        result = serializer.validated_data

        with transaction.atomic():
            # remove any invitation requests for this email before inviting
            OrganizationMember.objects.filter(
                Q(invite_status=InviteStatus.REQUESTED_TO_BE_INVITED.value)
                | Q(invite_status=InviteStatus.REQUESTED_TO_JOIN.value),
                email=result["email"],
                organization=organization,
            ).delete()

            om = OrganizationMember(
                organization=organization,
                email=result["email"],
                role=result["role"],
                inviter=request.user,
            )

            if settings.SENTRY_ENABLE_INVITES:
                om.token = om.generate_token()
            om.save()

        if result["teams"]:
            lock = locks.get(u"org:member:{}".format(om.id), duration=5)
            with TimedRetryPolicy(10)(lock.acquire):
                save_team_assignments(om, result["teams"])

        if settings.SENTRY_ENABLE_INVITES and result.get("sendInvite"):
            om.send_invite_email()
            member_invited.send_robust(member=om,
                                       user=request.user,
                                       sender=self,
                                       referrer=request.data.get("referrer"))

        self.create_audit_entry(
            request=request,
            organization_id=organization.id,
            target_object=om.id,
            data=om.get_audit_log_data(),
            event=AuditLogEntryEvent.MEMBER_INVITE if
            settings.SENTRY_ENABLE_INVITES else AuditLogEntryEvent.MEMBER_ADD,
        )

        return Response(serialize(om), status=201)
예제 #21
0
configure()

# Create the org, team and project if needed
from sentry.models import Team, Project, ProjectKey, User, Organization, OrganizationMember

user = User.objects.get(pk=1)

name = 'AgoraVoting'
name2 = 'AuthApi'

if Organization.objects.filter(name=name).count() == 0:
    organization = Organization()
    organization.name = name
    organization.save()

    om = OrganizationMember()
    om.organization = organization
    om.role = 'owner'
    om.user = user
    om.save()

    team = Team()
    team.name = name
    team.organization = organization
    team.save()

    project = Project()
    project.team = team
    project.name = name2
    project.organization = organization
    project.save()
예제 #22
0
 def test_token_generation(self):
     member = OrganizationMember(id=1,
                                 organization_id=1,
                                 email='*****@*****.**')
     with self.settings(SECRET_KEY='a'):
         assert member.token == 'f3f2aa3e57f4b936dfd4f42c38db003e'