Skip to main content

permissions-rbac

Gate a route or a service on who may do it — add a permission, wire a role, decide between require() and resolve_access, share a resource with a grant, or debug "this user can see something they should not" and "a Viewer with an edit grant is being refused". Use for any authorization work, any new org-scoped route, and any review of one. There is no UserRole, RoleChecker or CurrentAdmin in this project.

설치로 이동

소스 정보

저장소
vstorm-co/agenticos
최근 소스 활동
2026년 8월 27일 03:31
감지된 SKILL.md 언어
영어
스타
37
포크
7

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
2 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
permissions-rbac
description
Gate a route or a service on who may do it — add a permission, wire a role, decide between require() and resolve_access, share a resource with a grant, or debug "this user can see something they should not" and "a Viewer with an edit grant is being refused". Use for any authorization work, any new org-scoped route, and any review of one. There is no UserRole, RoleChecker or CurrentAdmin in this project.
# Permissions — three layers, and one rule people get wrong **Read `docs/permissions.md`** for the model and `docs/reference/permissions.md` for the generated catalog. `.claude/rules/exceptions-security.md` has the code shapes. This file is what to do and what not to. Permissions are defined in code; roles are composed from them. **Call sites check permissions, never role names**, so adding or re-shaping a role never means editing an endpoint. ## The rule that gets broken > `require(...)` gates belong on **collection** routes, not per-resource ones. | Route shape | Gate | |---|---| | `GET /agents`, `POST /agents`, `GET /agents/capabilities` | `dependencies=[Depends(require(Perm.AGENTS_EDIT))]` | | `GET/PATCH/DELETE /agents/{id}`, `POST /agents/{id}/publish` | **No route gate.** The service calls `resolve_access` | A role gate cannot see the grants on a row. Put one on a per-resource route and it refuses a Viewer holding an explicit `edit` grant *before* `resolve_access` ever widens their access — which directly contradicts the platform's own promise that a grant widens what a role allows and never narrows it. `tests/api/test_platform_routes.py` enforces both halves. If it fails after you added a gate, the gate is the bug. ## The two calls ```python # Collection route — a permission, as a route dependency. @router.post("", dependencies=[Depends(require(Perm.AGENTS_EDIT))]) async def create_agent(ctx: Auth, service: AgentSvc) -> Any: ... # One row — in the service, never as a route gate. if not await resolve_access(self.db, ctx, agent, Perm.AGENTS_EDIT, resource_type=AGENT): raise AuthorizationError(message="...") ``` Listings need the third call, or shared rows silently vanish: ```python extra = await visible_resource_ids(db, ctx, resource_type=AGENT, perm=Perm.AGENTS_VIEW) # None means the role already reaches everything — skip the grant lookup. # An empty list is NOT the same as None; a context with no subject gets []. ``` ## The three layers 1. **`users.is_app_admin`** — the deployment superadmin. `CurrentAppAdmin`. `uv run agenticos cmd create-app-admin <email> [--revoke]`. 2. **The organization role** — `owner`, `admin`, `builder`, `operator`, `member`, `viewer`. Composed from permissions in `app/core/permissions.py`. 3. **Visibility and grants** — a grant on one row. `effective = max(role scope, grant on this resource)`. Capability **scopes** (`knowledge:read`, `web:read`, `code:execute`) are a separate axis, not a fourth layer: they are what an *organization* allows its agents to do, checked when an agent is assembled rather than per request. See the `agent-capability` skill. Resource permissions carry a `Scope`: `none`, `own`, `shared`, `all`. It answers the question a role cannot — not "may this role touch agents?" but *which* agents. ## What does not exist `UserRole`, `User.has_role()`, `RoleChecker`, `CurrentAdmin`, `CurrentSuperuser`. The `users.role` column was dropped before the migration chain was squashed, so there is no revision to point at: `0001_baseline` simply creates `users` without it. Do not reintroduce any of them, and do not add `--role` to a CLI command. Authority inside an organization is a membership row plus this catalog. Only two user aliases exist: `CurrentUser` and `CurrentAppAdmin`. Anything org-scoped goes through `Auth` (`AuthContext`) plus a permission. ## Adding a permission 1. Add to `Perm` in `app/core/permissions.py`. 2. Add it to every role in the matrix that should have it — with a `Scope` if it is a resource permission. 3. Gate the collection routes; hand per-resource decisions to a service. 4. Test the **refusal**, and test it cross-tenant. Most of this platform's value is in what it refuses. 5. The frontend reads the effective permission set — check what it hides, and add an integration test that a button is actually gone (see the `frontend-feature` and `e2e-tests` skills). ## Always test - **Tenant isolation** — unreachable from another organization *including when the caller owns the row*. - **A grant widening, not promoting** — a Viewer with an `edit` grant on one agent edits that agent and nothing else. - **A context with no subject** — API key, embed session, channel run. See `docs/permissions.md#contexts-with-no-subject`. ## Depth `references/catalog.md` — the permission list, the role matrix, and how `resolve_access` decides.
GitHub에서 보기