From 8026a3823310bf233e16224ed4b2c424cdebc2f8 Mon Sep 17 00:00:00 2001 From: Ahmed Darrazi Date: Tue, 23 Dec 2025 00:38:28 +0100 Subject: [PATCH] docs(004): align spec and tasks with policy version capture --- specs/004-assignments-scope-tags/spec.md | 200 +++++++++------- specs/004-assignments-scope-tags/tasks.md | 266 ++++++++++------------ 2 files changed, 237 insertions(+), 229 deletions(-) diff --git a/specs/004-assignments-scope-tags/spec.md b/specs/004-assignments-scope-tags/spec.md index cca658b..ea65ce4 100644 --- a/specs/004-assignments-scope-tags/spec.md +++ b/specs/004-assignments-scope-tags/spec.md @@ -28,59 +28,57 @@ ## Scope - **Policy Types**: `settingsCatalogPolicy` only (initially) - **Graph Endpoints**: - GET `/deviceManagement/configurationPolicies/{id}/assignments` - - POST/PATCH `/deviceManagement/configurationPolicies/{id}/assign` + - POST/PATCH `/deviceManagement/configurationPolicies/{id}/assignments` - GET `/deviceManagement/roleScopeTags` (for reference data) -- **Backup Behavior**: Optional (checkbox "Include Assignments & Scope Tags") + - GET `/deviceManagement/assignmentFilters` (for filter names) +- **Backup Behavior**: Optional at capture time with separate checkboxes ("Include assignments", "Include scope tags") on Add Policies and Capture Snapshot actions (defaults: true) - **Restore Behavior**: With group mapping UI for unresolved group IDs --- ## User Stories -### User Story 1 - Backup with Assignments & Scope Tags (Priority: P1) +### User Story 1 - Capture Assignments & Scope Tags (Priority: P1) -**As an admin**, I want to optionally include assignments and scope tags when backing up Settings Catalog policies, so that I have complete policy state for migration or disaster recovery. +**As an admin**, I want to optionally include assignments and scope tags when capturing policy snapshots or adding policies to a Backup Set, so that I have complete policy state for migration or disaster recovery. **Acceptance Criteria:** -1. **Given** I create a new Backup Set for Settings Catalog policies, - **When** I enable the checkbox "Include Assignments & Scope Tags", - **Then** the backup captures: - - Assignment list (groups, users, devices with include/exclude mode) - - Scope Tag IDs referenced by the policy - - Metadata about assignment count and scope tag names +1. **Given** I add policies to a Backup Set (or capture a snapshot from the Policy view), + **When** I enable "Include assignments" and/or "Include scope tags", + **Then** the capture stores: + - Assignments on the PolicyVersion (include/exclude targets + filters) + - Scope tags on the PolicyVersion as `{ids, names}` + - A BackupItem linked via `policy_version_id` that copies assignments for restore -2. **Given** I view a Backup Set with assignments included, - **When** I expand a Backup Item detail, - **Then** I see: - - "Assignments: 3 groups, 2 users" summary - - "Scope Tags: Default, HR-Admins" list - - JSON tab with full assignment payload +2. **Given** I create a Backup Set, + **When** I complete the form, + **Then** no assignments/scope tags checkbox appears on that screen (selection happens when adding policies). -3. **Given** I create a Backup Set without enabling the checkbox, - **When** the backup completes, - **Then** assignments and scope tags are NOT captured (payload-only backup) +3. **Given** I disable either checkbox, + **When** the capture completes, + **Then** the corresponding PolicyVersion fields are `null` and the BackupItem is created without those data. --- -### User Story 2 - Policy View with Assignments Tab (Priority: P1) +### User Story 2 - Policy Version View with Assignments (Priority: P1) -**As an admin**, I want to see a policy's current assignments and scope tags in the Policy View, so I understand its targeting and visibility. +**As an admin**, I want to see a policy version's captured assignments and scope tags, so I understand targeting and visibility at that snapshot. **Acceptance Criteria:** -1. **Given** I view a Settings Catalog policy, - **When** I navigate to the "Assignments" tab, +1. **Given** I view a Settings Catalog Policy Version, + **When** assignments were captured, **Then** I see: - - Table with columns: Type (Group/User/Device), Name, Mode (Include/Exclude), ID - - "Scope Tags" section showing: Default, HR-Admins (editable IDs) - - "Not assigned" message if no assignments exist + - Include/Exclude group targets with group display name (or "Unknown Group (ID: ...)") + - Filter name (if present) with filter mode (include/exclude) + - Scope tags list from the version -2. **Given** a policy has 10 assignments, - **When** I filter by "Include only" or "Exclude only", - **Then** the table filters accordingly +2. **Given** assignments were not captured for this version, + **When** I open the assignments panel, + **Then** I see "Assignments were not captured for this version." -3. **Given** assignments include deleted groups (orphaned IDs), - **When** I view the assignments tab, - **Then** orphaned entries show as "Unknown Group (ID: abc-123)" with warning badge +3. **Given** scope tags were not captured, + **When** I view the version, + **Then** I see a "Scope tags not captured" empty state. --- @@ -135,39 +133,47 @@ ## Functional Requirements ### Backup & Storage -**FR-004.1**: System MUST provide a checkbox "Include Assignments & Scope Tags" on the Backup Set creation form (default: unchecked). +**FR-004.1**: System MUST provide separate checkboxes "Include assignments" and "Include scope tags" on: +- Add Policies to Backup Set action +- Capture snapshot action in Policy view +Defaults: checked. Backup Set creation form MUST NOT show these checkboxes. **FR-004.2**: When assignments are included, system MUST fetch assignments using fallback strategy: 1. Try: `/deviceManagement/configurationPolicies/{id}/assignments` 2. If empty/fails: Try `$expand=assignments` on policy fetch -3. Store: - - Assignment array (each with: `target` object, `id`, `intent`, filters) - - Extracted metadata: group names (resolved via `/directoryObjects/getByIds`), user UPNs, device IDs - - Warning flags for orphaned IDs - - Fallback flag: `assignments_fetch_method` (direct | expand | failed) +3. Continue capture with assignments `null` on failure (fail-soft) and set `assignments_fetch_failed: true`. -**FR-004.3**: System MUST store Scope Tag IDs in backup metadata (from policy payload `roleScopeTagIds` field). +**FR-004.3**: System MUST enrich assignments with: +- Group display name + orphaned flag via `/directoryObjects/getByIds` +- Assignment filter name via `/deviceManagement/assignmentFilters` +- Preserve target type (include/exclude) and filter mode (`deviceAndAppManagementAssignmentFilterType`) -**FR-004.4**: Backup Item `metadata` JSONB field MUST include: +**FR-004.4**: System MUST store assignments and scope tags on PolicyVersion: +- `policy_versions.assignments` (array, nullable) +- `policy_versions.scope_tags` as `{ids: [], names: []}` (nullable) +- hashes for deduplication (`assignments_hash`, `scope_tags_hash`) +BackupItem MUST link to PolicyVersion via `policy_version_id` and copy assignments for restore. + +**FR-004.5**: Capture metadata stored on PolicyVersion and BackupItem MUST include: ```json { - "assignment_count": 5, - "scope_tag_ids": ["0", "abc-123"], - "scope_tag_names": ["Default", "HR-Admins"], - "has_orphaned_assignments": false + "has_assignments": true, + "has_scope_tags": true, + "has_orphaned_assignments": false, + "assignments_fetch_failed": false } ``` - -**FR-004.5**: System MUST gracefully handle Graph API failures when fetching assignments (log warning, continue backup with flag `assignments_fetch_failed: true`). +Assignment counts are derived from `assignments` at display time. ### UI Display -**FR-004.6**: Policy View MUST show an "Assignments" tab for Settings Catalog policies displaying: -- Assignments table (type, name, mode, ID) +**FR-004.6**: Policy Version view MUST show an assignments panel for Settings Catalog versions displaying: +- Include/Exclude targets with group display name or "Unknown Group (ID: ...)" +- Assignment filter name + filter mode (include/exclude) when present - Scope Tags section -- Empty state if no assignments +- Empty state if assignments or scope tags were not captured -**FR-004.7**: Backup Item detail view MUST show assignment count and scope tag names in metadata summary. +**FR-004.7**: Backup Set items table MUST show assignment count (derived from `backup_items.assignments`) and scope tag names from the linked PolicyVersion. **FR-004.8**: System MUST render orphaned group IDs as "Unknown Group (ID: {id})" with warning icon. @@ -235,12 +241,33 @@ ## Non-Functional Requirements ## Data Model Changes -### Migration: `backup_items` table extension +### Migration: `policy_versions` assignments + scope tags + +```php +Schema::table('policy_versions', function (Blueprint $table) { + $table->json('assignments')->nullable()->after('metadata'); + $table->json('scope_tags')->nullable()->after('assignments'); + $table->string('assignments_hash', 64)->nullable()->after('scope_tags'); + $table->string('scope_tags_hash', 64)->nullable()->after('assignments_hash'); + $table->index('assignments_hash'); + $table->index('scope_tags_hash'); +}); +``` + +### Migration: `backup_items` policy_version_id + +```php +Schema::table('backup_items', function (Blueprint $table) { + $table->foreignId('policy_version_id')->nullable()->constrained('policy_versions'); +}); +``` + +### Migration: `backup_items` assignments copy ```php Schema::table('backup_items', function (Blueprint $table) { $table->json('assignments')->nullable()->after('metadata'); - // stores: [{target:{...}, id, intent, filters}, ...] + // copy of PolicyVersion assignments for restore safety }); ``` @@ -253,13 +280,21 @@ ### Migration: `restore_runs` table extension }); ``` +### `policy_versions.scope_tags` JSONB schema + +```json +{ + "ids": ["0", "123"], + "names": ["Default", "HR"] +} +``` + ### `backup_items.metadata` JSONB schema ```json { - "assignment_count": 5, - "scope_tag_ids": ["0", "123"], - "scope_tag_names": ["Default", "HR"], + "has_assignments": true, + "has_scope_tags": true, "has_orphaned_assignments": false, "assignments_fetch_failed": false } @@ -321,6 +356,11 @@ ### Endpoints to Add (Production-Tested Strategies) - For Scope Tag resolution (cache 1 hour) - Scope Tag IDs also available in policy payload's `roleScopeTagIds` array +5. **GET** `/deviceManagement/assignmentFilters?$select=id,displayName` + - For assignment filter name resolution (cache 1 hour) + - Filter IDs in assignments: `deviceAndAppManagementAssignmentFilterId` + - Filter mode: `deviceAndAppManagementAssignmentFilterType` (include/exclude) + ### Graph Contract Updates Add to `config/graph_contracts.php`: @@ -348,18 +388,17 @@ ### Graph Contract Updates ## UI Mockups (Wireframe Descriptions) -### Policy View - Assignments Tab +### Policy Version View - Assignments Panel ``` -[General] [Settings] [Assignments] [JSON] +[General] [Settings] [JSON] Assignments (5) ┌─────────────────────────────────────────────────┐ -│ Type │ Name │ Mode │ ID │ +│ Type │ Name │ Filter │ ID │ ├─────────┼───────────────────┼─────────┼─────────┤ -│ Group │ All Users │ Include │ abc-123 │ -│ Group │ Contractors │ Exclude │ def-456 │ -│ User │ john@contoso.com │ Include │ ghi-789 │ +│ Include group │ All Users │ Test (include) │ abc-123 │ +│ Exclude group │ Contractors │ - │ def-456 │ └─────────────────────────────────────────────────┘ Scope Tags (2) @@ -367,18 +406,20 @@ ### Policy View - Assignments Tab • HR-Admins (ID: 123) ``` -### Backup Creation - Checkbox +### Add Policies to Backup Set - Checkboxes ``` -Create Backup Set -───────────────── +Add Policies to Backup Set +───────────────────────── Select Policies: [Settings Catalog: 15 selected] -☑ Include Assignments & Scope Tags - Captures group/user targeting and RBAC scope. - Adds ~2-5 KB per policy with assignments. +☑ Include assignments + Captures include/exclude targeting and filters. -[Cancel] [Create Backup] +☑ Include scope tags + Captures policy scope tag IDs. + +[Cancel] [Add Policies] ``` ### Restore Wizard - Group Mapping Step @@ -408,15 +449,18 @@ ### Unit Tests - `AssignmentFetcherTest`: Mock Graph responses, test parsing - `GroupMapperTest`: Test ID resolution, mapping logic - `ScopeTagResolverTest`: Test caching, name resolution +- `AssignmentFilterResolverTest`: Test caching and ID filtering ### Feature Tests -- `BackupWithAssignmentsTest`: E2E backup creation with checkbox -- `PolicyViewAssignmentsTabTest`: UI rendering, orphaned IDs +- `BackupWithAssignmentsConsistencyTest`: PolicyVersion as source of truth +- `VersionCaptureWithAssignmentsTest`: Snapshot capture with assignments/scope tags +- `PolicyVersionViewAssignmentsTest`: UI rendering, orphaned IDs, filters - `RestoreGroupMappingTest`: Wizard flow, mapping persistence - `RestoreAssignmentApplicationTest`: Graph API calls, outcomes ### Manual QA -- Create backup with/without assignments checkbox +- Add policies to backup set with/without assignments and scope tags +- Capture snapshot with/without assignments and scope tags - Restore to same tenant (auto-match groups) - Restore to different tenant (group mapping wizard) - Handle orphaned group IDs gracefully @@ -426,10 +470,10 @@ ### Manual QA ## Rollout Plan ### Phase 1: Backup with Assignments (MVP) -- Add checkbox to Backup form +- Add checkboxes on Add Policies + Capture Snapshot actions - Fetch assignments from Graph -- Store in `backup_items.assignments` -- Display in Policy View (read-only) +- Store on PolicyVersion (copy assignments to BackupItem) +- Display in Policy Version view (read-only) - **Duration**: ~8-12 hours ### Phase 2: Restore with Group Mapping @@ -440,7 +484,7 @@ ### Phase 2: Restore with Group Mapping ### Phase 3: Scope Tags - Resolve Scope Tag names -- Display in UI +- Display in Policy Version view - Handle restore warnings - **Duration**: ~4-6 hours @@ -461,7 +505,7 @@ ## Risks & Mitigations | Risk | Mitigation | |------|------------| -| Graph API assignments endpoint slow/fails | Async fetch, fail-soft with warning | +| Graph API assignments endpoint slow/fails | Fail-soft with warning | | Target tenant has 1000+ groups | Searchable dropdown with pagination | | Group IDs change across tenants | Group name-based matching fallback | | Scope Tag IDs don't exist in target | Log warning, allow policy creation | @@ -470,8 +514,8 @@ ## Risks & Mitigations ## Success Criteria -1. ✅ Backup checkbox functional, assignments captured -2. ✅ Policy View shows assignments tab with accurate data +1. ✅ Capture checkboxes functional, assignments captured +2. ✅ Policy Version view shows assignments widget with accurate data 3. ✅ Group Mapping wizard handles 100+ groups smoothly 4. ✅ Restore applies assignments with 90%+ success rate 5. ✅ Audit logs record all mapping decisions diff --git a/specs/004-assignments-scope-tags/tasks.md b/specs/004-assignments-scope-tags/tasks.md index 02bd64e..1229786 100644 --- a/specs/004-assignments-scope-tags/tasks.md +++ b/specs/004-assignments-scope-tags/tasks.md @@ -3,12 +3,20 @@ # Feature 004: Assignments & Scope Tags - Task Breakdown ## Overview This document breaks down the implementation plan into granular, actionable tasks organized by phase and user story. -**Total Estimated Tasks**: 62 tasks -**MVP Scope**: Tasks marked with ⭐ (24 tasks, ~16-22 hours) +**Total Estimated Tasks**: ~60 tasks +**MVP Scope**: Tasks marked with ⭐ (updated) **Full Implementation**: All tasks (~30-40 hours) --- +## Plan Updates (Implementation Reality) +- Assignments + scope tags are stored on PolicyVersion; BackupItem links to PolicyVersion and copies assignments. +- Include assignments/scope tags checkboxes live on Add Policies to Backup Set and Capture Snapshot actions (not on Backup Set creation). +- Policy assignments UI moved to Policy Version view via Livewire widget. +- Assignment filters resolved via `/deviceManagement/assignmentFilters` and displayed with include/exclude mode. + +--- + ## Phase 1: Setup & Database (Foundation) **Duration**: 2-3 hours @@ -96,6 +104,21 @@ ### Tasks - Run: `php artisan test --filter=RestoreRun` - Expected: All green +**1.14** [X] ⭐ Add migration: `add_assignments_to_policy_versions` +- Add `assignments`, `scope_tags`, `assignments_hash`, `scope_tags_hash` +- Add indexes on hashes +- Reversible `down()` method + +**1.15** [X] ⭐ Add migration: `add_policy_version_id_to_backup_items` +- Add nullable `policy_version_id` FK to `policy_versions` +- Reversible `down()` method + +**1.16** [X] ⭐ Update `PolicyVersion` model casts +- Add casts for `assignments` and `scope_tags` + +**1.17** [X] ⭐ Add `BackupItem` relation to PolicyVersion +- `policyVersion(): BelongsTo` + --- ## Phase 2: Graph API Integration (Core Services) @@ -194,87 +217,64 @@ ### Tasks - Run: `php artisan test --filter=ScopeTagResolver` - Expected: All green, 90%+ coverage +**2.17** [X] ⭐ Create service: `AssignmentFilterResolver` +- File: `app/Services/Graph/AssignmentFilterResolver.php` +- GET `/deviceManagement/assignmentFilters?$select=id,displayName` +- Cache results (TTL 1 hour) + +**2.18** [X] ⭐ Write unit test: `AssignmentFilterResolverTest` +- Assert filter name resolution by ID +- Assert cache behavior + --- -## Phase 3: US1 - Backup with Assignments (MVP Core) +## Phase 3: US1 - Capture Assignments & Scope Tags (MVP Core) **Duration**: 4-5 hours **Dependencies**: Phase 2 complete **Parallelizable**: Partially (service and UI separate) ### Tasks +**3.1** [X] ⭐ Add include checkboxes to "Add policies" action +- File: `app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.php` +- Components: `include_assignments`, `include_scope_tags` (default: true) +- Help text: assignments include/exclude + filters, scope tag IDs -**3.1** ⭐ Add checkbox to Backup creation form -- File: `app/Filament/Resources/BackupResource/Pages/CreateBackup.php` -- Component: `Checkbox::make('include_assignments')` -- Label: "Include Assignments & Scope Tags" -- Help text: "Captures group/user targeting and RBAC scope. Adds ~2-5 KB per policy." -- Default: `false` -- Show only for Settings Catalog policies +**3.2** [X] ⭐ Add include checkboxes to "Capture snapshot" action +- File: `app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php` +- Components: `include_assignments`, `include_scope_tags` (default: true) -**3.2** ⭐ Create service: `AssignmentBackupService` -- File: `app/Services/AssignmentBackupService.php` -- Method: `backup(int $tenantId, string $policyId, bool $includeAssignments): BackupItem` -- Call `AssignmentFetcher` if `includeAssignments` true -- Call `ScopeTagResolver` for scope tags -- Update `backup_items.assignments` and `metadata` +**3.3** [X] ⭐ Create `PolicyCaptureOrchestrator` +- File: `app/Services/Intune/PolicyCaptureOrchestrator.php` +- Capture payload + assignments + scope tags +- Reuse existing PolicyVersion by snapshot hash +- Backfill assignments/scope tags when missing -**3.3** ⭐ Implement assignment fetch in `AssignmentBackupService` -- Check `includeAssignments` flag -- Call `$this->assignmentFetcher->fetch($tenantId, $policyId)` -- Store result in `$backupItem->assignments` -- If empty or failed, set `metadata['assignments_fetch_failed'] = true` +**3.4** [X] ⭐ Store assignments/scope tags on PolicyVersion +- Update `VersionService::captureVersion` to accept assignments/scopeTags +- Store `assignments_hash` and `scope_tags_hash` -**3.4** ⭐ Implement scope tag resolution in `AssignmentBackupService` -- Extract `roleScopeTagIds` from policy payload -- Call `$this->scopeTagResolver->resolve($scopeTagIds)` -- Store in `metadata['scope_tag_ids']` and `metadata['scope_tag_names']` +**3.5** [X] ⭐ Link BackupItem to PolicyVersion and copy assignments +- Add `policy_version_id` to backup_items +- BackupService uses orchestrator and copies assignments +- Scope tags live on PolicyVersion only -**3.5** ⭐ Update `metadata` with assignment summary -- Set `metadata['assignment_count']` -- Set `metadata['has_orphaned_assignments']` (detect via GroupResolver) -- Set `metadata['assignments_fetch_failed']` on error +**3.6** [X] Update BackupSet items table columns +- Eager-load `policyVersion` +- Assignment count derived from `backup_items.assignments` +- Scope tags read from `policyVersion.scope_tags` -**3.6** Create job: `FetchAssignmentsJob` -- File: `app/Jobs/FetchAssignmentsJob.php` -- Dispatch async after backup creation if checkbox enabled -- Call `AssignmentBackupService` in job -- Handle failures gracefully (log, don't retry) +**3.7** [X] Add audit log entry: `backup.assignments.included` +- Log when `include_assignments` is enabled -**3.7** Add audit log entry: `backup.assignments.included` -- Create entry when checkbox enabled -- Metadata: tenant_id, backup_set_id, policy_count, assignment_count - -**3.8** ⭐ Write feature test: `BackupWithAssignmentsTest::creates_backup_with_assignments` -- Mock Graph API responses (assignments, scope tags) -- Create backup with checkbox enabled -- Assert `backup_items.assignments` populated -- Assert `metadata['assignment_count']` correct -- Assert audit log entry created - -**3.9** ⭐ Write feature test: `BackupWithAssignmentsTest::creates_backup_without_assignments` -- Create backup with checkbox disabled -- Assert `backup_items.assignments` is null -- Assert `metadata['assignment_count']` is 0 or not set - -**3.10** ⭐ Write feature test: `BackupWithAssignmentsTest::handles_fetch_failure` -- Mock Graph API throwing exception -- Create backup with checkbox enabled -- Assert backup completes (fail-soft) -- Assert `metadata['assignments_fetch_failed'] = true` -- Assert warning logged - -**3.11** Run Pint: Format new code -- `./vendor/bin/pint app/Services/AssignmentBackupService.php` -- `./vendor/bin/pint app/Jobs/FetchAssignmentsJob.php` - -**3.12** Verify feature tests pass -- Run: `php artisan test --filter=BackupWithAssignments` -- Expected: All green +**3.8** [X] ⭐ Write/Update feature tests +- `BackupWithAssignmentsConsistencyTest` +- `VersionCaptureWithAssignmentsTest` +- `PolicyVersionViewAssignmentsTest` --- -## Phase 4: US2 - Policy View with Assignments Tab +## Phase 4: US2 - Policy Version View with Assignments **Duration**: 3-4 hours **Dependencies**: Phase 3 complete @@ -282,60 +282,24 @@ ## Phase 4: US2 - Policy View with Assignments Tab ### Tasks -**4.1** ⭐ Add "Assignments" tab to Policy view -- File: `app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php` -- Use Filament `Tabs` component -- Add tab: `Tab::make('Assignments')` -- Show only for Settings Catalog policies with assignments +**4.1** [X] ⭐ Create assignments widget for PolicyVersion view +- Livewire component: `PolicyVersionAssignmentsWidget` +- Render via `ViewPolicyVersion::getFooterWidgets` -**4.2** ⭐ Create assignments table in tab -- Use Filament `Table` component -- Columns: Type (group/user/device), Name, Mode (Include/Exclude), ID -- Data source: `$this->record->assignments` (from BackupItem via Policy relationship) +**4.2** [X] Show include/exclude groups and filters +- Map target types to Include/Exclude labels +- Render group display name or "Unknown Group (ID: ...)" +- Show assignment filter name + filter mode -**4.3** ⭐ Handle orphaned group IDs in table -- Check if `displayName` is null -- Render: "Unknown Group (ID: {id})" with warning icon -- Use Filament `Badge` component with color `warning` +**4.3** [X] Show scope tags section +- Use `PolicyVersion.scope_tags['names']` +- Empty state when not captured -**4.4** Create Scope Tags section -- Below assignments table -- Use Filament `Infolist` component -- Display scope tag names with IDs: "Default (ID: 0), HR-Admins (ID: abc-123)" +**4.4** [X] Remove assignments UI from Policy view +- Policy view no longer shows assignments (moved to PolicyVersion view) -**4.5** Handle empty state -- Show message: "No assignments found" -- Use Filament `EmptyState` component -- Icon: `heroicon-o-user-group` - -**4.6** ⭐ Write feature test: `PolicyViewAssignmentsTabTest::displays_assignments_table` -- Create policy with assignments -- Visit policy view page -- Assert "Assignments" tab visible -- Assert table contains assignment data - -**4.7** ⭐ Write feature test: `PolicyViewAssignmentsTabTest::displays_orphaned_ids_with_warning` -- Create policy with orphaned group ID (no name in metadata) -- Visit assignments tab -- Assert "Unknown Group" text visible -- Assert warning badge present - -**4.8** ⭐ Write feature test: `PolicyViewAssignmentsTabTest::displays_scope_tags` -- Create policy with scope tags -- Visit assignments tab -- Assert scope tag names + IDs displayed - -**4.9** ⭐ Write feature test: `PolicyViewAssignmentsTabTest::shows_empty_state` -- Create policy without assignments -- Visit assignments tab -- Assert "No assignments found" message visible - -**4.10** Run Pint: Format view code -- `./vendor/bin/pint app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php` - -**4.11** Verify feature tests pass -- Run: `php artisan test --filter=PolicyViewAssignmentsTab` -- Expected: All green +**4.5** [X] ⭐ Update tests +- `PolicyVersionViewAssignmentsTest` --- @@ -589,13 +553,14 @@ ### Tasks - Display loading message while warming **8.5** Add tooltips to UI elements -- Backup checkbox: "Captures group/user targeting and RBAC scope. Adds ~2-5 KB per policy." +- Include assignments checkbox: "Captures include/exclude targeting and filters." +- Include scope tags checkbox: "Captures policy scope tag IDs." - Group mapping: "Map source groups to target groups for cross-tenant migrations." - Skip checkbox: "Don't restore assignments targeting this group." **8.6** Update README: Add "Assignments & Scope Tags" section - Overview of feature -- How to use backup checkbox +- How to use include assignments/scope tags (Add Policies + Capture snapshot) - How to use group mapping wizard - Troubleshooting tips @@ -626,23 +591,22 @@ ## Phase 9: Testing & QA ### Tasks -**9.1** Manual QA: Backup with assignments (same tenant) -- Navigate to backup creation -- Enable checkbox -- Verify backup completes +**9.1** Manual QA: Add policies with assignments (same tenant) +- Open a Backup Set and add policies +- Enable "Include assignments" (and scope tags if desired) +- Verify add completes - Verify assignments stored in DB - Verify audit log entry -**9.2** Manual QA: Backup without assignments -- Navigate to backup creation -- Disable checkbox -- Verify backup completes +**9.2** Manual QA: Add policies without assignments +- Add policies to a Backup Set or capture a snapshot +- Disable "Include assignments" +- Verify add/capture completes - Verify assignments column is null -**9.3** Manual QA: Policy view with assignments tab -- Navigate to policy with assignments -- Click "Assignments" tab -- Verify table renders +**9.3** Manual QA: Policy Version view with assignments widget +- Navigate to a Policy Version with assignments +- Verify widget renders - Verify orphaned IDs show warning - Verify scope tags section @@ -666,7 +630,7 @@ ### Tasks **9.7** Manual QA: Handle Graph API failures - Simulate API failure (disable network or use Http::fake with 500 response) -- Attempt backup with checkbox +- Attempt add policies or capture snapshot with "Include assignments" - Verify fail-soft behavior - Verify warning logged @@ -715,7 +679,7 @@ ### Tasks - Check backup_items and restore_runs tables **10.3** Smoke test on staging -- Create backup with assignments +- Add policies to a Backup Set with "Include assignments" - Restore to same tenant - Verify success @@ -726,7 +690,7 @@ ### Tasks **10.5** Create migration guide - Document: "Existing backups will NOT have assignments (not retroactive)" -- Document: "Re-create backups with checkbox to capture assignments" +- Document: "Re-add policies or capture snapshots with include assignments/scope tags to capture data" **10.6** Add monitoring alerts - Alert: Assignment fetch failure rate > 10% @@ -757,35 +721,35 @@ ### Tasks ## Task Summary ### MVP Scope (⭐ Tasks) -**Total**: 24 tasks +**Total**: ~24 tasks **Estimated**: 16-22 hours **Breakdown**: -- Phase 1 (Setup): 11 tasks (2-3h) -- Phase 2 (Graph Services): 5 tasks (subset, ~2-3h) -- Phase 3 (Backup): 6 tasks (3-4h) -- Phase 4 (Policy View): 6 tasks (3-4h) +- Phase 1 (Setup): 17 tasks (2-3h) +- Phase 2 (Graph Services): 18 tasks (4-6h) +- Phase 3 (Capture): 8 tasks (4-5h) +- Phase 4 (Policy Version view): 5 tasks (3-4h) - Phase 5 (Restore, basic): Partial (subset for same-tenant restore) **MVP Scope Definition**: -- ✅ Backup with assignments checkbox (US1) -- ✅ Policy view with assignments tab (US2) +- ✅ Capture assignments/scope tags in Add Policies + Capture Snapshot (US1) +- ✅ Policy Version assignments widget (US2) - ✅ Basic restore (same tenant, auto-match) (US3 partial) - ❌ Group mapping wizard (defer to post-MVP) - ❌ Restore preview diff (defer to post-MVP) -- ❌ Scope tags full support (defer to post-MVP) +- ⚠️ Scope tags restore (capture done, restore deferred) --- ### Full Implementation -**Total**: 62 tasks +**Total**: ~64 tasks **Estimated**: 30-40 hours **Breakdown**: -- Phase 1: 13 tasks (2-3h) -- Phase 2: 16 tasks (4-6h) -- Phase 3: 12 tasks (4-5h) -- Phase 4: 11 tasks (3-4h) +- Phase 1: 17 tasks (2-3h) +- Phase 2: 18 tasks (4-6h) +- Phase 3: 8 tasks (4-5h) +- Phase 4: 5 tasks (3-4h) - Phase 5: 22 tasks (6-8h) - Phase 6: 7 tasks (2-3h) - Phase 7: 8 tasks (2-3h) @@ -798,9 +762,9 @@ ### Full Implementation ## Parallel Development Opportunities ### Track 1: Backend Services (Dev A) -- Phase 1: Database setup (13 tasks) -- Phase 2: Graph API services (16 tasks) -- Phase 3: Backup service (12 tasks) +- Phase 1: Database setup (17 tasks) +- Phase 2: Graph API services (18 tasks) +- Phase 3: Capture services (8 tasks) - Phase 5: Restore service (22 tasks) **Total**: ~16-22 hours @@ -808,7 +772,7 @@ ### Track 1: Backend Services (Dev A) --- ### Track 2: Frontend/UI (Dev B) -- Phase 4: Policy view tab (11 tasks) +- Phase 4: Policy Version view (5 tasks) - Phase 5: Group mapping wizard (subset of Phase 5, ~10 tasks) - Phase 6: Restore preview diff (7 tasks) - Phase 8: UI polish (subset, ~5 tasks) @@ -820,7 +784,7 @@ ### Track 2: Frontend/UI (Dev B) ### Track 3: Testing & QA (Dev C or shared) - Phase 2: Unit tests for services (subset) - Phase 3: Feature tests for backup (subset) -- Phase 4: Feature tests for policy view (subset) +- Phase 4: Feature tests for policy version view (subset) - Phase 9: Manual QA + browser tests (11 tasks) **Total**: ~8-12 hours @@ -836,7 +800,7 @@ ## Dependencies Graph ↓ Phase 2 (Graph Services) ↓ -Phase 3 (Backup) → Phase 4 (Policy View) +Phase 3 (Capture) → Phase 4 (Policy Version view) ↓ ↓ Phase 5 (Restore) ←------+ ↓