示例#1
0
    def test_send_request_notification_email(self):
        organization = self.create_organization()

        user1 = self.create_user(email="manager@localhost")
        user2 = self.create_user(email="owner@localhost")
        user3 = self.create_user(email="member@localhost")

        self.create_member(organization=organization,
                           user=user1,
                           role="manager")
        self.create_member(organization=organization, user=user2, role="owner")
        self.create_member(organization=organization,
                           user=user3,
                           role="member")

        member = OrganizationMember(
            id=1,
            role="manager",
            organization=organization,
            email="*****@*****.**",
            inviter=user3,
            invite_status=InviteStatus.REQUESTED_TO_BE_INVITED.value,
        )
        with self.options({"system.url-prefix":
                           "http://example.com"}), self.tasks():
            member.send_request_notification_email()

        assert len(mail.outbox) == 2

        assert mail.outbox[0].to == ["manager@localhost"]
        assert mail.outbox[1].to == ["owner@localhost"]

        expected_subject = "Access request to %s" % (organization.name, )
        assert mail.outbox[0].subject == expected_subject
示例#2
0
    def serialize(self, obj: OrganizationMember, attrs: Mapping[str, Any],
                  user: Any, **kwargs: Any) -> MutableMapping[str, JSONData]:
        d = {
            "id": str(obj.id),
            "email": obj.get_email(),
            "name":
            obj.user.get_display_name() if obj.user else obj.get_email(),
            "user": attrs["user"],
            "role": obj.role,
            "roleName": roles.get(obj.role).name,
            "pending": obj.is_pending,
            "expired": obj.token_expired,
            "flags": {
                "sso:linked":
                bool(getattr(obj.flags, "sso:linked")),
                "sso:invalid":
                bool(getattr(obj.flags, "sso:invalid")),
                "member-limit:restricted":
                bool(getattr(obj.flags, "member-limit:restricted")),
            },
            "dateCreated": obj.date_added,
            "inviteStatus": obj.get_invite_status_name(),
            "inviterName":
            obj.inviter.get_display_name() if obj.inviter else None,
        }

        if "externalUsers" in self.expand:
            d["externalUsers"] = attrs.get("externalUsers", [])

        return d
示例#3
0
def openid_login_callback(request):
    #构造需要检查签名的内容
    OPENID_RESPONSE = dict(request.GET)
    SIGNED_CONTENT = []
    #import json
    #print json.dumps(OPENID_RESPONSE, indent=4)
    for k in OPENID_RESPONSE['openid.signed'][0].split(","):
        response_data = OPENID_RESPONSE["openid.%s" % k]
        SIGNED_CONTENT.append("%s:%s\n" % (k, response_data[0]))
    SIGNED_CONTENT = "".join(SIGNED_CONTENT).encode("UTF-8")
    # 使用associate请求获得的mac_key与SIGNED_CONTENT进行assoc_type hash,
    # 检查是否与OpenID Server返回的一致
    SIGNED_CONTENT_SIG = base64.b64encode(
        hmac.new(base64.b64decode(request.session.get('mac_key', '')),
                 SIGNED_CONTENT, hashlib.sha256).digest())

    if SIGNED_CONTENT_SIG != OPENID_RESPONSE['openid.sig'][0]:
        return '认证失败,请重新登录验证'

    request.session.pop('mac_key', None)
    email = request.GET.get('openid.sreg.email', '')
    fullname = request.GET.get('openid.sreg.fullname', '')
    next_url = request.GET.get('next', '/')

    login_user = User.objects.filter(username__iexact=email)
    if login_user.exists():
        login_user = login_user[0]
        login_user.set_password("sentry_netease_openid_pwd")
        login_user.name = fullname  # update by hzwangzhiwei @20160329
        login_user.save()
    else:
        #不存在数据,则增加数据数用户表
        login_user = User(username=email, name=fullname, email=email)
        login_user.set_password("sentry_netease_openid_pwd")
        login_user.save()  #save to db

    # 如果不存在将这个人加入到组织member表中
    if not OrganizationMember.objects.filter(
            user=login_user, organization=Organization.get_default()).exists():
        # 同时给他们默认的trace收集
        # 将用户到组织中
        orgMember = OrganizationMember(user=login_user,
                                       organization=Organization.get_default())
        orgMember.save()
        orgMember = OrganizationMember.objects.get(
            user=login_user, organization=Organization.get_default())
        # 保存组织者到第一个小组
        orgMemTeam = OrganizationMemberTeam(organizationmember=orgMember,
                                            team=Team.objects.get(id=1))
        orgMemTeam.save()

    # HACK: grab whatever the first backend is and assume it works
    login_user.backend = settings.AUTHENTICATION_BACKENDS[0]

    auth.login(request, login_user)
    # can_register should only allow a single registration
    request.session.pop('can_register', None)
    request.session.pop('needs_captcha', None)
    return HttpResponseRedirect(next_url)
示例#4
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 == ["*****@*****.**"]
示例#5
0
 def test_delete_expired_leave_null_expires(self):
     organization = self.create_organization()
     member = OrganizationMember.objects.create(
         organization=organization,
         role="member",
         email="*****@*****.**",
         token="abc-def",
         token_expires_at=None,
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.get(id=member.id)
    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()
示例#7
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()
 def test_delete_expired_leave_null_expires(self):
     organization = self.create_organization()
     member = OrganizationMember.objects.create(
         organization=organization,
         role='member',
         email='*****@*****.**',
         token='abc-def',
         token_expires_at=None
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.get(id=member.id)
示例#9
0
    def post(self, request, organization):
        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)
    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 == ['*****@*****.**']
示例#11
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 == ['*****@*****.**']
示例#12
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)
    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 == ['*****@*****.**']
示例#14
0
 def test_delete_expired_clear(self):
     organization = self.create_organization()
     ninety_one_days = timezone.now() - timedelta(days=1)
     member = OrganizationMember.objects.create(
         organization=organization,
         role="member",
         email="*****@*****.**",
         token="abc-def",
         token_expires_at=ninety_one_days,
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.filter(id=member.id).first() is None
示例#15
0
 def test_delete_expired_miss(self):
     organization = self.create_organization()
     tomorrow = timezone.now() + timedelta(days=1)
     member = OrganizationMember.objects.create(
         organization=organization,
         role='member',
         email='*****@*****.**',
         token='abc-def',
         token_expires_at=tomorrow
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.get(id=member.id)
示例#16
0
 def test_delete_expired_clear(self):
     organization = self.create_organization()
     ninety_one_days = timezone.now() - timedelta(days=1)
     member = OrganizationMember.objects.create(
         organization=organization,
         role='member',
         email='*****@*****.**',
         token='abc-def',
         token_expires_at=ninety_one_days
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.filter(id=member.id).first() is None
示例#17
0
 def test_delete_expired_miss(self):
     organization = self.create_organization()
     tomorrow = timezone.now() + timedelta(days=1)
     member = OrganizationMember.objects.create(
         organization=organization,
         role="member",
         email="*****@*****.**",
         token="abc-def",
         token_expires_at=tomorrow,
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.get(id=member.id)
示例#18
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()
示例#19
0
 def test_delete_expired_leave_claimed(self):
     user = self.create_user()
     organization = self.create_organization()
     member = OrganizationMember.objects.create(
         organization=organization,
         role="member",
         user=user,
         email="*****@*****.**",
         token="abc-def",
         token_expires_at="2018-01-01 10:00:00",
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.get(id=member.id)
    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 == ['*****@*****.**']
示例#21
0
 def test_delete_expired_leave_claimed(self):
     user = self.create_user()
     organization = self.create_organization()
     member = OrganizationMember.objects.create(
         organization=organization,
         role='member',
         user=user,
         email='*****@*****.**',
         token='abc-def',
         token_expires_at='2018-01-01 10:00:00'
     )
     OrganizationMember.delete_expired(timezone.now())
     assert OrganizationMember.objects.get(id=member.id)
示例#22
0
def from_member(
    member: OrganizationMember,
    scopes: Optional[Iterable[str]] = None,
    is_superuser: bool = False,
) -> Access:
    if scopes is not None:
        scopes = set(scopes) & member.get_scopes()
    else:
        scopes = member.get_scopes()

    permissions = get_permissions_for_user(
        member.user_id) if is_superuser else frozenset()

    return OrganizationMemberAccess(member, scopes, permissions)
    def get(self, request: Request) -> Response:
        org = Organization(id=1, slug="default", name="Default")
        requester = User(name="Rick Swan")
        pending_member = OrganizationMember(email="*****@*****.**",
                                            organization=org,
                                            inviter=requester)
        recipient = User(name="James Bond")
        recipient_member = OrganizationMember(user=recipient, organization=org)

        notification = InviteRequestNotification(pending_member, requester)

        # hack to avoid a query
        notification.role_based_recipient_strategy.set_member_in_cache(
            recipient_member)
        return render_preview_email_for_notification(notification, recipient)
示例#24
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)
示例#25
0
    def serialize(
        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
    ) -> MutableMapping[str, JSONData]:

        d = {
            "schemas": [SCIM_SCHEMA_USER],
            "id": str(obj.id),
            "userName": obj.get_email(),  # TODO: does this get weird with secondary emails?
            "name": {"givenName": "N/A", "familyName": "N/A"},
            "emails": [
                {"primary": True, "value": obj.get_email(), "type": "work"}
            ],  # TODO: secondary emails?
            "active": True,
            "meta": {"resourceType": "User"},
        }
        return d
示例#26
0
    def serialize(
        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
    ) -> OrganizationMemberSCIMSerializerResponse:

        result: OrganizationMemberSCIMSerializerResponse = {
            "schemas": [SCIM_SCHEMA_USER],
            "id": str(obj.id),
            "userName": obj.get_email(),
            "name": {"givenName": "N/A", "familyName": "N/A"},
            "emails": [{"primary": True, "value": obj.get_email(), "type": "work"}],
            "meta": {"resourceType": "User"},
        }
        if "active" in self.expand:
            result["active"] = True

        return result
示例#27
0
 def test_legacy_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.legacy_token == "df41d9dfd4ba25d745321e654e15b5d0"
    def get(self, request: Request) -> Response:
        org = Organization(id=1, slug="default", name="Default")
        user_to_join = User(name="Rick Swan")
        pending_member = OrganizationMember(
            email="*****@*****.**",
            organization=org,
            user=user_to_join,
            invite_status=InviteStatus.REQUESTED_TO_JOIN.value,
        )
        recipient = User(name="James Bond")
        recipient_member = OrganizationMember(user=recipient, organization=org)

        notification = JoinRequestNotification(pending_member, user_to_join)

        # hack to avoid a query
        notification.role_based_recipient_strategy.set_member_in_cache(recipient_member)
        return render_preview_email_for_notification(notification, recipient)
示例#29
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)
示例#30
0
    def handle(self, request, organization_slug):
        try:
            organization = Organization.objects.get(slug=organization_slug)
        except Organization.DoesNotExist:
            messages.add_message(
                request,
                messages.ERROR,
                ERR_LINK_INVALID,
            )
            return self.redirect(reverse('sentry'))

        try:
            om = OrganizationMember.objects.get(
                organization=organization,
                user=request.user,
            )
        except OrganizationMember.DoesNotExist():
            messages.add_message(
                request,
                messages.ERROR,
                ERR_LINK_INVALID,
            )
            return self.redirect(reverse('sentry'))

        try:
            auth_provider = AuthProvider.objects.get(organization=organization)
        except AuthProvider.DoesNotExist:
            messages.add_message(
                request,
                messages.ERROR,
                ERR_LINK_INVALID,
            )
            return self.redirect(
                reverse('sentry-organization-home', args=[organization.slug]))

        if request.method == 'POST':
            helper = AuthHelper(
                request=request,
                organization=organization,
                auth_provider=auth_provider,
                flow=AuthHelper.FLOW_LINK_IDENTITY,
            )
            helper.init_pipeline()
            return helper.next_step()

        provider = auth_provider.get_provider()

        context = {
            'organization': organization,
            'provider_key': provider.key,
            'provider_name': provider.name,
        }

        return self.respond('sentry/auth-link-identity.html', context)
 def _get_member(self, organization, member_id):
     try:
         return OrganizationMember.objects.get(
             Q(invite_status=InviteStatus.REQUESTED_TO_BE_INVITED.value)
             | Q(invite_status=InviteStatus.REQUESTED_TO_JOIN.value),
             organization=organization,
             user__isnull=True,
             id=member_id,
         )
     except ValueError:
         raise OrganizationMember.DoesNotExist()
示例#32
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
示例#33
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)
示例#34
0
 def _get_member(
     self,
     request: Request,
     organization: Organization,
     member_id: int | str,
     invite_status: InviteStatus | None = None,
 ) -> OrganizationMember:
     try:
         return OrganizationMember.objects.get_member_invite_query(
             member_id).get(organization=organization)
     except ValueError:
         raise OrganizationMember.DoesNotExist()
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
示例#36
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
示例#37
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})
示例#38
0
 def call_to_action(self, org: Organization, user: User,
                    member: OrganizationMember):
     # send invite to setup 2fa
     email_context = {"url": member.get_invite_link(), "organization": org}
     subject = "{} {} Mandatory: Enable Two-Factor Authentication".format(
         options.get("mail.subject-prefix"), org.name.capitalize())
     message = MessageBuilder(
         subject=subject,
         template="sentry/emails/setup_2fa.txt",
         html_template="sentry/emails/setup_2fa.html",
         type="user.setup_2fa",
         context=email_context,
     )
     message.send_async([member.email])
示例#39
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
示例#40
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
示例#41
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()
    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)