feat(BA-5682): backfill project-user scopes from association_groups_users#11080
feat(BA-5682): backfill project-user scopes from association_groups_users#11080
Conversation
…sers Copy every (user_id, group_id) row from association_groups_users into association_scopes_entities as project-scope user memberships with relation_type='ref', per BEP-1048 (Project ─ref→ User). Idempotent via ON CONFLICT on uq_scope_id_entity_id; downgrade is a no-op since backfilled rows cannot be distinguished from live auto-sync rows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an Alembic data migration to backfill project→user membership edges into the RBAC scope-entity mapping table, aligning project membership lookups with legacy association_groups_users semantics (per BEP-1048).
Changes:
- Introduce migration
c8d4e5f6a7b9to copy(user_id, group_id)→(scope_type='project', scope_id=group_id, entity_type='user', entity_id=user_id, relation_type='ref'). - Make the backfill idempotent via
INSERT ... SELECT ... ON CONFLICT .... - Leave
downgrade()as a no-op due to inability to distinguish backfilled rows from future live-synced rows.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| op.execute(""" | ||
| INSERT INTO association_scopes_entities | ||
| (scope_type, scope_id, entity_type, entity_id, relation_type) | ||
| SELECT | ||
| 'project', | ||
| group_id::text, | ||
| 'user', | ||
| user_id::text, | ||
| 'ref' | ||
| FROM association_groups_users | ||
| ON CONFLICT ON CONSTRAINT uq_scope_id_entity_id DO NOTHING | ||
| """) |
There was a problem hiding this comment.
ON CONFLICT ... DO NOTHING can leave pre-existing project→user edges with relation_type='auto' unchanged (e.g. rows inserted by earlier migrations before relation_type existed), even though this migration’s goal is to backfill/ensure relation_type='ref'. Since RelationType affects scope-chain traversal (AUTO edges are traversed; REF terminates), this can preserve incorrect semantics. Consider switching to ON CONFLICT ... DO UPDATE SET relation_type = 'ref' (optionally guarded with a WHERE association_scopes_entities.entity_type = 'user') so existing rows are upgraded to REF as well as inserted when missing.
Summary
c8d4e5f6a7b9that copies every(user_id, group_id)row fromassociation_groups_usersintoassociation_scopes_entitiesas a project-scope user membership withrelation_type='ref', per BEP-1048 (Project ─ref→ User).INSERT ... SELECT ... ON CONFLICT ON CONSTRAINT uq_scope_id_entity_id DO NOTHING, so the migration is idempotent and naturally deduplicates. Rows are migrated regardless ofis_activeto match legacy lookup semantics.downgrade()is a no-op: once the auto-sync path lands, backfilled rows cannot be distinguished from live ones, so the schema is torn down by its own future migration rather than here.Test plan
alembic upgrade headon a DB populated withassociation_groups_usersrows; verify matching(project, group_id, user, user_id, ref)rows appear inassociation_scopes_entities.alembic upgrade headand confirm no duplicate rows / no errors (idempotency).Resolves BA-5682