Repository navigation
bug(auth): new Permission members never reach the permissions table on existing deployments, so LLC roles cannot be granted them #16295
Description
Activity
- addedbugSomething isn't workingSomething isn't workingarea: auth-rbacWave 2 · cluster E — Auth, identity & RBAC canonWave 2 · cluster E — Auth, identity & RBAC canon
on Sep 11, 2026 A decision is needed here, and it also settles #16327's third criterion ("is the script still needed at all?").
What the script is, on base
a12b4bf79:autobot-infrastructure/shared/scripts/migrations/migrate_config_users.pyis a one-off migration of users from the YAMLsecurity_configinto PostgreSQL. Nothing in the repository invokes it; its only other mention is a line in the hardcoded-values baseline. Seeding permissions and system roles is one step inside it, and that step is broken: both loops index tuples and dict keys as dicts (#16327). So today nothing seeds these tables, and nothing re-seeds them either, which is this issue.Options:
- One idempotent seed function, used by both. This is my recommendation.
- Put the seed in
user_management: upsert everySYSTEM_PERMISSIONSrow and the system roles' grants fromROLE_PERMISSIONS. - This issue's step, run by the builtin updater, calls it on every update.
- The migration script calls the same function in place of its own broken loops. That fixes bug(infra): migrate_config_users.py reads SYSTEM_PERMISSIONS and SYSTEM_ROLES as lists of dicts, so both seeding loops raise TypeError #16327 by deleting duplicated logic rather than repairing it.
- One seeder can't drift from itself, and this issue's guard then covers both paths.
- Put the seed in
- Fix the script's loops (bug(infra): migrate_config_users.py reads SYSTEM_PERMISSIONS and SYSTEM_ROLES as lists of dicts, so both seeding loops raise TypeError #16327), and build this issue's step separately. That's the smallest change each, but it leaves two seeders to drift apart. The script's copy was already stale unnoticed, because nothing ran it.
- Retire the script, and let this step do all the seeding. Only correct once no deployment still holds YAML-config users to migrate. That needs host evidence I don't have, so it's no reason to delete the script now.
Sequencing either way: after #16298, which delivers #16270. It rewrites
_build_system_permissionsand edits the script's permission loop, and #16327 is blocked on it with a native edge.- One idempotent seed function, used by both. This is my recommendation.
- addedneeds-decisionBlocked on an owner decision; options and a recommendation are on the issueBlocked on an owner decision; options and a recommendation are on the issue
on Sep 11, 2026 Owner decision, 2026-09-11: seed on every update.
- One idempotent seed function in
user_managementupserts every missingSYSTEM_PERMISSIONSrow and the system roles' grants fromROLE_PERMISSIONS. - The builtin updater runs it on every deploy.
- It only adds: it never removes or overwrites an admin's custom grants, and a test pins that.
migrate_config_users.py's broken seed step is replaced by a call to this function, which settles bug(infra): migrate_config_users.py reads SYSTEM_PERMISSIONS and SYSTEM_ROLES as lists of dicts, so both seeding loops raise TypeError #16327's third criterion.
Implementation waits for #16298 (#16270), which reshapes
_build_system_permissionsand adds the new members.- One idempotent seed function in
- removedneeds-decisionBlocked on an owner decision; options and a recommendation are on the issueBlocked on an owner decision; options and a recommendation are on the issue
on Sep 11, 2026
Finding
Nothing re-seeds the
permissions/role_permissionstables when thePermissionenum grows.SYSTEM_PERMISSIONS/SYSTEM_ROLESare consumed at runtime by no code path. Their only consumers are migration 062, a one-time reconcile with hardcoded lists, andautobot-infrastructure/shared/scripts/migrations/migrate_config_users.py, a setup-time script.autobot_shared/auth/permissions.pysays a new member is seen by both backends automatically on the next deploy. That holds for the code-side gates (role_has_permission,require_permission), which readROLE_PERMISSIONS. It does not hold for the database.Consequence.
llc/services/role_permission.py:_resolve_permissionlooks a permission up by name in thepermissionstable and raisesunknown permissionon a miss. It deliberately does not auto-create names. So on an existing deployment, a member added after the database was seeded cannot be granted to an LLC company role.Members affected today:
admin.reporting_line.write(feat(llc): who may edit a card — the creator, their manager, and that manager's manager #15765). Migration 087 does not insert it.chat.use,chat.history,teams.read,teams.create,teams.manage,teams.deleteandwebhooks.trigger.Acceptance criteria
Permissionmember exists as a row inpermissionson an existing deployment after an update, through a migration or an idempotent seed step run by the builtin updater.role_permissionsmatchROLE_PERMISSIONSafter the same step.Permissionmember is added without the mechanism above covering it, so the next member cannot drift silently.admin.reporting_line.writeand the auth: extend Permission to cover chat, teams and webhooks, define key scopes as permission bundles, and return effective permissions from /me #16270 members. This needs host evidence.Found while delivering #16270.