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