def _is_only_owner(self, member):
        if member.role != roles.get_top_dog().id:
            return False

        queryset = OrganizationMember.objects.filter(
            organization=member.organization_id, role=roles.get_top_dog().id, user__isnull=False, user__is_active=True
        ).exclude(id=member.id)
        if queryset.exists():
            return False

        return True
Example #2
0
    def handle(self, request):
        form = self.get_form(request)
        if form.is_valid():
            org = form.save()

            OrganizationMember.objects.create(
                organization=org,
                user=request.user,
                role=roles.get_top_dog().id,
            )

            AuditLogEntry.objects.create(
                organization=org,
                actor=request.user,
                ip_address=request.META['REMOTE_ADDR'],
                target_object=org.id,
                event=AuditLogEntryEvent.ORG_ADD,
                data=org.get_audit_log_data(),
            )

            url = reverse('sentry-create-team', args=[org.slug])

            return HttpResponseRedirect(url)

        context = {
            'form': form,
        }

        return self.respond('sentry/create-organization.html', context)
Example #3
0
    def handle(self, request, organization):
        queryset = OrganizationMember.objects.filter(
            Q(user__is_active=True) | Q(user__isnull=True), organization=organization
        ).select_related("user")

        queryset = sorted(queryset, key=lambda x: x.email or x.user.get_display_name())

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

        oms = list(queryset)

        authenticators = Authenticator.objects.bulk_users_have_2fa([om.user_id for om in oms])

        member_list = []
        for om in oms:
            needs_sso = bool(auth_provider and not getattr(om.flags, "sso:linked"))
            member_list.append((om, needs_sso, authenticators[om.user_id]))

        # if the member is not the only owner we allow them to leave the org
        member_can_leave = any(
            1
            for om, _, _ in member_list
            if (om.role == roles.get_top_dog().id and om.user != request.user and om.user is not None)
        )

        # TODO(dcramer): ideally member:write could approve
        can_approve_requests_globally = request.access.has_scope("org:write")
        can_add_members = request.access.has_scope("org:write")
        can_remove_members = request.access.has_scope("member:delete")

        # pending requests
        if can_approve_requests_globally:
            access_requests = list(
                OrganizationAccessRequest.objects.filter(
                    team__organization=organization, member__user__is_active=True
                ).select_related("team", "member__user")
            )
        elif request.access.has_scope("team:write") and request.access.teams:
            access_requests = list(
                OrganizationAccessRequest.objects.filter(
                    member__user__is_active=True, team__in=request.access.teams
                ).select_related("team", "member__user")
            )
        else:
            access_requests = []

        context = {
            "org_has_sso": auth_provider is not None,
            "member_list": member_list,
            "request_list": access_requests,
            "ref": request.GET.get("ref"),
            "can_add_members": can_add_members,
            "can_remove_members": can_remove_members,
            "member_can_leave": member_can_leave,
        }

        return self.respond("sentry/organization-members.html", context)
Example #4
0
    def handle(self, request):
        org_list = Organization.objects.filter(
            member_set__role=roles.get_top_dog().id,
            member_set__user=request.user,
        )
        org_results = []
        for org in sorted(org_list, key=lambda x: x.name):
            # O(N) query
            org_results.append({
                'organization': org,
                'single_owner': org.has_single_owner(),
            })

        form = self.get_form(request)
        if form.is_valid():
            avail_org_slugs = set([
                o['organization'].slug for o in org_results
            ])
            orgs_to_remove = set(
                request.POST.getlist('oID')
            ).intersection(avail_org_slugs)
            for result in org_results:
                if result['single_owner']:
                    orgs_to_remove.add(result['organization'].slug)

            logging.getLogger('sentry.deletions').info(
                'User (id=%s) removal requested by self',
                request.user.id)

            for org_slug in orgs_to_remove:
                client.delete('/organizations/{}/'.format(org_slug),
                              request.user, is_sudo=True)

            remaining_org_ids = [
                o.id for o in org_list
                if o.slug in avail_org_slugs.difference(orgs_to_remove)
            ]

            if remaining_org_ids:
                OrganizationMember.objects.filter(
                    organization__in=remaining_org_ids,
                    user=request.user,
                ).delete()

            User.objects.filter(
                id=request.user.id,
            ).update(
                is_active=False,
            )

            logout(request)

            return self.respond('sentry/post-remove-account.html')

        context = {
            'form': form,
            'organization_results': org_results,
        }

        return self.respond('sentry/remove-account.html', context)
Example #5
0
 def get_owners(self):
     from sentry.models import User
     return User.objects.filter(
         sentry_orgmember_set__role=roles.get_top_dog().id,
         sentry_orgmember_set__organization=self,
         is_active=True,
     )
    def get(self, request):
        try:
            data = request.GET['data']
        except KeyError:
            raise Http404

        try:
            data, project = self.get_validated_data(data, request.user)
        except InvalidPayload as exc:
            return Response({'detail': exc.message}, status=400)

        organizations = Organization.objects.filter(
            status=OrganizationStatus.ACTIVE,
            id__in=OrganizationMember.objects.filter(
                user=request.user,
                role=roles.get_top_dog().id,
            ).values_list('organization_id', flat=True),
        )

        return Response({
            'organizations': serialize(
                list(organizations),
                request.user,
                DetailedOrganizationSerializer(),
                access=request.access
            ),
            'project': {
                'slug': project.slug,
                'id': project.id,
            }
        })
Example #7
0
    def get_default_owner(self):
        if not hasattr(self, '_default_owner'):
            from sentry.models import User

            self._default_owner = User.objects.filter(
                sentry_orgmember_set__role=roles.get_top_dog().id,
                sentry_orgmember_set__organization=self,
            )[0]
        return self._default_owner
Example #8
0
 def has_single_owner(self):
     from sentry.models import OrganizationMember
     count = OrganizationMember.objects.filter(
         organization=self,
         role=roles.get_top_dog().id,
         user__isnull=False,
         user__is_active=True,
     )[:2].count()
     return count == 1
Example #9
0
    def get_default_owner(self):
        if not hasattr(self, '_default_owner'):
            from sentry.models import User

            self._default_owner = User.objects.filter(
                sentry_orgmember_set__role=roles.get_top_dog().id,
                sentry_orgmember_set__organization=self,
            )[0]
        return self._default_owner
Example #10
0
 def has_single_owner(self):
     from sentry.models import OrganizationMember
     count = OrganizationMember.objects.filter(
         organization=self,
         role=roles.get_top_dog().id,
         user__isnull=False,
         user__is_active=True,
     )[:2].count()
     return count == 1
Example #11
0
def _sso_params(member):
    """
    Return a tuple of (requires_sso, sso_is_valid) for a given member.
    """
    # TODO(dcramer): we want to optimize this access pattern as its several
    # network hops and needed in a lot of places
    from sentry.models import AuthIdentity  # Django 1.9 setup issue
    from sentry.models import AuthProvider  # Django 1.9 setup issue
    from sentry.models import OrganizationMember  # Django 1.9 setup issue
    try:
        auth_provider = AuthProvider.objects.get(
            organization=member.organization_id,
        )
    except AuthProvider.DoesNotExist:
        sso_is_valid = True
        requires_sso = False
    else:
        if auth_provider.flags.allow_unlinked:
            requires_sso = False
            sso_is_valid = True
        else:
            requires_sso = True
            try:
                auth_identity = AuthIdentity.objects.get(
                    auth_provider=auth_provider,
                    user=member.user_id,
                )
            except AuthIdentity.DoesNotExist:
                sso_is_valid = False
                # If an owner is trying to gain access,
                # allow bypassing SSO if there are no other
                # owners with SSO enabled.
                if member.role == roles.get_top_dog().id:
                    requires_sso = AuthIdentity.objects.filter(
                        auth_provider=auth_provider,
                        user__in=OrganizationMember.objects.filter(
                            organization=member.organization_id,
                            role=roles.get_top_dog().id,
                            user__is_active=True,
                        ).exclude(id=member.id).values_list('user_id')
                    ).exists()
            else:
                sso_is_valid = auth_identity.is_valid(member)
    return requires_sso, sso_is_valid
Example #12
0
    def handle(self, request, organization):
        queryset = OrganizationMember.objects.filter(
            Q(user__is_active=True) | Q(user__isnull=True),
            organization=organization,
        ).select_related('user')

        queryset = sorted(queryset, key=lambda x: x.email or x.user.get_display_name())

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

        member_list = []
        for om in queryset:
            needs_sso = bool(auth_provider and not getattr(om.flags, 'sso:linked'))
            member_list.append((om, needs_sso))

        # if the member is not the only owner we allow them to leave the org
        member_can_leave = any(
            1 for om, _ in member_list
            if (om.role == roles.get_top_dog().id
                and om.user != request.user
                and om.user is not None)
        )

        # TODO(dcramer): ideally member:write could approve
        can_approve_requests_globally = request.access.has_scope('org:write')
        can_add_members = request.access.has_scope('org:write')
        can_remove_members = request.access.has_scope('member:delete')

        # pending requests
        if can_approve_requests_globally:
            access_requests = list(OrganizationAccessRequest.objects.filter(
                team__organization=organization,
                member__user__is_active=True,
            ).select_related('team', 'member__user'))
        elif request.access.has_scope('team:write') and request.access.teams:
            access_requests = list(OrganizationAccessRequest.objects.filter(
                member__user__is_active=True,
                team__in=request.access.teams,
            ).select_related('team', 'member__user'))
        else:
            access_requests = []

        context = {
            'org_has_sso': auth_provider is not None,
            'member_list': member_list,
            'request_list': access_requests,
            'ref': request.GET.get('ref'),
            'can_add_members': can_add_members,
            'can_remove_members': can_remove_members,
            'member_can_leave': member_can_leave,
        }

        return self.respond('sentry/organization-members.html', context)
Example #13
0
def createuser(email, password, superuser, no_password, no_input):
    "Create a new user."
    if not no_input:
        if not email:
            email = _get_email()

        if not (password or no_password):
            password = _get_password()

        if superuser is None:
            superuser = _get_superuser()

    if superuser is None:
        superuser = False

    if not email:
        raise click.ClickException('Invalid or missing email address.')

    # TODO(mattrobenolt): Accept password over stdin?
    if not no_password and not password:
        raise click.ClickException(
            'No password set and --no-password not passed.')

    from sentry import roles
    from sentry.models import User
    from django.conf import settings

    user = User(
        email=email,
        username=email,
        is_superuser=superuser,
        is_staff=superuser,
        is_active=True,
    )

    if password:
        user.set_password(password)

    user.save()

    click.echo('User created: %s' % (email, ))

    # TODO(dcramer): kill this when we improve flows
    if settings.SENTRY_SINGLE_ORGANIZATION:
        from sentry.models import Organization, OrganizationMember
        org = Organization.get_default()
        if superuser:
            role = roles.get_top_dog().id
        else:
            role = org.default_role
        OrganizationMember.objects.create(
            organization=org,
            user=user,
            role=role,
        )
        click.echo('Added to organization: %s' % (org.slug, ))
Example #14
0
    def handle(self, request, organization):
        queryset = OrganizationMember.objects.filter(
            organization=organization,
        ).select_related('user')

        queryset = sorted(queryset, key=lambda x: x.email or x.user.get_display_name())

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

        member_list = []
        for om in queryset:
            needs_sso = bool(auth_provider and not getattr(om.flags, 'sso:linked'))
            member_list.append((om, needs_sso))

        # if the member is not the only owner we allow them to leave the org
        member_can_leave = any(
            1 for om, _ in member_list
            if (om.role == roles.get_top_dog().id
                and om.user != request.user
                and om.user is not None)
        )

        can_approve_requests_globally = (
            request.access.has_scope('member:write')
            or request.access.has_scope('org:write')
        )
        can_remove_members = request.access.has_scope('member:delete')

        # pending requests
        if can_approve_requests_globally:
            access_requests = list(OrganizationAccessRequest.objects.filter(
                team__organization=organization,
            ).select_related('team', 'member__user'))
        elif request.access.has_scope('team:write'):
            access_requests = list(OrganizationAccessRequest.objects.filter(
                team__in=OrganizationMemberTeam.objects.filter(
                    organizationmember__organization=organization,
                    organizationmember__user=request.user,
                ).values('team'),
            ).select_related('team', 'member__user'))
        else:
            access_requests = []

        context = {
            'org_has_sso': auth_provider is not None,
            'member_list': member_list,
            'request_list': access_requests,
            'ref': request.GET.get('ref'),
            'can_remove_members': can_remove_members,
            'member_can_leave': member_can_leave,
        }

        return self.respond('sentry/organization-members.html', context)
Example #15
0
 def test_single_org_superuser(self):
     with self.settings(SENTRY_SINGLE_ORGANIZATION=True):
         rv = self.invoke('[email protected]', '--no-password', '--superuser')
         assert rv.exit_code == 0, rv.output
         assert '*****@*****.**' in rv.output
         assert OrganizationMember.objects.count() == 1
         member = OrganizationMember.objects.all()[0]
         assert member.user.email == '*****@*****.**'
         assert member.organization.slug in rv.output
         assert member.role == roles.get_top_dog().id
Example #16
0
def createuser(email, password, superuser, no_password, no_input):
    "Create a new user."
    if not no_input:
        if not email:
            email = _get_email()

        if not (password or no_password):
            password = _get_password()

        if superuser is None:
            superuser = _get_superuser()

    if superuser is None:
        superuser = False

    if not email:
        raise click.ClickException('Invalid or missing email address.')

    # TODO(mattrobenolt): Accept password over stdin?
    if not no_password and not password:
        raise click.ClickException('No password set and --no-password not passed.')

    from sentry import roles
    from sentry.models import User
    from django.conf import settings

    user = User(
        email=email,
        username=email,
        is_superuser=superuser,
        is_staff=superuser,
        is_active=True,
    )

    if password:
        user.set_password(password)

    user.save()

    click.echo('User created: %s' % (email,))

    # TODO(dcramer): kill this when we improve flows
    if settings.SENTRY_SINGLE_ORGANIZATION:
        from sentry.models import Organization, OrganizationMember
        org = Organization.get_default()
        if superuser:
            role = roles.get_top_dog().id
        else:
            role = org.default_role
        OrganizationMember.objects.create(
            organization=org,
            user=user,
            role=role,
        )
        click.echo('Added to organization: %s' % (org.slug,))
Example #17
0
    def dispatch(self, request):
        # need this check for tests since the route will exist even if DEMO_MODE=False
        if not settings.DEMO_MODE:
            raise Http404

        # TODO: add way to ensure we generate unique petnames
        name = generate_random_name()

        slug = slugify(name)

        email = create_fake_email(slug, "demo")
        user = User.objects.create(
            email=email,
            username=email,
            is_managed=True,
            flags=User.flags["demo_mode"],
        )

        org = Organization.objects.create(
            name=name,
            slug=slug,
            flags=Organization.flags["demo_mode"],
        )
        team = org.team_set.create(name=org.name)

        owner = User.objects.get(email=settings.DEMO_ORG_OWNER_EMAIL)
        OrganizationMember.objects.create(organization=org,
                                          user=owner,
                                          role=roles.get_top_dog().id)

        member = OrganizationMember.objects.create(organization=org,
                                                   user=user,
                                                   role="member")
        OrganizationMemberTeam.objects.create(team=team,
                                              organizationmember=member,
                                              is_active=True)

        python_project = Project.objects.create(name="Python",
                                                organization=org,
                                                platform="python")
        python_project.add_team(team)

        reat_project = Project.objects.create(name="React",
                                              organization=org,
                                              platform="javascript-react")
        reat_project.add_team(team)

        populate_python_project(python_project)
        populate_react_project(reat_project)

        # delete all DSNs for the org so people don't send events
        ProjectKey.objects.filter(project__organization=org).delete()

        auth.login(request, user)
        return self.redirect(auth.get_login_redirect(request))
Example #18
0
    def handle(self, request):
        org_list = Organization.objects.filter(
            member_set__role=roles.get_top_dog().id,
            member_set__user=request.user,
        )
        org_results = []
        for org in sorted(org_list, key=lambda x: x.name):
            # O(N) query
            org_results.append({
                'organization': org,
                'single_owner': org.has_single_owner(),
            })

        form = self.get_form(request)
        if form.is_valid():
            avail_org_slugs = set(
                [o['organization'].slug for o in org_results])
            orgs_to_remove = set(
                request.POST.getlist('oID')).intersection(avail_org_slugs)
            for result in org_results:
                if result['single_owner']:
                    orgs_to_remove.add(result['organization'].slug)

            logging.getLogger('sentry.deletions').info(
                'User (id=%s) removal requested by self', request.user.id)

            for org_slug in orgs_to_remove:
                client.delete('/organizations/{}/'.format(org_slug),
                              request=request,
                              is_sudo=True)

            remaining_org_ids = [
                o.id for o in org_list
                if o.slug in avail_org_slugs.difference(orgs_to_remove)
            ]

            if remaining_org_ids:
                OrganizationMember.objects.filter(
                    organization__in=remaining_org_ids,
                    user=request.user,
                ).delete()

            User.objects.filter(id=request.user.id, ).update(is_active=False, )

            logout(request)

            return self.respond('sentry/post-remove-account.html')

        context = {
            'form': form,
            'organization_results': org_results,
        }

        return self.respond('sentry/remove-account.html', context)
Example #19
0
    def post(self, request):
        """
        Create a New Organization
        `````````````````````````

        Create a new organization owned by the request's user.  To create
        an organization only the name is required.

        :param string name: the human readable name for the new organization.
        :param string slug: the unique URL slug for this organization.  If
                            this is not provided a slug is automatically
                            generated based on the name.
        :auth: required, user-context-needed
        """
        if not request.user.is_authenticated():
            return Response({'detail': 'This endpoint requires user info'},
                            status=401)

        serializer = OrganizationSerializer(data=request.DATA)

        if serializer.is_valid():
            result = serializer.object

            try:
                with transaction.atomic():
                    org = Organization.objects.create(
                        name=result['name'],
                        slug=result.get('slug'),
                    )
            except IntegrityError:
                return Response(
                    {
                        'detail':
                        'An organization with this slug already exists.'
                    },
                    status=409,
                )

            OrganizationMember.objects.create(
                user=request.user,
                organization=org,
                role=roles.get_top_dog().id,
            )

            self.create_audit_entry(
                request=request,
                organization=org,
                target_object=org.id,
                event=AuditLogEntryEvent.ORG_ADD,
                data=org.get_audit_log_data(),
            )

            return Response(serialize(org, request.user), status=201)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example #20
0
 def test_single_org_superuser(self):
     with self.settings(SENTRY_SINGLE_ORGANIZATION=True):
         rv = self.invoke("[email protected]",
                          "--no-password", "--superuser")
         assert rv.exit_code == 0, rv.output
         assert "*****@*****.**" in rv.output
         assert OrganizationMember.objects.count() == 1
         member = OrganizationMember.objects.all()[0]
         assert member.user.email == "*****@*****.**"
         assert member.organization.slug in rv.output
         assert member.role == roles.get_top_dog().id
Example #21
0
def _sso_params(member):
    """
    Return a tuple of (requires_sso, sso_is_valid) for a given member.
    """
    # TODO(dcramer): we want to optimize this access pattern as its several
    # network hops and needed in a lot of places
    try:
        auth_provider = AuthProvider.objects.get(
            organization=member.organization_id,
        )
    except AuthProvider.DoesNotExist:
        sso_is_valid = True
        requires_sso = False
    else:
        if auth_provider.flags.allow_unlinked:
            requires_sso = False
            sso_is_valid = True
        else:
            requires_sso = True
            try:
                auth_identity = AuthIdentity.objects.get(
                    auth_provider=auth_provider,
                    user=member.user_id,
                )
            except AuthIdentity.DoesNotExist:
                sso_is_valid = False
                # If an owner is trying to gain access,
                # allow bypassing SSO if there are no other
                # owners with SSO enabled.
                if member.role == roles.get_top_dog().id:
                    requires_sso = AuthIdentity.objects.filter(
                        auth_provider=auth_provider,
                        user__in=OrganizationMember.objects.filter(
                            organization=member.organization_id,
                            role=roles.get_top_dog().id,
                            user__is_active=True,
                        ).exclude(id=member.id).values_list('user_id')
                    ).exists()
            else:
                sso_is_valid = auth_identity.is_valid(member)
    return requires_sso, sso_is_valid
def create_demo_org(quick=False) -> Organization:
    # wrap the main org setup in transaction
    with transaction.atomic():
        name = generate_random_name()

        slug = slugify(name)

        demo_org = DemoOrganization.create_org(name=name, slug=slug)
        org = demo_org.organization

        logger.info("create_demo_org.created_org", {"organization_slug": slug})

        owner = User.objects.get(email=settings.DEMO_ORG_OWNER_EMAIL)
        OrganizationMember.objects.create(organization=org, user=owner, role=roles.get_top_dog().id)

        team = org.team_set.create(name=org.name)
        python_project = Project.objects.create(name="Python", organization=org, platform="python")
        python_project.add_team(team)

        react_project = Project.objects.create(
            name="React", organization=org, platform="javascript-react"
        )
        react_project.add_team(team)

        # we'll be adding transactions later
        Project.objects.filter(organization=org).update(
            flags=F("flags").bitor(Project.flags.has_transactions)
        )

    logger.info(
        "create_demo_org.post-transaction",
        extra={"organization_slug": org.slug, "quick": quick},
    )
    try:
        handle_react_python_scenario(react_project, python_project, quick=quick)
    except Exception as e:
        logger.error(
            "create_demo_org.population_error",
            extra={"organization_slug": org.slug, "quick": quick, "error": str(e)},
        )
        # delete the organization if data population fails
        org.status = OrganizationStatus.PENDING_DELETION
        org.save()
        delete_organization.apply_async(kwargs={"object_id": org.id})
        raise

    # update the org status now that it's populated
    demo_org.status = DemoOrgStatus.PENDING
    demo_org.save()

    return org
Example #23
0
    def post(self, request):
        """
        Create a New Organization
        `````````````````````````

        Create a new organization owned by the request's user.  To create
        an organization only the name is required.

        :param string name: the human readable name for the new organization.
        :param string slug: the unique URL slug for this organization.  If
                            this is not provided a slug is automatically
                            generated based on the name.
        :auth: required, user-context-needed
        """
        if not request.user.is_authenticated():
            return Response({'detail': 'This endpoint requires user info'},
                            status=401)

        serializer = OrganizationSerializer(data=request.DATA)

        if serializer.is_valid():
            result = serializer.object

            try:
                with transaction.atomic():
                    org = Organization.objects.create(
                        name=result['name'],
                        slug=result.get('slug'),
                    )
            except IntegrityError:
                return Response(
                    {'detail': 'An organization with this slug already exists.'},
                    status=409,
                )

            OrganizationMember.objects.create(
                user=request.user,
                organization=org,
                role=roles.get_top_dog().id,
            )

            self.create_audit_entry(
                request=request,
                organization=org,
                target_object=org.id,
                event=AuditLogEntryEvent.ORG_ADD,
                data=org.get_audit_log_data(),
            )

            return Response(serialize(org, request.user), status=201)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example #24
0
    def handle(self, **options):
        email = options['email']
        is_superuser = options['is_superuser']
        password = options['password']

        if not options['noinput']:
            try:
                if not email:
                    email = self.get_email()

                if not (password or options['nopassword']):
                    password = self.get_password()

                if is_superuser is None:
                    is_superuser = self.get_superuser()
            except KeyboardInterrupt:
                self.stderr.write("\nOperation cancelled.")
                sys.exit(1)

        if not email:
            raise CommandError('Invalid or missing email address')

        if not options['nopassword'] and not password:
            raise CommandError('No password set and --no-password not passed')

        user = User(
            email=email,
            username=email,
            is_superuser=is_superuser,
            is_staff=is_superuser,
            is_active=True,
        )

        if password:
            user.set_password(password)

        user.save()

        self.stdout.write('User created: %s' % (email, ))

        # TODO(dcramer): kill this when we improve flows
        if settings.SENTRY_SINGLE_ORGANIZATION:
            org = Organization.get_default()
            OrganizationMember.objects.create(
                organization=org,
                user=user,
                role=roles.get_top_dog().id,
            )
            self.stdout.write('Added to organization: %s' % (org.slug, ))
Example #25
0
    def post(self, request):
        try:
            data = request.DATA['data']
        except KeyError:
            raise Http404

        try:
            data, project = self.get_validated_data(data, request.user)
        except InvalidPayload as exc:
            return Response({'detail': exc.message}, status=400)

        transaction_id = data['transaction_id']

        team_id = request.DATA.get('team')
        if team_id is None:
            return Response(
                {'detail': 'Choose a team to transfer the project to'},
                status=400)

        try:
            team = Team.objects.get(id=team_id, )
        except Team.DoesNotExist:
            return Response({'detail': 'Invalid team'}, status=400)

        # check if user is an owner of the team's org
        is_team_org_owner = OrganizationMember.objects.filter(
            user__is_active=True,
            user=request.user,
            role=roles.get_top_dog().id,
            organization_id=team.organization_id,
        ).exists()

        if not is_team_org_owner:
            return Response({'detail': 'Invalid team'}, status=400)

        project.transfer_to(team)

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

        return Response(status=204)
Example #26
0
    def handle(self, request):
        form = self.get_form(request)
        if form.is_valid():
            org = form.save()

            om = OrganizationMember.objects.create(
                organization=org,
                user=request.user,
                role=roles.get_top_dog().id,
            )

            team = org.team_set.create(
                name=org.name,
            )

            OrganizationMemberTeam.objects.create(
                team=team,
                organizationmember=om,
                is_active=True
            )

            AuditLogEntry.objects.create(
                organization=org,
                actor=request.user,
                ip_address=request.META['REMOTE_ADDR'],
                target_object=org.id,
                event=AuditLogEntryEvent.ORG_ADD,
                data=org.get_audit_log_data(),
            )

            url = reverse('sentry-create-project', args=[org.slug])

            return HttpResponseRedirect('{}?team={}'.format(url, team.slug))

        context = {
            'form': form,
        }

        return self.respond('sentry/create-organization.html', context)
    def handle(self, request, *args, **kwargs):
        try:
            d = request.GET['data']
        except KeyError:
            raise Http404

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

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

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

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

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

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

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

        context = {
            'project': project,
            'form': form,
        }
        return self.respond('sentry/projects/accept_project_transfer.html', context)
Example #28
0
    def post(self, request):
        """
        Create a New Organization
        `````````````````````````

        Create a new organization owned by the request's user.  To create
        an organization only the name is required.

        :param string name: the human readable name for the new organization.
        :param string slug: the unique URL slug for this organization.  If
                            this is not provided a slug is automatically
                            generated based on the name.
        :auth: required, user-context-needed
        """
        if not request.user.is_authenticated():
            return Response({'detail': 'This endpoint requires user info'},
                            status=401)

        if not features.has('organizations:create', actor=request.user):
            return Response({
                'detail': 'Organizations are not allowed to be created by this user.'
            }, status=401)

        limit = options.get('api.rate-limit.org-create')
        if limit and ratelimiter.is_limited(
            u'org-create:{}'.format(request.user.id),
            limit=5, window=3600,
        ):
            return Response({
                'detail': 'You are attempting to create too many organizations too quickly.'
            }, status=429)

        serializer = OrganizationSerializer(data=request.DATA)

        if serializer.is_valid():
            result = serializer.object

            try:
                with transaction.atomic():
                    org = Organization.objects.create(
                        name=result['name'],
                        slug=result.get('slug'),
                    )
            except IntegrityError:
                return Response(
                    {'detail': 'An organization with this slug already exists.'},
                    status=409,
                )

            om = OrganizationMember.objects.create(
                organization=org,
                user=request.user,
                role=roles.get_top_dog().id,
            )

            if result.get('defaultTeam'):
                team = org.team_set.create(
                    name=org.name,
                )

                OrganizationMemberTeam.objects.create(
                    team=team,
                    organizationmember=om,
                    is_active=True
                )

            self.create_audit_entry(
                request=request,
                organization=org,
                target_object=org.id,
                event=AuditLogEntryEvent.ORG_ADD,
                data=org.get_audit_log_data(),
            )

            return Response(serialize(org, request.user), status=201)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example #29
0
    def handle(self, request, organization, project):
        form = self.get_form(request)

        if form.is_valid():
            email = form.cleaned_data.get('email')
            try:
                owner = OrganizationMember.objects.filter(
                    user__email__iexact=email,
                    role=roles.get_top_dog().id,
                    user__is_active=True,
                )[0]
            except IndexError:
                messages.add_message(
                    request, messages.ERROR, six.text_type(
                        _('Could not find owner with that email')))
                return self.respond('sentry/projects/transfer.html', context={'form': form})

            transaction_id = uuid4().hex
            url_data = sign(
                actor_id=request.user.id,
                from_organization_id=organization.id,
                project_id=project.id,
                user_id=owner.user_id,
                transaction_id=transaction_id)

            has_new_teams = features.has(
                'organizations:new-teams',
                organization,
                actor=request.user,
            )
            context = {
                'email': email,
                'from_org': organization.name,
                'project_name': project.slug if has_new_teams else project.name,
                'request_time': timezone.now(),
                'url':
                absolute_uri('/accept-transfer/') + '?' + urlencode({'data': url_data}),
                'requester': request.user
            }
            MessageBuilder(
                subject='%sRequest for Project Transfer' %
                (options.get('mail.subject-prefix'), ),
                template='sentry/emails/transfer_project.txt',
                html_template='sentry/emails/transfer_project.html',
                type='org.confirm_project_transfer_request',
                context=context,
            ).send_async([email])

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

            messages.add_message(
                request, messages.SUCCESS,
                _(u'A request was sent to move project %r to a different organization') %
                ((project.slug if has_new_teams else project.name).encode('utf-8'), )
            )

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

        context = {
            'form': form,
        }

        return self.respond('sentry/projects/transfer.html', context)
Example #30
0
    def post(self, request):
        """
        Create a New Organization
        `````````````````````````

        Create a new organization owned by the request's user.  To create
        an organization only the name is required.

        :param string name: the human readable name for the new organization.
        :param string slug: the unique URL slug for this organization.  If
                            this is not provided a slug is automatically
                            generated based on the name.
        :auth: required, user-context-needed
        """
        if not request.user.is_authenticated():
            return Response({'detail': 'This endpoint requires user info'},
                            status=401)

        if not features.has('organizations:create', actor=request.user):
            return Response(
                {
                    'detail':
                    'Organizations are not allowed to be created by this user.'
                },
                status=401)

        limit = options.get('api.rate-limit.org-create')
        if limit and ratelimiter.is_limited(
                u'org-create:{}'.format(request.user.id),
                limit=limit,
                window=3600,
        ):
            return Response(
                {
                    'detail':
                    'You are attempting to create too many organizations too quickly.'
                },
                status=429)

        serializer = OrganizationSerializer(data=request.DATA)

        if serializer.is_valid():
            result = serializer.object

            try:
                with transaction.atomic():
                    org = Organization.objects.create(
                        name=result['name'],
                        slug=result.get('slug'),
                    )
            except IntegrityError:
                return Response(
                    {
                        'detail':
                        'An organization with this slug already exists.'
                    },
                    status=409,
                )

            om = OrganizationMember.objects.create(
                organization=org,
                user=request.user,
                role=roles.get_top_dog().id,
            )

            if result.get('defaultTeam'):
                team = org.team_set.create(name=org.name, )

                OrganizationMemberTeam.objects.create(team=team,
                                                      organizationmember=om,
                                                      is_active=True)

            self.create_audit_entry(
                request=request,
                organization=org,
                target_object=org.id,
                event=AuditLogEntryEvent.ORG_ADD,
                data=org.get_audit_log_data(),
            )

            analytics.record('organization.created',
                             org,
                             actor_id=request.user.id
                             if request.user.is_authenticated() else None)

            return Response(serialize(org, request.user), status=201)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example #31
0
    def get(self, request):
        """
        List your Organizations
        ```````````````````````

        Return a list of organizations available to the authenticated
        session.  This is particularly useful for requests with an
        user bound context.  For API key based requests this will
        only return the organization that belongs to the key.

        :qparam bool owner: restrict results to organizations in which you are
                            an organization owner

        :auth: required
        """
        owner_only = request.GET.get("owner") in ("1", "true")

        queryset = Organization.objects.distinct()

        if request.auth and not request.user.is_authenticated():
            if hasattr(request.auth, "project"):
                queryset = queryset.filter(
                    id=request.auth.project.organization_id)
            elif request.auth.organization is not None:
                queryset = queryset.filter(id=request.auth.organization.id)

        elif owner_only:
            # This is used when closing an account
            queryset = queryset.filter(
                member_set__role=roles.get_top_dog().id,
                member_set__user=request.user,
                status=OrganizationStatus.VISIBLE,
            )
            org_results = []
            for org in sorted(queryset, key=lambda x: x.name):
                # O(N) query
                org_results.append({
                    "organization": serialize(org),
                    "singleOwner": org.has_single_owner()
                })

            return Response(org_results)

        elif not (is_active_superuser(request)
                  and request.GET.get("show") == "all"):
            queryset = queryset.filter(
                id__in=OrganizationMember.objects.filter(
                    user=request.user).values("organization"))

        query = request.GET.get("query")
        if query:
            tokens = tokenize_query(query)
            for key, value in six.iteritems(tokens):
                if key == "query":
                    value = " ".join(value)
                    queryset = queryset.filter(
                        Q(name__icontains=value)
                        | Q(slug__icontains=value)
                        | Q(members__email__iexact=value))
                elif key == "slug":
                    queryset = queryset.filter(in_iexact("slug", value))
                elif key == "email":
                    queryset = queryset.filter(
                        in_iexact("members__email", value))
                elif key == "platform":
                    queryset = queryset.filter(
                        project__in=ProjectPlatform.objects.filter(
                            platform__in=value).values("project_id"))
                elif key == "id":
                    queryset = queryset.filter(id__in=value)
                elif key == "status":
                    try:
                        queryset = queryset.filter(status__in=[
                            OrganizationStatus[v.upper()] for v in value
                        ])
                    except KeyError:
                        queryset = queryset.none()
                elif key == "member_id":
                    queryset = queryset.filter(
                        id__in=OrganizationMember.objects.filter(
                            id__in=value).values("organization"))
                else:
                    queryset = queryset.none()

        sort_by = request.GET.get("sortBy")
        if sort_by == "members":
            queryset = queryset.annotate(member_count=Count("member_set"))
            order_by = "-member_count"
            paginator_cls = OffsetPaginator
        elif sort_by == "projects":
            queryset = queryset.annotate(project_count=Count("project"))
            order_by = "-project_count"
            paginator_cls = OffsetPaginator
        elif sort_by == "events":
            queryset = queryset.annotate(
                event_count=Sum("stats__events_24h")).filter(
                    stats__events_24h__isnull=False)
            order_by = "-event_count"
            paginator_cls = OffsetPaginator
        else:
            order_by = "-date_added"
            paginator_cls = DateTimePaginator

        return self.paginate(
            request=request,
            queryset=queryset,
            order_by=order_by,
            on_results=lambda x: serialize(x, request.user),
            paginator_cls=paginator_cls,
        )
    def handle(self, request, organization, project):
        form = self.get_form(request)

        if form.is_valid():
            email = form.cleaned_data.get('email')
            try:
                owner = OrganizationMember.objects.filter(
                    user__email__iexact=email,
                    role=roles.get_top_dog().id,
                    user__is_active=True,
                )[0]
            except IndexError:
                messages.add_message(
                    request, messages.ERROR,
                    six.text_type(_('Could not find owner with that email')))
                return self.respond('sentry/projects/transfer.html',
                                    context={'form': form})

            transaction_id = uuid4().hex
            url_data = sign(actor_id=request.user.id,
                            from_organization_id=organization.id,
                            project_id=project.id,
                            user_id=owner.user_id,
                            transaction_id=transaction_id)
            context = {
                'email':
                email,
                'from_org':
                organization.name,
                'project_name':
                project.name,
                'request_time':
                timezone.now(),
                'url':
                absolute_uri('/accept-transfer/') + '?' +
                urlencode({'data': url_data}),
                'requester':
                request.user
            }
            MessageBuilder(
                subject='%sRequest for Project Transfer' %
                (options.get('mail.subject-prefix'), ),
                template='sentry/emails/transfer_project.txt',
                html_template='sentry/emails/transfer_project.html',
                type='org.confirm_project_transfer_request',
                context=context,
            ).send_async([email])

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

            messages.add_message(
                request, messages.SUCCESS,
                _(u'A request was sent to move project %r to a different organization'
                  ) % (project.name.encode('utf-8'), ))

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

        context = {
            'form': form,
        }

        return self.respond('sentry/projects/transfer.html', context)
    def post(self, request):
        try:
            data = request.DATA['data']
        except KeyError:
            raise Http404

        try:
            data, project = self.get_validated_data(data, request.user)
        except InvalidPayload as exc:
            return Response({'detail': exc.message}, status=400)

        transaction_id = data['transaction_id']

        org_slug = request.DATA.get('organization')
        team_id = request.DATA.get('team')

        if org_slug is not None and team_id is not None:
            return Response({
                'detail': 'Choose either a team or an organization, not both'
            }, status=400)

        if org_slug is None and team_id is None:
            return Response({
                'detail': 'Choose either a team or an organization to transfer the project to'
            }, status=400)

        if team_id:
            try:
                team = Team.objects.get(
                    id=team_id,
                )
            except Team.DoesNotExist:
                return Response({'detail': 'Invalid team'}, status=400)

            # check if user is an owner of the team's org
            is_team_org_owner = OrganizationMember.objects.filter(
                user__is_active=True,
                user=request.user,
                role=roles.get_top_dog().id,
                organization_id=team.organization_id,
            ).exists()

            if not is_team_org_owner:
                return Response({'detail': 'Invalid team'}, status=400)

            project.transfer_to(team=team)

        if org_slug:
            try:
                organization = Organization.objects.get(
                    slug=org_slug,
                )
            except Organization.DoesNotExist:
                return Response({'detail': 'Invalid organization'}, status=400)

            # check if user is an owner of the organization
            is_org_owner = OrganizationMember.objects.filter(
                user__is_active=True,
                user=request.user,
                role=roles.get_top_dog().id,
                organization_id=organization.id,
            ).exists()

            if not is_org_owner:
                return Response({'detail': 'Invalid organization'}, status=400)

            project.transfer_to(organization=organization)

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

        return Response(status=204)
Example #34
0
    def post(self, request, project):
        """
        Transfer a Project
        ````````````````

        Schedules a project for transfer to a new organization.

        :pparam string organization_slug: the slug of the organization the
                                          project belongs to.
        :pparam string project_slug: the slug of the project to delete.
        :param string email: email of new owner. must be an organization owner
        :auth: required
        """
        if project.is_internal_project():
            return Response(
                '{"error": "Cannot transfer projects internally used by Sentry."}',
                status=status.HTTP_403_FORBIDDEN,
            )

        email = request.data.get("email")

        if email is None:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        if not request.user.is_authenticated():
            return Response(status=status.HTTP_403_FORBIDDEN)

        try:
            owner = OrganizationMember.objects.filter(
                user__email__iexact=email,
                role=roles.get_top_dog().id,
                user__is_active=True)[0]
        except IndexError:
            return Response(
                {
                    "detail":
                    "Could not find an organization owner with that email"
                },
                status=status.HTTP_404_NOT_FOUND,
            )

        transaction_id = uuid4().hex
        url_data = sign(
            actor_id=request.user.id,
            from_organization_id=project.organization.id,
            project_id=project.id,
            user_id=owner.user_id,
            transaction_id=transaction_id,
        )

        context = {
            "email":
            email,
            "from_org":
            project.organization.name,
            "project_name":
            project.slug,
            "request_time":
            timezone.now(),
            "url":
            absolute_uri("/accept-transfer/") + "?" +
            urlencode({"data": url_data}),
            "requester":
            request.user,
        }
        MessageBuilder(
            subject="{}Request for Project Transfer".format(
                options.get("mail.subject-prefix")),
            template="sentry/emails/transfer_project.txt",
            html_template="sentry/emails/transfer_project.html",
            type="org.confirm_project_transfer_request",
            context=context,
        ).send_async([email])

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

        return Response(status=status.HTTP_204_NO_CONTENT)
Example #35
0
    def post(self, request, project):
        """
        Transfer a Project
        ````````````````

        Schedules a project for transfer to a new organization.

        :pparam string organization_slug: the slug of the organization the
                                          project belongs to.
        :pparam string project_slug: the slug of the project to delete.
        :param string email: email of new owner. must be an organization owner
        :auth: required
        """
        if project.is_internal_project():
            return Response(
                '{"error": "Cannot transfer projects internally used by Sentry."}',
                status=status.HTTP_403_FORBIDDEN
            )

        email = request.DATA.get('email')

        if email is None:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        if not request.user.is_authenticated():
            return Response(status=status.HTTP_403_FORBIDDEN)

        try:
            owner = OrganizationMember.objects.filter(
                user__email__iexact=email,
                role=roles.get_top_dog().id,
                user__is_active=True,
            )[0]
        except IndexError:
            return Response({'detail': 'Could not find owner with that email'},
                            status=status.HTTP_404_NOT_FOUND)

        transaction_id = uuid4().hex
        url_data = sign(
            actor_id=request.user.id,
            from_organization_id=project.organization.id,
            project_id=project.id,
            user_id=owner.user_id,
            transaction_id=transaction_id)

        context = {
            'email': email,
            'from_org': project.organization.name,
            'project_name': project.slug,
            'request_time': timezone.now(),
            'url':
            absolute_uri('/accept-transfer/') + '?' + urlencode({'data': url_data}),
            'requester': request.user
        }
        MessageBuilder(
            subject='%sRequest for Project Transfer' %
            (options.get('mail.subject-prefix'), ),
            template='sentry/emails/transfer_project.txt',
            html_template='sentry/emails/transfer_project.html',
            type='org.confirm_project_transfer_request',
            context=context,
        ).send_async([email])

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

        return Response(status=status.HTTP_204_NO_CONTENT)
Example #36
0
def createuser(email, password, superuser, no_password, no_input):
    "Create a new user."
    if not no_input:
        if not email:
            email = _get_email()

        if not (password or no_password):
            password = _get_password()

        if superuser is None:
            superuser = _get_superuser()

    if superuser is None:
        superuser = False

    if not email:
        raise click.ClickException('Invalid or missing email address.')

    # TODO(mattrobenolt): Accept password over stdin?
    if not no_password and not password:
        raise click.ClickException('No password set and --no-password not passed.')

    from sentry import roles
    from sentry.models import User
    from django.conf import settings

    user = User(
        email=email,
        username=email,
        is_superuser=superuser,
        is_staff=superuser,
        is_active=True,
    )

    if password:
        user.set_password(password)

    user.save()

    click.echo('User created: %s' % (email, ))

    # TODO(dcramer): kill this when we improve flows
    if settings.SENTRY_SINGLE_ORGANIZATION:
        from sentry.models import (Organization, OrganizationMember, OrganizationMemberTeam, Team)

        org = Organization.get_default()
        if superuser:
            role = roles.get_top_dog().id
        else:
            role = org.default_role
        member = OrganizationMember.objects.create(
            organization=org,
            user=user,
            role=role,
        )

        # if we've only got a single team let's go ahead and give
        # access to that team as its likely the desired outcome
        teams = list(Team.objects.filter(organization=org)[0:2])
        if len(teams) == 1:
            OrganizationMemberTeam.objects.create(
                team=teams[0],
                organizationmember=member,
            )
        click.echo('Added to organization: %s' % (org.slug, ))
Example #37
0
    def get(self, request):
        """
        List your Organizations
        ```````````````````````

        Return a list of organizations available to the authenticated
        session.  This is particularly useful for requests with an
        user bound context.  For API key based requests this will
        only return the organization that belongs to the key.

        :qparam bool member: restrict results to organizations which you have
                             membership
        :qparam bool owner: restrict results to organizations which are owner

        :auth: required
        """
        member_only = request.GET.get('member') in ('1', 'true')
        owner_only = request.GET.get('owner') in ('1', 'true')

        queryset = Organization.objects.all()

        if request.auth and not request.user.is_authenticated():
            if hasattr(request.auth, 'project'):
                queryset = queryset.filter(
                    id=request.auth.project.organization_id)
            elif request.auth.organization is not None:
                queryset = queryset.filter(id=request.auth.organization.id)

        elif owner_only:
            # This is used when closing an account
            queryset = queryset.filter(
                member_set__role=roles.get_top_dog().id,
                member_set__user=request.user,
                status=OrganizationStatus.VISIBLE,
            )
            org_results = []
            for org in sorted(queryset, key=lambda x: x.name):
                # O(N) query
                org_results.append({
                    'organization': serialize(org),
                    'singleOwner': org.has_single_owner(),
                })

            return Response(org_results)

        elif member_only or not is_active_superuser(request):
            queryset = queryset.filter(
                id__in=OrganizationMember.objects.filter(
                    user=request.user, ).values('organization'), )

        query = request.GET.get('query')
        if query:
            tokens = tokenize_query(query)
            for key, value in six.iteritems(tokens):
                if key == 'query':
                    value = ' '.join(value)
                    queryset = queryset.filter(
                        Q(name__icontains=value) | Q(slug__icontains=value)
                        | Q(members__email__iexact=value))
                elif key == 'slug':
                    queryset = queryset.filter(in_iexact('slug', value))
                elif key == 'email':
                    queryset = queryset.filter(
                        in_iexact('members__email', value))
                elif key == 'platform':
                    queryset = queryset.filter(
                        project__in=ProjectPlatform.objects.filter(
                            platform__in=value, ).values('project_id'))
                elif key == 'id':
                    queryset = queryset.filter(id__in=value)
                elif key == 'status':
                    try:
                        queryset = queryset.filter(status__in=[
                            OrganizationStatus[v.upper()] for v in value
                        ])
                    except KeyError:
                        queryset = queryset.none()
                else:
                    queryset = queryset.none()

        sort_by = request.GET.get('sortBy')
        if sort_by == 'members':
            queryset = queryset.annotate(member_count=Count('member_set'), )
            order_by = '-member_count'
            paginator_cls = OffsetPaginator
        elif sort_by == 'projects':
            queryset = queryset.annotate(project_count=Count('project'), )
            order_by = '-project_count'
            paginator_cls = OffsetPaginator
        elif sort_by == 'events':
            queryset = queryset.annotate(
                event_count=Sum('stats__events_24h'), ).filter(
                    stats__events_24h__isnull=False, )
            order_by = '-event_count'
            paginator_cls = OffsetPaginator
        else:
            order_by = '-date_added'
            paginator_cls = DateTimePaginator

        return self.paginate(
            request=request,
            queryset=queryset,
            order_by=order_by,
            on_results=lambda x: serialize(x, request.user),
            paginator_cls=paginator_cls,
        )
Example #38
0
def createuser(email, password, superuser, no_password, no_input, force_update):
    "Create a new user."
    if not no_input:
        if not email:
            email = _get_email()

        if not (password or no_password):
            password = _get_password()

        if superuser is None:
            superuser = _get_superuser()

    if superuser is None:
        superuser = False

    if not email:
        raise click.ClickException("Invalid or missing email address.")

    # TODO(mattrobenolt): Accept password over stdin?
    if not no_password and not password:
        raise click.ClickException("No password set and --no-password not passed.")

    from django.conf import settings

    from sentry import roles
    from sentry.models import User

    fields = dict(
        email=email, username=email, is_superuser=superuser, is_staff=superuser, is_active=True
    )

    verb = None
    try:
        user = User.objects.get(username=email)
    except User.DoesNotExist:
        user = None

    if user is not None:
        if force_update:
            user.update(**fields)
            verb = "updated"
        else:
            click.echo(f"User: {email} exists, use --force-update to force")
            sys.exit(3)
    else:
        user = User.objects.create(**fields)
        verb = "created"

        # TODO(dcramer): kill this when we improve flows
        if settings.SENTRY_SINGLE_ORGANIZATION:
            from sentry.models import Organization, OrganizationMember, OrganizationMemberTeam, Team

            org = Organization.get_default()
            if superuser:
                role = roles.get_top_dog().id
            else:
                role = org.default_role
            member = OrganizationMember.objects.create(organization=org, user=user, role=role)

            # if we've only got a single team let's go ahead and give
            # access to that team as its likely the desired outcome
            teams = list(Team.objects.filter(organization=org)[0:2])
            if len(teams) == 1:
                OrganizationMemberTeam.objects.create(team=teams[0], organizationmember=member)
            click.echo(f"Added to organization: {org.slug}")

    if password:
        user.set_password(password)
        user.save()

    click.echo(f"User {verb}: {email}")
    def handle(self, request, *args, **kwargs):
        try:
            d = request.GET['data']
        except KeyError:
            raise Http404

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

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

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

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

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

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

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

        context = {
            'project': project,
            'form': form,
        }
        return self.respond('sentry/projects/accept_project_transfer.html',
                            context)
Example #40
0
    def post(self, request):
        """
        Create a New Organization
        `````````````````````````

        Create a new organization owned by the request's user.  To create
        an organization only the name is required.

        :param string name: the human readable name for the new organization.
        :param string slug: the unique URL slug for this organization.  If
                            this is not provided a slug is automatically
                            generated based on the name.
        :param bool agreeTerms: a boolean signaling you agree to the applicable
                                terms of service and privacy policy.
        :auth: required, user-context-needed
        """
        if not request.user.is_authenticated():
            return Response({"detail": "This endpoint requires user info"},
                            status=401)

        if not features.has("organizations:create", actor=request.user):
            return Response(
                {
                    "detail":
                    "Organizations are not allowed to be created by this user."
                },
                status=401)

        limit = options.get("api.rate-limit.org-create")
        if limit and ratelimiter.is_limited(
                "org-create:{}".format(
                    request.user.id), limit=limit, window=3600):
            return Response(
                {
                    "detail":
                    "You are attempting to create too many organizations too quickly."
                },
                status=429,
            )

        serializer = OrganizationSerializer(data=request.data)

        if serializer.is_valid():
            result = serializer.validated_data

            try:
                with transaction.atomic():
                    org = Organization.objects.create(name=result["name"],
                                                      slug=result.get("slug"))

                    om = OrganizationMember.objects.create(
                        organization=org,
                        user=request.user,
                        role=roles.get_top_dog().id)

                    if result.get("defaultTeam"):
                        team = org.team_set.create(name=org.name)

                        OrganizationMemberTeam.objects.create(
                            team=team, organizationmember=om, is_active=True)

                    self.create_audit_entry(
                        request=request,
                        organization=org,
                        target_object=org.id,
                        event=AuditLogEntryEvent.ORG_ADD,
                        data=org.get_audit_log_data(),
                    )

                    analytics.record(
                        "organization.created",
                        org,
                        actor_id=request.user.id
                        if request.user.is_authenticated() else None,
                    )

            except IntegrityError:
                return Response(
                    {
                        "detail":
                        "An organization with this slug already exists."
                    },
                    status=409)

            # failure on sending this signal is acceptable
            if result.get("agreeTerms"):
                terms_accepted.send_robust(
                    user=request.user,
                    organization=org,
                    ip_address=request.META["REMOTE_ADDR"],
                    sender=type(self),
                )

            return Response(serialize(org, request.user), status=201)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example #41
0
    def get(self, request):
        """
        List your Organizations
        ```````````````````````

        Return a list of organizations available to the authenticated
        session.  This is particularly useful for requests with an
        user bound context.  For API key based requests this will
        only return the organization that belongs to the key.

        :qparam bool member: restrict results to organizations which you have
                             membership
        :qparam bool owner: restrict results to organizations which are owner

        :auth: required
        """
        owner_only = request.GET.get('owner') in ('1', 'true')

        queryset = Organization.objects.distinct()

        if request.auth and not request.user.is_authenticated():
            if hasattr(request.auth, 'project'):
                queryset = queryset.filter(id=request.auth.project.organization_id)
            elif request.auth.organization is not None:
                queryset = queryset.filter(id=request.auth.organization.id)

        elif owner_only:
            # This is used when closing an account
            queryset = queryset.filter(
                member_set__role=roles.get_top_dog().id,
                member_set__user=request.user,
                status=OrganizationStatus.VISIBLE,
            )
            org_results = []
            for org in sorted(queryset, key=lambda x: x.name):
                # O(N) query
                org_results.append({
                    'organization': serialize(org),
                    'singleOwner': org.has_single_owner(),
                })

            return Response(org_results)

        elif not (is_active_superuser(request) and request.GET.get('show') == 'all'):
            queryset = queryset.filter(
                id__in=OrganizationMember.objects.filter(
                    user=request.user,
                ).values('organization'),
            )

        query = request.GET.get('query')
        if query:
            tokens = tokenize_query(query)
            for key, value in six.iteritems(tokens):
                if key == 'query':
                    value = ' '.join(value)
                    queryset = queryset.filter(
                        Q(name__icontains=value) | Q(slug__icontains=value) |
                        Q(members__email__iexact=value)
                    )
                elif key == 'slug':
                    queryset = queryset.filter(in_iexact('slug', value))
                elif key == 'email':
                    queryset = queryset.filter(in_iexact('members__email', value))
                elif key == 'platform':
                    queryset = queryset.filter(
                        project__in=ProjectPlatform.objects.filter(
                            platform__in=value,
                        ).values('project_id')
                    )
                elif key == 'id':
                    queryset = queryset.filter(id__in=value)
                elif key == 'status':
                    try:
                        queryset = queryset.filter(status__in=[
                            OrganizationStatus[v.upper()] for v in value
                        ])
                    except KeyError:
                        queryset = queryset.none()
                else:
                    queryset = queryset.none()

        sort_by = request.GET.get('sortBy')
        if sort_by == 'members':
            queryset = queryset.annotate(
                member_count=Count('member_set'),
            )
            order_by = '-member_count'
            paginator_cls = OffsetPaginator
        elif sort_by == 'projects':
            queryset = queryset.annotate(
                project_count=Count('project'),
            )
            order_by = '-project_count'
            paginator_cls = OffsetPaginator
        elif sort_by == 'events':
            queryset = queryset.annotate(
                event_count=Sum('stats__events_24h'),
            ).filter(
                stats__events_24h__isnull=False,
            )
            order_by = '-event_count'
            paginator_cls = OffsetPaginator
        else:
            order_by = '-date_added'
            paginator_cls = DateTimePaginator

        return self.paginate(
            request=request,
            queryset=queryset,
            order_by=order_by,
            on_results=lambda x: serialize(x, request.user),
            paginator_cls=paginator_cls,
        )
Example #42
0
def createuser(email, password, superuser, no_password, no_input):
    "Create a new user."
    if not no_input:
        if not email:
            email = _get_email()

        if not (password or no_password):
            password = _get_password()

        if superuser is None:
            superuser = _get_superuser()

    if superuser is None:
        superuser = False

    if not email:
        raise click.ClickException('Invalid or missing email address.')

    # TODO(mattrobenolt): Accept password over stdin?
    if not no_password and not password:
        raise click.ClickException(
            'No password set and --no-password not passed.')

    from sentry import roles
    from sentry.models import User
    from django.conf import settings

    user = User(
        email=email,
        username=email,
        is_superuser=superuser,
        is_staff=superuser,
        is_active=True,
    )

    if password:
        user.set_password(password)

    user.save()

    click.echo('User created: %s' % (email, ))

    # TODO(dcramer): kill this when we improve flows
    if settings.SENTRY_SINGLE_ORGANIZATION:
        from sentry.models import (Organization, OrganizationMember,
                                   OrganizationMemberTeam, Team)

        org = Organization.get_default()
        if superuser:
            role = roles.get_top_dog().id
        else:
            role = org.default_role
        member = OrganizationMember.objects.create(
            organization=org,
            user=user,
            role=role,
        )

        # if we've only got a single team let's go ahead and give
        # access to that team as its likely the desired outcome
        teams = list(Team.objects.filter(organization=org)[0:2])
        if len(teams) == 1:
            OrganizationMemberTeam.objects.create(
                team=teams[0],
                organizationmember=member,
            )
        click.echo('Added to organization: %s' % (org.slug, ))
 def determine_member_recipients(self) -> Iterable[OrganizationMember]:
     # Explicitly typing to satisfy mypy.
     members: Iterable[
         OrganizationMember] = OrganizationMember.objects.get_contactable_members_for_org(
             self.organization.id).filter(role=roles.get_top_dog().id, )
     return members
Example #44
0
    def post(self, request):
        try:
            data = request.data["data"]
        except KeyError:
            raise Http404

        try:
            data, project = self.get_validated_data(data, request.user)
        except InvalidPayload as e:
            return Response({"detail": six.text_type(e)}, status=400)

        transaction_id = data["transaction_id"]

        org_slug = request.data.get("organization")
        team_id = request.data.get("team")

        if org_slug is not None and team_id is not None:
            return Response(
                {
                    "detail":
                    "Choose either a team or an organization, not both"
                },
                status=400)

        if org_slug is None and team_id is None:
            return Response(
                {
                    "detail":
                    "Choose either a team or an organization to transfer the project to"
                },
                status=400,
            )

        if team_id:
            try:
                team = Team.objects.get(id=team_id)
            except Team.DoesNotExist:
                return Response({"detail": "Invalid team"}, status=400)

            # check if user is an owner of the team's org
            is_team_org_owner = OrganizationMember.objects.filter(
                user__is_active=True,
                user=request.user,
                role=roles.get_top_dog().id,
                organization_id=team.organization_id,
            ).exists()

            if not is_team_org_owner:
                return Response({"detail": "Invalid team"}, status=400)

            project.transfer_to(team=team)

        if org_slug:
            try:
                organization = Organization.objects.get(slug=org_slug)
            except Organization.DoesNotExist:
                return Response({"detail": "Invalid organization"}, status=400)

            # check if user is an owner of the organization
            is_org_owner = OrganizationMember.objects.filter(
                user__is_active=True,
                user=request.user,
                role=roles.get_top_dog().id,
                organization_id=organization.id,
            ).exists()

            if not is_org_owner:
                return Response({"detail": "Invalid organization"}, status=400)

            project.transfer_to(organization=organization)

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

        return Response(status=204)
Example #45
0
    def handle(self, request, organization, team=None):
        # 当前登陆人具有权限的小组
        team_list = Team.objects.get_for_user(organization=organization,
                                              user=request.user)
        if not team_list:
            team_list = []

        if team is None:
            if len(team_list) > 0:
                team = team_list[0]
            if team:
                # 当前登陆用户有小组,直接选择第一个小组,跳转过去
                redirect_uri = reverse('sentry-organization-team-members',
                                       args=[organization.slug, team.slug])
            else:
                # 当前用不存在任何小组,那么,直接跳转到首页
                redirect_to = reverse('sentry')
            # 跳转
            return self.redirect(redirect_uri)

        queryset = OrganizationMember.objects.raw(
            '''SELECT DISTINCT sentry_organizationmember.*, sentry_organizationmember_teams.team_id 
                                                        FROM sentry_organizationmember_teams 
                                                            join sentry_organizationmember 
                                                            on sentry_organizationmember_teams.organizationmember_id = sentry_organizationmember.id 
                                                        where team_id = %s''' %
            (team.id, ))
        # 过滤组织中所有人,其中小组为 team 的成员
        queryset = sorted(queryset,
                          key=lambda x: x.email or x.user.get_display_name())

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

        member_list = []
        for om in queryset:
            needs_sso = bool(auth_provider
                             and not getattr(om.flags, 'sso:linked'))
            member_list.append((om, needs_sso))

        # if the member is not the only owner we allow them to leave the org
        member_can_leave = any(
            1 for om, _ in member_list
            if (om.role == roles.get_top_dog().id and om.user != request.user
                and om.user is not None))

        # TODO(dcramer): ideally member:write could approve
        can_approve_requests_globally = request.access.has_scope('org:write')
        can_add_members = request.access.has_scope('org:write')
        can_remove_members = request.access.has_scope('member:delete')

        # pending requests
        if can_approve_requests_globally:
            access_requests = list(
                OrganizationAccessRequest.objects.filter(
                    team__organization=organization,
                    member__user__is_active=True,
                ).select_related('team', 'member__user'))
        elif request.access.has_scope('team:write') and request.access.teams:
            access_requests = list(
                OrganizationAccessRequest.objects.filter(
                    member__user__is_active=True,
                    team__in=request.access.teams,
                ).select_related('team', 'member__user'))
        else:
            access_requests = []

        context = {
            'org_has_sso': auth_provider is not None,
            'member_list': member_list,
            'request_list': access_requests,
            'ref': request.GET.get('ref'),
            'can_add_members': can_add_members,
            'can_remove_members': can_remove_members,
            'member_can_leave': member_can_leave,
            'team_list': team_list,
            'team': team
        }

        return self.respond('sentry/organization-members.html', context)
    def post(self, request, project):
        """
        Transfer a Project
        ````````````````

        Schedules a project for transfer to a new organization.

        :pparam string organization_slug: the slug of the organization the
                                          project belongs to.
        :pparam string project_slug: the slug of the project to delete.
        :param string email: email of new owner. must be an organization owner
        :auth: required
        """
        if project.is_internal_project():
            return Response(
                '{"error": "Cannot transfer projects internally used by Sentry."}',
                status=status.HTTP_403_FORBIDDEN)

        email = request.DATA.get('email')

        if email is None:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        if not request.user.is_authenticated():
            return Response(status=status.HTTP_403_FORBIDDEN)

        try:
            owner = OrganizationMember.objects.filter(
                user__email__iexact=email,
                role=roles.get_top_dog().id,
                user__is_active=True,
            )[0]
        except IndexError:
            return Response({'detail': 'Could not find owner with that email'},
                            status=status.HTTP_404_NOT_FOUND)

        transaction_id = uuid4().hex
        url_data = sign(actor_id=request.user.id,
                        from_organization_id=project.organization.id,
                        project_id=project.id,
                        user_id=owner.user_id,
                        transaction_id=transaction_id)

        has_new_teams = features.has(
            'organizations:new-teams',
            project.organization,
            actor=request.user,
        )
        context = {
            'email':
            email,
            'from_org':
            project.organization.name,
            'project_name':
            project.slug if has_new_teams else project.name,
            'request_time':
            timezone.now(),
            'url':
            absolute_uri('/accept-transfer/') + '?' +
            urlencode({'data': url_data}),
            'requester':
            request.user
        }
        MessageBuilder(
            subject='%sRequest for Project Transfer' %
            (options.get('mail.subject-prefix'), ),
            template='sentry/emails/transfer_project.txt',
            html_template='sentry/emails/transfer_project.html',
            type='org.confirm_project_transfer_request',
            context=context,
        ).send_async([email])

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

        return Response(status=status.HTTP_204_NO_CONTENT)
Example #47
0
def create_demo_org(quick=False) -> Organization:
    with sentry_sdk.start_transaction(op="create_demo_org", name="create_demo_org", sampled=True):
        sentry_sdk.set_tag("quick", quick)
        # wrap the main org setup in transaction
        with transaction.atomic():
            name = generate_random_name()

            slug = slugify(name)

            projects = []

            demo_org = DemoOrganization.create_org(name=name, slug=slug)
            org = demo_org.organization

            logger.info("create_demo_org.created_org", {"organization_slug": slug})

            owner = User.objects.get(email=settings.DEMO_ORG_OWNER_EMAIL)
            OrganizationMember.objects.create(
                organization=org, user=owner, role=roles.get_top_dog().id
            )

            team = org.team_set.create(name=org.name)

            def create_project(name, platform):
                project = Project.objects.create(name=name, organization=org, platform=platform)
                project.add_team(team)
                projects.append(project)
                return project

            python_project = create_project("Python", "python")
            react_project = create_project("React", "javascript-react")
            react_native_project = create_project("React-Native", "react-native")
            android_project = create_project("Android", "android")
            ios_project = create_project("iOS", "apple-ios")

            populate_org_members(org, team)
            # we'll be adding transactions later
            Project.objects.filter(organization=org).update(
                flags=F("flags").bitor(Project.flags.has_transactions)
            )

        logger.info(
            "create_demo_org.post-transaction",
            extra={"organization_slug": org.slug, "quick": quick},
        )

        with sentry_sdk.start_span(op="handle_react_python_mobile_scenario"):
            try:
                data_population = DataPopulation(org, quick=quick)
                data_population.generate_releases(projects)
                data_population.generate_saved_search(projects)
                data_population.handle_react_python_scenario(react_project, python_project)
                data_population.handle_mobile_scenario(
                    ios_project, android_project, react_native_project
                )

            except Exception as e:
                logger.error(
                    "create_demo_org.population_error",
                    extra={"organization_slug": org.slug, "quick": quick, "error": str(e)},
                )
                # delete the organization if data population fails
                org.status = OrganizationStatus.PENDING_DELETION
                org.save()
                delete_organization.apply_async(kwargs={"object_id": org.id})
                raise

        # update the org status now that it's populated
        demo_org.status = DemoOrgStatus.PENDING
        demo_org.save()

        logger.info(
            "create_demo_org.complete",
            extra={"organization_slug": org.slug, "quick": quick},
        )

        return org