예제 #1
0
    def get_allowed_roles(self, request, organization, member=None):
        from sentry.auth.superuser import is_active_superuser  # Django 1.9 setup issue
        from sentry.models import OrganizationMember  # Django 1.9 setup issue
        can_admin = request.access.has_scope('member:admin')

        allowed_roles = []
        if can_admin and not is_active_superuser(request):
            acting_member = OrganizationMember.objects.get(
                user=request.user,
                organization=organization,
            )
            if member and roles.get(acting_member.role).priority < roles.get(
                    member.role).priority:
                can_admin = False
            else:
                allowed_roles = [
                    r for r in roles.get_all()
                    if r.priority <= roles.get(acting_member.role).priority
                ]
                can_admin = bool(allowed_roles)
        elif is_active_superuser(request):
            allowed_roles = roles.get_all()
        return (
            can_admin,
            allowed_roles,
        )
예제 #2
0
    def handle(self, request, organization, member_id):
        try:
            member = OrganizationMember.objects.get(
                Q(user__is_active=True) | Q(user__isnull=True),
                organization=organization,
                id=member_id,
            )
        except OrganizationMember.DoesNotExist:
            return self.redirect(get_login_url())

        if request.POST.get('op') == 'reinvite' and member.is_pending:
            return self.resend_invite(request, organization, member)
        elif request.POST.get('op') == 'regenerate' and member.is_pending:
            return self.resend_invite(request, organization, member, regen=True)

        can_admin = request.access.has_scope('member:delete')

        if can_admin and not request.is_superuser():
            acting_member = OrganizationMember.objects.get(
                user=request.user,
                organization=organization,
            )
            if roles.get(acting_member.role).priority < roles.get(member.role).priority:
                can_admin = False
            else:
                allowed_roles = [
                    r for r in roles.get_all()
                    if r.priority <= roles.get(acting_member.role).priority
                ]
                can_admin = bool(allowed_roles)
        elif request.is_superuser():
            allowed_roles = roles.get_all()

        if member.user == request.user or not can_admin:
            return self.view_member(request, organization, member)

        form = self.get_form(request, member, allowed_roles)
        if form.is_valid():
            member = form.save(request.user, organization, request.META['REMOTE_ADDR'])

            messages.add_message(request, messages.SUCCESS,
                _('Your changes were saved.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, member.id])

            return self.redirect(redirect)

        context = {
            'member': member,
            'form': form,
            'invite_link': member.get_invite_link(),
            'role_list': [
                (r, r in allowed_roles)
                for r in roles.get_all()
            ]
        }

        return self.respond('sentry/organization-member-settings.html', context)
    def handle(self, request, organization, member_id):
        try:
            member = OrganizationMember.objects.get(
                Q(user__is_active=True) | Q(user__isnull=True),
                organization=organization,
                id=member_id,
            )
        except OrganizationMember.DoesNotExist:
            return self.redirect(get_login_url())

        if request.POST.get('op') == 'reinvite' and member.is_pending:
            return self.resend_invite(request, organization, member)

        can_admin = request.access.has_scope('member:delete')

        if can_admin and not request.is_superuser():
            acting_member = OrganizationMember.objects.get(
                user=request.user,
                organization=organization,
            )
            if roles.get(acting_member.role).priority < roles.get(member.role).priority:
                can_admin = False
            else:
                allowed_roles = [
                    r for r in roles.get_all()
                    if r.priority <= roles.get(acting_member.role).priority
                ]
                can_admin = bool(allowed_roles)
        elif request.is_superuser():
            allowed_roles = roles.get_all()

        if member.user == request.user or not can_admin:
            return self.view_member(request, organization, member)

        form = self.get_form(request, member, allowed_roles)
        if form.is_valid():
            member = form.save(request.user, organization, request.META['REMOTE_ADDR'])

            messages.add_message(request, messages.SUCCESS,
                _('Your changes were saved.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, member.id])

            return self.redirect(redirect)

        context = {
            'member': member,
            'form': form,
            'role_list': [
                (r, r in allowed_roles)
                for r in roles.get_all()
            ]
        }

        return self.respond('sentry/organization-member-settings.html', context)
예제 #4
0
파일: base.py 프로젝트: ForkRepo/sentry
    def get_allowed_roles(self, request, organization, member=None):
        can_admin = request.access.has_scope("member:delete")

        allowed_roles = []
        if can_admin and not request.is_superuser():
            acting_member = OrganizationMember.objects.get(user=request.user, organization=organization)
            if member and roles.get(acting_member.role).priority < roles.get(member.role).priority:
                can_admin = False
            else:
                allowed_roles = [r for r in roles.get_all() if r.priority <= roles.get(acting_member.role).priority]
                can_admin = bool(allowed_roles)
        elif request.is_superuser():
            allowed_roles = roles.get_all()
        return (can_admin, allowed_roles)
예제 #5
0
    def handle(self, request, organization):
        form = self.get_form(request, organization)
        if form.is_valid():
            om, created = form.save(request.user, organization,
                                    request.META['REMOTE_ADDR'])

            if created:
                messages.add_message(request, messages.SUCCESS,
                                     _('The organization member was added.'))
            else:
                messages.add_message(
                    request, messages.INFO,
                    _('The organization member already exists.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, om.id])

            return HttpResponseRedirect(redirect)

        context = {
            'form': form,
            'is_invite': settings.SENTRY_ENABLE_INVITES,
            'role_list': roles.get_all(),
        }

        return self.respond('sentry/create-organization-member.html', context)
예제 #6
0
 def determine_member_recipients(self) -> Iterable[OrganizationMember]:
     members: Iterable[
         OrganizationMember] = OrganizationMember.objects.get_contactable_members_for_org(
             self.organization.id).filter(
                 role__in=(r.id for r in roles.get_all()
                           if r.has_scope("member:write")), )
     return members
예제 #7
0
    def post(self, request, project):
        """
        Request to Add CODEOWNERS to a Project
        ````````````````````````````````````
        :pparam string organization_slug: the slug of the organization the member will belong to
        :pparam string project_slug: the slug of the project
        :auth: required
        """

        requester_name = request.user.get_display_name()
        integrations_roles = [
            r.id for r in roles.get_all() if r.has_scope("org:integrations")
        ]
        recipients = OrganizationMember.objects.get_contactable_members_for_org(
            project.organization.id).filter(role__in=integrations_roles)

        for recipient in recipients:
            msg = MessageBuilder(**get_codeowners_request_builder_args(
                project, recipient, requester_name))
            email = recipient.get_email()
            logger.info("send_email",
                        extra={
                            "organization_id": project.organization.id,
                            "email": email
                        })
            msg.send_async([email])

        return self.respond(status=202)
    def handle(self, request, organization):
        form = self.get_form(request, organization)
        if form.is_valid():
            om, created = form.save(request.user, organization, request.META['REMOTE_ADDR'])

            if created:
                messages.add_message(request, messages.SUCCESS,
                    _('The organization member was added.'))

                member_invited.send(member=om, user=request.user, sender=self)

            else:
                messages.add_message(request, messages.INFO,
                    _('The organization member already exists.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, om.id])

            return HttpResponseRedirect(redirect)

        context = {
            'form': form,
            'is_invite': settings.SENTRY_ENABLE_INVITES,
            'role_list': roles.get_all(),
        }

        return self.respond('sentry/create-organization-member.html', context)
예제 #9
0
    def post(self, request, organization):
        """
        Add a invite request to Organization
        ````````````````````````````````````

        Creates an invite request given an email and sugested role / teams.

        :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 suggested role of the new member
        :param array teams: the suggested 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)

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

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

        result = serializer.validated_data

        with transaction.atomic():
            om = OrganizationMember.objects.create(
                organization=organization,
                email=result["email"],
                role=result["role"],
                inviter=request.user,
                invite_status=InviteStatus.REQUESTED_TO_BE_INVITED.value,
            )

            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"])

            self.create_audit_entry(
                request=request,
                organization_id=organization.id,
                target_object=om.id,
                data=om.get_audit_log_data(),
                event=AuditLogEntryEvent.INVITE_REQUEST_ADD,
            )

        return Response(serialize(om), status=201)
예제 #10
0
def send_invite_request_notification_email(member_id):
    try:
        om = OrganizationMember.objects.select_related(
            "inviter", "organization").get(id=member_id)
    except OrganizationMember.DoesNotExist:
        return

    link_args = {"organization_slug": om.organization.slug}

    context = {
        "email":
        om.email,
        "organization_name":
        om.organization.name,
        "pending_requests_link":
        absolute_uri(
            reverse("sentry-organization-members-requests", kwargs=link_args)),
    }

    if om.requested_to_join:
        email_args = {
            "template": "sentry/emails/organization-join-request.txt",
            "html_template": "sentry/emails/organization-join-request.html",
        }
        context["settings_link"] = absolute_uri(
            reverse("sentry-organization-settings",
                    args=[om.organization.slug]))

    elif om.requested_to_be_invited:
        email_args = {
            "template": "sentry/emails/organization-invite-request.txt",
            "html_template": "sentry/emails/organization-invite-request.html",
        }
        context["inviter_name"] = om.inviter.get_salutation_name
    else:
        raise RuntimeError("This member is not pending invitation")

    recipients = OrganizationMember.objects.select_related("user").filter(
        organization_id=om.organization_id,
        user__isnull=False,
        invite_status=InviteStatus.APPROVED.value,
        role__in=(r.id for r in roles.get_all()
                  if r.has_scope("member:write")),
    )

    msg = MessageBuilder(
        subject=f"Access request to {om.organization.name}",
        type="organization.invite-request",
        context=context,
        **email_args,
    )

    for recipient in recipients:
        try:
            msg.send_async([recipient.get_email()])
        except Exception as e:
            logger = get_logger(name="sentry.mail")
            logger.exception(e)
예제 #11
0
    def handle(self, request):
        try:
            integration = get_integration_from_request(request, 'jira')
        except AtlassianConnectValidationError:
            return self.get_response(
                {'error_message': 'Unable to verify installation.'})
        except ExpiredSignatureError:
            return self.get_response({'refresh_required': True})

        if not request.user.is_authenticated():
            return self.get_response({
                'login_required':
                True,
                'login_url':
                absolute_uri(reverse('sentry-login')),
            })

        organizations = list(request.user.get_orgs().filter(
            id__in=OrganizationMember.objects.filter(
                role__in=[r.id for r in roles.get_all() if r.is_global],
                user=request.user,
            ).values('organization'), ))

        form = JiraConfigForm(organizations, request.POST)

        if request.method == 'GET' or not form.is_valid():
            active_orgs = OrganizationIntegration.objects.filter(
                integration__provider='jira',
                integration=integration,
                organization__in=organizations).values_list('organization_id',
                                                            flat=True)

            form = JiraConfigForm(organizations,
                                  initial={'organizations': active_orgs})
            return self.get_response({
                'form': form,
                'organizations': organizations
            })

        enabled_orgs = [
            o for o in organizations
            if o.id in form.cleaned_data['organizations']
        ]
        disabled_orgs = list(set(organizations) - set(enabled_orgs))

        # Remove Jira integrations not in the set of enabled organizations
        OrganizationIntegration.objects.filter(
            integration__provider='jira',
            integration=integration,
            organization__in=disabled_orgs,
        ).delete()

        # Ensure all enabled integrations.
        for org in enabled_orgs:
            integration.add_organization(org, request.user)

        return self.get_response({'form': form, 'completed': True})
    def view_member(self, request, organization, member, all_teams):
        context = {
            'member': member,
            'enabled_teams': set(member.teams.all()),
            'all_teams': all_teams,
            'role_list': roles.get_all(),
        }

        return self.respond('sentry/organization-member-details.html', context)
예제 #13
0
 def get_allowed_roles_to_invite(self):
     """
     Return a list of roles which that member could invite
     Must check if member member has member:admin first before checking
     """
     return [
         r for r in roles.get_all()
         if r.priority <= roles.get(self.role).priority
     ]
예제 #14
0
    def send_request_notification_email(self):
        from sentry.utils.email import MessageBuilder

        link_args = {"organization_slug": self.organization.slug}

        context = {
            "email":
            self.email,
            "inviter":
            self.inviter,
            "organization":
            self.organization,
            "organization_link":
            absolute_uri(
                reverse("sentry-organization-index",
                        args=[self.organization.slug])),
            "pending_requests_link":
            absolute_uri(
                reverse("sentry-organization-members-requests",
                        kwargs=link_args)),
        }

        if self.requested_to_join:
            email_args = {
                "template": "sentry/emails/organization-join-request.txt",
                "html_template":
                "sentry/emails/organization-join-request.html",
            }
        elif self.requested_to_be_invited:
            email_args = {
                "template": "sentry/emails/organization-invite-request.txt",
                "html_template":
                "sentry/emails/organization-invite-request.html",
            }
        else:
            raise RuntimeError("This member is not pending invitation")

        recipients = OrganizationMember.objects.select_related("user").filter(
            organization_id=self.organization_id,
            user__isnull=False,
            invite_status=InviteStatus.APPROVED.value,
            role__in=(r.id for r in roles.get_all()
                      if r.has_scope("member:write")),
        )

        msg = MessageBuilder(subject="Access request to %s" %
                             (self.organization.name, ),
                             type="organization.invite-request",
                             context=context,
                             **email_args)

        for recipient in recipients:
            try:
                msg.send_async([recipient.get_email()])
            except Exception as e:
                logger = get_logger(name="sentry.mail")
                logger.exception(e)
예제 #15
0
    def view_member(self, request, organization, member):
        context = {
            'member': member,
            'enabled_teams': set(member.teams.all()),
            'all_teams': Team.objects.filter(organization=organization, ),
            'role_list': roles.get_all(),
        }

        return self.respond('sentry/organization-member-details.html', context)
def get_allowed_roles(request, organization, member=None):
    can_admin = request.access.has_scope("member:admin")

    allowed_roles = []
    if can_admin and not is_active_superuser(request):
        acting_member = member or OrganizationMember.objects.get(
            user=request.user, organization=organization
        )
        if member and roles.get(acting_member.role).priority < roles.get(member.role).priority:
            can_admin = False
        else:
            allowed_roles = [
                r for r in roles.get_all() if r.priority <= roles.get(acting_member.role).priority
            ]
            can_admin = bool(allowed_roles)
    elif is_active_superuser(request):
        allowed_roles = roles.get_all()
    return (can_admin, allowed_roles)
예제 #17
0
    def handle(self, request):
        try:
            integration = get_integration_from_request(request, "jira")
        except AtlassianConnectValidationError:
            return self.get_response({"error_message": "Unable to verify installation."})
        except ExpiredSignatureError:
            return self.get_response({"refresh_required": True})

        if not request.user.is_authenticated():
            parsed_user_agent = user_agent_parser.ParseUserAgent(
                request.META.get("HTTP_USER_AGENT", "")
            )
            # not enabling cross site cookies seems to be a common problem with Safari
            # as a result, there is a Safari specific link to instructions when is_safari=true
            is_safari = parsed_user_agent.get("family") == "Safari"
            return self.get_response(
                {
                    "login_required": True,
                    "is_safari": is_safari,
                    "login_url": absolute_uri(reverse("sentry-login")),
                }
            )

        organizations = list(
            request.user.get_orgs().filter(
                id__in=OrganizationMember.objects.filter(
                    role__in=[r.id for r in roles.get_all() if r.is_global], user=request.user
                ).values("organization")
            )
        )

        form = JiraConfigForm(organizations, request.POST)

        if request.method == "GET" or not form.is_valid():
            active_orgs = OrganizationIntegration.objects.filter(
                integration__provider="jira",
                integration=integration,
                organization__in=organizations,
            ).values_list("organization_id", flat=True)

            form = JiraConfigForm(organizations, initial={"organizations": active_orgs})
            return self.get_response({"form": form, "organizations": organizations})

        enabled_orgs = [o for o in organizations if o.id in form.cleaned_data["organizations"]]
        disabled_orgs = list(set(organizations) - set(enabled_orgs))

        # Remove Jira integrations not in the set of enabled organizations
        OrganizationIntegration.objects.filter(
            integration__provider="jira", integration=integration, organization__in=disabled_orgs
        ).delete()

        # Ensure all enabled integrations.
        for org in enabled_orgs:
            integration.add_organization(org, request.user)

        return self.get_response({"form": form, "completed": True})
예제 #18
0
    def handle(self, request):
        try:
            integration = get_integration_from_request(request, "jira")
        except AtlassianConnectValidationError:
            return self.get_response(
                {"error_message": "Unable to verify installation."})
        except ExpiredSignatureError:
            return self.get_response({"refresh_required": True})

        if not request.user.is_authenticated():
            return self.get_response({
                "login_required":
                True,
                "login_url":
                absolute_uri(reverse("sentry-login"))
            })

        organizations = list(request.user.get_orgs().filter(
            id__in=OrganizationMember.objects.filter(
                role__in=[r.id for r in roles.get_all() if r.is_global],
                user=request.user).values("organization")))

        form = JiraConfigForm(organizations, request.POST)

        if request.method == "GET" or not form.is_valid():
            active_orgs = OrganizationIntegration.objects.filter(
                integration__provider="jira",
                integration=integration,
                organization__in=organizations,
            ).values_list("organization_id", flat=True)

            form = JiraConfigForm(organizations,
                                  initial={"organizations": active_orgs})
            return self.get_response({
                "form": form,
                "organizations": organizations
            })

        enabled_orgs = [
            o for o in organizations
            if o.id in form.cleaned_data["organizations"]
        ]
        disabled_orgs = list(set(organizations) - set(enabled_orgs))

        # Remove Jira integrations not in the set of enabled organizations
        OrganizationIntegration.objects.filter(
            integration__provider="jira",
            integration=integration,
            organization__in=disabled_orgs).delete()

        # Ensure all enabled integrations.
        for org in enabled_orgs:
            integration.add_organization(org, request.user)

        return self.get_response({"form": form, "completed": True})
def get_allowed_roles(request, organization, member=None):
    can_admin = request.access.has_scope('member:admin')

    allowed_roles = []
    if can_admin and not is_active_superuser(request):
        acting_member = member or OrganizationMember.objects.get(
            user=request.user,
            organization=organization,
        )
        if member and roles.get(acting_member.role).priority < roles.get(member.role).priority:
            can_admin = False
        else:
            allowed_roles = [
                r for r in roles.get_all()
                if r.priority <= roles.get(acting_member.role).priority
            ]
            can_admin = bool(allowed_roles)
    elif is_active_superuser(request):
        allowed_roles = roles.get_all()
    return (can_admin, allowed_roles, )
예제 #20
0
파일: base.py 프로젝트: zmyer/sentry
    def get_allowed_roles(self, request, organization, member=None):
        can_admin = request.access.has_scope('member:delete')

        allowed_roles = []
        if can_admin and not request.is_superuser():
            acting_member = OrganizationMember.objects.get(
                user=request.user,
                organization=organization,
            )
            if member and roles.get(acting_member.role).priority < roles.get(member.role).priority:
                can_admin = False
            else:
                allowed_roles = [
                    r for r in roles.get_all()
                    if r.priority <= roles.get(acting_member.role).priority
                ]
                can_admin = bool(allowed_roles)
        elif request.is_superuser():
            allowed_roles = roles.get_all()
        return (can_admin, allowed_roles,)
    def _serialize_member(self, member, request, allowed_roles=None):
        context = serialize(member,
                            serializer=OrganizationMemberWithTeamsSerializer())

        if request.access.has_scope('member:admin'):
            context['invite_link'] = member.get_invite_link()

        context['roles'] = serialize(roles.get_all(),
                                     serializer=RoleSerializer(),
                                     allowed_roles=allowed_roles)

        return context
    def _serialize_member(self, member, request, allowed_roles=None):
        context = serialize(member, serializer=OrganizationMemberWithTeamsSerializer())

        if request.access.has_scope("member:admin"):
            context["invite_link"] = member.get_invite_link()
            context["user"] = serialize(member.user, request.user, DetailedUserSerializer())

        context["isOnlyOwner"] = self.is_only_owner(member)
        context["roles"] = serialize(
            roles.get_all(), serializer=RoleSerializer(), allowed_roles=allowed_roles
        )

        return context
    def _serialize_member(self, member, request, allowed_roles=None):
        context = serialize(
            member,
            serializer=OrganizationMemberWithTeamsSerializer()
        )

        if request.access.has_scope('member:admin'):
            context['invite_link'] = member.get_invite_link()

        context['roles'] = serialize(
            roles.get_all(), serializer=RoleSerializer(), allowed_roles=allowed_roles)

        return context
    def handle(self, request, organization, member_id):
        try:
            member = OrganizationMember.objects.get(
                Q(user__is_active=True) | Q(user__isnull=True),
                organization=organization,
                id=member_id,
            )
        except OrganizationMember.DoesNotExist:
            return self.redirect(auth.get_login_url())

        if request.POST.get('op') == 'reinvite' and member.is_pending:
            return self.resend_invite(request, organization, member)
        elif request.POST.get('op') == 'regenerate' and member.is_pending:
            return self.resend_invite(request,
                                      organization,
                                      member,
                                      regen=True)

        can_admin, allowed_roles = self.get_allowed_roles(
            request, organization, member)

        all_teams = Team.objects.filter(organization=organization,
                                        status=TeamStatus.VISIBLE)

        if member.user == request.user or not can_admin:
            return self.view_member(request, organization, member, all_teams)

        form = self.get_form(request, member, all_teams, allowed_roles)
        if form.is_valid():
            member = form.save(request.user, organization,
                               request.META['REMOTE_ADDR'])

            messages.add_message(request, messages.SUCCESS,
                                 _('Your changes were saved.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, member.id])

            return self.redirect(redirect)

        context = {
            'member': member,
            'form': form,
            'invite_link': member.get_invite_link(),
            'role_list': [(r, r in allowed_roles) for r in roles.get_all()],
            'all_teams': all_teams
        }

        return self.respond('sentry/organization-member-settings.html',
                            context)
    def handle(self, request, organization, member_id):
        try:
            member = OrganizationMember.objects.get(
                Q(user__is_active=True) | Q(user__isnull=True),
                organization=organization,
                id=member_id,
            )
        except OrganizationMember.DoesNotExist:
            return self.redirect(auth.get_login_url())

        if request.POST.get('op') == 'reinvite' and member.is_pending:
            return self.resend_invite(request, organization, member)
        elif request.POST.get('op') == 'regenerate' and member.is_pending:
            return self.resend_invite(request, organization, member, regen=True)

        can_admin, allowed_roles = self.get_allowed_roles(request, organization, member)

        all_teams = Team.objects.filter(
            organization=organization,
            status=TeamStatus.VISIBLE
        )

        if member.user == request.user or not can_admin:
            return self.view_member(request, organization, member, all_teams)

        form = self.get_form(request, member, all_teams, allowed_roles)
        if form.is_valid():
            member = form.save(request.user, organization, request.META['REMOTE_ADDR'])

            messages.add_message(request, messages.SUCCESS,
                _('Your changes were saved.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, member.id])

            return self.redirect(redirect)

        context = {
            'member': member,
            'form': form,
            'invite_link': member.get_invite_link(),
            'role_list': [
                (r, r in allowed_roles)
                for r in roles.get_all()
            ],
            'all_teams': all_teams
        }

        return self.respond('sentry/organization-member-settings.html', context)
예제 #26
0
    def handle(self, request):
        try:
            integration = get_integration_from_request(request, 'jira')
        except AtlassianConnectValidationError:
            return self.get_response({'error_message': 'Unable to verify installation.'})
        except ExpiredSignatureError:
            return self.get_response({'refresh_required': True})

        if not request.user.is_authenticated():
            return self.get_response({
                'login_required': True,
                'login_url': absolute_uri(reverse('sentry-login')),
            })

        organizations = list(request.user.get_orgs().filter(
            id__in=OrganizationMember.objects.filter(
                role__in=[r.id for r in roles.get_all() if r.is_global],
                user=request.user,
            ).values('organization'),
        ))

        form = JiraConfigForm(organizations, request.POST)

        if request.method == 'GET' or not form.is_valid():
            active_orgs = OrganizationIntegration.objects.filter(
                integration__provider='jira',
                integration=integration,
                organization__in=organizations
            ).values_list('organization_id', flat=True)

            form = JiraConfigForm(organizations, initial={'organizations': active_orgs})
            return self.get_response({'form': form, 'organizations': organizations})

        enabled_orgs = [o for o in organizations if o.id in form.cleaned_data['organizations']]
        disabled_orgs = list(set(organizations) - set(enabled_orgs))

        # Remove Jira integrations not in the set of enabled organizations
        OrganizationIntegration.objects.filter(
            integration__provider='jira',
            integration=integration,
            organization__in=disabled_orgs,
        ).delete()

        # Ensure all enabled integrations.
        for org in enabled_orgs:
            integration.add_organization(org, request.user)

        return self.get_response({'form': form, 'completed': True})
예제 #27
0
    def handle(self, request):
        try:
            integration = get_integration_from_request(request, 'jira')
        except AtlassianConnectValidationError:
            return self.get_response(
                {'error_message': 'Unable to verify installation.'})

        organizations = request.user.get_orgs().filter(
            id__in=OrganizationMember.objects.filter(
                role__in=[r.id for r in roles.get_all() if r.is_global],
                user=request.user,
            ).values('organization'), )
        form = JiraConfigForm(organizations, request.POST)

        if request.method == 'GET' or not form.is_valid():
            active_orgs = OrganizationIntegration.objects.filter(
                integration__provider='jira',
                integration=integration,
                organization__in=organizations).values_list('organization_id',
                                                            flat=True)

            form = JiraConfigForm(organizations,
                                  initial={'organizations': active_orgs})
            return self.get_response({'form': form})

        enabled_orgs = form.cleaned_data['organizations']
        disabled_orgs = list(
            set(o.id for o in organizations) - set(enabled_orgs))

        # Remove organization and project Jira integrations not in the set of
        # enabled organizations
        OrganizationIntegration.objects.filter(
            integration__provider='jira',
            integration=integration,
            organization__in=disabled_orgs,
        ).delete()
        ProjectIntegration.objects.filter(
            integration__provider='jira',
            integration=integration,
            integration__organizations__in=disabled_orgs,
        ).delete()

        # Ensure all enabled integrations.
        for org_id in enabled_orgs:
            integration.add_organization(org_id)

        return self.get_response({'form': form, 'completed': True})
    def handle(self, request, organization, member_id):
        try:
            member = OrganizationMember.objects.get(
                Q(user__is_active=True) | Q(user__isnull=True),
                id=member_id,
            )
        except OrganizationMember.DoesNotExist:
            return self.redirect(reverse('sentry'))

        if request.POST.get('op') == 'reinvite' and member.is_pending:
            return self.resend_invite(request, organization, member)

        can_admin = request.access.has_scope('member:delete')

        if can_admin and not request.is_superuser():
            acting_member = OrganizationMember.objects.get(
                user=request.user,
                organization=organization,
            )
            can_admin = acting_member.can_manage_member(member)

        if member.user == request.user or not can_admin:
            return self.view_member(request, organization, member)

        form = self.get_form(request, member)
        if form.is_valid():
            member = form.save(request.user, organization,
                               request.META['REMOTE_ADDR'])

            messages.add_message(request, messages.SUCCESS,
                                 _('Your changes were saved.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, member.id])

            return self.redirect(redirect)

        context = {
            'member': member,
            'form': form,
            'role_list': roles.get_all(),
        }

        return self.respond('sentry/organization-member-settings.html',
                            context)
예제 #29
0
    def view_member(self, request, organization, member):
        context = {
            'member':
            member,
            'enabled_teams':
            set(member.teams.all()),
            # 'all_teams': Team.objects.filter(
            #     organization=organization,
            # ),
            # 当前登陆人具有权限的小组 update by hzwangzhiwei @20160830
            'all_teams':
            Team.objects.get_for_user(organization=organization,
                                      user=request.user),
            'role_list':
            roles.get_all(),
        }

        return self.respond('sentry/organization-member-details.html', context)
예제 #30
0
    def handle(self, request):
        try:
            integration = get_integration_from_request(request)
        except AtlassianConnectValidationError:
            return self.get_response({'error_message': 'Unable to verify installation.'})

        organizations = request.user.get_orgs().filter(
            id__in=OrganizationMember.objects.filter(
                role__in=[r.id for r in roles.get_all() if r.is_global],
            ),
        )
        form = JiraConfigForm(organizations, request.POST)

        if request.method == 'GET' or not form.is_valid():
            active_orgs = OrganizationIntegration.objects.filter(
                integration__provider='jira',
                integration=integration,
                organization__in=organizations
            ).values_list('organization_id', flat=True)

            form = JiraConfigForm(organizations, initial={'organizations': active_orgs})
            return self.get_response({'form': form})

        enabled_orgs = form.cleaned_data['organizations']
        disabled_orgs = list(set(o.id for o in organizations) - set(enabled_orgs))

        # Remove organization and project Jira integrations not in the set of
        # enabled organizations
        OrganizationIntegration.objects.filter(
            integration__provider='jira',
            integration=integration,
            organization__in=disabled_orgs,
        ).delete()
        ProjectIntegration.objects.filter(
            integration__provider='jira',
            integration=integration,
            integration__organizations__in=disabled_orgs,
        ).delete()

        # Ensure all enabled integrations.
        for org_id in enabled_orgs:
            integration.add_organization(org_id)

        return self.get_response({'form': form, 'completed': True})
예제 #31
0
    def handle(self, request, organization, member_id):
        try:
            member = OrganizationMember.objects.get(
                Q(user__is_active=True) | Q(user__isnull=True),
                id=member_id,
            )
        except OrganizationMember.DoesNotExist:
            return self.redirect(reverse('sentry'))

        if request.POST.get('op') == 'reinvite' and member.is_pending:
            return self.resend_invite(request, organization, member)

        can_admin = request.access.has_scope('member:delete')

        if can_admin and not is_active_superuser(request):
            acting_member = OrganizationMember.objects.get(
                user=request.user,
                organization=organization,
            )
            can_admin = acting_member.can_manage_member(member)

        if member.user == request.user or not can_admin:
            return self.view_member(request, organization, member)

        form = self.get_form(request, member)
        if form.is_valid():
            member = form.save(request.user, organization, request.META['REMOTE_ADDR'])

            messages.add_message(request, messages.SUCCESS,
                _('Your changes were saved.'))

            redirect = reverse('sentry-organization-member-settings',
                               args=[organization.slug, member.id])

            return self.redirect(redirect)

        context = {
            'member': member,
            'form': form,
            'role_list': roles.get_all(),
        }

        return self.respond('sentry/organization-member-settings.html', context)
    def handle(self, request, organization):
        can_admin, allowed_roles = self.get_allowed_roles(
            request, organization)

        all_teams = Team.objects.filter(organization=organization,
                                        status=TeamStatus.VISIBLE)

        form = self.get_form(request, organization, all_teams, allowed_roles)
        if form.is_valid():
            om, created = form.save(request.user, organization,
                                    request.META['REMOTE_ADDR'])

            user_display = form.cleaned_data.get('email', None)
            if not user_display:
                user_display = form.cleaned_data['user']

            if created:
                messages.add_message(
                    request, messages.SUCCESS,
                    _('The organization member %s was added.') % user_display)

                member_invited.send(member=om, user=request.user, sender=self)

            else:
                messages.add_message(
                    request, messages.INFO,
                    _('The organization member %s already exists.') %
                    user_display)

            redirect = reverse('sentry-organization-members',
                               args=[organization.slug])

            return HttpResponseRedirect(redirect)

        context = {
            'form': form,
            'is_invite': settings.SENTRY_ENABLE_INVITES,
            'role_list': [(r, r in allowed_roles) for r in roles.get_all()],
            'all_teams': list(all_teams),
        }

        return self.respond('sentry/create-organization-member.html', context)
예제 #33
0
    def get(self, request, organization, member_id):
        """Currently only returns allowed invite roles for member invite"""

        try:
            member = self._get_member(request, organization, member_id)
        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist

        _, allowed_roles = get_allowed_roles(request, organization, member)

        allowed_roles = [{'role': serialize(r, serializer=RoleSerializer()),
                          'allowed': r in allowed_roles} for r in roles.get_all()]

        context = serialize(
            member,
        )

        context['allowed_roles'] = allowed_roles

        return Response(context)
    def get(self, request, organization, member_id):
        """Currently only returns allowed invite roles for member invite"""

        try:
            member = self._get_member(request, organization, member_id)
        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist

        _, allowed_roles = get_allowed_roles(request, organization, member)

        allowed_roles = [{
            'role': serialize(r, serializer=RoleSerializer()),
            'allowed': r in allowed_roles
        } for r in roles.get_all()]

        context = serialize(member, )

        context['allowed_roles'] = allowed_roles

        return Response(context)
    def handle(self, request, organization):
        can_admin, allowed_roles = self.get_allowed_roles(request, organization)

        all_teams = Team.objects.filter(
            organization=organization,
            status=TeamStatus.VISIBLE
        )

        form = self.get_form(request, organization, all_teams, allowed_roles)
        if form.is_valid():
            om, created = form.save(request.user, organization, request.META['REMOTE_ADDR'])

            user_display = form.cleaned_data.get('email', None)
            if not user_display:
                user_display = form.cleaned_data['user']

            if created:
                messages.add_message(request, messages.SUCCESS,
                    _('The organization member %s was added.') % user_display)

                member_invited.send(member=om, user=request.user, sender=self)

            else:
                messages.add_message(request, messages.INFO,
                    _('The organization member %s already exists.') % user_display)

            redirect = reverse('sentry-organization-members', args=[organization.slug])

            return HttpResponseRedirect(redirect)

        context = {
            'form': form,
            'is_invite': settings.SENTRY_ENABLE_INVITES,
            'role_list': [
                (r, r in allowed_roles)
                for r in roles.get_all()
            ],
            'all_teams': list(all_teams),
        }

        return self.respond('sentry/create-organization-member.html', context)
예제 #36
0
    def serialize(self, obj, attrs, user, access):
        from sentry import experiments
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = self._team_list(obj, access)
        project_list = self._project_list(obj, access)

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj, ).select_related('user'))

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer,
                        self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['experiments'] = experiment_assignments
        context['quota'] = {
            'maxRate':
            max_rate[0],
            'maxRateInterval':
            max_rate[1],
            'accountLimit':
            int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )),
            'projectLimit':
            int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )),
        }

        context.update({
            'isDefault':
            obj.is_default,
            'defaultRole':
            obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership':
            bool(obj.flags.allow_joinleave),
            'require2FA':
            bool(obj.flags.require_2fa),
            'allowSharedIssues':
            not obj.flags.disable_shared_issues,
            'enhancedPrivacy':
            bool(obj.flags.enhanced_privacy),
            'dataScrubber':
            bool(
                obj.get_option('sentry:require_scrub_data',
                               REQUIRE_SCRUB_DATA_DEFAULT)),
            'dataScrubberDefaults':
            bool(
                obj.get_option('sentry:require_scrub_defaults',
                               REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            'sensitiveFields':
            obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT)
            or [],
            'safeFields':
            obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [],
            'storeCrashReports':
            bool(
                obj.get_option('sentry:store_crash_reports',
                               STORE_CRASH_REPORTS_DEFAULT)),
            'scrubIPAddresses':
            bool(
                obj.get_option('sentry:require_scrub_ip_address',
                               REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            'scrapeJavaScript':
            bool(
                obj.get_option('sentry:scrape_javascript',
                               SCRAPE_JAVASCRIPT_DEFAULT)),
            'trustedRelays':
            obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT)
            or [],
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user,
                                        ProjectSummarySerializer())
        context['access'] = access.scopes
        context[
            'pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
                team__organization=obj, ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user,
                                               OnboardingTasksSerializer())
        return context
    def put(self, request, organization, member_id):
        """
        Update an invite request to Organization
        ````````````````````````````````````````

        Update and/or approve an invite request to an organization.

        :pparam string organization_slug: the slug of the organization the member will belong to
        :param string member_id: the member ID
        :param boolean approve: allows the member to be invited
        :param string role: the suggested role of the new member
        :param array teams: the suggested slugs of the teams the member should belong to.

        :auth: required
        """

        try:
            member = self._get_member(organization, member_id)
        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist

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

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

        result = serializer.validated_data

        if result.get("role"):
            member.update(role=result["role"])

        if "teams" in result:
            save_team_assignments(member, result["teams"])

        if "approve" in request.data:
            _, allowed_roles = get_allowed_roles(request, organization)

            serializer = ApproveInviteRequestSerializer(
                data=request.data,
                context={
                    "request": request,
                    "organization": organization,
                    "member": member,
                    "allowed_roles": allowed_roles,
                },
            )

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

            result = serializer.validated_data

            if result.get("approve") and not member.invite_approved:
                member.approve_invite()
                member.save()

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

                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,
                )

        return Response(
            serialize(member,
                      serializer=OrganizationMemberWithTeamsSerializer()),
            status=status.HTTP_200_OK,
        )
예제 #38
0
    def serialize(self, obj, attrs, user, access):
        from sentry import experiments

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj).select_related("user"))

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer,
                        self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context["experiments"] = experiment_assignments
        context["quota"] = {
            "maxRate":
            max_rate[0],
            "maxRateInterval":
            max_rate[1],
            "accountLimit":
            int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key="sentry:account-rate-limit",
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )),
            "projectLimit":
            int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key="sentry:project-rate-limit",
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )),
        }

        context.update({
            "isDefault":
            obj.is_default,
            "defaultRole":
            obj.default_role,
            "availableRoles": [{
                "id": r.id,
                "name": r.name
            } for r in roles.get_all()],
            "openMembership":
            bool(obj.flags.allow_joinleave),
            "require2FA":
            bool(obj.flags.require_2fa),
            "allowSharedIssues":
            not obj.flags.disable_shared_issues,
            "enhancedPrivacy":
            bool(obj.flags.enhanced_privacy),
            "dataScrubber":
            bool(
                obj.get_option("sentry:require_scrub_data",
                               REQUIRE_SCRUB_DATA_DEFAULT)),
            "dataScrubberDefaults":
            bool(
                obj.get_option("sentry:require_scrub_defaults",
                               REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            "sensitiveFields":
            obj.get_option("sentry:sensitive_fields", SENSITIVE_FIELDS_DEFAULT)
            or [],
            "safeFields":
            obj.get_option("sentry:safe_fields", SAFE_FIELDS_DEFAULT) or [],
            "storeCrashReports":
            convert_crashreport_count(
                obj.get_option("sentry:store_crash_reports")),
            "attachmentsRole":
            six.text_type(
                obj.get_option("sentry:attachments_role",
                               ATTACHMENTS_ROLE_DEFAULT)),
            "debugFilesRole":
            six.text_type(
                obj.get_option("sentry:debug_files_role",
                               DEBUG_FILES_ROLE_DEFAULT)),
            "eventsMemberAdmin":
            bool(
                obj.get_option("sentry:events_member_admin",
                               EVENTS_MEMBER_ADMIN_DEFAULT)),
            "scrubIPAddresses":
            bool(
                obj.get_option("sentry:require_scrub_ip_address",
                               REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            "scrapeJavaScript":
            bool(
                obj.get_option("sentry:scrape_javascript",
                               SCRAPE_JAVASCRIPT_DEFAULT)),
            "allowJoinRequests":
            bool(obj.get_option("sentry:join_requests",
                                JOIN_REQUESTS_DEFAULT)),
            "relayPiiConfig":
            six.text_type(obj.get_option("sentry:relay_pii_config") or u"")
            or None,
            "apdexThreshold":
            int(
                obj.get_option("sentry:apdex_threshold",
                               APDEX_THRESHOLD_DEFAULT)),
        })

        trusted_relays_raw = obj.get_option("sentry:trusted-relays") or []
        # serialize trusted relays info into their external form
        context["trustedRelays"] = [
            TrustedRelaySerializer(raw).data for raw in trusted_relays_raw
        ]

        context["access"] = access.scopes
        if access.role is not None:
            context["role"] = access.role
        context[
            "pendingAccessRequests"] = OrganizationAccessRequest.objects.filter(
                team__organization=obj).count()
        context["onboardingTasks"] = serialize(onboarding_tasks, user,
                                               OnboardingTasksSerializer())
        return context
예제 #39
0
    def serialize(self, obj, attrs, user):
        from sentry import features
        from sentry.app import env
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = list(Team.objects.filter(
            organization=obj,
            status=TeamStatus.VISIBLE,
        ))

        for team in team_list:
            team._organization_cache = obj

        project_list = list(Project.objects.filter(
            organization=obj,
            status=ProjectStatus.VISIBLE,
        ))

        for project in project_list:
            project._organization_cache = obj

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        feature_list = []
        if features.has('organizations:sso', obj, actor=user):
            feature_list.append('sso')
        if features.has('organizations:onboarding', obj, actor=user) and \
                not OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.append('onboarding')
        if features.has('organizations:api-keys', obj, actor=user) or \
                ApiKey.objects.filter(organization=obj).exists():
            feature_list.append('api-keys')
        if features.has('organizations:group-unmerge', obj, actor=user):
            feature_list.append('group-unmerge')
        if features.has('organizations:github-apps', obj, actor=user):
            feature_list.append('github-apps')
        if features.has('organizations:integrations-v3', obj, actor=user):
            feature_list.append('integrations-v3')
        if features.has('organizations:new-settings', obj, actor=user):
            feature_list.append('new-settings')
        if features.has('organizations:require-2fa', obj, actor=user):
            feature_list.append('require-2fa')
        if features.has('organizations:environments', obj, actor=user):
            feature_list.append('environments')
        if features.has('organizations:repos', obj, actor=user):
            feature_list.append('repos')
        if features.has('organizations:internal-catchall', obj, actor=user):
            feature_list.append('internal-catchall')
        if features.has('organizations:suggested-commits', obj, actor=user):
            feature_list.append('suggested-commits')
        if features.has('organizations:new-teams', obj, actor=user):
            feature_list.append('new-teams')
        if features.has('organizations:unreleased-changes', obj, actor=user):
            feature_list.append('unreleased-changes')
        if features.has('organizations:relay', obj, actor=user):
            feature_list.append('relay')
        if features.has('organizations:health', obj, actor=user):
            feature_list.append('health')

        if getattr(obj.flags, 'allow_joinleave'):
            feature_list.append('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):
            feature_list.append('shared-issues')
        if getattr(obj.flags, 'require_2fa'):
            feature_list.append('require-2fa')

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['quota'] = {
            'maxRate': max_rate[0],
            'maxRateInterval': max_rate[1],
            'accountLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=0,
                )
            ),
            'projectLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=100,
                )
            ),
        }

        context.update({
            'isDefault': obj.is_default,
            'defaultRole': obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership': bool(obj.flags.allow_joinleave),
            'require2FA': bool(obj.flags.require_2fa),
            'allowSharedIssues': not obj.flags.disable_shared_issues,
            'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
            'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', False)),
            'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', False)),
            'sensitiveFields': obj.get_option('sentry:sensitive_fields', None) or [],
            'safeFields': obj.get_option('sentry:safe_fields', None) or [],
            'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', False)),
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user, ProjectSummarySerializer())
        if env.request:
            context['access'] = access.from_request(env.request, obj).scopes
        else:
            context['access'] = access.from_user(user, obj).scopes
        context['features'] = feature_list
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
예제 #40
0
    def serialize(self, obj, attrs, user, access):
        from sentry import experiments
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = self._team_list(obj, access)
        project_list = self._project_list(obj, access)

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['experiments'] = experiment_assignments
        context['quota'] = {
            'maxRate': max_rate[0],
            'maxRateInterval': max_rate[1],
            'accountLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )
            ),
            'projectLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )
            ),
        }

        context.update({
            'isDefault': obj.is_default,
            'defaultRole': obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership': bool(obj.flags.allow_joinleave),
            'require2FA': bool(obj.flags.require_2fa),
            'allowSharedIssues': not obj.flags.disable_shared_issues,
            'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
            'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)),
            'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [],
            'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [],
            'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)),
            'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)),
            'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [],
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user, ProjectSummarySerializer())
        context['access'] = access.scopes
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
예제 #41
0
    def put(self, request: Request, organization, member_id) -> Response:
        """
        Update an invite request to Organization
        ````````````````````````````````````````

        Update and/or approve an invite request to an organization.

        :pparam string organization_slug: the slug of the organization the member will belong to
        :param string member_id: the member ID
        :param boolean approve: allows the member to be invited
        :param string role: the suggested role of the new member
        :param array teams: the suggested slugs of the teams the member should belong to.

        :auth: required
        """

        try:
            member = self._get_member(organization, member_id)
        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist

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

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

        result = serializer.validated_data

        if result.get("role"):
            member.update(role=result["role"])

        if "teams" in result:
            save_team_assignments(member, result["teams"])

        if "approve" in request.data:
            _, allowed_roles = get_allowed_roles(request, organization)

            serializer = ApproveInviteRequestSerializer(
                data=request.data,
                context={
                    "request": request,
                    "organization": organization,
                    "member": member,
                    "allowed_roles": allowed_roles,
                },
            )

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

            result = serializer.validated_data

            if result.get("approve") and not member.invite_approved:
                api_key = get_api_key_for_audit_log(request)
                member.approve_member_invitation(
                    request.user,
                    api_key,
                    request.META["REMOTE_ADDR"],
                    request.data.get("referrer"),
                )

        return Response(
            serialize(member,
                      serializer=OrganizationMemberWithTeamsSerializer()),
            status=status.HTTP_200_OK,
        )
예제 #42
0
    def serialize(self, obj, attrs, user):
        from sentry import features
        from sentry.app import env
        from sentry.api.serializers.models.team import TeamWithProjectsSerializer

        team_list = list(Team.objects.filter(
            organization=obj,
            status=TeamStatus.VISIBLE,
        ))
        for team in team_list:
            team._organization_cache = obj

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        feature_list = []
        if features.has('organizations:sso', obj, actor=user):
            feature_list.append('sso')
        if features.has('organizations:onboarding', obj, actor=user) and \
                not OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.append('onboarding')
        if features.has('organizations:api-keys', obj, actor=user) or \
                ApiKey.objects.filter(organization=obj).exists():
            feature_list.append('api-keys')
        if features.has('organizations:group-unmerge', obj, actor=user):
            feature_list.append('group-unmerge')
        if features.has('organizations:integrations-v3', obj, actor=user):
            feature_list.append('integrations-v3')

        if getattr(obj.flags, 'allow_joinleave'):
            feature_list.append('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):
            feature_list.append('shared-issues')

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['quota'] = {
            'maxRate':
            max_rate[0],
            'maxRateInterval':
            max_rate[1],
            'accountLimit':
            int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=0,
                )
            ),
            'projectLimit':
            int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=100,
                )
            ),
        }
        context.update(
            {
                'isDefault': obj.is_default,
                'defaultRole': obj.default_role,
                'availableRoles': [{
                    'id': r.id,
                    'name': r.name,
                } for r in roles.get_all()],
                'openMembership': bool(obj.flags.allow_joinleave),
                'allowSharedIssues': not obj.flags.disable_shared_issues,
                'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
                'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', False)),
                'dataScrubberDefaults':
                bool(obj.get_option('sentry:require_scrub_defaults', False)),
                'sensitiveFields': obj.get_option('sentry:sensitive_fields', None) or [],
                'safeFields': obj.get_option('sentry:safe_fields', None) or [],
                'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', False)),
            }
        )
        context['teams'] = serialize(team_list, user, TeamWithProjectsSerializer())
        if env.request:
            context['access'] = access.from_request(env.request, obj).scopes
        else:
            context['access'] = access.from_user(user, obj).scopes
        context['features'] = feature_list
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
예제 #43
0
    def serialize(self, obj, attrs, user):
        from sentry import features, experiments
        from sentry.features.base import OrganizationFeature
        from sentry.app import env
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = sorted(Team.objects.filter(
            organization=obj,
            status=TeamStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for team in team_list:
            team._organization_cache = obj

        project_list = sorted(Project.objects.filter(
            organization=obj,
            status=ProjectStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for project in project_list:
            project._organization_cache = obj

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        # Retrieve all registered organization features
        org_features = features.all(feature_type=OrganizationFeature).keys()
        feature_list = set()

        for feature_name in org_features:
            if not feature_name.startswith('organizations:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len('organizations:'):])

        # Do not include the onboarding feature if OrganizationOptions exist
        if 'onboarding' in feature_list and \
                OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.remove('onboarding')

        # Include api-keys feature if they previously had any api-keys
        if 'api-keys' not in feature_list and ApiKey.objects.filter(organization=obj).exists():
            feature_list.add('api-keys')

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.add('legacy-rate-limits')
        if getattr(obj.flags, 'allow_joinleave'):
            feature_list.add('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):
            feature_list.add('shared-issues')
        if getattr(obj.flags, 'require_2fa'):
            feature_list.add('require-2fa')

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['experiments'] = experiment_assignments
        context['quota'] = {
            'maxRate': max_rate[0],
            'maxRateInterval': max_rate[1],
            'accountLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )
            ),
            'projectLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )
            ),
        }

        context.update({
            'isDefault': obj.is_default,
            'defaultRole': obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership': bool(obj.flags.allow_joinleave),
            'require2FA': bool(obj.flags.require_2fa),
            'allowSharedIssues': not obj.flags.disable_shared_issues,
            'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
            'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)),
            'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [],
            'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [],
            'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)),
            'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)),
            'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [],
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user, ProjectSummarySerializer())
        if env.request:
            context['access'] = access.from_request(env.request, obj).scopes
        else:
            context['access'] = access.from_user(user, obj).scopes
        context['features'] = feature_list
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
예제 #44
0
    def serialize(self, obj, attrs, user, access):
        from sentry import experiments

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(organization=obj).select_related("user")
        )

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context["experiments"] = experiment_assignments
        context["quota"] = {
            "maxRate": max_rate[0],
            "maxRateInterval": max_rate[1],
            "accountLimit": int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key="sentry:account-rate-limit",
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )
            ),
            "projectLimit": int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key="sentry:project-rate-limit",
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )
            ),
        }

        context.update(
            {
                "isDefault": obj.is_default,
                "defaultRole": obj.default_role,
                "availableRoles": [{"id": r.id, "name": r.name} for r in roles.get_all()],
                "openMembership": bool(obj.flags.allow_joinleave),
                "require2FA": bool(obj.flags.require_2fa),
                "allowSharedIssues": not obj.flags.disable_shared_issues,
                "enhancedPrivacy": bool(obj.flags.enhanced_privacy),
                "dataScrubber": bool(
                    obj.get_option("sentry:require_scrub_data", REQUIRE_SCRUB_DATA_DEFAULT)
                ),
                "dataScrubberDefaults": bool(
                    obj.get_option("sentry:require_scrub_defaults", REQUIRE_SCRUB_DEFAULTS_DEFAULT)
                ),
                "sensitiveFields": obj.get_option(
                    "sentry:sensitive_fields", SENSITIVE_FIELDS_DEFAULT
                )
                or [],
                "safeFields": obj.get_option("sentry:safe_fields", SAFE_FIELDS_DEFAULT) or [],
                "storeCrashReports": bool(
                    obj.get_option("sentry:store_crash_reports", STORE_CRASH_REPORTS_DEFAULT)
                ),
                "attachmentsRole": six.text_type(
                    obj.get_option("sentry:attachments_role", ATTACHMENTS_ROLE_DEFAULT)
                ),
                "scrubIPAddresses": bool(
                    obj.get_option(
                        "sentry:require_scrub_ip_address", REQUIRE_SCRUB_IP_ADDRESS_DEFAULT
                    )
                ),
                "scrapeJavaScript": bool(
                    obj.get_option("sentry:scrape_javascript", SCRAPE_JAVASCRIPT_DEFAULT)
                ),
                "trustedRelays": obj.get_option("sentry:trusted-relays", TRUSTED_RELAYS_DEFAULT)
                or [],
            }
        )
        context["access"] = access.scopes
        context["pendingAccessRequests"] = OrganizationAccessRequest.objects.filter(
            team__organization=obj
        ).count()
        context["onboardingTasks"] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
예제 #45
0
    def serialize(self, obj, attrs, user):
        from sentry import features, experiments
        from sentry.features.base import OrganizationFeature
        from sentry.app import env
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = sorted(Team.objects.filter(
            organization=obj,
            status=TeamStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for team in team_list:
            team._organization_cache = obj

        project_list = sorted(Project.objects.filter(
            organization=obj,
            status=ProjectStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for project in project_list:
            project._organization_cache = obj

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        # Retrieve all registered organization features
        org_features = features.all(feature_type=OrganizationFeature).keys()
        feature_list = set()

        for feature_name in org_features:
            if not feature_name.startswith('organizations:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len('organizations:'):])

        # Do not include the onboarding feature if OrganizationOptions exist
        if 'onboarding' in feature_list and \
                OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.remove('onboarding')

        # Include api-keys feature if they previously had any api-keys
        if 'api-keys' not in feature_list and ApiKey.objects.filter(organization=obj).exists():
            feature_list.add('api-keys')

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.add('legacy-rate-limits')
        if getattr(obj.flags, 'allow_joinleave'):  # noqa: B009
            feature_list.add('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):  # noqa: B009
            feature_list.add('shared-issues')
        if getattr(obj.flags, 'require_2fa'):  # noqa: B009
            feature_list.add('require-2fa')

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['experiments'] = experiment_assignments
        context['quota'] = {
            'maxRate': max_rate[0],
            'maxRateInterval': max_rate[1],
            'accountLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )
            ),
            'projectLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )
            ),
        }

        context.update({
            'isDefault': obj.is_default,
            'defaultRole': obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership': bool(obj.flags.allow_joinleave),
            'require2FA': bool(obj.flags.require_2fa),
            'allowSharedIssues': not obj.flags.disable_shared_issues,
            'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
            'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)),
            'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [],
            'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [],
            'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)),
            'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)),
            'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [],
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user, ProjectSummarySerializer())
        if env.request:
            context['access'] = access.from_request(env.request, obj).scopes
        else:
            context['access'] = access.from_user(user, obj).scopes
        context['features'] = feature_list
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
예제 #46
0
    def serialize(self, obj, attrs, user):
        from sentry import features, experiments
        from sentry.app import env
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = sorted(Team.objects.filter(
            organization=obj,
            status=TeamStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for team in team_list:
            team._organization_cache = obj

        project_list = sorted(Project.objects.filter(
            organization=obj,
            status=ProjectStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for project in project_list:
            project._organization_cache = obj

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        feature_list = []
        if features.has('organizations:sso', obj, actor=user):
            feature_list.append('sso')
        if features.has('organizations:onboarding', obj, actor=user) and \
                not OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.append('onboarding')
        if features.has('organizations:api-keys', obj, actor=user) or \
                ApiKey.objects.filter(organization=obj).exists():
            feature_list.append('api-keys')
        if features.has('organizations:group-unmerge', obj, actor=user):
            feature_list.append('group-unmerge')
        if features.has('organizations:github-apps', obj, actor=user):
            feature_list.append('github-apps')
        if features.has('organizations:require-2fa', obj, actor=user):
            feature_list.append('require-2fa')
        if features.has('organizations:repos', obj, actor=user):
            feature_list.append('repos')
        if features.has('organizations:internal-catchall', obj, actor=user):
            feature_list.append('internal-catchall')
        if features.has('organizations:new-issue-ui', obj, actor=user):
            feature_list.append('new-issue-ui')
        if features.has('organizations:github-enterprise', obj, actor=user):
            feature_list.append('github-enterprise')
        if features.has('organizations:bitbucket-integration', obj, actor=user):
            feature_list.append('bitbucket-integration')
        if features.has('organizations:jira-integration', obj, actor=user):
            feature_list.append('jira-integration')
        if features.has('organizations:vsts-integration', obj, actor=user):
            feature_list.append('vsts-integration')
        if features.has('organizations:integrations-issue-basic', obj, actor=user):
            feature_list.append('integrations-issue-basic')
        if features.has('organizations:integrations-issue-sync', obj, actor=user):
            feature_list.append('integrations-issue-sync')
        if features.has('organizations:suggested-commits', obj, actor=user):
            feature_list.append('suggested-commits')
        if features.has('organizations:new-teams', obj, actor=user):
            feature_list.append('new-teams')
        if features.has('organizations:unreleased-changes', obj, actor=user):
            feature_list.append('unreleased-changes')
        if features.has('organizations:relay', obj, actor=user):
            feature_list.append('relay')
        if features.has('organizations:js-loader', obj, actor=user):
            feature_list.append('js-loader')
        if features.has('organizations:health', obj, actor=user):
            feature_list.append('health')
        if features.has('organizations:discover', obj, actor=user):
            feature_list.append('discover')
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.append('legacy-rate-limits')
        if getattr(obj.flags, 'allow_joinleave'):
            feature_list.append('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):
            feature_list.append('shared-issues')
        if getattr(obj.flags, 'require_2fa'):
            feature_list.append('require-2fa')
        if features.has('organizations:event-attachments', obj, actor=user):
            feature_list.append('event-attachments')

        experiment_assignments = experiments.all(org=obj)

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['experiments'] = experiment_assignments
        context['quota'] = {
            'maxRate': max_rate[0],
            'maxRateInterval': max_rate[1],
            'accountLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )
            ),
            'projectLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )
            ),
        }

        context.update({
            'isDefault': obj.is_default,
            'defaultRole': obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership': bool(obj.flags.allow_joinleave),
            'require2FA': bool(obj.flags.require_2fa),
            'allowSharedIssues': not obj.flags.disable_shared_issues,
            'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
            'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)),
            'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [],
            'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [],
            'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)),
            'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)),
            'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [],
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user, ProjectSummarySerializer())
        if env.request:
            context['access'] = access.from_request(env.request, obj).scopes
        else:
            context['access'] = access.from_user(user, obj).scopes
        context['features'] = feature_list
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context