Repository navigation
feat(guard): add an organization-aware permission guard - #38
Merged
Merged
Conversation
RequireUser and require_auth take permissions, permission_mode, organization and organization_param. Matching is exact. A token with permissions_omitted is checked against a live read of /users/me/permissions, which fails closed with 503 permissions_unavailable. require_permission is the verify-only form, and denies provider tokens until seamless-auth-oidc-provider#47. User and VerifiedUser gain permissions, organization_roles, organization_permissions and permissions_omitted. The reference apps gain the four routes of the pending permission-guards contract. Closes #32
Contributor
Author
|
Conformance harness run locally against this branch and the API worktree on feat/organization-roles-in-token, with SEAMLESS_VERIFY_CONTRACTS=permission-guards,content-type-gate: full adapter suite, 39 on both FastAPI and Django passed. Spec is in the seamless-cli PR for the permission-guards contract. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #32
Adds the permission guard from the wave 3 guard contract, on FastAPI and Django, for sessions and for verify-only tokens. Needs the auth API at v0.23 (seamless-auth-api #576, #577, both still open).
API
UserandVerifiedUsergainpermissions,organization_roles,organization_permissionsandpermissions_omitted. New exports:PermissionRule,PermissionMode,InsufficientPermission,PermissionsUnavailable,require_permission, andseamless_auth.fastapi.PermissionHTTPException.Decisions to review
HTTPExceptionbody indetail. A separateadd_exception_handlerstep, once forgotten, would change the response shape, so on its first refusal the guard registers its handler inscope["starlette.exception_handlers"], a Starlette internal. A handler the application registered wins. If Starlette drops that key the status stays right and the body falls back todetail, and the tests fail on that upgrade.permissionsororganizationPermissions, or anorganizationIdthat is not a string or null, is the 503, since a missing list would otherwise read as holding nothing. Non-string entries in a list are ignored.permissions_omitted, the live read'sorganizationIddecides, not the token'sorg_id, and the read happens even when the token'sorg_idalready mismatches the route. An empty required list denies before any read.roles, which raises. A bare string, a name that is not a non-empty string, or the other options withoutpermissionsraise when the guard is built, because that guard would look scoped and check nothing.Adapterrefuses to construct withoutauth_server_url, so the contract's "no URL configured" 503 cannot occur on this adapter.Existing FastAPI role guard
The divergence is real, documented in the README and pinned by
tests/test_fastapi.py:RequireUser(auth, roles=[...])answers{"detail":"insufficient_role"}.test_fastapi_the_session_role_guard_still_answers_under_detailrecords it. Not changed here. The same holds forRequireUser'sunauthenticated,enrollment_requiredandcross_site_request_blocked, which the contract says stay as they are, so the permission guard's 401 and recovery refusal on FastAPI are{"detail":...}. The issue's "same 403 response shape as the existing role guard" does not hold on FastAPI; this follows the contract instead.What was run
On Python 3.11:
uv run ruff check .: passeduv run ruff format --check .: passeduv run mypy: passeduv run pytest: 1082 passed, 425 of them new. Also 1082 passed on Python 3.14scripts/check-manifest.sh: matchesWhat was not run
permission-guardsspecs and an API with #576 and #577. CI's conformance job does not set the contract and CI is unchanged, so its green result says nothing about these routes.Release
release-please, from this
featcommit. No version or changelog edit by hand. Hold the release until the API's v0.23 is out if the README's requirement should be true on publish.