Skip to content

PR13 — Dynamic Enterprise RBAC: org-scoped roles, permission catalog, escalation guards - #32

Merged
man4ish merged 1 commit into
mainfrom
feature/pr13-dynamic-rbac-activation
Aug 6, 2026
Merged

PR13 — Dynamic Enterprise RBAC: org-scoped roles, permission catalog, escalation guards#32
man4ish merged 1 commit into
mainfrom
feature/pr13-dynamic-rbac-activation

Conversation

@man4ish

@man4ish man4ish commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Activates dynamic, database-driven permission assignment for enterprise RBAC:

  • Role gains a nullable organization_id (migration 0016_role_org_scope, not applied to any live database as part of this PR): NULL = platform-wide role, otherwise owned by exactly one org and invisible to every other org. Uniqueness is scope-aware (platform-wide names reserved everywhere; org-custom names private per org), enforced in role_service.py since MySQL's NULL semantics can't express this as a single DB constraint.
  • JWT permissions claim now unions global-role and org-membership-role permissions (previously global-only) — this is the cutover permission_parity.py existed to prepare for; that module is removed as part of this change.
  • Two new default org-assignable roles: scientist (workflow.execute, dataset.read, model.use) and viewer (dataset.read, workflow.read).
  • New role/permission catalog CRUD: POST/PUT/DELETE /platform/roles(/{id}), GET/POST /organizations/{id}/roles, GET /organizations/{id}/permissions, PUT/DELETE /organizations/{id}/roles/{id}.
  • Security fix: closes a privilege-escalation path the JWT merge above would otherwise activate. An Org Admin could already assign any existing role by name — including literal admin/platform_admin — to another org member with no content check; a custom role could also be created holding a GLOBAL-scope permission. Both are now rejected unless the caller is a Platform Admin, and every rejection is audit-logged (ROLE_ASSIGNMENT_DENIED).
  • Fixed a real data leak: GET /orgs/{org_id}/roles previously returned every role in the system, including other orgs' private custom roles.

Dependency

This PR is part of PR13 — Dynamic Enterprise RBAC Activation.
It must be reviewed and merged in dependency order with the other PR13 repositories.
Do not merge independently.

Dependency order: auth (this PR) → policy-engine → api-gateway → control-center → studio → docs. This repo's schema/JWT changes are the foundation the others read from.

Validation

Tests performed: 687 passed (30 new PR13-specific: org-scope CRUD, uniqueness rules, JWT permission merge, both escalation guards + audit logging, migration round-trip against SQLite and real MySQL). Full suite, zero failures.

Security checks: escalation-guard tests confirm an Org Admin cannot (a) create a custom role holding a GLOBAL-scope permission, or (b) assign an existing GLOBAL-scope-permission role (including admin/platform_admin) to another member — both independently verified via direct API calls bypassing any UI, both produce a ROLE_ASSIGNMENT_DENIED audit event. Platform Admin retains both capabilities.

Compatibility notes: fully additive — organization_id is nullable, every existing role gets NULL with no backfill; every pre-PR13 test/call site continues to pass unmodified with default organization_id=None. Migration is reversible (tested against both SQLite and a real throwaway MySQL database). Migration 0016 has not been applied to any live/shared database as part of preparing this PR — schema changes take effect only once this PR is merged and the migration is explicitly run per the deployment runbook (omnibioai-studio/scripts/PR13-DEPLOYMENT-RUNBOOK.md).

Related PRs

Part of the PR13 review set, all on feature/pr13-dynamic-rbac-activation:

  • omnibioai-policy-engine — fixes a real ACTION_PERMISSION_MAP gap this PR's dynamic permissions depend on for real enforcement
  • omnibioai-api-gateway — new tests confirming permission forwarding per role tier
  • omnibioai-control-centerPR #20 (draft, open) — Roles & Permissions UI consuming the endpoints this PR adds
  • omnibioai-studio — deployment runbook + smoke-test extension for this PR's role tiers
  • omnibioai-docs — architecture documentation for the changes in this PR

Review only — do not merge. No migration applied, no deployment performed, live stack untouched.

Database-driven org-scoped custom roles (Role.organization_id, nullable
FK -- NULL is platform-wide, otherwise owned by that org and invisible to
others), a JWT permission cutover (permissions claim now unions global
and org-membership role permissions -- the merge permission_parity.py was
built to prepare for and is now removed), new scientist/viewer default
roles matching this PR's RBAC scenario matrix, and full role/permission
catalog CRUD for both the platform-wide and org-scoped surfaces.

Also closes a privilege-escalation path the JWT merge would otherwise
activate: an Org Admin could already assign any existing role by name
(including "admin"/"platform_admin") to another org member with no
content check, and could create a custom role holding a GLOBAL-scope
permission -- both now rejected unless the caller is a Platform Admin,
and every rejection is audit-logged (ROLE_ASSIGNMENT_DENIED).

687 tests pass (30 new PR13-specific).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant