From 72349d84156f28b21db2cd27ab0295198603b8d0 Mon Sep 17 00:00:00 2001 From: Dev Date: Tue, 7 Apr 2026 12:45:14 +0200 Subject: [PATCH] docs(07-04): complete UserAccessAuditViewModel plan - Add 07-04-SUMMARY.md with task commits and decisions - Update STATE.md: progress 77%, session record, decisions - Update ROADMAP.md: phase 7 plan count updated to 5/8 summaries --- .planning/ROADMAP.md | 4 +- .planning/STATE.md | 13 ++- .../07-user-access-audit/07-04-SUMMARY.md | 103 ++++++++++++++++++ 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 .planning/phases/07-user-access-audit/07-04-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index ce87a30..730a030 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -53,7 +53,7 @@ Plans: 2. Running the audit returns a list of all access entries the user holds across the selected sites 3. Results distinguish between direct role assignments, SharePoint group memberships, and inherited access 4. Results can be exported to CSV or HTML in the same format established by v1.0 export patterns -**Plans:** 4/8 plans executed +**Plans:** 5/8 plans executed Plans: - [ ] 07-01-PLAN.md — UserAccessEntry model + service interfaces (Wave 1) - [ ] 07-02-PLAN.md — UserAccessAuditService implementation (Wave 2) @@ -96,6 +96,6 @@ Plans: | 4. Bulk Operations and Provisioning | v1.0 | 10/10 | Complete | 2026-04-03 | | 5. Distribution and Hardening | v1.0 | 3/3 | Complete | 2026-04-03 | | 6. Global Site Selection | 5/5 | Complete | 2026-04-07 | - | -| 7. User Access Audit | 4/8 | In Progress| | - | +| 7. User Access Audit | 5/8 | In Progress| | - | | 8. Simplified Permissions | v1.1 | 0/? | Not started | - | | 9. Storage Visualization | v1.1 | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index e40a3ac..0754d83 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: completed -stopped_at: Completed 07-06-PLAN.md -last_updated: "2026-04-07T10:41:47.594Z" +stopped_at: Completed 07-04-PLAN.md +last_updated: "2026-04-07T10:45:02.830Z" last_activity: 2026-04-07 — Roadmap created (Phases 6-9), 10/10 requirements mapped progress: total_phases: 4 completed_phases: 1 total_plans: 13 - completed_plans: 9 + completed_plans: 10 --- # Project State @@ -51,6 +51,7 @@ Phase 6 [ ] → Phase 7 [ ] → Phase 8 [ ] → Phase 9 [ ] | Phase 07-user-access-audit P03 | 2 | 1 tasks | 1 files | | Phase 07-user-access-audit P02 | 1 | 1 tasks | 1 files | | Phase 07-user-access-audit P06 | 2 | 2 tasks | 2 files | +| Phase 07-user-access-audit P04 | 2 | 1 tasks | 1 files | ## Accumulated Context @@ -82,6 +83,8 @@ Decisions are logged in PROJECT.md Key Decisions table. - [Phase 07-user-access-audit]: Bidirectional contains matching for user login — handles both plain email and full SharePoint claim formats - [Phase 07-user-access-audit]: UserAccessCsvExportService has two write modes: WriteAsync (per-user files to directory) and WriteSingleFileAsync (combined for SaveFileDialog) - [Phase 07-user-access-audit]: HTML sortTable() scoped per group so sorting in by-user view keeps each user's rows together +- [Phase 07-04]: CollectionViewSource bound at construction; ApplyGrouping() swaps PropertyGroupDescription between UserLogin/SiteUrl on IsGroupByUser toggle +- [Phase 07-04]: ExportCsvAsync uses WriteSingleFileAsync (combined file) not WriteAsync (per-user directory) to match SaveFileDialog single-path UX ### Pending Todos @@ -93,6 +96,6 @@ None. ## Session Continuity -Last session: 2026-04-07T10:41:47.591Z -Stopped at: Completed 07-06-PLAN.md +Last session: 2026-04-07T10:45:02.828Z +Stopped at: Completed 07-04-PLAN.md Resume file: None diff --git a/.planning/phases/07-user-access-audit/07-04-SUMMARY.md b/.planning/phases/07-user-access-audit/07-04-SUMMARY.md new file mode 100644 index 0000000..1dd710e --- /dev/null +++ b/.planning/phases/07-user-access-audit/07-04-SUMMARY.md @@ -0,0 +1,103 @@ +--- +phase: 07-user-access-audit +plan: 04 +subsystem: viewmodel +tags: [viewmodel, wpf, people-picker, debounce, collectionview, grouping, filtering, export, mvvm] + +requires: + - phase: 07-01 + provides: [UserAccessEntry, AccessType, IUserAccessAuditService, IGraphUserSearchService, GraphUserResult] + - phase: 07-02 + provides: [UserAccessAuditService] + - phase: 07-03 + provides: [GraphUserSearchService] + - phase: 07-06 + provides: [UserAccessCsvExportService, UserAccessHtmlExportService] +provides: + - UserAccessAuditViewModel with full orchestration of people picker, site selection, audit execution, grouping, filtering, summary banner, export +affects: [07-05, 07-07, 07-08] + +tech-stack: + added: [] + patterns: [CollectionViewSource grouping toggle, debounced CancellationTokenSource search, FeatureViewModelBase extension, dual-constructor pattern, _hasLocalSiteOverride site override] + +key-files: + created: + - SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs + modified: [] + +key-decisions: + - "CollectionViewSource is created over Results in constructor; ApplyGrouping() clears and re-adds PropertyGroupDescription on IsGroupByUser toggle (UserLogin or SiteUrl)" + - "Debounced search uses _searchCts CancellationTokenSource cancelled on each SearchQuery change; Task.Delay(300, ct) pattern with OperationCanceledException swallowed" + - "OnResultsChanged partial rebuilds grouping/filter when Results collection reference is replaced after RunOperationAsync" + - "ExportCsvAsync calls WriteSingleFileAsync (combined single-file export) rather than WriteAsync (per-user directory) to match SaveFileDialog single-path UX" + +patterns-established: + - "UserAccessAuditViewModel: same _hasLocalSiteOverride + OnGlobalSitesChanged guard as PermissionsViewModel" + - "Dual constructor: full DI constructor + internal test constructor omitting export services — both initialize all commands and wire collection events" + - "Summary properties (TotalAccessCount, SitesCount, HighPrivilegeCount) are computed getters calling Results LINQ — NotifySummaryProperties() triggers all three" + +requirements-completed: [UACC-01, UACC-02] + +duration: 2min +completed: 2026-04-07 +--- + +# Phase 7 Plan 04: UserAccessAuditViewModel Summary + +**UserAccessAuditViewModel wires people-picker (300ms debounced Graph search), multi-site selection with override guard, IUserAccessAuditService.AuditUsersAsync execution, CollectionViewSource group-by-user/site toggle with real-time filter, computed summary banner (TotalAccessCount, SitesCount, HighPrivilegeCount), and CSV/HTML export commands — zero-error build.** + +## Performance + +- **Duration:** ~2 min +- **Started:** 2026-04-07T10:42:51Z +- **Completed:** 2026-04-07T10:44:56Z +- **Tasks:** 1 +- **Files modified:** 1 + +## Accomplishments + +- UserAccessAuditViewModel.cs (~300 lines) extends FeatureViewModelBase and implements all 10 observable properties, 5 commands, CollectionViewSource grouping/filtering, and dual constructors +- Debounced people-picker: _searchCts cancelled/recreated on SearchQuery change, 300ms Task.Delay, IsSearching spinner, 2-char minimum guard consistent with GraphUserSearchService +- CollectionViewSource grouping: ApplyGrouping() swaps PropertyGroupDescription between UserLogin and SiteUrl; FilterPredicate applies to 6 fields case-insensitively +- Summary banner computed properties (TotalAccessCount, SitesCount, HighPrivilegeCount) notified via NotifySummaryProperties() after each RunOperationAsync and tenant switch + +## Task Commits + +1. **Task 1: Implement UserAccessAuditViewModel** - `3de737a` (feat) + +**Plan metadata:** (docs commit pending) + +## Files Created/Modified + +- `SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs` — Full orchestration ViewModel for User Access Audit tab + +## Decisions Made + +1. **CollectionViewSource bound at construction** — ResultsView is created from a `new CollectionViewSource { Source = Results }` in the constructor. When Results is replaced by a new collection in RunOperationAsync, OnResultsChanged re-applies grouping and filter. This avoids ICollectionView rebinding complexity in XAML. + +2. **WriteSingleFileAsync for CSV export** — UserAccessCsvExportService has two modes: WriteAsync (per-user files to directory) and WriteSingleFileAsync (combined). The ViewModel uses WriteSingleFileAsync since the SaveFileDialog returns a single file path — the per-directory mode is for batch export scenarios. + +3. **SelectedUsers UPNs as login keys** — AuditUsersAsync receives `SelectedUsers.Select(u => u.UserPrincipalName)` as the targetUserLogins parameter, matching the UPN-based bidirectional matching in UserAccessAuditService. + +## Deviations from Plan + +None — plan executed exactly as written. + +## Issues Encountered + +None. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- UserAccessAuditViewModel ready for XAML binding in 07-05 (View) +- All observable properties, commands, and ResultsView ICollectionView available for DataGrid/ComboBox/AutoComplete binding +- Export commands wired to UserAccessCsvExportService.WriteSingleFileAsync and UserAccessHtmlExportService.WriteAsync + +--- +*Phase: 07-user-access-audit* +*Completed: 2026-04-07*