PR13 — Dynamic Enterprise RBAC: org-scoped roles, permission catalog, escalation guards - #32
Merged
Merged
Conversation
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>
This was referenced Aug 6, 2026
man4ish
marked this pull request as ready for review
August 6, 2026 02:50
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.
Summary
Activates dynamic, database-driven permission assignment for enterprise RBAC:
Rolegains a nullableorganization_id(migration0016_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 inrole_service.pysince MySQL's NULL semantics can't express this as a single DB constraint.permissionsclaim now unions global-role and org-membership-role permissions (previously global-only) — this is the cutoverpermission_parity.pyexisted to prepare for; that module is removed as part of this change.scientist(workflow.execute,dataset.read,model.use) andviewer(dataset.read,workflow.read).POST/PUT/DELETE /platform/roles(/{id}),GET/POST /organizations/{id}/roles,GET /organizations/{id}/permissions,PUT/DELETE /organizations/{id}/roles/{id}.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).GET /orgs/{org_id}/rolespreviously 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 aROLE_ASSIGNMENT_DENIEDaudit event. Platform Admin retains both capabilities.Compatibility notes: fully additive —
organization_idis nullable, every existing role getsNULLwith no backfill; every pre-PR13 test/call site continues to pass unmodified with defaultorganization_id=None. Migration is reversible (tested against both SQLite and a real throwaway MySQL database). Migration0016has 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 realACTION_PERMISSION_MAPgap this PR's dynamic permissions depend on for real enforcementomnibioai-api-gateway— new tests confirming permission forwarding per role tieromnibioai-control-center— PR #20 (draft, open) — Roles & Permissions UI consuming the endpoints this PR addsomnibioai-studio— deployment runbook + smoke-test extension for this PR's role tiersomnibioai-docs— architecture documentation for the changes in this PRReview only — do not merge. No migration applied, no deployment performed, live stack untouched.