Kontext / Ziel
Diese PR standardisiert Tenant‑RBAC Enforcement in der Filament‑UI: statt ad-hoc Gate::*, abort_if/abort_unless und kopierten ->visible()/->disabled()‑Closures gibt es jetzt eine zentrale, wiederverwendbare Implementierung für Actions (Header/Table/Bulk).
Links zur Spec:
spec.md
plan.md
quickstart.md
Was ist drin
Neue zentrale Helper-API: UiEnforcement (Tenant-plane RBAC‑UX “source of truth” für Filament Actions)
Standardisierte Tooltip-Texte und Context-DTO (UiTooltips, TenantAccessContext)
Migration vieler tenant‑scoped Filament Action-Surfaces auf das Standardpattern (ohne ad-hoc Auth-Patterns)
CI‑Guard (Test) gegen neue ad-hoc Patterns in app/Filament/**:
verbietet Gate::allows/denies/check/authorize, use Illuminate\Support\Facades\Gate, abort_if/abort_unless
Legacy-Allowlist ist aktuell leer (neue Verstöße failen sofort)
RBAC-UX Semantik (konsequent & testbar)
Non-member: UI Actions hidden (kein Tenant‑Leak); Execution wird blockiert (Filament hidden→disabled chain), Defense‑in‑depth enthält zusätzlich serverseitige Guards.
Member ohne Capability: Action visible aber disabled + Standard-Tooltip; Execution wird blockiert (keine Side Effects).
Member mit Capability: Action enabled und ausführbar.
Destructive actions: über ->destructive() immer mit ->requiresConfirmation() + klare Warntexte (Execution bleibt über ->action(...)).
Wichtig: In Filament v5 sind hidden/disabled Actions typischerweise “silently blocked” (200, keine Ausführung). Die Tests prüfen daher UI‑State + “no side effects”, nicht nur HTTP‑Statuscodes.
Sicherheit / Scope
Keine neuen DB-Tabellen, keine Migrations, keine Microsoft Graph Calls (DB‑only bei Render; kein outbound HTTP).
Tenant Isolation bleibt Isolation‑Boundary (deny-as-not-found auf Tenant‑Ebene, Capability erst nach Membership).
Kein Asset-Setup erforderlich; keine neuen Filament Assets.
Compliance Notes (Repo-Regeln)
Filament v5 / Livewire v4.0+ kompatibel.
Keine Änderungen an Provider‑Registrierung (Laravel 11+/12: providers.php bleibt der Ort; hier unverändert).
Global Search: keine gezielte Änderung am Global‑Search-Verhalten in dieser PR.
Tests / Qualität
Pest Feature/Unit Tests für Member/Non-member/Tooltip/Destructive/Regression‑Guard.
Guard-Test: “No ad-hoc Filament auth patterns”.
Full suite laut Tasks: vendor/bin/sail artisan test --compact → 837 passed, 5 skipped.
Checklist: requirements.md vollständig (16/16).
Review-Fokus
API‑Usage in neuen/angepassten Filament Actions: UiEnforcement::forAction/forTableAction/forBulkAction(...)->requireCapability(...)->apply()
Guard-Test soll “red” werden, sobald jemand neue ad-hoc Auth‑Patterns einführt (by design).
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #81
## Summary
- introduce the Provider Connection Filament resource (list/create/edit) with DB-only controls, grouped action dropdowns, and badge-driven status/health rendering
- wire up the provider foundation stack (migrations, models, policies, providers, operations, badges, and audits) plus the required spec docs/checklists
- standardize Inventory Sync notifications so the job no longer writes its own DB rows; terminal notifications now flow exclusively through OperationRunCompleted while the start surface still shows the queued toast
## Testing
- ./vendor/bin/sail php ./vendor/bin/pint --dirty
- ./vendor/bin/sail artisan test tests/Unit/Badges/ProviderConnectionBadgesTest.php
- ./vendor/bin/sail artisan test tests/Feature/ProviderConnections tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php
- ./vendor/bin/sail artisan test tests/Feature/Inventory/RunInventorySyncJobTest.php tests/Feature/Inventory/InventorySyncStartSurfaceTest.php
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #73
Summary:
completes Feature 060 by adding the suite-wide TagBadge catalog (spec/domain/renderer) plus migration notes/tests/docs/specs/plan/checklist.
standardizes all inert “tag-like” badges (policy type/category/platform, tenant environment, backup schedule frequency, etc.) to use the new catalog so only neutral colors are emitted.
fixes remaining Feature 059 regressions (inventory run/restore badges, Inventory Coverage tables, Boolean-enabled streak) and adds the BooleanEnabled badge mappings/guards/tests plus new QA tasks/checklist.
Testing:
BooleanEnabledBadgesTest.php
PolicyGeneralViewTest.php
PolicySettingsStandardViewTest.php
SettingsCatalogPolicyNormalizedDisplayTest.php
PolicyViewSettingsCatalogReadableTest.php (partial/visual checks skipped)
TagBadgeCatalogTest.php
TagBadgePaletteInvariantTest.php
NoForbiddenTagBadgeColorsTest.php
NoAdHocStatusBadgesTest.php
Manual QA per quickstart.md confirmed.
Next steps:
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #72
## Summary
- centralize all status-like badge semantics via `BadgeCatalog`/`BadgeRenderer` and new per-domain mappings plus coverage for every affected entity
- replace ad-hoc badge colors in Filament tables/views with the shared catalog and add a guard test that blocks new inline semantics
- stabilize restore views by avoiding `@php(...)` shorthand so Blade compiles cleanly, and document BADGE-001 in the constitution/templates
## Testing
- `vendor/bin/sail php vendor/bin/pint --dirty`
- `vendor/bin/sail artisan test tests/Unit/Badges tests/Feature/Guards/NoAdHocStatusBadgesTest.php`
- `vendor/bin/sail artisan test tests/Feature/Monitoring/OperationsDbOnlyTest.php tests/Feature/Monitoring/OperationsTenantScopeTest.php`
- `vendor/bin/sail artisan test tests/Feature/RestoreRunWizardMetadataTest.php tests/Feature/Filament/SettingsCatalogRestoreApplySettingsPatchTest.php`
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #71
Kurzbeschreibung
Versteckt die Rerun-Row-Action für archivierte (soft-deleted) RestoreRuns und verhindert damit fehlerhafte Neu-Starts aus dem Archiv; ergänzt einen Regressionstest.
Änderungen
Code: RestoreRunResource.php — Sichtbarkeit der rerun-Action geprüft auf ! $record->trashed() und defensive Abbruchprüfung im Action-Handler.
Tests: RestoreRunRerunTest.php — neuer Test rerun action is hidden for archived restore runs.
Warum
Archivierte RestoreRuns durften nicht neu gestartet werden; UI zeigte trotzdem die Option. Das führte zu verwirrendem Verhalten und möglichen Fehlern beim Enqueueing.
Verifikation / QA
Unit/Feature:
./vendor/bin/sail artisan test tests/Feature/RestoreRunRerunTest.php
Stil/format:
./vendor/bin/pint --dirty
Manuell (UI):
Als Tenant-Admin Filament → Restore Runs öffnen.
Filter Archived aktivieren (oder Trashed filter auswählen).
Sicherstellen, dass für archivierte Einträge die Rerun-Action nicht sichtbar ist.
Auf einem aktiven (nicht-archivierten) Run prüfen, dass Rerun sichtbar bleibt und wie erwartet eine neue RestoreRun erzeugt.
Wichtige Hinweise
Kein DB-Migration required.
Diese PR enthält nur den UI-/Filament-Fix; die zuvor gemachten operative Fixes für Queue/adapter-Reconciliation bleiben ebenfalls auf dem Branch (z. B. frühere commits während der Debugging-Session).
T055 (Schema squash) wurde bewusst zurückgestellt und ist nicht Teil dieses PRs.
Merge-Checklist
Tests lokal laufen (RestoreRunRerunTest grünt)
Pint läuft ohne ungepatchte Fehler
Branch gepusht: 056-remove-legacy-bulkops (PR-URL: https://git.cloudarix.de/ahmido/TenantAtlas/compare/dev...056-remove-legacy-bulkops)
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #65
Summary
This PR introduces Unified Operations Runs + Monitoring Hub (053).
Goal: Standardize how long-running operations are tracked and monitored using the existing tenant-scoped run record (BulkOperationRun) as the canonical “operation run”, and surface it in a single Monitoring → Operations hub (view-only, tenant-scoped, role-aware).
Phase 1 adoption scope (per spec):
• Drift generation (drift.generate)
• Backup Set “Add Policies” (backup_set.add_policies)
Note: This PR does not convert every run type yet (e.g. GroupSyncRuns / InventorySyncRuns remain separate for now). This is intentionally incremental.
⸻
What changed
Monitoring / Operations hub
• Moved/organized run monitoring under Monitoring → Operations
• Added:
• status buckets (queued / running / succeeded / partially succeeded / failed)
• filters (run type, status bucket, time range)
• run detail “Related” links (e.g. Drift findings, Backup Set context)
• All hub pages are DB-only and view-only (no rerun/cancel/delete actions)
Canonical run semantics
• Added canonical helpers on BulkOperationRun:
• runType() (resource.action)
• statusBucket() derived from status + counts (testable semantics)
Drift integration (Phase 1)
• Drift generation start behavior now:
• creates/reuses a BulkOperationRun with drift context payload (scope_key + baseline/current run ids)
• dispatches generation job
• emits DB notifications including “View run” link
• On generation failure: stores sanitized failure entries + sends failure notification
Permissions / tenant isolation
• Monitoring run list/view is tenant-scoped and returns 403 for cross-tenant access
• Readonly can view runs but cannot start drift generation
⸻
Tests
Added/updated Pest coverage:
• BulkOperationRunStatusBucketTest.php
• DriftGenerationDispatchTest.php
• GenerateDriftFindingsJobNotificationTest.php
• RunAuthorizationTenantIsolationTest.php
Validation run locally:
• ./vendor/bin/pint --dirty
• targeted tests from feature quickstart / drift monitoring tests
⸻
Manual QA
1. Go to Monitoring → Operations
• verify filters (run type / status / time range)
• verify run detail shows counts + sanitized failures + “Related” links
2. Open Drift Landing
• with >=2 successful inventory runs for scope: should queue drift generation + show notification with “View run”
• as readonly: should not start generation
3. Run detail
• drift.generate runs show “Drift findings” related link
• failure entries are sanitized (no secrets/tokens/raw payload dumps)
⸻
Notes / Ops
• Queue workers must be restarted after deploy so they load the new code:
• php artisan queue:restart (or Sail equivalent)
• This PR standardizes monitoring for Phase 1 producers only; follow-ups will migrate additional run types into the unified pattern.
⸻
Spec / Docs
• SpecKit artifacts added under specs/053-unify-runs-monitoring/
• Checklists are complete:
• requirements checklist PASS
• writing checklist PASS
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #60
Status Update
Committed the async “Add selected” flow: job-only handler, deterministic run reuse, sanitized failure tracking, observation updates, and the new BulkOperationService/Progress test coverage.
All relevant tasks in tasks.md are marked done, and the checklist under requirements.md is fully satisfied (PASS).
Ran ./vendor/bin/pint --dirty plus BackupSetPolicyPickerTableTest.php—all green.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #59
Summary
Adds a tenant-scoped Entra Groups “Directory Cache” to enable DB-only group name resolution across the app (no render-time Graph calls), plus sync runs + observability.
What’s included
• Entra Groups cache
• New entra_groups storage (tenant-scoped) for group metadata (no memberships).
• Retention semantics: groups become stale / retained per spec (no hard delete on first miss).
• Group Sync Runs
• New “Group Sync Runs” UI (list + detail) with tenant isolation (403 on cross-tenant access).
• Manual “Sync Groups” action: creates/reuses a run, dispatches job, DB notification with “View run” link.
• Scheduled dispatcher command wired in console.php.
• DB-only label resolution (US3)
• Shared EntraGroupLabelResolver with safe fallback Unresolved (…last8) and UUID guarding.
• Refactors to prefer cached names (no typeahead / no live Graph) in:
• Tenant RBAC group selects
• Policy version assignments widget
• Restore results + restore wizard group mapping labels
Safety / Guardrails
• No render-time Graph calls: fail-hard guard test verifies UI paths don’t call GraphClientInterface during page render.
• Tenant isolation & authorization: policies + scoped queries enforced (cross-tenant access returns 403, not 404).
• Data minimization: only group metadata is cached (no membership/owners).
Tests / Verification
• Added/updated tests under tests/Feature/DirectoryGroups and tests/Unit/DirectoryGroups:
• Start sync → run record + job dispatch + upserts
• Retention purge semantics
• Scheduled dispatch wiring
• Render-time Graph guard
• UI/resource access isolation
• Ran:
• ./vendor/bin/pint --dirty
• ./vendor/bin/sail artisan test tests/Feature/DirectoryGroups
• ./vendor/bin/sail artisan test tests/Unit/DirectoryGroups
Notes / Follow-ups
• UI polish remains (picker/lookup UX, consistent progress widget/toasts across modules, navigation grouping).
• pr-gate checklist still has non-blocking open items (mostly UX/ops polish); requirements gate is green.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #57
Summary
This PR implements Spec 049 – Backup/Restore Job Orchestration: all critical Backup/Restore execution paths are job-only, idempotent, tenant-scoped, and observable via run records + DB notifications (Phase 1). The UI no longer performs heavy Graph work inside request/Filament actions for these flows.
Why
We want predictable UX and operations at MSP scale:
• no timeouts / long-running requests
• reproducible run state + per-item results
• safe error persistence (no secrets / no token leakage)
• strict tenant isolation + auditability for write paths
What changed
Foundational (Runs + Idempotency + Observability)
• Added a shared RunIdempotency helper (dedupe while queued/running).
• Added a read-only BulkOperationRuns surface (list + view) for status/progress.
• Added DB notifications for run status changes (with “View run” link).
US1 – Policy “Capture snapshot” is job-only
• Policy detail “Capture snapshot” now:
• creates/reuses a run (dedupe key: tenant + policy.capture_snapshot + policy DB id)
• dispatches a queued job
• returns immediately with notification + link to run detail
• Graph capture work moved fully into the job; request path stays Graph-free.
US3 – Restore runs orchestration is job-only + safe
• Live restore execution is queued and updates RestoreRun status/progress.
• Per-item outcomes are persisted deterministically (per internal DB record).
• Audit logging is written for live restore.
• Preview/dry-run is enforced as read-only (no writes).
Tenant isolation / authorization (non-negotiable)
• Run list/view/start are tenant-scoped and policy-guarded (cross-tenant access => 403, not 404).
• Explicit Pest tests cover cross-tenant denial and start authorization.
Tests / Verification
• ./vendor/bin/pint --dirty
• Targeted suite (examples):
• policy capture snapshot queued + idempotency tests
• restore orchestration + audit logging + preview read-only tests
• run authorization / tenant isolation tests
Notes / Scope boundaries
• Phase 1 UX = DB notifications + run detail page. A global “progress widget” is tracked as Phase 2 and not required for merge.
• Resilience/backoff is tracked in tasks but can be iterated further after merge.
Review focus
• Dedupe behavior for queued/running runs (reuse vs create-new)
• Tenant scoping & policy gates for all run surfaces
• Restore safety: audit event + preview no-writes
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #56
Ordering + limit-only Test für created_at DESC in DependencyExtractionFeatureTest.php
UI Test für masked Identifier (ID: 123456…) + Guest-Access blocked in InventoryItemDependenciesTest.php
Quickstart ergänzt um manuellen <2s Check in quickstart.md
pr-gate Checkbox-Format normalisiert (kein leading space) in pr-gate.md
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #49
What Changed
Removed per-file uses(TestCase::class ...) bindings in Unit tests to avoid Pest v4 “folder already uses the test case” discovery failure (kept RefreshDatabase where needed).
Updated the backup scheduling job test to pass the newly required BulkOperationService when manually calling RunBackupScheduleJob::handle().
Where
Unit (bulk cleanup across 56 files)
RunBackupScheduleJobTest.php
Verification
./vendor/bin/sail test → 443 passed, 5 skipped
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #45
expose enrollment config subtypes as their own policy types (limit/platform restrictions/notifications) with preview-only restore risk and proper Graph contracts
classify enrollment configs by their @odata.type + deviceEnrollmentConfigurationType so sync only keeps ESP in windowsEnrollmentStatusPage and the rest stay in their own types, including new restore-normalizer UI blocks + warnings
hydrate enrollment notifications: snapshot fetch now downloads each notification template + localized messages, normalized view surfaces template names/subjects/messages, and restore previews keep preview-only behavior
tenant UI tweaks: Tenant list and detail actions moved into an action group; “Open in Entra” re-added in index, and detail now has “Deactivate” + tests covering the new menu layout and actions
tests added/updated for sync, snapshots, restores, normalized settings, tenant UI, plus Pint/test suite run
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #31
Tenants: Tenant anlegen/öffnen → tenant_id, app_client_id, app_client_secret setzen → Make current (wichtig).
Inventory → Policies: oben Sync from Intune.
In der Tabelle nach Type = “Driver Updates (Windows)” (windowsDriverUpdateProfile) filtern und Policy öffnen.
Auf der Policy: Settings-Tab prüfen (Block „Driver Update Profile“), dann Capture snapshot klicken und unter Versions die Version ansehen.
Restore-Test (nur im Test-Tenant!): Version öffnen → Restore to Intune erst als Dry-run, dann Execute; danach unter Backups & Restore → Restore Runs Ergebnis prüfen (soll graph_path mit deviceManagement/windowsDriverUpdateProfiles/... zeigen).
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #27
Added a resolver/validation flow that fetches endpoint security template definitions and enforces them before CREATE/PATCH so we don’t call Graph with invalid settings.
Hardened restore endpoint resolution (built-in fallback to deviceManagement/configurationPolicies, clearer error metadata, preview-only fallback when metadata is missing) and exposed Graph path/method in restore UI details.
Stripped read-only fields when PATCHing endpointSecurityIntent so the request no longer fails with “properties not patchable”.
Added regression tests covering endpoint security restore, intent sanitization, unknown type safety, Graph error metadata, and endpoint resolution behavior.
Testing
GraphClientEndpointResolutionTest.php
./vendor/bin/pint --dirty
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #25
Hydrate configurationPolicies/{id}/settings for endpoint security/baseline policies so snapshots include real rule data.
Treat those types like Settings Catalog policies in the normalizer so they show the searchable settings table, recognizable categories, and readable choice values (firewall-specific formatting + interface badge parsing).
Improve “General” tab cards: badge lists for platforms/technologies, template reference summary (name/family/version/ID), and ISO timestamps rendered as YYYY‑MM‑DD HH:MM:SS; added regression test for the view.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #23
Replaces the “Add Policies” picker with a modal table (search, pagination, multi-select).
Adds filters: policy type, platform, last synced, ignored, has versions; “Select all” applies to the current filtered results.
Improves identifiers shown (short external id), and fixes has-versions filtering behavior.
Backup set items table: groups row actions (View policy / Remove) into an action group.
Adds bulk action to remove multiple backup items at once.
Updates/adds tests covering the picker table bulk add and backup items bulk remove.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #21
This PR completes Feature 014 (Enrollment & Autopilot).
Adds normalization for:
Autopilot deployment profiles (windowsAutopilotDeploymentProfile)
Enrollment Status Page / ESP (windowsEnrollmentStatusPage)
Enrollment Restrictions (enrollmentRestriction, restore remains preview-only)
Improves settings readability:
Autopilot OOBE settings are expanded into readable key/value entries
Enrollment restriction platform restrictions are shown as explicit fields (with sensible defaults)
Array/list values render as badges (avoids Blade rendering crashes on non-string values)
Fixes enrollment configuration type collisions during sync:
Canonical type resolution prevents enrollmentRestriction from “claiming” ESP items
Safe reclassification updates existing wrong rows instead of skipping
Enhances reclassification command:
Can detect ESP even if a policy has no local versions (fetches snapshot from Graph)
Dry-run by default; apply with --write
Tests
Added/updated unit + Filament feature tests for normalization and UI rendering.
Preview-only enforcement for enrollment restrictions is covered.
Targeted test suite and Pint are green.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #20
Created a safe session branch, committed everything, fast-forward merged back into feat/012-windows-update-rings, then pushed.
Commit: 074a656 feat(rings): update rings + update profiles
Push is done; upstream tracking is se
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #18
Summary
add appProtectionPolicy coverage for assignments, normalize settings for UI, and skip targetedManagedAppConfiguration noise during inventory
wire up derived Graph endpoints/contracts so restores use the correct /assign paths per platform and assignments no longer rely on unsupported $expand
add normalization logic/tests plus Pact/Plan updates so capture+restore behave more like Intune’s app protection workflows and no longer expose unsupported fields
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #11
Summary
add mobileApp contract details (assignments, expanded type family, scope tag select) and spec/test coverage so App snapshots stay metadata-only yet still capture roleScopeTagIds.
guard restores so scope tags are written back whenever a snapshot carries them, even without explicit foundation mappings, and document it via a new Filament restore test.
keep existing restore/sync behaviors in place while ensuring mobileApp assignments and metadata continue to flow through the backup/restore pipeline.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #10
Resolves assignment filter names when Graph stores filter IDs at assignment root.
Tracks assignment fetch success/failure and shows clearer UI states for versions.
Adds scope tag fallback display in backup set items.
Restored versions now capture applied assignments consistently.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #8