## Summary - turn the Monitoring audit log placeholder into a real workspace-scoped audit review surface - introduce a shared audit recorder, richer audit value objects, and additive audit log schema evolution - add audit outcome and actor badges, permission-aware related navigation, and durable audit retention coverage ## Included - canonical `/admin/audit-log` list and detail inspection UI - audit model helpers, taxonomy expansion, actor/target snapshots, and recorder/builder services - operation terminal audit writes and purge command retention changes - spec 134 design artifacts and focused Pest coverage for audit foundation behavior ## Validation - `vendor/bin/sail bin pint --dirty --format agent` - `vendor/bin/sail artisan test --compact tests/Unit/Audit tests/Unit/Badges/AuditBadgesTest.php tests/Feature/Filament/AuditLogPageTest.php tests/Feature/Filament/AuditLogDetailInspectionTest.php tests/Feature/Filament/AuditLogAuthorizationTest.php tests/Feature/Monitoring/AuditCoverageGovernanceTest.php tests/Feature/Monitoring/AuditCoverageOperationsTest.php tests/Feature/Console/TenantpilotPurgeNonPersistentDataTest.php` ## Notes - Livewire v4.0+ compliance is preserved within the existing Filament v5 application. - No provider registration changes were needed; panel provider registration remains in `bootstrap/providers.php`. - No new globally searchable resource was introduced. - The audit page remains read-only; no destructive actions were added. - No new asset pipeline changes were introduced; existing deploy-time `php artisan filament:assets` behavior remains unchanged. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #163
281 lines
22 KiB
Markdown
281 lines
22 KiB
Markdown
# Implementation Plan: Audit Log Foundation
|
|
|
|
**Branch**: `134-audit-log-foundation` | **Date**: 2026-03-11 | **Spec**: [spec.md](./spec.md)
|
|
**Input**: Feature specification from `/specs/134-audit-log-foundation/spec.md`
|
|
|
|
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
|
|
|
|
## Summary
|
|
|
|
Turn the existing placeholder Audit Log page into a real workspace-scoped Monitoring surface backed by a normalized, immutable audit event foundation. Extend the existing `audit_logs` persistence model rather than creating a second history table, consolidate the current tenant and workspace audit writers behind one reusable recorder with actor and target snapshot support, centralize event taxonomy and outcome semantics, instrument the highest-value governance and operational workflows at service or job boundaries, and expose a filterable audit list with detail inspection and permission-aware related links at `/admin/audit-log`.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4.15 / Laravel 12
|
|
**Primary Dependencies**: Filament v5, Livewire v4.0+, Tailwind CSS v4
|
|
**Storage**: PostgreSQL via Laravel Sail; existing `audit_logs` table expanded in place; JSON context payload remains application-shaped rather than raw archival payloads
|
|
**Testing**: Pest v4 feature and unit tests on PHPUnit 12
|
|
**Target Platform**: Laravel Sail web application with canonical workspace Monitoring routes under `/admin` and tenant-context navigation under `/admin/t/{tenant}`
|
|
**Project Type**: Laravel monolith / Filament web application
|
|
**Performance Goals**: Audit page remains DB-only at render time, default result sets are indexed and reverse-chronological, filter option loading is bounded to current workspace scope, and entry inspection never requires remote calls
|
|
**Constraints**: Preserve `/admin/audit-log` as the canonical route; keep workspace and tenant isolation semantics intact; no new Microsoft Graph calls; no user-facing edit or delete path for audit events; retain compatibility with existing `audit_logs` readers while migrating to richer actor, target, and outcome semantics
|
|
**Scale/Scope**: One expanded audit event store, one reusable recorder foundation, one canonical Monitoring page, first-wave instrumentation across baselines, findings, backup/restore, operation outcomes, and selected workspace-admin changes, plus focused regression coverage
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
- Inventory-first: PASS — no inventory-vs-snapshot semantics change; the feature introduces a cross-domain evidence layer over existing records.
|
|
- Read/write separation: PASS — existing write workflows keep their current preview and confirmation behavior where applicable, and this feature adds stronger audit coverage rather than replacing those safeguards.
|
|
- Graph contract path: PASS — no new Microsoft Graph calls or contracts are introduced.
|
|
- Deterministic capabilities: PASS — the feature reuses the canonical capability registry, specifically `Capabilities::AUDIT_VIEW`, plus existing target-resource capabilities for related links.
|
|
- RBAC-UX planes and isolation: PASS — the canonical audit surface remains in the `/admin` workspace plane; no `/system` route is introduced; non-members remain 404; workspace members lacking `audit.view` are 403; target drill-downs preserve their own 404/403 behavior.
|
|
- Workspace isolation: PASS — audit queries are explicitly bounded by active workspace context before any filter or result shaping.
|
|
- RBAC-UX destructive confirmation: PASS / N/A — the audit surface itself is read-only and introduces no destructive action.
|
|
- RBAC-UX global search: PASS — no new globally searchable resource is introduced.
|
|
- Tenant isolation: PASS — tenant-owned events appear only through workspace-bounded queries plus entitled tenant filtering; tenant context is additive filter state, not a second canonical audit plane.
|
|
- Run observability: PASS — existing long-running workflows continue to use `OperationRun`; this plan adds high-value audit entries alongside those runs rather than bypassing them.
|
|
- Ops-UX 3-surface feedback: PASS — operational audit coverage must not add new progress surfaces or custom completion notifications.
|
|
- Ops-UX lifecycle: PASS — no direct `OperationRun` lifecycle updates are added; operation-related audit writes occur beside existing service-owned transitions.
|
|
- Ops-UX summary counts: PASS — existing run producers remain the only source of `summary_counts`; audit rows summarize outcomes secondarily.
|
|
- Ops-UX guards: PASS — existing regression guards remain intact; new tests focus on audit coverage and visibility.
|
|
- Ops-UX system runs: PASS — initiator-null workflows remain auditable through the audit stream without introducing terminal DB notifications.
|
|
- Automation: PASS — no new queued orchestration rules are added beyond emitting audit entries at existing service/job boundaries.
|
|
- Data minimization: PASS — existing `AuditContextSanitizer` is retained and expanded as needed to protect secrets, tokens, and oversized payloads.
|
|
- Badge semantics (BADGE-001): PASS — audit outcome and actor-kind badges must be added through `BadgeCatalog` / `BadgeRenderer`, not page-local mappings.
|
|
- UI naming (UI-NAMING-001): PASS — event summaries, list labels, and detail copy use domain-first `Verb + Object` vocabulary and keep raw event keys secondary.
|
|
- Filament UI Action Surface Contract: PASS — the custom Monitoring page gains list filtering and inspection only; no bulk or destructive mutations are introduced.
|
|
- Filament UI UX-001: PASS — the audit page remains a structured Monitoring work surface with search, sort, filters, explicit empty state, and readable detail inspection rather than raw JSON-first presentation.
|
|
- Filament v5 / Livewire v4 compliance: PASS — the design stays inside the existing Filament v5 / Livewire v4 application.
|
|
- Provider registration (`bootstrap/providers.php`): PASS — no new panel provider is introduced; the existing admin panel provider remains registered in `bootstrap/providers.php`.
|
|
- Global search resource rule: PASS — no new Resource is made globally searchable.
|
|
- Asset strategy: PASS — no heavy new assets are required; existing deploy-time `php artisan filament:assets` behavior remains sufficient.
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/134-audit-log-foundation/
|
|
├── plan.md
|
|
├── research.md
|
|
├── data-model.md
|
|
├── quickstart.md
|
|
├── contracts/
|
|
│ └── audit-log-review.openapi.yaml
|
|
├── checklists/
|
|
│ └── requirements.md
|
|
└── tasks.md
|
|
```
|
|
|
|
### Source Code (repository root)
|
|
```text
|
|
app/
|
|
├── Filament/
|
|
│ └── Pages/
|
|
│ └── Monitoring/
|
|
│ └── AuditLog.php # MODIFY — replace placeholder with real table/inspection page
|
|
├── Models/
|
|
│ ├── AuditLog.php # MODIFY — richer casts/relations/immutability semantics
|
|
│ ├── BaselineProfile.php # reference — covered target domain
|
|
│ ├── BackupSet.php # reference — covered target domain
|
|
│ ├── Finding.php # reference — covered target domain
|
|
│ ├── OperationRun.php # reference — related operational context
|
|
│ └── RestoreRun.php # reference — covered target domain
|
|
├── Services/
|
|
│ ├── Audit/
|
|
│ │ ├── WorkspaceAuditLogger.php # MODIFY or adapt into shared recorder facade
|
|
│ │ ├── ... # NEW shared actor/target/context recorder helpers
|
|
│ ├── Intune/
|
|
│ │ └── AuditLogger.php # MODIFY or deprecate behind shared recorder
|
|
│ ├── Findings/
|
|
│ │ └── FindingWorkflowService.php # MODIFY — normalize event writes
|
|
│ ├── Settings/
|
|
│ │ └── SettingsWriter.php # MODIFY — adopt richer target/context semantics
|
|
│ ├── Auth/
|
|
│ │ └── WorkspaceMembershipManager.php # MODIFY — preserve admin-change coverage in shared taxonomy
|
|
│ └── SystemConsole/
|
|
│ └── SystemConsoleAuditLogger.php # MODIFY — compatibility with shared recorder and actor kinds
|
|
├── Support/
|
|
│ ├── Audit/
|
|
│ │ ├── AuditActionId.php # MODIFY — expand or supersede taxonomy registry
|
|
│ │ ├── AuditContextSanitizer.php # MODIFY — retain safe redaction boundary
|
|
│ │ └── ... # NEW audit actor/target/outcome enums or value objects
|
|
│ ├── Badges/
|
|
│ │ ├── BadgeCatalog.php # MODIFY — add audit outcome/actor-kind badge domains if needed
|
|
│ │ └── BadgeDomain.php # MODIFY — add audit badge domains if needed
|
|
│ ├── Filament/
|
|
│ │ ├── FilterOptionCatalog.php # MODIFY — audit filter options if centralized here
|
|
│ │ └── FilterPresets.php # reference — date-range filter pattern
|
|
│ └── Navigation/
|
|
│ └── RelatedNavigationResolver.php # reference — permission-aware drill-down links
|
|
database/
|
|
├── migrations/
|
|
│ └── ... # NEW migration(s) evolving `audit_logs` shape and indexes
|
|
resources/
|
|
└── views/
|
|
└── filament/
|
|
└── pages/
|
|
└── monitoring/
|
|
└── audit-log.blade.php # MODIFY if custom page shell remains
|
|
tests/
|
|
├── Feature/
|
|
│ ├── Filament/
|
|
│ │ └── AuditLog*Test.php # NEW canonical audit page access/filter/detail tests
|
|
│ └── Monitoring/
|
|
│ └── AuditCoverage*Test.php # NEW workflow-to-audit coverage tests where feature-level fit is better
|
|
└── Unit/
|
|
└── Audit/
|
|
└── *Test.php # NEW recorder, taxonomy, redaction, actor/target snapshot tests
|
|
```
|
|
|
|
**Structure Decision**: Keep the feature inside the existing Laravel/Filament monolith and evolve the existing `audit_logs` subsystem in place. The implementation centers on one shared audit recorder foundation, one canonical Monitoring page at `/admin/audit-log`, targeted migrations for richer event semantics and indexing, and focused Pest coverage rather than introducing a separate reporting service or external audit pipeline.
|
|
|
|
## Complexity Tracking
|
|
|
|
> No Constitution Check violations. No justifications needed.
|
|
|
|
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
|
| — | — | — |
|
|
|
|
## Phase 0 — Research (DONE)
|
|
|
|
Output:
|
|
- `specs/134-audit-log-foundation/research.md`
|
|
|
|
Key findings captured:
|
|
- The repo already has an `AuditLog` model, a legacy tenant-scoped `App\Services\Intune\AuditLogger`, and a newer `App\Services\Audit\WorkspaceAuditLogger`, so the feature should consolidate and expand an existing foundation rather than create a parallel one.
|
|
- Current `audit_logs` persistence is too narrow for the new spec: it stores `action`, `status`, `resource_type`, `resource_id`, actor email or name, `metadata`, and `recorded_at`, but lacks first-class actor type, target label snapshots, normalized outcome semantics, and strong canonical indexing for the planned filters.
|
|
- The codebase already has early taxonomy centralization in `App\Support\Audit\AuditActionId`, but many covered workflows still use free-form action strings such as `finding.triaged`, so event naming needs one authoritative registry.
|
|
- The existing placeholder Monitoring page at `/admin/audit-log` can stay in place as the canonical route, while existing table patterns from `OperationRunResource` and `AlertDeliveryResource` provide the best reusable filter, date-range, and drill-down conventions.
|
|
- `AuditContextSanitizer` already provides a redaction boundary via `SecretClassificationService`; the new design should reuse and tighten that boundary rather than invent a second redaction path.
|
|
- Current commands such as `tenantpilot:purge-nonpersistent` still delete `audit_logs`, which conflicts with the new explicit retention posture and must be corrected in implementation.
|
|
|
|
## Phase 1 — Design & Contracts (DONE)
|
|
|
|
Outputs:
|
|
- `specs/134-audit-log-foundation/data-model.md`
|
|
- `specs/134-audit-log-foundation/contracts/audit-log-review.openapi.yaml`
|
|
- `specs/134-audit-log-foundation/quickstart.md`
|
|
|
|
Design highlights:
|
|
- Extend `audit_logs` in place with richer actor, target, summary, outcome, context, and relation semantics while preserving compatibility for existing readers during migration.
|
|
- Consolidate `AuditLogger`, `WorkspaceAuditLogger`, and system-console wrappers behind one shared audit recorder foundation with actor and target snapshot helpers and a single event taxonomy registry.
|
|
- Keep `/admin/audit-log` as the canonical workspace Monitoring surface, with tenant context expressed only as a default filter and never as a second route or second audit model.
|
|
- Instrument high-value domain boundaries at service and job edges, especially findings workflow, baseline capture or compare, backup and restore flows, operation outcomes, and workspace-admin changes.
|
|
- Make the audit UI summary-first with bounded filters, permission-aware related links, and an inspection surface that keeps raw structured payload secondary.
|
|
|
|
## Phase 1 — Agent Context Update (DONE)
|
|
|
|
Run:
|
|
- `.specify/scripts/bash/update-agent-context.sh copilot`
|
|
|
|
## Phase 2 — Implementation Outline (tasks created in `/speckit.tasks`)
|
|
|
|
### Step 1 — Normalize the audit event contract and retention posture
|
|
|
|
Goal: implement FR-134-01 through FR-134-07, FR-134-19 through FR-134-22, and the initial rollout constraints.
|
|
|
|
Changes:
|
|
- Define the canonical audit taxonomy and outcome semantics in a single shared registry, expanding or superseding `AuditActionId` so covered workflows stop inventing ad hoc event names.
|
|
- Define actor-kind and target-kind semantics, including human, system, scheduler, and optional integration actor classes.
|
|
- Make the retention stance explicit in implementation by stopping short-lived purge flows from casually deleting audit history and documenting the v1 “keep until policy is defined” posture.
|
|
|
|
Tests:
|
|
- Add unit tests for event-type normalization, outcome mapping, and redaction boundaries.
|
|
- Add regression coverage proving non-persistent purge flows no longer remove durable audit entries.
|
|
|
|
### Step 2 — Evolve `audit_logs` into the first-class event store
|
|
|
|
Goal: implement FR-134-02 through FR-134-06, FR-134-17, and DR1 through DR6.
|
|
|
|
Changes:
|
|
- Add the missing audit semantics to `audit_logs` through additive migrations and compatibility-safe backfills: richer summary field, outcome, actor type, actor label snapshot, target label snapshot, optional operation or domain relation shortcuts, and stronger workspace or tenant or event indexes.
|
|
- Update `App\Models\AuditLog` with explicit casts, relations, and helper accessors for the new semantics.
|
|
- Preserve intelligibility for existing rows with fallback mapping or backfill rules rather than treating old rows as unreadable.
|
|
- Enforce immutable-by-default application behavior by keeping the model append-oriented and by ensuring no user-facing edit or delete flows exist.
|
|
|
|
Tests:
|
|
- Add migration tests or schema assertions for new indexes and nullable-scope rules.
|
|
- Add unit tests for label snapshot fallback and old-row compatibility.
|
|
|
|
### Step 3 — Consolidate audit writing into one reusable recorder foundation
|
|
|
|
Goal: implement FR-134-18 and support all event-writing coverage tasks.
|
|
|
|
Changes:
|
|
- Introduce one shared recorder with actor, target, summary, and context composition helpers.
|
|
- Adapt `Intune\AuditLogger`, `WorkspaceAuditLogger`, and `SystemConsoleAuditLogger` into wrappers or compatibility layers over the shared recorder so current call sites can migrate incrementally.
|
|
- Centralize redaction and payload shaping through `AuditContextSanitizer` and explicit context schemas.
|
|
|
|
Tests:
|
|
- Add unit tests for actor resolution, target resolution, target label snapshots, context shaping, and immutable write behavior.
|
|
- Add regression tests ensuring wrappers still produce valid first-class audit rows.
|
|
|
|
### Step 4 — Instrument first-wave governance and admin event sources
|
|
|
|
Goal: implement FR-134-08 through FR-134-10 plus the governance and administration acceptance criteria.
|
|
|
|
Changes:
|
|
- Normalize findings workflow events in `FindingWorkflowService` so assignment, status changes, reopen, resolve, close, and risk-acceptance actions use the shared taxonomy and richer actor or target context.
|
|
- Expand baseline-related writes so baseline profile creation, update, status change, archive, capture start or completion or failure, and compare start or completion or failure use the shared recorder and target snapshots.
|
|
- Keep existing workspace-admin writes from `WorkspaceMembershipManager`, `SettingsWriter`, onboarding flows, verification acknowledgements, and alert configuration changes aligned to the same taxonomy and summary format.
|
|
|
|
Tests:
|
|
- Add focused feature and unit tests for findings and baseline audit coverage.
|
|
- Add positive and negative authorization coverage for workspace-admin changes that surface in the audit log.
|
|
|
|
### Step 5 — Instrument backup, restore, and operations outcomes
|
|
|
|
Goal: implement FR-134-11 and FR-134-12 while respecting existing Ops-UX rules.
|
|
|
|
Changes:
|
|
- Normalize backup set creation, update, archive, and retention-related audit coverage explicitly rather than treating them as generic backup workflow side effects.
|
|
- Normalize restore initiation, completion, failure, and partial outcomes from existing restore services and jobs.
|
|
- Add high-value operation completion, failure, and retry or rerun audit coverage without changing `OperationRun` ownership or feedback surfaces.
|
|
- Preserve the existing Ops-UX contract explicitly: queued feedback stays presenter-owned via `OperationUxPresenter`, terminal notifications remain initiator-only `OperationRunCompleted`, and `OperationRun.status` / `OperationRun.outcome` transitions continue to flow only through `OperationRunService`.
|
|
|
|
Tests:
|
|
- Add focused workflow tests for backup and restore audit coverage, especially partial or failed outcomes.
|
|
- Add regression tests proving operation-run notifications and lifecycle ownership remain unchanged while audit entries are added, including guards for queued-toast usage, terminal notification exactness, and no direct job-level DB notifications or status/outcome transitions outside `OperationRunService`.
|
|
|
|
### Step 6 — Replace the placeholder Monitoring page with a real audit work surface
|
|
|
|
Goal: implement FR-134-13 through FR-134-16, FR-134-21, FR-134-24, UX1 through UX7, and IA1 through IA4.
|
|
|
|
Changes:
|
|
- Keep `App\Filament\Pages\Monitoring\AuditLog` as the canonical route owner and implement it as a workspace-scoped table surface with reverse-chronological sorting, search, core filters, and a clear empty state.
|
|
- Reuse `FilterPresets`, `FilterOptionCatalog`, `OperateHubShell`, and related-navigation helpers for tenant default filtering, date-range filters, and permission-aware drill-down links.
|
|
- Add a detail inspection surface that emphasizes summary, actor, target, outcome, context, and only then raw identifiers or raw context.
|
|
- Add centralized badge semantics for audit outcomes and actor kinds.
|
|
- Enforce the custom-page Action Surface Contract and UX-001 expectations explicitly, including a stable inspection affordance, no unsupported bulk actions, and an empty state with exactly one clear CTA.
|
|
- Keep the audit page DB-only at render time by ensuring filter loading and detail inspection rely only on application data already stored in PostgreSQL and never trigger remote calls.
|
|
|
|
Tests:
|
|
- Add feature tests for authorized access, 404 non-member denial, 403 missing-capability denial, filter behavior, empty states, and permission-aware related links.
|
|
- Add regression tests proving the old placeholder content is gone, raw JSON is not the primary UI, the Action Surface Contract stays satisfied for the custom Monitoring page, and render-time inspection remains DB-only with no external calls.
|
|
|
|
### Step 7 — Verify compatibility and complete rollout hardening
|
|
|
|
Goal: finish AC1 through AC10 and protect future expansion.
|
|
|
|
Changes:
|
|
- Audit existing consumers such as system-console access logs and preserve compatibility or explicitly scope them out of the workspace audit page while still reading from the same event store.
|
|
- Review all existing direct `AuditLog::query()` readers and adapt them to the richer schema where needed.
|
|
- Confirm deployment assumptions: no new panel provider, no heavy assets, and no deviation from current `php artisan filament:assets` deployment step.
|
|
|
|
Tests:
|
|
- Run focused Pest suites for audit model, recorder, Filament page access and filters, and first-wave workflow instrumentation.
|
|
- Run Pint on dirty files during implementation.
|
|
|
|
## Constitution Check (Post-Design)
|
|
|
|
Re-check result: PASS.
|
|
|
|
- Livewire v4.0+ compliance: preserved because the design remains inside the existing Filament v5 and Livewire v4 application.
|
|
- Provider registration location: unchanged; no new panel provider is introduced, and the current panel remains registered in `bootstrap/providers.php`.
|
|
- Globally searchable resources: unchanged; no new globally searchable Resource is added.
|
|
- Destructive actions: unchanged on the audit page; it remains read-only with inspection and related-link affordances only.
|
|
- Asset strategy: unchanged; no heavy or shared asset registration is required, and current deployment behavior including `php artisan filament:assets` remains sufficient.
|
|
- Testing plan: add unit coverage for recorder, taxonomy, actor or target snapshots, and redaction; add feature coverage for audit page access, filters, details, empty states, and permission-aware links; add workflow coverage for findings, baselines, backup or restore, operation outcomes, and workspace-admin event emission.
|
|
|