Skip to content

RBAC DB migration#7285

Open
thabofletcher wants to merge 23 commits intomainfrom
rbac-migration
Open

RBAC DB migration#7285
thabofletcher wants to merge 23 commits intomainfrom
rbac-migration

Conversation

@thabofletcher
Copy link
Contributor

@thabofletcher thabofletcher commented Feb 1, 2026

Ticket: N/A - Internal infrastructure for RBAC system

Description Of Changes

Adds database migrations and SQLAlchemy models to support the new dynamic Role-Based Access Control (RBAC) system. This creates the foundational database schema that enables:

  • Dynamic Roles: Create, modify, and delete roles at runtime (vs. hardcoded roles)
  • Fine-grained Permissions: Map permissions to roles with granular control
  • Role Hierarchy: Parent-child role relationships for permission inheritance
  • Resource Scoping: Assign roles with optional resource-level restrictions
  • Temporal Validity: Time-bounded role assignments (valid_from/valid_until)
  • Separation of Duties: Constraints preventing conflicting role combinations

Important: These migrations only CREATE new tables - no existing tables are modified.

Code Changes

New Database Tables (5):

  • rbac_role - Dynamic role definitions with hierarchy support
  • rbac_permission - Permission definitions (seeded from SCOPE_REGISTRY)
  • rbac_role_permission - Role to permission mapping (many-to-many)
  • rbac_user_role - User role assignments with resource scoping + temporal validity
  • rbac_role_constraint - Separation of duties constraints

Migrations (3):

  1. d9ee4ea46797_add_rbac_tables.py - Creates all 5 RBAC tables with indexes
  2. f5f526cbc35a_seed_rbac_defaults.py - Seeds permissions from SCOPE_REGISTRY and creates system roles
  3. 9f6555f12ad1_add_rbac_management_scopes.py - Adds RBAC management scopes (idempotent)

SQLAlchemy Models:

  • src/fides/api/models/rbac/ - New model package with all RBAC entities
  • Updated sql_models.py to import RBAC models

Steps to Confirm

  1. Run migrations: alembic upgrade head
  2. Verify tables created: \dt rbac_* in psql
  3. Verify permissions seeded: SELECT COUNT(*) FROM rbac_permission; (should be ~135)
  4. Verify roles seeded: SELECT * FROM rbac_role; (should have 7 system roles)
  5. Test downgrade: alembic downgrade -1 (repeat 3x), verify tables removed

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • All UX related changes have been reviewed by a designer
    • No UX review needed
  • Followup issues:
    • Followup issues created
    • No followup issues
  • Database migrations:
    • Ensure that your downrev is up to date with the latest revision on main
    • Ensure that your downgrade() migration is correct and works
      • If a downgrade migration is not possible for this change, please call this out in the PR description!
    • No migrations
  • Documentation:
    • Documentation complete, PR opened in fidesdocs
    • Documentation issue created in fidesdocs
    • If there are any new client scopes created as part of the pull request, remember to update public-facing documentation that references our scope registry
    • No documentation updates required

@vercel
Copy link
Contributor

vercel bot commented Feb 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
fides-plus-nightly Ignored Ignored Preview Feb 12, 2026 1:45am
fides-privacy-center Ignored Ignored Feb 12, 2026 1:45am

Request Review

thabofletcher added a commit that referenced this pull request Feb 3, 2026
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@thabofletcher thabofletcher marked this pull request as ready for review February 3, 2026 01:08
@thabofletcher thabofletcher requested a review from a team as a code owner February 3, 2026 01:08
@thabofletcher thabofletcher requested review from erosselli and removed request for a team February 3, 2026 01:08
@thabofletcher thabofletcher added the db-migration This indicates that a change includes a database migration label Feb 3, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 3, 2026

Greptile Overview

Greptile Summary

This PR introduces the initial database schema and ORM models for a dynamic RBAC system, including Alembic migrations to create RBAC tables and seed permissions/roles, plus unit tests covering basic model behavior (roles, permissions, assignments, and constraints). The RBAC models are wired into the existing SQLAlchemy model registry via src/fides/api/models/sql_models.py.

Key issues to address before merging:

  • The first RBAC migration’s revision metadata appears inconsistent (Revises in the docstring does not match down_revision), which can create an unintended branch in the Alembic graph.
  • The ORM definition of rbac_role_permission does not match the table created by migrations (PK/id-column mismatch). This is likely to cause runtime failures when the ORM interacts with the real table.

After those are fixed, consider aligning server_default usage (SQL expressions vs strings) and making time-dependent tests deterministic.

Confidence Score: 2/5

  • Not safe to merge until migration graph and ORM/schema mismatches are resolved.
  • Score is low because there are two likely runtime-breaking issues: (1) Alembic down_revision mismatch can create multiple heads/incorrect ordering, and (2) rbac_role_permission ORM mapping does not match the table created/seeded by migrations. The remaining items are consistency/flakiness concerns.
  • src/fides/api/alembic/migrations/versions/xx_2026_01_31_1000_d9ee4ea46797_add_rbac_tables.py and src/fides/api/models/rbac/rbac_role_permission.py (plus migrations that insert into rbac_role_permission).

Important Files Changed

Filename Overview
changelog/7285.yaml Adds a changelog entry for the RBAC DB migration work.
src/fides/api/alembic/migrations/versions/xx_2026_01_31_1000_d9ee4ea46797_add_rbac_tables.py Creates 5 RBAC tables; migration header Revises docstring does not match down_revision, and uses string server_default literals.
src/fides/api/alembic/migrations/versions/xx_2026_01_31_1100_f5f526cbc35a_seed_rbac_defaults.py Seeds RBAC permissions/roles and role-permission mappings; inserts into rbac_role_permission by id and defines a helper inside upgrade().
src/fides/api/alembic/migrations/versions/xx_2026_02_01_0900_9f6555f12ad1_add_rbac_management_scopes.py Adds RBAC management scopes and assigns them to Owner; inserts into rbac_role_permission by id which conflicts with the ORM model PK definition.
src/fides/api/alembic/migrations/versions/xx_2026_02_01_1000_a8b9c0d1e2f3_seed_fidesplus_scopes.py Seeds fidesplus scopes and assigns to roles; also inserts into rbac_role_permission by id.
src/fides/api/models/rbac/init.py Exports RBAC model classes for package import.
src/fides/api/models/rbac/rbac_permission.py Defines RBACPermission model with relationship to roles via rbac_role_permission.
src/fides/api/models/rbac/rbac_role.py Defines RBACRole model with hierarchy and permission aggregation helpers.
src/fides/api/models/rbac/rbac_role_constraint.py Defines RBACRoleConstraint model and helper methods for constraint type checks.
src/fides/api/models/rbac/rbac_role_permission.py Defines RBACRolePermission junction model with composite PK, but migrations create an id PK column (schema mismatch).
src/fides/api/models/rbac/rbac_user_role.py Defines RBACUserRole assignment model; valid_from uses server_default="now()" as a string rather than a SQL expression.
src/fides/api/models/sql_models.py Imports RBAC models and adds them to sql_model_map.
tests/api/models/test_rbac.py Adds unit tests for RBAC models; time-based assertions could be made deterministic with freezegun.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

thabofletcher and others added 14 commits February 11, 2026 11:25
Add industry-standard RBAC (Role-Based Access Control) system with:

Database tables:
- rbac_role: Dynamic role definitions with hierarchy support
- rbac_permission: Permission definitions (seeded from SCOPE_REGISTRY)
- rbac_role_permission: Role-permission mapping (many-to-many)
- rbac_user_role: User role assignments with resource scoping + temporal validity
- rbac_role_constraint: Separation of duties constraints

SQLAlchemy models:
- RBACRole: Supports role hierarchy via parent_role_id
- RBACPermission: Permission definitions
- RBACRolePermission: Junction table
- RBACUserRole: Supports resource-scoped and time-bounded assignments
- RBACRoleConstraint: Static/dynamic SoD and cardinality constraints

Migrations:
- Add RBAC tables with indexes and constraints
- Seed default roles and permissions from existing role definitions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ations

Changed revision IDs to unique values:
- add_rbac_tables: a1b2c3d4e5f6 -> d9ee4ea46797
- seed_rbac_defaults: b2c3d4e5f6a7 -> f5f526cbc35a

The original IDs conflicted with existing migrations:
- xx_2025_11_10_1200_a1b2c3d4e5f6_add_test_datastore_to_connectiontype.py
- xx_2025_11_12_1430_b2c3d4e5f6a7_add_default_identity_definitions.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit adds comprehensive tests for the RBAC models:

- TestRBACPermission: Tests for creating and persisting permission records
- TestRBACRole: Tests for custom roles, role hierarchy, and permission inheritance
- TestRBACUserRole: Tests for role assignments, resource scoping, and temporal validity
- TestRBACRoleConstraint: Tests for SoD and cardinality constraints

Tests cover:
- Basic CRUD operations
- Parent-child role relationships
- Permission inheritance through role hierarchy
- Scoped role assignments (resource_type + resource_id)
- Temporal role validity (valid_from, valid_until)
- Separation of duties constraints
- Cardinality constraints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add passive_deletes=True to the user relationship so SQLAlchemy
defers to the database's ON DELETE CASCADE behavior instead of
trying to set user_id=NULL (which causes IntegrityError).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add scopes required for RBAC management UI:
- rbac_role:create/read/update/delete
- rbac_permission:read
- rbac_user_role:create/read/update/delete
- rbac_constraint:create/read/update/delete
- rbac:evaluate

Also add migration for existing installations to add these scopes
and assign them to the Owner role.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This migration adds fidesplus-specific permissions (discovery_monitor,
custom_field, taxonomy, etc.) to the rbac_permission table and assigns
them to the appropriate system roles.

This replaces the runtime seeding that was previously in fidesplus
startup, following the pattern of keeping all DB changes in fides.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updated down_revision from 6d5f70dd0ba5 to 627c230d9917
to align with the current head on main branch.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use RelationshipProperty[Type] for SQLAlchemy relationship type annotations
to match the pattern used elsewhere in the fides codebase.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Addresses Greptile review feedback:

1. Fix migration docstring to match actual down_revision (627c230d9917)

2. Change rbac_role_permission from id-based PK to composite PK:
   - Migration now creates table with (role_id, permission_id) as composite PK
   - Removed id column and updated_at column (not needed for junction table)
   - Updated all seed migrations to not insert id column

3. Update RBACRolePermission model to override Base columns:
   - Set id = None to remove inherited id column
   - Set updated_at = None to remove inherited updated_at column
   - Composite PK on (role_id, permission_id) matches migration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…et down_revision to main head

Co-authored-by: Cursor <cursoragent@cursor.com>
thabofletcher and others added 2 commits February 11, 2026 12:00
- Update first RBAC migration down_revision to f85bd4c08401 (current main head)
- Add backfill:exec scope to base seed migration
- Add chat_provider:read, chat_provider:update scopes to fidesplus seed
- Add privacy_assessment:read/create/update/delete scopes to fidesplus seed
- Assign new scopes to owner and contributor roles as appropriate

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update column comments to match model docstrings exactly
- Replace unique constraint + non-unique index with unique index for
  code and key columns (matching model unique=True, index=True)
- Remove extraneous indexes not defined in models:
  - ix_rbac_role_permission_role_id/permission_id
  - ix_rbac_user_role_resource/validity
  - ix_rbac_role_constraint_type
- Add column comments to junction table columns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
thabofletcher and others added 2 commits February 11, 2026 15:39
Adds system.operations data category annotations to db_dataset.yml for
the five new RBAC tables to satisfy the fides_db_scan CI check.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@galvana galvana self-requested a review February 11, 2026 23:53
Copy link
Contributor

@galvana galvana left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some inline comments and created a separate PR with a suggestion for a more flexible constraint model #7367. The PR has a README.md with an explanation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should match 1:1 - otherwise this fails:
https://github.com/ethyca/fides/actions/runs/21926704315/job/63322448576

But its completely possible I'm doing this a non-standard way

from fides.api.models.rbac.rbac_role import RBACRole


class RBACPermission(Base):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we're introducing the concept of permissions, which makes more sense than scopes to me. Do you see any issues with us renaming scopes to permissions? Or do you see this table name as just implementation detail?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its a relatively minor nuance that we should watch out for if we are missing the boat on it but this is the way I see it:

Users have roles, and are assigned permissions, those permissions allow them to obtain a scope, and the scope defines what they can access in the application.

That being said, with fine grained access controls like this there should generally be a 1:1 mapping between a permission and its scope

@galvana galvana removed the request for review from erosselli February 12, 2026 00:29
- Add RBAC management scopes to scope_registry.py so they're part of
  the canonical SCOPE_REGISTRY (rbac_role:*, rbac_permission:read,
  rbac_user_role:*, rbac_constraint:*, rbac:evaluate)

- Add TestRBACScopeRegistrySync test class that parses the RBAC seed
  migration and compares its SCOPE_DOCS against SCOPE_REGISTRY. This
  prevents drift where someone adds a scope without updating both
  locations, which could break the RBAC system.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
thabofletcher and others added 2 commits February 11, 2026 16:56
RBAC management scopes don't need to be in the legacy SCOPE_REGISTRY since:
- RBAC management only works when RBAC is enabled
- When RBAC is enabled, permissions are checked against the database
- The drift detection test only checks one direction (registry → migration)

The RBAC scopes remain in the migration's SCOPE_DOCS to be seeded into
the database, where they'll be used when RBAC is enabled.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update the first RBAC migration (d9ee4ea46797) to depend on
d304f57aea6d (stagedresourceancestor distance) instead of f85bd4c08401.

This resolves the "Multiple head revisions" error caused by both
migrations pointing to the same parent after merging main.

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

Labels

db-migration This indicates that a change includes a database migration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants