feat(policy): safe cut-over to policies (status, compare, guard, generate) #18

Merged
KofTwentyTwo merged 1 commits from feature/policy-cutover into develop 2026-10-13 19:48:21 +00:00
KofTwentyTwo commented 2026-10-13 19:40:23 +00:00 (Migrated from github.com)

Makes saved policies take effect safely (DESIGN.md section 12.8), per the coordinator's cut-over task.

What is in it

  • Host SPI AuthentikHostPermissionSource: what a user, or a group's members, gets today from the host (kof22: adminPermissions() for kof22-admins). New config:
    • mapperEnabled: the host's flag. A host that calls the mapper without setting it counts as on.
    • cutoverGuardGroups
    • mapperOverride
    • staticGroupPermissions: the floor.
  • Enforcement status: OFF, ACTIVE, OVERRIDDEN or BLOCKED, cached 60 s. It appears in three places:
    • a banner widget, first on group and policy records and on the Edit Policy step: "Policies are saved but NOT enforced at sign-in on this instance (mapper off)"
    • the Identity home's Connection card ("Policies at sign-in")
    • Compare Access
  • Compare Access: a read-only TABLE widget on the app home (authentik.admin). For every active person it compares today's access (host source) with the mapper's grant (group policies and qqq.io/permissions with ancestors, static fallback, bootstrap subjects). It shows added, removed and unchanged; guarded people are marked; the footer gives the guard result.
  • Cut-over guard: while a member of a guarded group would lose a permission and there is no override, the mapper grants today's access plus the static fallback and bootstrap subjects instead of the policies. The floor and the bootstrap subject apply in every state, so there is no lockout.
  • Generate Policy From Current Access: a group action.
    • The expressible part of today's access, joined with the current policy, becomes the document.
    • Named permissions the app defines go to qqq.io/permissions; unknown ones are listed as skipped.
    • Review shows the generated JSON and the diff.
    • The operator's own access today counts for the subset rule.
  • Rollback is one switch: the host flag off (kof22: AUTHENTIK_QBIT_MAPPER=false).

Tests

CI=true ./mvnw verify: 202 unit tests and 44 contract tests on Authentik 2026.8.3, green. PolicyCutoverTest covers:

  • status OFF, then BLOCKED, OVERRIDDEN and ACTIVE
  • the compare rows and the widget
  • the banner on the screens and the home
  • the mapper falling back while blocked, and policies applied once it passes
  • generate: Review notes, the saved policy and named permissions, no change on a rerun, and no source configured

🤖 Generated with Claude Code

Makes saved policies take effect safely (DESIGN.md section 12.8), per the coordinator's cut-over task. ## What is in it - **Host SPI** `AuthentikHostPermissionSource`: what a user, or a group's members, gets today from the host (kof22: `adminPermissions()` for `kof22-admins`). New config: - `mapperEnabled`: the host's flag. A host that calls the mapper without setting it counts as on. - `cutoverGuardGroups` - `mapperOverride` - `staticGroupPermissions`: the floor. - **Enforcement status**: OFF, ACTIVE, OVERRIDDEN or BLOCKED, cached 60 s. It appears in three places: - a banner widget, first on group and policy records and on the Edit Policy step: "Policies are saved but NOT enforced at sign-in on this instance (mapper off)" - the Identity home's Connection card ("Policies at sign-in") - Compare Access - **Compare Access**: a read-only TABLE widget on the app home (`authentik.admin`). For every active person it compares today's access (host source) with the mapper's grant (group policies and `qqq.io/permissions` with ancestors, static fallback, bootstrap subjects). It shows added, removed and unchanged; guarded people are marked; the footer gives the guard result. - **Cut-over guard**: while a member of a guarded group would lose a permission and there is no override, the mapper grants today's access plus the static fallback and bootstrap subjects instead of the policies. The floor and the bootstrap subject apply in every state, so there is no lockout. - **Generate Policy From Current Access**: a group action. - The expressible part of today's access, joined with the current policy, becomes the document. - Named permissions the app defines go to `qqq.io/permissions`; unknown ones are listed as skipped. - Review shows the generated JSON and the diff. - The operator's own access today counts for the subset rule. - **Rollback** is one switch: the host flag off (kof22: `AUTHENTIK_QBIT_MAPPER=false`). ## Tests `CI=true ./mvnw verify`: 202 unit tests and 44 contract tests on Authentik 2026.8.3, green. `PolicyCutoverTest` covers: - status OFF, then BLOCKED, OVERRIDDEN and ACTIVE - the compare rows and the widget - the banner on the screens and the home - the mapper falling back while blocked, and policies applied once it passes - generate: Review notes, the saved policy and named permissions, no change on a rerun, and no source configured 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.