lablup / lablup/backend.ai

RBAC scope-entity permission model consolidation & validation

Open
#11,814 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
670
Forks
183
Avg merge
17h 7m
Merged PRs (30d)
358

Description

## Background

RBAC scope_entities (association_scopes_entities) and role_permission (permissions) are implemented per BEP-1048, but gaps remain:

1. scope_entities cannot express the maximum **delegable** permission per entity. Relations are distinguished only by relation_type (AUTO/REF), and REF's read-only meaning is not enforced as a permission cap.

2. Permission resolution does not clip scope-derived permissions by the relation's cap, so a permission granted at a scope can exceed what the relation should allow for the entity (e.g., a REF relation should only yield read-only).

3. RBAC scope_entities/role_permission repository access (querier/creator) is not consolidated into the repositories/ops layer.

## Resolution Model: 1-hop direct-scope query (no scope walk)

The Permission-bitmask-based permission check resolves permissions by querying only the scopes the target entity is directly associated with in scope_entities (1-hop). The recursive scope walk is removed.

- Premise: RBAC entity creation paths associate each entity directly with all of its effective scopes (primary + additional scope refs), so multi-hop traversal over scope-to-scope edges is redundant.

- Consequence: cap clipping reduces to a single-edge bitwise AND (granted permission AND the edge's permission_cap). No running-min accumulation along a multi-hop descent path is needed.

## Cap Semantics (design principle)

- cap is the \***ceiling on delegation through an edge**\*: it bounds how much of a scope's permission flows to the entity through that scope-entity edge. It is applied at \***resolution time**\* as a bitwise AND of the edge's permission_cap with the permission resolved via that scope (single-edge clip under the 1-hop model). A NULL cap means "no ceiling" (full).

- cap does \***not**\* reject or reduce a permission whose resolution does not cross the capped edge. The union/additive model has no deny; cap only bounds the permission flowing through its own edge. A permission resolved via one scope edge is never reduced by another edge's cap.

- \***Entity-scope direct grants bypass cap.**\* A permission whose scope is the target entity itself (scope_type/scope_id == the entity, i.e. the entity-as-scope pattern) — e.g. the read/write grant created by a VFolder invitation — is matched directly (Layer 1) and is not clipped by any edge cap. The ref edge's cap only stops the invitee's own scope permissions from applying beyond the cap (Layer 2).

## Goals

- Introduce a permission (cap) concept on scope_entities and reconcile AUTO/REF into the cap.

- Switch permission resolution to 1-hop direct-scope queries on the Permission bitmask, and enforce the cap by \***clipping edge-derived permissions (bitwise AND)**\* at resolution time (no rejection of assignment).

- Consolidate related repository access into repositories/ops.

## In Scope

- Finalize permission representation (done: Permission IntFlag bitmask in common/data/permission/types.py)

- association_scopes_entities schema change (permission_cap column) + migration/backfill

- Migrate permission resolution to 1-hop + cap-based: query direct scopes only, clip each edge-derived permission by that edge's cap (bitwise AND), leave direct entity-scope grants (Layer 1) un-clipped, and remove the recursive scope walk

- Consolidate scope_entities/role_permission repository access into repositories/ops

## Out of Scope

- Write-path rejection of over-cap role_permission assignment — \***dropped as outdated**\*. Enforcement is resolution-time clipping, not assignment rejection. Over-cap assignment is allowed; it simply has no effect beyond the cap when resolved through the edge.

- GRANT_\* (grant:\*) operations — intentionally dropped as legacy; the Permission bitmask has no grant bits.

- object_permissions table removal — tracked by BA-4360 (under BA-4622, currently Not Planned; needs reopening). Resolution code still reads ObjectPermissionRow, so it must be cleaned up before/with the operation column removal.

- Role preset / role_permission_preset (separate epic: BA-6173)

- Auto-grant/revoke of default role on scope creation (separate epic)

- General repository-wide ops consolidation beyond RBAC (separate track)

## Design Decisions

- permission representation: Permission IntFlag bitmask (READ/UPDATE/CREATE/SOFT_DELETE/HARD_DELETE), shared by grants (permissions.permission) and caps (scope_entities.permission_cap).

- Backfill rule (as shipped in BA-6283): REF -> read-only cap (READ bit), AUTO -> NULL (NULL = no ceiling). relation_type is kept for now; decide its removal once resolution no longer reads it (candidate: BA-4622).

- Resolution is 1-hop over direct scope associations; the recursive scope walk is removed (see Resolution Model above).

## Definition of Done

- scope_entities has a permission_cap column and existing AUTO/REF data is backfilled without loss (done: BA-6283)

- Permission resolution queries direct scopes only (1-hop) and clips each edge-derived permission by that edge's cap (bitwise AND); a permission resolved through a REF/low-cap edge cannot exceed the cap. Covered by tests.

- A permission resolved via an un-capped edge, or granted directly at the entity scope (sharing/invitation, scope == entity), is not clipped by any other edge's cap. Covered by tests.

- Permission resolution operates on the Permission bitmask column with regression parity to current decisions for AUTO-edge setups. (REF edges contributing capped read-only access via the invitee's own scopes is a deliberate behavior change, covered by new-behavior tests.)

- Target repository access is unified through repositories/ops

## Related

- BA-6173 RBAC Role Preset management (Epic 2)

- BA-4622 Clean up RBAC legacy code / BA-4360 Remove object_permissions table and business logic

JIRA Issue: BA-6194

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.