# Admin Authorization

## Purpose

The admin authorization model separates a user's organizational role from the
individual admin capabilities granted to that role. The schema supports multiple
roles even though the initial non-superuser role is only `admin`.

## Core Rules

1. `superuser` is a hard-coded emergency and governance role.
   - It does not depend on database permission mappings.
   - It bypasses all admin permission checks.
   - Creating a replacement superuser or resetting its password remains an
     installer responsibility.
   - Superuser-only account governance actions continue to require the
     `Superuser` authorization policy.

2. Normal admin access uses Role-Based Access Control.
   - A user may have one or more Identity roles through `user_roles`.
   - A role may have one or more admin permissions through
     `admin_role_permissions`.
   - A user's effective permissions are the union of permissions assigned to all
     of the user's roles.

3. Roles and role-permission mappings are database data.
   - Existing ASP.NET Core Identity `roles` and `user_roles` tables remain the
     source of role membership.
   - `admin_permissions` stores the known permission catalog.
   - `admin_role_permissions` maps any Identity role to any admin permission.
   - Adding roles such as `support`, `auditor`, or `security_admin` does not
     require another schema change.

4. Permission definitions are owned by application code.
   - Permission keys are declared in `AdminPermissions`.
   - Startup and installer seeding synchronize those known definitions into the
     database.
   - The database decides which roles receive known permissions; it must not be
     used to invent capabilities that have no application implementation.

5. Permissions are action-oriented.
   - Controllers require a module permission such as `admin.tenants`.
   - Actions also require a capability permission such as
     `admin.tenants.create` or `admin.tenants.delete`.
   - Operations with materially different authorization boundaries must be
     separate actions and separate permission keys.

6. Server-side checks are authoritative.
   - Admin controllers retain `[Authorize(Policy = "Admin")]` as the outer admin
     boundary.
   - Controllers and actions use `AdminPermissionAttribute` for database-backed
     permission checks.
   - Non-admin access to `/admin/*` continues to return HTTP 404.
   - Missing action permissions also return HTTP 404 so inaccessible admin
     capabilities are not exposed.

7. Navigation and operation links use the same permission catalog.
   - Admin menus reference the same `AdminPermissions` constants used by the
     corresponding controller actions.
   - Buttons and links are hidden when the current user lacks the target action
     permission.
   - UI visibility is only a usability measure; direct requests are always
     protected by controller/action checks.

## Initial Mapping

The initial `admin` role receives every known permission except permissions
marked as superuser-only. This preserves current behavior while allowing the
mapping to be divided among more roles later.

The initial superuser-only permissions are:

- `admin.accounts.set_admin`
- `admin.accounts.set_disabled`
- `admin.accounts.reset_password`

No role or permission maintenance UI is included yet. Until one is introduced,
the permission catalog is updated in code and role-permission mappings may be
managed through controlled database changes or future installer commands.

## Adding an Admin Capability

1. Add a constant and definition to `AdminPermissions`.
2. Apply the module permission to the controller.
3. Apply the capability permission to every relevant action.
4. Use the same action permission for related menu items, links, and buttons.
5. Add or update role-permission mappings through the approved seed or
   administrative workflow.
6. Verify direct access, navigation visibility, `admin`, and `superuser`
   behavior.
