.agents/skills/sentry-security/references/enforcement-layers.md
Sentry enforces security checks across multiple layers. A check in any layer counts as enforcement. Before reporting a missing check, verify it does not exist in any of the layers below.
1. Authentication class → authenticate() / authenticate_token()
2. Permission class → has_permission()
3. convert_args() → resolve URL kwargs → has_object_permission()
4. Access module → determine_access() → from_rpc_auth() / from_request()
5. Handler method → get() / post() / put() / delete()
6. Business logic classes → Validator, Refresher, GrantExchanger, etc.
7. Serializer → validate_*() methods, FK scoping
api/authentication.py)Verifies identity and token validity. May also enforce scoping (e.g., UserAuthTokenAuthentication checks scoping_organization_id at lines 530-555).
Not all authentication classes enforce scoping — some delegate to downstream layers.
api/permissions.py)has_permission() runs during DRF's initial(). Checks scope strings (e.g., org:read, project:write). Does not check object-level access.
api/bases/*.py)Resolves URL kwargs (e.g., organization_id_or_slug) into model objects. Calls check_object_permissions() which triggers has_object_permission().
auth/access.py)determine_access() is called from convert_args() in organization/project base endpoints. For org auth tokens, from_rpc_auth() compares auth.organization_id against the requested organization — returns NoAccess() on mismatch, which blocks all scope checks.
Key functions:
| Function | File | What it checks |
|---|---|---|
from_rpc_auth() | auth/access.py | Org auth token's organization_id matches requested org |
from_request() | auth/access.py | Session-based access with org membership |
determine_access() | api/bases/organization.py | Dispatches to the right access builder |
The endpoint's get()/post()/etc. May perform additional checks specific to the operation.
Classes like Validator, ManualTokenRefresher, GrantExchanger, and Refresher in sentry_apps/token_exchange/ run their own validation before performing operations.
Key class: Validator (sentry_apps/token_exchange/validator.py) checks:
ApiApplication.is_active (added in PR #105269)validate_*() methods and field-level validators may enforce org/project scoping on FK references.
OAuth views (OAuthAuthorizeView, OAuthDeviceView, OAuthTokenView) are plain Django views, not DRF endpoints. Layers 1–4 do not apply to the view itself. Check the view's own authentication decorators, dispatch(), and handler logic directly.
However, tokens issued by these views are later used at DRF API endpoints where layers 1–7 do apply. See "Cross-Flow Enforcement" below.
For token and credential issuance, enforcement may exist in a different request flow than the one being reviewed:
If the issued credential cannot be used because a separate enforcement point blocks it, classify based on where the enforcement lives:
Example (LOW — centralized): OAuth authorize view issues a token to a member-limit:restricted member. The token exists, but is_member_disabled_from_limit() in OrganizationPermission.determine_access() rejects it at every organization-scoped DRF endpoint. Since the token is only usable against organization endpoints (which all inherit this permission class), the enforcement covers all relevant paths. Do not report.
Example (MEDIUM — scattered): A token is issued without checking X, and X is only validated in specific endpoint subclasses (not the base). Some endpoints may not inherit the check. Report as needs verification.
Before marking a finding as HIGH, confirm the check is absent from all layers AND from cross-flow enforcement:
□ Authentication class does not enforce it
□ Permission class does not enforce it
□ convert_args() / has_object_permission() does not enforce it
□ Access module (from_rpc_auth / from_request) does not enforce it
□ Handler method does not enforce it
□ Business logic classes do not enforce it
□ Serializer does not enforce it
□ Cross-flow: the issued credential is not blocked at usage time
If the check exists in any layer or in a cross-flow enforcement point, the finding is either invalid, LOW (if enforcement is centralized in a base class), or at most MEDIUM (if enforcement is scattered or fragile).