Skip to content

feat: RBAC — role-based access control integrated with Databricks workspace identity#21

Open
Rampim wants to merge 4 commits intomainfrom
feature/rbac-unity-catalog
Open

feat: RBAC — role-based access control integrated with Databricks workspace identity#21
Rampim wants to merge 4 commits intomainfrom
feature/rbac-unity-catalog

Conversation

@Rampim
Copy link
Copy Markdown
Collaborator

@Rampim Rampim commented Apr 7, 2026

Migrated from the original repo. See issue #19 for full context.

Closes #6

Summary

  • Three roles: use (query only), manage (configure gateways + clear cache + manage users), owner (full control)
  • Workspace admin = Owner: Databricks workspace admins are automatically treated as owners via SCIM /Me check
  • Persistent storage: user_roles table in Lakebase
  • Enforced on API: gateway create/delete → owner; update/clear-cache → manage; app settings → owner
  • Frontend enforcement: Create button, Delete button, Settings tab hidden based on role

Permission matrix

Role Query Configure gateways Create/Delete gateways Manage users
use
manage
owner

Test plan

  • Workspace admin sees all buttons (New Gateway, Delete, Settings tab, Users section)
  • User with use role: only sees query + read-only views
  • User with manage role: can configure gateways, clear cache, manage users
  • Owner can add/remove role assignments from Settings → Users
  • /api/users/me returns correct role for each user type
  • Requests without user token return 401

…kspace identity

Implements issue #6: access control using Databricks workspace identity and Unity
Catalog-style permission levels.

| Role   | Query | Configure gateways | Create/Delete gateways | Manage users |
|--------|-------|--------------------|------------------------|--------------|
| use    | ✓     |                    |                        |              |
| manage | ✓     | ✓                  |                        |              |
| owner  | ✓     | ✓                  | ✓                      | ✓            |

Workspace admins (Databricks 'admins' group) are always treated as owner.

- `services/rbac.py`: role resolution — checks SCIM /Me for workspace admin, then
  user_roles table, then defaults to 'use'
- `api/rbac_routes.py`: `/api/users/me`, `/api/users`, `/api/users/{email}/role`
  (POST/DELETE) — all owner-gated except /me
- `storage_pgvector.py` + `storage_local.py` + `storage_dynamic.py` + `database.py`:
  user_roles table (pgvector) / dict (local) with get/set/list/delete CRUD
- `gateway_routes.py`: POST /gateways → owner; PUT /gateways → manage;
  DELETE /gateways → owner; DELETE cache → manage; PUT /settings → owner
- `main.py`: registers rbac_router at /api prefix

- `context/RoleContext.jsx`: fetches /api/users/me on mount; provides
  { role, isOwner, isManage, loading }
- `main.jsx`: wraps app in RoleProvider
- `services/api.js`: getMyRole, listUsers, setUserRole, deleteUserRole
- `GatewayListPage`: "New Gateway" button hidden for non-owners
- `GatewayDetailPage`: "Delete gateway" hidden for non-owners; Settings tab
  hidden for 'use' role
- `SettingsPage`: adds "Access Control → Users" sidebar section (owner only)
  with role matrix table, user list, add/remove user form

Co-authored-by: Isaac
Per updated spec: manage can list, assign, and remove user roles.
Previously only owner could do so.

Updated role matrix:
  use    → query only
  manage → configure gateways, clear cache, manage users
  owner  → full control (create/delete gateways + everything manage can do)

Co-authored-by: Isaac
Performance:
- rbac.py: cache is_workspace_admin result (60s TTL, keyed on token) and
  get_user_role result (120s TTL, keyed on identity) to avoid SCIM+DB I/O on
  every protected request; shared module-level httpx.AsyncClient eliminates
  per-call TCP handshake
- rbac_routes.py / gateway_routes.py: call invalidate_role_cache after every
  set_user_role / delete_user_role write so changes take effect immediately

Auth model:
- gateway_routes._require_role: switch from _get_token() (which has a
  service-token fallback) to extract_bearer_token() — using the SP token for
  authorization checks would grant the SP's role to unauthenticated callers

Code reuse:
- rbac.py / rbac_routes.py / gateway_routes.py: replace inline
  `if not host.startswith("http")` with ensure_https() from app.auth,
  matching the pattern used in 4 other route files

Bug:
- SettingsPage users useEffect: dep array had [isOwner, ...] but condition
  checked isManage — manage-role users would never see the users list load;
  fixed to [isManage, activeSection]; added usersLoadedRef to avoid re-fetching
  after removing the last user

UX / efficiency:
- handleAddUser: use response from setUserRole for optimistic local upsert
  instead of a second listUsers round-trip
- RoleContext: useMemo on context value to avoid re-rendering all consumers
  on unrelated parent state changes
- SettingsPage: useMemo on SIDEBAR (stable once role resolves)

Co-authored-by: Isaac
Delete _get_token() and replace all call sites with extract_bearer_token(),
which requires a real user token (X-Forwarded-Access-Token or Authorization
Bearer) and raises 401 immediately if absent. No SP/service token fallback
anywhere in the request path.

Co-authored-by: Isaac
@Rampim Rampim force-pushed the feature/rbac-unity-catalog branch from f41f8dc to 0052fb9 Compare April 7, 2026 21:05
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.

ACL support — owner, manage, and use roles

2 participants