def has_object_permission(self, request, view, organization): if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, organization, scopes=request.auth.get_scopes(), ) elif request.auth: if request.auth.organization_id == organization.id: request.access = access.from_auth(request.auth) else: request.access = access.DEFAULT else: request.access = access.from_request(request, organization) if auth.is_user_signed_request(request): # if the user comes from a signed request # we let them pass if sso is enabled logger.info( 'access.signed-sso-passthrough', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) elif request.user.is_authenticated(): # session auth needs to confirm various permissions if self.needs_sso(request, organization): logger.info( 'access.must-sso', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) raise SsoRequired(organization) if self.is_not_2fa_compliant( request.user, organization): logger.info( 'access.not-2fa-compliant', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) raise TwoFactorRequired() allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_scope(s) for s in allowed_scopes)
def determine_access(self, request, organization): from sentry.api.base import logger if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, organization, scopes=request.auth.get_scopes(), ) elif request.auth: request.access = access.from_auth(request.auth, organization) else: request.access = access.from_request(request, organization) if auth.is_user_signed_request(request): # if the user comes from a signed request # we let them pass if sso is enabled logger.info( 'access.signed-sso-passthrough', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) elif request.user.is_authenticated(): # session auth needs to confirm various permissions if self.needs_sso(request, organization): logger.info( 'access.must-sso', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) raise SsoRequired(organization) if self.is_not_2fa_compliant( request, organization): logger.info( 'access.not-2fa-compliant', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) raise TwoFactorRequired()
def has_object_permission(self, request, view, organization): if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request(request, organization, scopes=request.auth.get_scopes()) elif request.auth: if request.auth is ROOT_KEY: return True return request.auth.organization_id == organization.id else: request.access = access.from_request(request, organization) allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_scope(s) for s in allowed_scopes)
def serialize(self, obj, attrs, user): from sentry import features from sentry.app import env from sentry.api.serializers.models.team import TeamWithProjectsSerializer team_list = list(Team.objects.filter(organization=obj, status=TeamStatus.VISIBLE)) feature_list = [] if features.has("organizations:events", obj, actor=user): feature_list.append("events") if features.has("organizations:sso", obj, actor=user): feature_list.append("sso") if getattr(obj.flags, "allow_joinleave"): feature_list.append("open-membership") context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) context["quota"] = { "maxRate": quotas.get_organization_quota(obj), "projectLimit": int( OrganizationOption.objects.get_value(organization=obj, key="sentry:project-rate-limit", default=100) ), } context["teams"] = serialize(team_list, user, TeamWithProjectsSerializer()) if env.request: context["access"] = access.from_request(env.request, obj).scopes else: context["access"] = access.from_user(user, obj).scopes context["features"] = feature_list context["pendingAccessRequests"] = OrganizationAccessRequest.objects.filter(team__organization=obj).count() return context
def get_view_response(self, request, group): from sentry.models import Event self.selected = request.path == self.get_url(group) if not self.selected: return response = self.view(request, group) if not response: return if isinstance(response, HttpResponseRedirect): return response if not isinstance(response, Response): raise NotImplementedError("Use self.render() when returning responses.") event = group.get_latest_event() or Event() event.group = group request.access = access.from_request(request, group.organization) return response.respond( request, { "plugin": self, "project": group.project, "group": group, "event": event, "can_admin_event": request.access.has_scope("event:write"), "can_remove_event": request.access.has_scope("event:delete"), }, )
def dispatch(self, request, *args, **kwargs): """ Identical to rest framework's dispatch except we add the ability to convert arguments (for common URL params). """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.load_json_body(request) self.request = request self.headers = self.default_response_headers # deprecate? if settings.SENTRY_API_RESPONSE_DELAY: time.sleep(settings.SENTRY_API_RESPONSE_DELAY / 1000.0) origin = request.META.get('HTTP_ORIGIN', 'null') # A "null" value should be treated as no Origin for us. # See RFC6454 for more information on this behavior. if origin == 'null': origin = None try: if origin and request.auth: allowed_origins = request.auth.get_allowed_origins() if not is_valid_origin(origin, allowed=allowed_origins): response = Response('Invalid origin: %s' % (origin, ), status=400) self.response = self.finalize_response( request, response, *args, **kwargs) return self.response self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) (args, kwargs) = self.convert_args(request, *args, **kwargs) self.args = args self.kwargs = kwargs else: handler = self.http_method_not_allowed if getattr(request, 'access', None) is None: # setup default access request.access = access.from_request(request) response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(request, exc) if origin: self.add_cors_headers(request, response) self.response = self.finalize_response( request, response, *args, **kwargs) return self.response
def has_object_permission(self, request, view, organization): if request.auth: return request.auth.organization_id == organization.id request.access = access.from_request(request, organization) allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_scope(s) for s in allowed_scopes)
def get_view_response(self, request, group): from sentry.models import Event self.selected = request.path == self.get_url(group) if not self.selected: return response = self.view(request, group) if not response: return if isinstance(response, HttpResponseRedirect): return response if not isinstance(response, Response): raise NotImplementedError('Use self.render() when returning responses.') event = group.get_latest_event() or Event() event.group = group request.access = access.from_request(request, group.organization) return response.respond( request, { 'plugin': self, 'project': group.project, 'group': group, 'event': event, 'can_admin_event': request.access.has_scope('event:write'), 'can_remove_event': request.access.has_scope('event:admin'), } )
def serialize(self, obj, attrs, user): from sentry import features from sentry.app import env from sentry.api.serializers.models.team import TeamWithProjectsSerializer team_list = list(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, )) for team in team_list: team._organization_cache = obj onboarding_tasks = list(OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user')) feature_list = [] if features.has('organizations:sso', obj, actor=user): feature_list.append('sso') if features.has('organizations:callsigns', obj, actor=user): feature_list.append('callsigns') if features.has('organizations:new-tracebacks', obj, actor=user): feature_list.append('new-tracebacks') if features.has('organizations:onboarding', obj, actor=user) and \ not OrganizationOption.objects.filter(organization=obj).exists(): feature_list.append('onboarding') if features.has('organizations:api-keys', obj, actor=user) or \ ApiKey.objects.filter(organization=obj).exists(): feature_list.append('api-keys') if getattr(obj.flags, 'allow_joinleave'): feature_list.append('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): feature_list.append('shared-issues') context = super(DetailedOrganizationSerializer, self).serialize( obj, attrs, user) context['quota'] = { 'maxRate': quotas.get_organization_quota(obj), 'projectLimit': int(OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=100, )), } context['teams'] = serialize( team_list, user, TeamWithProjectsSerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context
def has_object_permission(self, request, view, project): if request.auth: return request.auth.organization_id == project.organization_id request.access = access.from_request(request, project.organization) for scope in self.scope_map.get(request.method, []): if request.access.has_team_scope(project.team, scope): return True return False
def build_request(self, user=None, active_superuser=False, **params): request = RequestFactory().get('/', params) request.session = {} if active_superuser: request.superuser = MockSuperUser() if user is None: user = self.user request.user = user request.access = from_request(request, self.org) return request
def has_object_permission(self, request, view, team): if request.auth: if request.auth is ROOT_KEY: return True return request.auth.organization_id == team.organization_id request.access = access.from_request(request, team.organization) allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_team_scope(team, s) for s in allowed_scopes)
def has_object_permission(self, request, view, project): if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, project.organization, scopes=request.auth.get_scopes(), ) elif request.auth: if request.auth is ROOT_KEY: return True return request.auth.organization_id == project.organization_id else: request.access = access.from_request(request, project.organization) allowed_scopes = set(self.scope_map.get(request.method, [])) return any( request.access.has_team_scope(project.team, s) for s in allowed_scopes)
def test_no_deleted_projects(self): self.create_member(organization=self.org, user=self.user, role="owner", teams=[self.team]) project = self.create_project( organization=self.org, status=ObjectStatus.PENDING_DELETION, teams=[self.team] ) request = self.make_request(user=self.proxy_user) result = access.from_request(request, self.org) assert result.has_project_access(project) is False assert result.has_project_membership(project) is False assert len(result.projects) == 1 assert list(result.projects)[0].id == self.project.id
def has_object_permission(self, request, view, team): if request.auth: if self.is_project_key(request): return False return request.auth.organization_id == team.organization_id request.access = access.from_request(request, team.organization) allowed_scopes = set(self.scope_map.get(request.method, [])) return any( request.access.has_team_scope(team, s) for s in allowed_scopes)
def test_unlinked_sso_with_no_owners(self): user = self.create_user() organization = self.create_organization(owner=user) self.create_team(organization=organization) AuthProvider.objects.create(organization=organization, provider="dummy") request = self.make_request(user=user) results = [access.from_user(user, organization), access.from_request(request, organization)] for result in results: assert not result.sso_is_valid assert not result.requires_sso
def test_superuser_with_organization_without_membership(self): request = self.make_request(user=self.superuser, is_superuser=True) result = access.from_request(request, self.org) assert result.has_permission("test.permission") assert not result.requires_sso assert result.sso_is_valid assert result.teams == frozenset() assert result.has_team_access(self.team1) assert result.projects == frozenset() assert result.has_project_access(self.project1)
def has_object_permission(self, request, view, project): if request.auth: if request.auth is ROOT_KEY: return True return request.auth.organization_id == project.organization_id request.access = access.from_request(request, project.organization) for scope in self.scope_map.get(request.method, []): if request.access.has_team_scope(project.team, scope): return True return False
def get_default_context(request, existing_context=None, team=None): from sentry import options from sentry.plugins.base import plugins context = { "URL_PREFIX": options.get("system.url-prefix"), "SINGLE_ORGANIZATION": settings.SENTRY_SINGLE_ORGANIZATION, "PLUGINS": plugins, # Maintain ONPREMISE key for backcompat (plugins?). TBH context could # probably be removed entirely: github.com/getsentry/sentry/pull/30970. "ONPREMISE": is_self_hosted(), "SELF_HOSTED": is_self_hosted(), } if existing_context: if team is None and "team" in existing_context: team = existing_context["team"] if "project" in existing_context: project = existing_context["project"] else: project = None else: project = None if team: organization = team.organization elif project: organization = project.organization else: organization = None if request: if (not existing_context or "TEAM_LIST" not in existing_context) and team: context["TEAM_LIST"] = Team.objects.get_for_user( organization=team.organization, user=request.user, with_projects=True ) user = request.user else: user = AnonymousUser() if not existing_context or "ACCESS" not in existing_context: if request: context["ACCESS"] = access.from_request( request=request, organization=organization ).to_django_context() else: context["ACCESS"] = access.from_user( user=user, organization=organization ).to_django_context() return context
def get_default_context(request, existing_context=None, team=None): from sentry import options from sentry.plugins.base import plugins context = { "URL_PREFIX": options.get("system.url-prefix"), "SINGLE_ORGANIZATION": settings.SENTRY_SINGLE_ORGANIZATION, "PLUGINS": plugins, "ONPREMISE": settings.SENTRY_ONPREMISE, } if existing_context: if team is None and "team" in existing_context: team = existing_context["team"] if "project" in existing_context: project = existing_context["project"] else: project = None else: project = None if team: organization = team.organization elif project: organization = project.organization else: organization = None if request: if (not existing_context or "TEAM_LIST" not in existing_context) and team: context["TEAM_LIST"] = Team.objects.get_for_user( organization=team.organization, user=request.user, with_projects=True) user = request.user else: user = AnonymousUser() if not existing_context or "ACCESS" not in existing_context: if request: context["ACCESS"] = access.from_request( request=request, organization=organization).to_django_context() else: context["ACCESS"] = access.from_user( user=user, organization=organization).to_django_context() return context
def determine_access(self, request, organization): from sentry.api.base import logger if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, organization, scopes=request.auth.get_scopes() ) elif request.auth: request.access = access.from_auth(request.auth, organization) else: request.access = access.from_request(request, organization) if auth.is_user_signed_request(request): # if the user comes from a signed request # we let them pass if sso is enabled logger.info( "access.signed-sso-passthrough", extra={"organization_id": organization.id, "user_id": request.user.id}, ) elif request.user.is_authenticated(): # session auth needs to confirm various permissions if self.needs_sso(request, organization): logger.info( "access.must-sso", extra={"organization_id": organization.id, "user_id": request.user.id}, ) raise SsoRequired(organization) if self.is_not_2fa_compliant(request, organization): logger.info( "access.not-2fa-compliant", extra={"organization_id": organization.id, "user_id": request.user.id}, ) raise TwoFactorRequired()
def has_object_permission(self, request, view, organization): if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, organization, scopes=request.auth.get_scopes(), ) elif request.auth: if request.auth is ROOT_KEY: return True return request.auth.organization_id == organization.id else: request.access = access.from_request(request, organization) # session auth needs to confirm various permissions if request.user.is_authenticated() and self.needs_sso(request, organization): logger.info('access.must-sso', extra={ 'organization_id': organization.id, 'user_id': request.user.id, }) raise NotAuthenticated(detail='Must login via SSO') allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_scope(s) for s in allowed_scopes)
def test_has_app_scopes(self): app_with_scopes = self.create_sentry_app(name="ScopeyTheApp", organization=self.org) app_with_scopes.update(scope_list=["team:read", "team:write"]) self.create_sentry_app_installation(organization=self.org, slug=app_with_scopes.slug, user=self.user) request = self.make_request(user=app_with_scopes.proxy_user) result = access.from_request(request, self.org) assert result.scopes == frozenset({"team:read", "team:write"}) assert result.has_scope("team:read") is True assert result.has_scope("team:write") is True assert result.has_scope("team:admin") is False
def has_object_permission(self, request, view, organization): if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, organization, scopes=request.auth.get_scopes(), ) elif request.auth: if request.auth is ROOT_KEY: return True return request.auth.organization_id == organization.id else: request.access = access.from_request(request, organization) # session auth needs to confirm various permissions if request.user.is_authenticated() and self.needs_sso(request, organization): logger.info('access.must-sso', extra={ 'organization_id': organization.id, 'user_id': request.user.id, }) raise NotAuthenticated(detail='Must login via SSO') allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_scope(s) for s in allowed_scopes)
def has_object_permission(self, request, view, organization): if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, organization, scopes=request.auth.get_scopes(), ) elif request.auth: return request.auth.organization_id == organization.id else: request.access = access.from_request(request, organization) if auth.is_user_signed_request(request): # if the user comes from a signed request # we let them pass if sso is enabled logger.info( 'access.signed-sso-passthrough', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) elif request.user.is_authenticated() and self.needs_sso(request, organization): # session auth needs to confirm various permissions logger.info( 'access.must-sso', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) raise NotAuthenticated(detail='Must login via SSO') allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_scope(s) for s in allowed_scopes)
def has_object_permission(self, request, view, organization): if request.user and request.user.is_authenticated() and request.auth: request.access = access.from_request( request, organization, scopes=request.auth.get_scopes(), ) elif request.auth: return request.auth.organization_id == organization.id else: request.access = access.from_request(request, organization) if auth.is_user_signed_request(request): # if the user comes from a signed request # we let them pass if sso is enabled logger.info( 'access.signed-sso-passthrough', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) elif request.user.is_authenticated() and self.needs_sso(request, organization): # session auth needs to confirm various permissions logger.info( 'access.must-sso', extra={ 'organization_id': organization.id, 'user_id': request.user.id, } ) raise NotAuthenticated(detail='Must login via SSO') allowed_scopes = set(self.scope_map.get(request.method, [])) return any(request.access.has_scope(s) for s in allowed_scopes)
def test_mixed_access(self): user = self.create_user() organization = self.create_organization(flags=0) # disable default allow_joinleave team = self.create_team(organization=organization) team_no_access = self.create_team(organization=organization) project = self.create_project(organization=organization, teams=[team]) project_no_access = self.create_project(organization=organization, teams=[team_no_access]) self.create_member(organization=organization, user=user, teams=[team]) request = self.make_request(user=user) results = [access.from_user(user, organization), access.from_request(request, organization)] for result in results: assert result.has_project_access(project) assert not result.has_project_access(project_no_access) assert not result.has_projects_access([project, project_no_access])
def serialize(self, obj, attrs, user): from sentry import features from sentry.app import env from sentry.api.serializers.models.team import TeamWithProjectsSerializer team_list = list(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, )) onboarding_tasks = list(OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user')) feature_list = [] if features.has('organizations:sso', obj, actor=user): feature_list.append('sso') if features.has('organizations:onboarding', obj, actor=user): feature_list.append('onboarding') if getattr(obj.flags, 'allow_joinleave'): feature_list.append('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): feature_list.append('shared-issues') context = super(DetailedOrganizationSerializer, self).serialize( obj, attrs, user) context['quota'] = { 'maxRate': quotas.get_organization_quota(obj), 'projectLimit': int(OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=100, )), } context['teams'] = serialize( team_list, user, TeamWithProjectsSerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context
def test_unique_projects(self): user = self.create_user() organization = self.create_organization(owner=self.user) team = self.create_team(organization=organization) other_team = self.create_team(organization=organization) self.create_member( organization=organization, user=user, role="owner", teams=[team, other_team] ) project = self.create_project(organization=organization, teams=[team, other_team]) request = self.make_request(user=user) results = [access.from_user(user, organization), access.from_request(request, organization)] for result in results: assert result.has_project_access(project) assert len(result.projects) == 1
def test_no_deleted_projects(self): user = self.create_user() organization = self.create_organization(owner=self.user) team = self.create_team(organization=organization) self.create_member(organization=organization, user=user, role="owner", teams=[team]) project = self.create_project( organization=organization, status=ObjectStatus.PENDING_DELETION, teams=[team] ) request = self.make_request(user=user) results = [access.from_user(user, organization), access.from_request(request, organization)] for result in results: assert result.has_project_access(project) is True assert result.has_project_membership(project) is False assert len(result.projects) == 0
def test_no_deleted_projects(self): self.create_member(organization=self.org, user=self.user, role="owner", teams=[self.team]) deleted_project = self.create_project( organization=self.org, status=ObjectStatus.PENDING_DELETION, teams=[self.team]) request = self.make_request(user=self.proxy_user) result = access.from_request(request, self.org) assert result.has_project_access(deleted_project) is False assert result.has_project_membership(deleted_project) is False # result.projects also contains other projects created by # self.create_sentry_app_installation assert deleted_project not in result.projects assert self.project in result.projects
def test_team_restricted_org_member_access(self): user = self.create_user() organization = self.create_organization() team = self.create_team(organization=organization) project = self.create_project(organization=organization, teams=[team]) member = self.create_member(organization=organization, user=user, teams=[team]) request = self.make_request(user=user) results = [access.from_user(user, organization), access.from_request(request, organization)] for result in results: assert result.sso_is_valid assert not result.requires_sso assert result.scopes == member.get_scopes() assert result.has_team_access(team) assert result.has_team_scope(team, "project:read") assert result.has_project_access(project) assert result.has_projects_access([project]) assert result.has_project_scope(project, "project:read") assert result.has_project_membership(project)
def test_owner_all_teams(self): user = self.create_user() organization = self.create_organization(owner=self.user) member = self.create_member(organization=organization, user=user, role="owner") team = self.create_team(organization=organization) project = self.create_project(organization=organization, teams=[team]) request = self.make_request(user=user) results = [access.from_user(user, organization), access.from_request(request, organization)] for result in results: assert result.sso_is_valid assert not result.requires_sso assert result.scopes == member.get_scopes() assert result.has_team_access(team) assert result.has_team_scope(team, "project:read") assert result.has_project_access(project) assert result.has_projects_access([project]) assert result.has_project_scope(project, "project:read") # owners should have access but not membership assert result.has_project_membership(project) is False
def test_superuser_with_organization_without_membership(self): org = self.create_organization() AuthProvider.objects.create(organization=org) team = self.create_team(organization=org) project = self.create_project(organization=org, teams=[team]) user = self.create_user(is_superuser=True) UserPermission.objects.create(user=user, permission="test.permission") request = self.make_request(user=user, is_superuser=True) result = access.from_request(request, org) assert result.has_permission("test.permission") assert not result.requires_sso assert result.sso_is_valid assert result.teams == frozenset({team}) assert result.has_team_access(team) assert result.projects == frozenset({project}) assert result.has_project_access(project)
def serialize(self, obj, attrs, user): from sentry import features from sentry.app import env from sentry.api.serializers.models.team import TeamWithProjectsSerializer team_list = list(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, )) feature_list = [] if features.has('organizations:sso', obj, actor=user): feature_list.append('sso') if features.has('organizations:my-issues', obj, actor=user): feature_list.append('my-issues') if getattr(obj.flags, 'allow_joinleave'): feature_list.append('open-membership') context = super(DetailedOrganizationSerializer, self).serialize( obj, attrs, user) context['quota'] = { 'maxRate': quotas.get_organization_quota(obj), 'projectLimit': int(OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=100, )), } context['teams'] = serialize( team_list, user, TeamWithProjectsSerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() return context
def test_no_deleted_teams(self): user = self.create_user() organization = self.create_organization(owner=self.user) team = self.create_team(organization=organization) deleted_team = self.create_team(organization=organization, status=TeamStatus.PENDING_DELETION) self.create_member(organization=organization, user=user, role="owner", teams=[team, deleted_team]) request = self.make_request(user=user) results = [ access.from_user(user, organization), access.from_request(request, organization) ] for result in results: assert result.has_team_access(team) is True assert result.has_team_access(deleted_team) is False assert result.teams == frozenset({team})
def test_member_no_teams_open_membership(self): user = self.create_user() organization = self.create_organization( owner=self.user, flags=Organization.flags.allow_joinleave ) member = self.create_member(organization=organization, user=user, role="member", teams=()) team = self.create_team(organization=organization) project = self.create_project(organization=organization, teams=[team]) request = self.make_request(user=user) results = [access.from_user(user, organization), access.from_request(request, organization)] for result in results: assert result.sso_is_valid assert not result.requires_sso assert result.scopes == member.get_scopes() assert result.has_team_access(team) assert result.has_team_scope(team, "project:read") assert result.has_project_access(project) assert result.has_projects_access([project]) assert result.has_project_scope(project, "project:read") assert not result.has_project_membership(project)
def serialize(self, obj, attrs, user): from sentry import features from sentry.app import env from sentry.api.serializers.models.team import TeamWithProjectsSerializer team_list = list(Team.objects.filter(organization=obj, status=TeamStatus.VISIBLE)) feature_list = [] if features.has("organizations:sso", obj, actor=user): feature_list.append("sso") if getattr(obj.flags, "allow_joinleave"): feature_list.append("open-membership") context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) context["teams"] = serialize(team_list, user, TeamWithProjectsSerializer()) if env.request: context["access"] = access.from_request(env.request, obj).scopes else: context["access"] = access.from_user(user, obj).scopes context["features"] = feature_list context["pendingAccessRequests"] = OrganizationAccessRequest.objects.filter(team__organization=obj).count() return context
def get_view_response(self, request, group): from sentry.models import Event self.selected = request.path == self.get_url(group) if not self.selected: return response = self.view(request, group) if not response: return if isinstance(response, HttpResponseRedirect): return response if not isinstance(response, Response): raise NotImplementedError( "Use self.render() when returning responses.") event = group.get_latest_event() or Event() event.group = group request.access = access.from_request(request, group.organization) return response.respond( request, { "plugin": self, "project": group.project, "group": group, "event": event, "can_admin_event": request.access.has_scope("event:write"), "can_remove_event": request.access.has_scope("event:admin"), }, )
def get_default_context(request, existing_context=None, team=None): from sentry import options from sentry.plugins import plugins context = { 'EVENTS_PER_PAGE': EVENTS_PER_PAGE, 'URL_PREFIX': options.get('system.url-prefix'), 'SINGLE_ORGANIZATION': settings.SENTRY_SINGLE_ORGANIZATION, 'PLUGINS': plugins, 'ALLOWED_HOSTS': list(settings.ALLOWED_HOSTS), } if existing_context: if team is None and 'team' in existing_context: team = existing_context['team'] if 'project' in existing_context: project = existing_context['project'] else: project = None else: project = None if team: organization = team.organization elif project: organization = project.organization else: organization = None if request: context.update({ 'request': request, }) if (not existing_context or 'TEAM_LIST' not in existing_context) and team: context['TEAM_LIST'] = Team.objects.get_for_user( organization=team.organization, user=request.user, with_projects=True, ) user = request.user else: user = AnonymousUser() if organization: context['selectedOrganization'] = serialize(organization, user) if team: context['selectedTeam'] = serialize(team, user) if project: context['selectedProject'] = serialize(project, user) if not existing_context or 'ACCESS' not in existing_context: if request: context['ACCESS'] = access.from_request( request=request, organization=organization, ).to_django_context() else: context['ACCESS'] = access.from_user( user=user, organization=organization, ).to_django_context() return context
def dispatch(self, request, *args, **kwargs): """ Identical to rest framework's dispatch except we add the ability to convert arguments (for common URL params). """ with sentry_sdk.start_span(op="base.dispatch.setup", description=type(self).__name__): self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.load_json_body(request) self.request = request self.headers = self.default_response_headers # deprecate? # Tags that will ultimately flow into the metrics backend at the end of # the request (happens via middleware/stats.py). request._metric_tags = {} if settings.SENTRY_API_RESPONSE_DELAY: with sentry_sdk.start_span( op="base.dispatch.sleep", description=type(self).__name__, ) as span: span.set_data("SENTRY_API_RESPONSE_DELAY", settings.SENTRY_API_RESPONSE_DELAY) time.sleep(settings.SENTRY_API_RESPONSE_DELAY / 1000.0) origin = request.META.get("HTTP_ORIGIN", "null") # A "null" value should be treated as no Origin for us. # See RFC6454 for more information on this behavior. if origin == "null": origin = None try: with sentry_sdk.start_span(op="base.dispatch.request", description=type(self).__name__): if origin and request.auth: allowed_origins = request.auth.get_allowed_origins() if not is_valid_origin(origin, allowed=allowed_origins): response = Response("Invalid origin: %s" % (origin,), status=400) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) (args, kwargs) = self.convert_args(request, *args, **kwargs) self.args = args self.kwargs = kwargs else: handler = self.http_method_not_allowed if getattr(request, "access", None) is None: # setup default access request.access = access.from_request(request) with sentry_sdk.start_span( op="base.dispatch.execute", description="{}.{}".format(type(self).__name__, handler.__name__), ): response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(request, exc) if origin: self.add_cors_headers(request, response) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
def serialize(self, obj, attrs, user): from sentry import features, experiments from sentry.features.base import OrganizationFeature from sentry.app import env from sentry.api.serializers.models.project import ProjectSummarySerializer from sentry.api.serializers.models.team import TeamSerializer team_list = sorted(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, ), key=lambda x: x.slug) for team in team_list: team._organization_cache = obj project_list = sorted(Project.objects.filter( organization=obj, status=ProjectStatus.VISIBLE, ), key=lambda x: x.slug) for project in project_list: project._organization_cache = obj onboarding_tasks = list( OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user') ) # Retrieve all registered organization features org_features = features.all(feature_type=OrganizationFeature).keys() feature_list = set() for feature_name in org_features: if not feature_name.startswith('organizations:'): continue if features.has(feature_name, obj, actor=user): # Remove the organization scope prefix feature_list.add(feature_name[len('organizations:'):]) # Do not include the onboarding feature if OrganizationOptions exist if 'onboarding' in feature_list and \ OrganizationOption.objects.filter(organization=obj).exists(): feature_list.remove('onboarding') # Include api-keys feature if they previously had any api-keys if 'api-keys' not in feature_list and ApiKey.objects.filter(organization=obj).exists(): feature_list.add('api-keys') # Organization flag features (not provided through the features module) if OrganizationOption.objects.filter( organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists(): feature_list.add('legacy-rate-limits') if getattr(obj.flags, 'allow_joinleave'): # noqa: B009 feature_list.add('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): # noqa: B009 feature_list.add('shared-issues') if getattr(obj.flags, 'require_2fa'): # noqa: B009 feature_list.add('require-2fa') experiment_assignments = experiments.all(org=obj, actor=user) context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) max_rate = quotas.get_maximum_quota(obj) context['experiments'] = experiment_assignments context['quota'] = { 'maxRate': max_rate[0], 'maxRateInterval': max_rate[1], 'accountLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:account-rate-limit', default=ACCOUNT_RATE_LIMIT_DEFAULT, ) ), 'projectLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=PROJECT_RATE_LIMIT_DEFAULT, ) ), } context.update({ 'isDefault': obj.is_default, 'defaultRole': obj.default_role, 'availableRoles': [{ 'id': r.id, 'name': r.name, } for r in roles.get_all()], 'openMembership': bool(obj.flags.allow_joinleave), 'require2FA': bool(obj.flags.require_2fa), 'allowSharedIssues': not obj.flags.disable_shared_issues, 'enhancedPrivacy': bool(obj.flags.enhanced_privacy), 'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)), 'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)), 'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [], 'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [], 'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)), 'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)), 'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)), 'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [], }) context['teams'] = serialize(team_list, user, TeamSerializer()) context['projects'] = serialize(project_list, user, ProjectSummarySerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context
def get_access(self, request, organization, *args, **kwargs): if organization is None: return access.DEFAULT return access.from_request(request, organization)
def serialize(self, obj, attrs, user): from sentry import features from sentry.app import env from sentry.api.serializers.models.project import ProjectSummarySerializer from sentry.api.serializers.models.team import TeamSerializer team_list = list(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, )) for team in team_list: team._organization_cache = obj project_list = list(Project.objects.filter( organization=obj, status=ProjectStatus.VISIBLE, )) for project in project_list: project._organization_cache = obj onboarding_tasks = list( OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user') ) feature_list = [] if features.has('organizations:sso', obj, actor=user): feature_list.append('sso') if features.has('organizations:onboarding', obj, actor=user) and \ not OrganizationOption.objects.filter(organization=obj).exists(): feature_list.append('onboarding') if features.has('organizations:api-keys', obj, actor=user) or \ ApiKey.objects.filter(organization=obj).exists(): feature_list.append('api-keys') if features.has('organizations:group-unmerge', obj, actor=user): feature_list.append('group-unmerge') if features.has('organizations:github-apps', obj, actor=user): feature_list.append('github-apps') if features.has('organizations:integrations-v3', obj, actor=user): feature_list.append('integrations-v3') if features.has('organizations:new-settings', obj, actor=user): feature_list.append('new-settings') if features.has('organizations:require-2fa', obj, actor=user): feature_list.append('require-2fa') if features.has('organizations:environments', obj, actor=user): feature_list.append('environments') if features.has('organizations:repos', obj, actor=user): feature_list.append('repos') if features.has('organizations:internal-catchall', obj, actor=user): feature_list.append('internal-catchall') if features.has('organizations:suggested-commits', obj, actor=user): feature_list.append('suggested-commits') if features.has('organizations:new-teams', obj, actor=user): feature_list.append('new-teams') if features.has('organizations:unreleased-changes', obj, actor=user): feature_list.append('unreleased-changes') if features.has('organizations:relay', obj, actor=user): feature_list.append('relay') if features.has('organizations:health', obj, actor=user): feature_list.append('health') if getattr(obj.flags, 'allow_joinleave'): feature_list.append('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): feature_list.append('shared-issues') if getattr(obj.flags, 'require_2fa'): feature_list.append('require-2fa') context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) max_rate = quotas.get_maximum_quota(obj) context['quota'] = { 'maxRate': max_rate[0], 'maxRateInterval': max_rate[1], 'accountLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:account-rate-limit', default=0, ) ), 'projectLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=100, ) ), } context.update({ 'isDefault': obj.is_default, 'defaultRole': obj.default_role, 'availableRoles': [{ 'id': r.id, 'name': r.name, } for r in roles.get_all()], 'openMembership': bool(obj.flags.allow_joinleave), 'require2FA': bool(obj.flags.require_2fa), 'allowSharedIssues': not obj.flags.disable_shared_issues, 'enhancedPrivacy': bool(obj.flags.enhanced_privacy), 'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', False)), 'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', False)), 'sensitiveFields': obj.get_option('sentry:sensitive_fields', None) or [], 'safeFields': obj.get_option('sentry:safe_fields', None) or [], 'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', False)), }) context['teams'] = serialize(team_list, user, TeamSerializer()) context['projects'] = serialize(project_list, user, ProjectSummarySerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context
def dispatch(self, request, *args, **kwargs): """ Identical to rest framework's dispatch except we add the ability to convert arguments (for common URL params). """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? if settings.SENTRY_API_RESPONSE_DELAY: time.sleep(settings.SENTRY_API_RESPONSE_DELAY / 1000.0) origin = request.META.get('HTTP_ORIGIN', 'null') # A "null" value should be treated as no Origin for us. # See RFC6454 for more information on this behavior. if origin == 'null': origin = None try: if origin and request.auth: allowed_origins = request.auth.get_allowed_origins() if not is_valid_origin(origin, allowed=allowed_origins): response = Response('Invalid origin: %s' % (origin, ), status=400) self.response = self.finalize_response( request, response, *args, **kwargs) return self.response self.initial(request, *args, **kwargs) if getattr(request, 'user', None) and request.user.is_authenticated(): raven.user_context({ 'id': request.user.id, 'username': request.user.username, 'email': request.user.email, }) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) (args, kwargs) = self.convert_args(request, *args, **kwargs) self.args = args self.kwargs = kwargs else: handler = self.http_method_not_allowed if getattr(request, 'access', None) is None: # setup default access request.access = access.from_request(request) response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(request, exc) if origin: self.add_cors_headers(request, response) self.response = self.finalize_response( request, response, *args, **kwargs) return self.response
def serialize(self, obj, attrs, user): from sentry import features from sentry.app import env from sentry.api.serializers.models.team import TeamWithProjectsSerializer team_list = list(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, )) for team in team_list: team._organization_cache = obj onboarding_tasks = list( OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user') ) feature_list = [] if features.has('organizations:sso', obj, actor=user): feature_list.append('sso') if features.has('organizations:onboarding', obj, actor=user) and \ not OrganizationOption.objects.filter(organization=obj).exists(): feature_list.append('onboarding') if features.has('organizations:api-keys', obj, actor=user) or \ ApiKey.objects.filter(organization=obj).exists(): feature_list.append('api-keys') if features.has('organizations:group-unmerge', obj, actor=user): feature_list.append('group-unmerge') if features.has('organizations:integrations-v3', obj, actor=user): feature_list.append('integrations-v3') if getattr(obj.flags, 'allow_joinleave'): feature_list.append('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): feature_list.append('shared-issues') context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) max_rate = quotas.get_maximum_quota(obj) context['quota'] = { 'maxRate': max_rate[0], 'maxRateInterval': max_rate[1], 'accountLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:account-rate-limit', default=0, ) ), 'projectLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=100, ) ), } context.update( { 'isDefault': obj.is_default, 'defaultRole': obj.default_role, 'availableRoles': [{ 'id': r.id, 'name': r.name, } for r in roles.get_all()], 'openMembership': bool(obj.flags.allow_joinleave), 'allowSharedIssues': not obj.flags.disable_shared_issues, 'enhancedPrivacy': bool(obj.flags.enhanced_privacy), 'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', False)), 'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', False)), 'sensitiveFields': obj.get_option('sentry:sensitive_fields', None) or [], 'safeFields': obj.get_option('sentry:safe_fields', None) or [], 'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', False)), } ) context['teams'] = serialize(team_list, user, TeamWithProjectsSerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context
def get_default_context(request, existing_context=None, team=None): from sentry import options from sentry.plugins import plugins context = { 'URL_PREFIX': options.get('system.url-prefix'), 'SINGLE_ORGANIZATION': settings.SENTRY_SINGLE_ORGANIZATION, 'PLUGINS': plugins, 'ONPREMISE': settings.SENTRY_ONPREMISE, } if existing_context: if team is None and 'team' in existing_context: team = existing_context['team'] if 'project' in existing_context: project = existing_context['project'] else: project = None else: project = None if team: organization = team.organization elif project: organization = project.organization else: organization = None if request: context.update({ 'request': request, }) if (not existing_context or 'TEAM_LIST' not in existing_context) and team: context['TEAM_LIST'] = Team.objects.get_for_user( organization=team.organization, user=request.user, with_projects=True, ) user = request.user else: user = AnonymousUser() if organization: context['selectedOrganization'] = serialize(organization, user) if team: context['selectedTeam'] = serialize(team, user) if project: context['selectedProject'] = serialize(project, user) if not existing_context or 'ACCESS' not in existing_context: if request: context['ACCESS'] = access.from_request( request=request, organization=organization, ).to_django_context() else: context['ACCESS'] = access.from_user( user=user, organization=organization, ).to_django_context() return context
def serialize(self, obj, attrs, user): from sentry import features, experiments from sentry.app import env from sentry.api.serializers.models.project import ProjectSummarySerializer from sentry.api.serializers.models.team import TeamSerializer team_list = sorted(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, ), key=lambda x: x.slug) for team in team_list: team._organization_cache = obj project_list = sorted(Project.objects.filter( organization=obj, status=ProjectStatus.VISIBLE, ), key=lambda x: x.slug) for project in project_list: project._organization_cache = obj onboarding_tasks = list( OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user')) feature_list = [] if features.has('organizations:sso', obj, actor=user): feature_list.append('sso') if features.has('organizations:onboarding', obj, actor=user) and \ not OrganizationOption.objects.filter(organization=obj).exists(): feature_list.append('onboarding') if features.has('organizations:api-keys', obj, actor=user) or \ ApiKey.objects.filter(organization=obj).exists(): feature_list.append('api-keys') if features.has('organizations:group-unmerge', obj, actor=user): feature_list.append('group-unmerge') if features.has('organizations:github-apps', obj, actor=user): feature_list.append('github-apps') if features.has('organizations:require-2fa', obj, actor=user): feature_list.append('require-2fa') if features.has('organizations:repos', obj, actor=user): feature_list.append('repos') if features.has('organizations:internal-catchall', obj, actor=user): feature_list.append('internal-catchall') if features.has('organizations:new-issue-ui', obj, actor=user): feature_list.append('new-issue-ui') if features.has('organizations:github-enterprise', obj, actor=user): feature_list.append('github-enterprise') if features.has('organizations:bitbucket-integration', obj, actor=user): feature_list.append('bitbucket-integration') if features.has('organizations:jira-integration', obj, actor=user): feature_list.append('jira-integration') if features.has('organizations:vsts-integration', obj, actor=user): feature_list.append('vsts-integration') if features.has('organizations:integrations-issue-basic', obj, actor=user): feature_list.append('integrations-issue-basic') if features.has('organizations:integrations-issue-sync', obj, actor=user): feature_list.append('integrations-issue-sync') if features.has('organizations:suggested-commits', obj, actor=user): feature_list.append('suggested-commits') if features.has('organizations:new-teams', obj, actor=user): feature_list.append('new-teams') if features.has('organizations:unreleased-changes', obj, actor=user): feature_list.append('unreleased-changes') if features.has('organizations:relay', obj, actor=user): feature_list.append('relay') if features.has('organizations:js-loader', obj, actor=user): feature_list.append('js-loader') if features.has('organizations:health', obj, actor=user): feature_list.append('health') if features.has('organizations:discover', obj, actor=user): feature_list.append('discover') if features.has('organizations:events-stream', obj, actor=user): feature_list.append('events-stream') if OrganizationOption.objects.filter( organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists(): feature_list.append('legacy-rate-limits') if getattr(obj.flags, 'allow_joinleave'): feature_list.append('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): feature_list.append('shared-issues') if getattr(obj.flags, 'require_2fa'): feature_list.append('require-2fa') if features.has('organizations:event-attachments', obj, actor=user): feature_list.append('event-attachments') experiment_assignments = experiments.all(org=obj) context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) max_rate = quotas.get_maximum_quota(obj) context['experiments'] = experiment_assignments context['quota'] = { 'maxRate': max_rate[0], 'maxRateInterval': max_rate[1], 'accountLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:account-rate-limit', default=ACCOUNT_RATE_LIMIT_DEFAULT, )), 'projectLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=PROJECT_RATE_LIMIT_DEFAULT, )), } context.update({ 'isDefault': obj.is_default, 'defaultRole': obj.default_role, 'availableRoles': [{ 'id': r.id, 'name': r.name, } for r in roles.get_all()], 'openMembership': bool(obj.flags.allow_joinleave), 'require2FA': bool(obj.flags.require_2fa), 'allowSharedIssues': not obj.flags.disable_shared_issues, 'enhancedPrivacy': bool(obj.flags.enhanced_privacy), 'dataScrubber': bool( obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)), 'dataScrubberDefaults': bool( obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)), 'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [], 'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [], 'storeCrashReports': bool( obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)), 'scrubIPAddresses': bool( obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)), 'scrapeJavaScript': bool( obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)), 'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [], }) context['teams'] = serialize(team_list, user, TeamSerializer()) context['projects'] = serialize(project_list, user, ProjectSummarySerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context[ 'pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context
def serialize(self, obj, attrs, user): from sentry import features, experiments from sentry.app import env from sentry.api.serializers.models.project import ProjectSummarySerializer from sentry.api.serializers.models.team import TeamSerializer team_list = sorted(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, ), key=lambda x: x.slug) for team in team_list: team._organization_cache = obj project_list = sorted(Project.objects.filter( organization=obj, status=ProjectStatus.VISIBLE, ), key=lambda x: x.slug) for project in project_list: project._organization_cache = obj onboarding_tasks = list( OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user') ) feature_list = [] if features.has('organizations:sso', obj, actor=user): feature_list.append('sso') if features.has('organizations:onboarding', obj, actor=user) and \ not OrganizationOption.objects.filter(organization=obj).exists(): feature_list.append('onboarding') if features.has('organizations:api-keys', obj, actor=user) or \ ApiKey.objects.filter(organization=obj).exists(): feature_list.append('api-keys') if features.has('organizations:group-unmerge', obj, actor=user): feature_list.append('group-unmerge') if features.has('organizations:github-apps', obj, actor=user): feature_list.append('github-apps') if features.has('organizations:require-2fa', obj, actor=user): feature_list.append('require-2fa') if features.has('organizations:repos', obj, actor=user): feature_list.append('repos') if features.has('organizations:internal-catchall', obj, actor=user): feature_list.append('internal-catchall') if features.has('organizations:new-issue-ui', obj, actor=user): feature_list.append('new-issue-ui') if features.has('organizations:github-enterprise', obj, actor=user): feature_list.append('github-enterprise') if features.has('organizations:bitbucket-integration', obj, actor=user): feature_list.append('bitbucket-integration') if features.has('organizations:jira-integration', obj, actor=user): feature_list.append('jira-integration') if features.has('organizations:vsts-integration', obj, actor=user): feature_list.append('vsts-integration') if features.has('organizations:integrations-issue-basic', obj, actor=user): feature_list.append('integrations-issue-basic') if features.has('organizations:integrations-issue-sync', obj, actor=user): feature_list.append('integrations-issue-sync') if features.has('organizations:suggested-commits', obj, actor=user): feature_list.append('suggested-commits') if features.has('organizations:new-teams', obj, actor=user): feature_list.append('new-teams') if features.has('organizations:unreleased-changes', obj, actor=user): feature_list.append('unreleased-changes') if features.has('organizations:relay', obj, actor=user): feature_list.append('relay') if features.has('organizations:js-loader', obj, actor=user): feature_list.append('js-loader') if features.has('organizations:health', obj, actor=user): feature_list.append('health') if features.has('organizations:discover', obj, actor=user): feature_list.append('discover') if OrganizationOption.objects.filter( organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists(): feature_list.append('legacy-rate-limits') if getattr(obj.flags, 'allow_joinleave'): feature_list.append('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): feature_list.append('shared-issues') if getattr(obj.flags, 'require_2fa'): feature_list.append('require-2fa') if features.has('organizations:event-attachments', obj, actor=user): feature_list.append('event-attachments') experiment_assignments = experiments.all(org=obj) context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) max_rate = quotas.get_maximum_quota(obj) context['experiments'] = experiment_assignments context['quota'] = { 'maxRate': max_rate[0], 'maxRateInterval': max_rate[1], 'accountLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:account-rate-limit', default=ACCOUNT_RATE_LIMIT_DEFAULT, ) ), 'projectLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=PROJECT_RATE_LIMIT_DEFAULT, ) ), } context.update({ 'isDefault': obj.is_default, 'defaultRole': obj.default_role, 'availableRoles': [{ 'id': r.id, 'name': r.name, } for r in roles.get_all()], 'openMembership': bool(obj.flags.allow_joinleave), 'require2FA': bool(obj.flags.require_2fa), 'allowSharedIssues': not obj.flags.disable_shared_issues, 'enhancedPrivacy': bool(obj.flags.enhanced_privacy), 'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)), 'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)), 'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [], 'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [], 'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)), 'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)), 'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)), 'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [], }) context['teams'] = serialize(team_list, user, TeamSerializer()) context['projects'] = serialize(project_list, user, ProjectSummarySerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context
def serialize(self, obj, attrs, user): from sentry import features, experiments from sentry.features.base import OrganizationFeature from sentry.app import env from sentry.api.serializers.models.project import ProjectSummarySerializer from sentry.api.serializers.models.team import TeamSerializer team_list = sorted(Team.objects.filter( organization=obj, status=TeamStatus.VISIBLE, ), key=lambda x: x.slug) for team in team_list: team._organization_cache = obj project_list = sorted(Project.objects.filter( organization=obj, status=ProjectStatus.VISIBLE, ), key=lambda x: x.slug) for project in project_list: project._organization_cache = obj onboarding_tasks = list( OrganizationOnboardingTask.objects.filter( organization=obj, ).select_related('user') ) # Retrieve all registered organization features org_features = features.all(feature_type=OrganizationFeature).keys() feature_list = set() for feature_name in org_features: if not feature_name.startswith('organizations:'): continue if features.has(feature_name, obj, actor=user): # Remove the organization scope prefix feature_list.add(feature_name[len('organizations:'):]) # Do not include the onboarding feature if OrganizationOptions exist if 'onboarding' in feature_list and \ OrganizationOption.objects.filter(organization=obj).exists(): feature_list.remove('onboarding') # Include api-keys feature if they previously had any api-keys if 'api-keys' not in feature_list and ApiKey.objects.filter(organization=obj).exists(): feature_list.add('api-keys') # Organization flag features (not provided through the features module) if OrganizationOption.objects.filter( organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists(): feature_list.add('legacy-rate-limits') if getattr(obj.flags, 'allow_joinleave'): feature_list.add('open-membership') if not getattr(obj.flags, 'disable_shared_issues'): feature_list.add('shared-issues') if getattr(obj.flags, 'require_2fa'): feature_list.add('require-2fa') experiment_assignments = experiments.all(org=obj, actor=user) context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user) max_rate = quotas.get_maximum_quota(obj) context['experiments'] = experiment_assignments context['quota'] = { 'maxRate': max_rate[0], 'maxRateInterval': max_rate[1], 'accountLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:account-rate-limit', default=ACCOUNT_RATE_LIMIT_DEFAULT, ) ), 'projectLimit': int( OrganizationOption.objects.get_value( organization=obj, key='sentry:project-rate-limit', default=PROJECT_RATE_LIMIT_DEFAULT, ) ), } context.update({ 'isDefault': obj.is_default, 'defaultRole': obj.default_role, 'availableRoles': [{ 'id': r.id, 'name': r.name, } for r in roles.get_all()], 'openMembership': bool(obj.flags.allow_joinleave), 'require2FA': bool(obj.flags.require_2fa), 'allowSharedIssues': not obj.flags.disable_shared_issues, 'enhancedPrivacy': bool(obj.flags.enhanced_privacy), 'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)), 'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)), 'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [], 'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [], 'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)), 'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)), 'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)), 'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [], }) context['teams'] = serialize(team_list, user, TeamSerializer()) context['projects'] = serialize(project_list, user, ProjectSummarySerializer()) if env.request: context['access'] = access.from_request(env.request, obj).scopes else: context['access'] = access.from_user(user, obj).scopes context['features'] = feature_list context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter( team__organization=obj, ).count() context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer()) return context