--- phase: 07-user-access-audit plan: 01 subsystem: core-models-interfaces tags: [models, interfaces, contracts, user-access-audit] dependency_graph: requires: [] provides: [UserAccessEntry, AccessType, IUserAccessAuditService, IGraphUserSearchService, GraphUserResult] affects: [07-02, 07-03, 07-04, 07-05, 07-06, 07-07, 07-08] tech_stack: added: [] patterns: [record types, interface contracts, C# nullable annotations] key_files: created: - SharepointToolbox/Core/Models/UserAccessEntry.cs - SharepointToolbox/Services/IUserAccessAuditService.cs - SharepointToolbox/Services/IGraphUserSearchService.cs modified: [] decisions: - "UserAccessEntry is fully denormalized (one row = one user + one object + one permission) for direct DataGrid binding without post-processing" - "IsHighPrivilege and IsExternalUser are pre-computed at scan time so the grid can show icons without re-evaluating strings" - "GraphUserResult is defined in IGraphUserSearchService.cs (same file as interface) since it is only used by that interface" metrics: duration_minutes: 5 completed_date: "2026-04-07" tasks_completed: 2 files_created: 3 files_modified: 0 --- # Phase 7 Plan 01: Data Models and Service Interfaces Summary **One-liner:** Contract layer with UserAccessEntry record (12-field denormalized model), AccessType enum, IUserAccessAuditService, IGraphUserSearchService, and GraphUserResult — zero-error foundation for all downstream Phase 7 plans. ## What Was Built Three files establishing the Wave 1 contract layer for the User Access Audit feature: 1. **UserAccessEntry.cs** — C# record with 12 positional properties representing one row in the audit results grid. Includes AccessType enum (Direct/Group/Inherited), pre-computed IsHighPrivilege and IsExternalUser flags, and SiteUrl/SiteTitle for multi-site grouping. 2. **IUserAccessAuditService.cs** — Service interface with single method `AuditUsersAsync` that accepts a session manager, list of target user login names, list of sites, scan options, progress reporter, and cancellation token. Returns `IReadOnlyList`. 3. **IGraphUserSearchService.cs** — Service interface with `SearchUsersAsync` for Graph API people-picker autocomplete, plus the `GraphUserResult` record (DisplayName, UserPrincipalName, nullable Mail). ## Tasks | # | Task | Status | Commit | |---|------|--------|--------| | 1 | Create UserAccessEntry model and AccessType enum | Done | e08df0f | | 2 | Create IUserAccessAuditService and IGraphUserSearchService interfaces | Done | 1a6989a | ## Decisions Made 1. **Denormalized record design** — Each UserAccessEntry row represents one user + one object + one permission level. This avoids nested object graphs and allows direct DataGrid binding and CSV export without flattening logic. 2. **Pre-computed flags** — IsHighPrivilege (Full Control, Site Collection Administrator) and IsExternalUser (#EXT# in login) are computed during the scan pass, not at display time. This keeps the ViewModel simple and the grid row data self-contained. 3. **GraphUserResult co-located with interface** — Defined in the same file as IGraphUserSearchService since it is exclusively used as the return type of that interface. No separate file needed. ## Deviations from Plan None — plan executed exactly as written. ## Verification - `dotnet build SharepointToolbox/SharepointToolbox.csproj` — 0 errors, 0 warnings - UserAccessEntry.cs: record with 12 fields + AccessType enum confirmed - IUserAccessAuditService.cs: AuditUsersAsync with correct 6-parameter signature confirmed - IGraphUserSearchService.cs: SearchUsersAsync with 4 parameters + GraphUserResult record confirmed ## Self-Check: PASSED Files confirmed present: - FOUND: SharepointToolbox/Core/Models/UserAccessEntry.cs - FOUND: SharepointToolbox/Services/IUserAccessAuditService.cs - FOUND: SharepointToolbox/Services/IGraphUserSearchService.cs Commits confirmed: - FOUND: e08df0f - FOUND: 1a6989a