Example #1
0
    def test_by_organization(self):
        organization = Organization(id=1)
        for n in range(100):
            assert not ratelimits.for_organization_member_invite(
                organization, "{}@example.com".format(randint(0, 1000000)))

        assert ratelimits.for_organization_member_invite(
            organization, "*****@*****.**")
Example #2
0
    def test_by_email(self):
        organization = Organization(id=1)
        email = "*****@*****.**"
        for n in range(10):
            assert not ratelimits.for_organization_member_invite(
                organization, email)

        assert ratelimits.for_organization_member_invite(organization, email)
Example #3
0
    def test_by_email(self):
        organization = Organization(id=1)
        email = "*****@*****.**"
        for n in range(2):
            assert not ratelimits.for_organization_member_invite(
                organization, email, config=RELAXED_CONFIG)

        assert ratelimits.for_organization_member_invite(organization,
                                                         email,
                                                         config=RELAXED_CONFIG)
Example #4
0
    def test_by_organization(self):
        organization = Organization(id=1)
        for n in range(5):
            assert not ratelimits.for_organization_member_invite(
                organization,
                f"{randint(0, 1000000)}@example.com",
                config=RELAXED_CONFIG)

        assert ratelimits.for_organization_member_invite(
            organization, "*****@*****.**", config=RELAXED_CONFIG)
Example #5
0
    def test_by_user(self):
        user = User(email="*****@*****.**")
        for n in range(100):
            assert not ratelimits.for_organization_member_invite(
                Organization(id=randint(0, 100000)),
                "{}@example.com".format(randint(0, 1000000)),
                user=user,
            )

        assert ratelimits.for_organization_member_invite(
            Organization(id=1), "*****@*****.**")
Example #6
0
    def test_by_api_token(self):
        token = ApiToken()
        for n in range(100):
            assert not ratelimits.for_organization_member_invite(
                Organization(id=randint(0, 100000)),
                "{}@example.com".format(randint(0, 1000000)),
                auth=token,
            )

        assert ratelimits.for_organization_member_invite(
            Organization(id=1), "*****@*****.**")
Example #7
0
    def test_by_user(self):
        user = User(email="*****@*****.**")
        for n in range(5):
            assert not ratelimits.for_organization_member_invite(
                Organization(id=randint(0, 100000)),
                f"{randint(0, 1000000)}@example.com",
                user=user,
                config=RELAXED_CONFIG,
            )

        assert ratelimits.for_organization_member_invite(
            Organization(id=1),
            "*****@*****.**",
            user=user,
            config=RELAXED_CONFIG)
Example #8
0
    def test_by_api_token(self):
        token = ApiToken(id=1)
        for n in range(5):
            assert not ratelimits.for_organization_member_invite(
                Organization(id=randint(0, 100000)),
                f"{randint(0, 1000000)}@example.com",
                auth=token,
                config=RELAXED_CONFIG,
            )

        assert ratelimits.for_organization_member_invite(
            Organization(id=1),
            "*****@*****.**",
            auth=token,
            config=RELAXED_CONFIG)
    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

        if ratelimits.for_organization_member_invite(
            organization=organization,
            email=result["email"],
            user=request.user,
            auth=request.auth,
        ):
            metrics.incr(
                "member-invite.attempt",
                instance="rate_limited",
                skip_internal=True,
                sample_rate=1.0,
            )
            return Response({"detail": ERR_RATE_LIMITED}, status=429)

        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(f"org:member:{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)
    def put(self, request, organization, member_id):
        try:
            om = self._get_member(request, organization, member_id)
        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist

        serializer = OrganizationMemberSerializer(data=request.data,
                                                  partial=True)

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

        try:
            auth_provider = AuthProvider.objects.get(organization=organization)
            auth_provider = auth_provider.get_provider()
        except AuthProvider.DoesNotExist:
            auth_provider = None

        allowed_roles = None
        result = serializer.validated_data

        # XXX(dcramer): if/when this expands beyond reinvite we need to check
        # access level
        if result.get("reinvite"):
            if om.is_pending:
                if ratelimits.for_organization_member_invite(
                        organization=organization,
                        email=om.email,
                        user=request.user,
                        auth=request.auth,
                ):
                    metrics.incr(
                        "member-invite.attempt",
                        instance="rate_limited",
                        skip_internal=True,
                        sample_rate=1.0,
                    )
                    return Response({"detail": ERR_RATE_LIMITED}, status=429)

                if result.get("regenerate"):
                    if request.access.has_scope("member:admin"):
                        om.regenerate_token()
                        om.save()
                    else:
                        return Response({"detail": ERR_INSUFFICIENT_SCOPE},
                                        status=400)
                if om.token_expired:
                    return Response({"detail": ERR_EXPIRED}, status=400)
                om.send_invite_email()
            elif auth_provider and not getattr(om.flags, "sso:linked"):
                om.send_sso_link_email(request.user, auth_provider)
            else:
                # TODO(dcramer): proper error message
                return Response({"detail": ERR_UNINVITABLE}, status=400)

        if "teams" in result:
            # dupe code from member_index
            # 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"}, status=400)

            with transaction.atomic():
                # teams may be empty
                OrganizationMemberTeam.objects.filter(
                    organizationmember=om).delete()
                OrganizationMemberTeam.objects.bulk_create([
                    OrganizationMemberTeam(team=team, organizationmember=om)
                    for team in teams
                ])

        if result.get("role"):
            _, allowed_roles = get_allowed_roles(request, organization)
            allowed_role_ids = {r.id for r in allowed_roles}

            # A user cannot promote others above themselves
            if result["role"] not in allowed_role_ids:
                return Response(
                    {
                        "role":
                        "You do not have permission to assign the given role."
                    },
                    status=403)

            # A user cannot demote a superior
            if om.role not in allowed_role_ids:
                return Response(
                    {
                        "role":
                        "You do not have permission to assign a role to the given user."
                    },
                    status=403,
                )

            if om.user == request.user and (result["role"] != om.role):
                return Response(
                    {"detail": "You cannot make changes to your own role."},
                    status=400)

            om.update(role=result["role"])

        self.create_audit_entry(
            request=request,
            organization=organization,
            target_object=om.id,
            target_user=om.user,
            event=AuditLogEntryEvent.MEMBER_EDIT,
            data=om.get_audit_log_data(),
        )

        context = self._serialize_member(om, request, allowed_roles)

        return Response(context)