Private
Public Access
1
0

fix(security): restrict auth-config mutations to Admin role (#5)

Restrict manager-wide authentication configuration mutations (OIDC, SMTP, IP allowlist) to Admin role. Operators now receive 403 forbidden_role.

- New admin_required helper in settings.rs
- 4 gate changes: update_settings, discover_oidc, test_oidc, update_ip_whitelist
- 5 new AuditAction variants + migration 019
- SPA friendly error message on 403
- 3 admin_required unit tests pass (43/43)
- Full integration tests deferred to issue #15

Closes #5
This commit is contained in:
Draco-Lunaris-Echo
2026-06-03 09:16:41 -05:00
committed by GitHub
parent f58d7a6f17
commit 88b190ac8d
8 changed files with 436 additions and 12 deletions

View File

@ -209,3 +209,53 @@ _(filled in at completion)_
- [x] 5c: Render pending hosts in the table with highlight styling
- [x] 5d: Add Approve/Deny action buttons to pending host rows
- [x] 5e: Implement "merge/overwrite" interactive modal for `fqdn`/`ip_address` collisions on approval
## Issue #5: Admin-Only Manager-Wide Configuration (Authz Gate)
**Spec:** [tasks/authz-gate-spec.md](authz-gate-spec.md) (v0.1.0)
**Branch:** `fix/5-operator-can-modify-auth-config`
**Status:** Draft spec — awaiting Kelly sign-off
### Phase 1: admin_required helper + 3 unit tests
- 1a: Add `admin_required` helper in `crates/pm-web/src/routes/settings.rs` (after `write_access_required` ~line 173). Returns 403 with code `forbidden_role` if not Admin.
- 1b: Add 3 unit tests in cfg(test) module: `admin_required_admin_passes`, `admin_required_operator_denied`, `admin_required_reporter_denied`.
- 1c: Run `cargo test -p pm-web --bins --tests` and confirm green.
### Phase 2: Gate changes + audit log calls
- 2a: Replace `write_access_required` with `admin_required` in `update_settings` (line 336).
- 2b: Replace `write_access_required` with `admin_required` in `update_ip_whitelist` (line 902).
- 2c: Replace `write_access_required` with `admin_required` in `discover_oidc` (line 561).
- 2d: Replace `write_access_required` with `admin_required` in `test_oidc` (line 619).
- 2e: Create `migrations/019_auth_config_audit_actions.sql` with 5 new enum values.
- 2f: Add 5 new variants to the `AuditAction` enum in `crates/pm-core/src/audit.rs` (or wherever defined).
- 2g: Add `write_audit_event` calls in each of the 4 handlers, after successful mutations.
- 2h: Run `cargo fmt --check --all`, `cargo clippy --all-targets -- -D warnings`, `cargo test -p pm-web --bins --tests` and confirm clean.
### Phase 3: Integration tests (8 new)
- 3a: `update_settings_operator_denied` — POST as Operator with OIDC fields → 403 `forbidden_role`.
- 3b: `update_settings_admin_allowed` — POST as Admin with OIDC fields → 200 + audit row written.
- 3c: `update_settings_smtp_operator_denied` — POST as Operator with SMTP fields → 403 `forbidden_role`.
- 3d: `update_settings_smtp_admin_allowed` — POST as Admin with SMTP fields → 200 + audit row written.
- 3e: `update_ip_whitelist_operator_denied` — POST as Operator → 403 `forbidden_role`.
- 3f: `update_ip_whitelist_admin_allowed` — POST as Admin → 200 + audit row written + in-memory `AuthConfig.ip_whitelist` updated.
- 3g: `discover_oidc_operator_denied` / `discover_oidc_admin_allowed` — 2 tests.
- 3h: `test_oidc_operator_denied` / `test_oidc_admin_allowed` — 2 tests.
- 3i: Run `cargo test -p pm-web --bins --tests` and confirm all green.
### Phase 4: SPA error message + 1 test
- 4a: Update `frontend/src/pages/SettingsPage.tsx` to detect `error.code === 'forbidden_role'` and show friendly message: "Only Admins can modify authentication configuration. Contact an Admin to make this change."
- 4b: Create `frontend/src/pages/__tests__/SettingsPage.test.tsx` with 1 test: `settings_page_forbidden_role_shows_friendly_message`.
- 4c: Run `npm test` in `frontend/` and confirm green.
### Phase 5: Documentation
- 5a: Update `docs/security-review.md` §2.3 (Authorization / RBAC) with 2 new rows.
- 5b: Annotate the 4 affected endpoints in `docs/REST_API.md` with "🔒 Admin only".
- 5c: Add a project-specific lesson in `tasks/lessons.md` about the role model (Admin = Manager-wide, Operator = per-host, Reporter = read-only).
### Phase 6: Review & commit
- 6a: Self-review against the 9 acceptance criteria in the spec.
- 6b: Manual pre-push checks (cargo fmt, cargo clippy, eslint, cargo test, npm test) — run all 6 from the recent lessons-learned entry.
- 6c: Commit on `fix/5-operator-can-modify-auth-config` with conventional format.
- 6d: Push to `github/fix/5-operator-can-modify-auth-config` via `github-echo` SSH alias.
- 6e: Open PR against `master` and comment on issue #5.
- 6f: Capture lessons in `tasks/lessons.md` (project-specific) and `git-workflow/references/lessons-learned.md` (skill-level).