5. Collapse per-operation permission rows into bitmask and drop operation column
- Dominant language
- Python
- Stars
- 670
- Forks
- 183
- Avg merge
- 15h 13m
- Merged PRs (30d)
- 368
Description
Background
Finalize the migration by collapsing per-operation rows into a single bitmask row per (role, scope, entity) and removing the legacy operation column.
Scope
Alembic migration: for each (role_id, scope_type, scope_id, entity_type), aggregate per-operation rows into a single row whose Permission bitmask is the OR of the group's operations; delete the redundant rows.
Replace the unique constraint uq_permissions_role_scope_entity_op (role, scope, entity, operation) with one on (role, scope, entity); drop the `operation` column and its StrEnum usage.
Remove operation-unit logic from the write path (PermissionRow.from_input, PermissionCreator, update_role_permissions, bulk add/remove, base/rbac/granter.py, base/rbac/revoker.py, and the vfolder share level change / unshare flows).
Idempotent per the alembic README backport strategy; downgrade restores per-operation rows and the operation column.
Revoke Semantics (partial revoke must NOT be a row DELETE)
Today each row carries exactly one operation, so revoking a subset of operations == deleting the matching rows. After the collapse a row carries the full bitmask, so a naive translation (DELETE WHERE permission & revoked_bits != 0) deletes the whole grant and over-revokes the remaining bits (e.g., an rw -> ro share downgrade would remove read access entirely).
Partial revoke becomes an atomic bitwise clear executed in SQL: UPDATE permissions SET permission = permission & ~revoked_bits WHERE ...; then DELETE rows whose mask reached NONE (do not keep empty grants).
No fetch-modify-write in Python: with per-operation rows, concurrent grant/revoke of different operations touched different rows; after the collapse they contend on one row, so the bit update must be a single SQL statement to avoid lost updates (READ COMMITTED is sufficient with atomic updates).
Entity-keyed full deletes (entity purge, full unshare with operations=None) are unaffected.
Grant Semantics (OR-merge upsert)
Replace ON CONFLICT DO NOTHING on (role, scope, entity, operation) with an OR-merge upsert on the new (role, scope, entity) constraint: ON CONFLICT DO UPDATE SET permission = permissions.permission | EXCLUDED.permission. Keeping DO NOTHING would make permission upgrades (e.g., ro -> rw re-share) silently no-op.
Depends On
Resolution switch story and API exposure story (resolution and API must use the Permission column only). Related: BA-4622 RBAC legacy cleanup; BA-4360 object_permissions removal should land before/with this story so the operation StrEnum can be fully retired.
Success Criteria
after migration, at most one permissions row exists per (role, scope, entity); the bitmask equals the OR of the previous per-operation grants.
the operation column and uq_permissions_role_scope_entity_op are removed; the write path no longer references operation.
partial revoke clears only the targeted bits (remaining bits keep working); a row whose mask reaches NONE is deleted. Covered by tests.
re-granting over an existing row ORs the new bits in (e.g., ro -> rw upgrade works). Covered by tests.
permission resolution results unchanged versus the resolution-switch story (regression parity).
pants lint/check/test pass for affected packages.
JIRA Issue: BA-6352
Contributor guide
Research direction
Start with the Alembic migration and the named write-path entry points: PermissionRow.from_input, PermissionCreator, update_role_permissions, bulk add/remove, base/rbac/granter.py, and base/rbac/revoker.py. Run the affected Pants lint/check/test targets and trace the vfolder share and unshare flows. Done means one bitmask row per role, scope, and entity, atomic partial revokes, OR-merge grants, and successful downgrade parity.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, sql
- Domain
- authorization, backend, databases
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100