From ac86bbc30235e293832d2b0a39944eda62aa0638 Mon Sep 17 00:00:00 2001 From: Dev Date: Thu, 2 Apr 2026 13:56:53 +0200 Subject: [PATCH] =?UTF-8?q?docs(02-02):=20complete=20PermissionsService=20?= =?UTF-8?q?plan=20=E2=80=94=20models,=20interface,=20scan=20engine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created 02-02-SUMMARY.md with full execution record - Updated STATE.md with decisions (CSOM type constraints) and progress - Updated ROADMAP.md (phase 02: 4/7 summaries, In Progress) - Marked PERM-07 complete in REQUIREMENTS.md --- .planning/REQUIREMENTS.md | 8 +- .planning/STATE.md | 13 +- .../phases/02-permissions/02-02-SUMMARY.md | 152 ++++++++++++++++++ 3 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 .planning/phases/02-permissions/02-02-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 2178821..c87370e 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -26,11 +26,11 @@ Requirements for initial release. Each maps to roadmap phases. - [x] **PERM-01**: User can scan permissions on a single SharePoint site with configurable depth - [x] **PERM-02**: User can scan permissions across multiple selected sites in one operation -- [ ] **PERM-03**: Permissions scan includes owners, members, guests, external users, and broken inheritance +- [x] **PERM-03**: Permissions scan includes owners, members, guests, external users, and broken inheritance - [x] **PERM-04**: User can choose to include or exclude inherited permissions - [x] **PERM-05**: User can export permissions report to CSV (raw data) - [x] **PERM-06**: User can export permissions report to interactive HTML (sortable, filterable, groupable by user) -- [ ] **PERM-07**: SharePoint 5,000-item list view threshold handled via pagination — no silent failures on large libraries +- [x] **PERM-07**: SharePoint 5,000-item list view threshold handled via pagination — no silent failures on large libraries ### Storage @@ -129,11 +129,11 @@ Which phases cover which requirements. Updated during roadmap creation. | FOUND-12 | Phase 1 | Complete | | PERM-01 | Phase 2 | Complete | | PERM-02 | Phase 2 | Complete | -| PERM-03 | Phase 2 | Pending | +| PERM-03 | Phase 2 | Complete | | PERM-04 | Phase 2 | Complete | | PERM-05 | Phase 2 | Complete | | PERM-06 | Phase 2 | Complete | -| PERM-07 | Phase 2 | Pending | +| PERM-07 | Phase 2 | Complete | | STOR-01 | Phase 3 | Pending | | STOR-02 | Phase 3 | Pending | | STOR-03 | Phase 3 | Pending | diff --git a/.planning/STATE.md b/.planning/STATE.md index 8b5c7b2..6d87f6f 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: planning -stopped_at: Completed 02-01-PLAN.md — Wave 0 test scaffold for permissions phase -last_updated: "2026-04-02T11:55:45.013Z" +stopped_at: Completed 02-02-PLAN.md +last_updated: "2026-04-02T11:56:31.288Z" last_activity: 2026-04-02 — Roadmap created, requirements mapped, all 42 v1 requirements assigned to phases progress: total_phases: 5 completed_phases: 1 total_plans: 15 - completed_plans: 11 + completed_plans: 12 percent: 13 --- @@ -62,6 +62,7 @@ Progress: [█░░░░░░░░░] 13% | Phase 02-permissions P05 | 1min | 1 tasks | 3 files | | Phase 02-permissions P03 | 1min | 1 tasks | 5 files | | Phase 02-permissions P01 | 5min | 2 tasks | 9 files | +| Phase 02-permissions P02 | 7min | 2 tasks | 4 files | ## Accumulated Context @@ -101,6 +102,8 @@ Recent decisions affecting current work: - [Phase 02-permissions]: InternalsVisibleTo added to AssemblyInfo.cs — required for test project to access internal DeriveAdminUrl; plan omitted this assembly attribute - [Phase 02-permissions]: Export service stubs created in Plan 02-01 so test project compiles before Plan 03 implementation - [Phase 02-permissions]: Principal.Email removed from CSOM load expression — Email only exists on User subtype, not Principal base class +- [Phase 02-permissions]: Folder is not a SecurableObject in CSOM — ListItem used for permission extraction — Required by CSOM type system; Folder inherits from ClientObject not SecurableObject +- [Phase 02-permissions]: Principal.Email excluded from CSOM Include — email not needed for PermissionEntry — Principal base type has no Email property; only User subtype does; avoids CS1061 ### Pending Todos @@ -114,6 +117,6 @@ None yet. ## Session Continuity -Last session: 2026-04-02T11:55:36.761Z -Stopped at: Completed 02-01-PLAN.md — Wave 0 test scaffold for permissions phase +Last session: 2026-04-02T11:56:31.286Z +Stopped at: Completed 02-02-PLAN.md Resume file: None diff --git a/.planning/phases/02-permissions/02-02-SUMMARY.md b/.planning/phases/02-permissions/02-02-SUMMARY.md new file mode 100644 index 0000000..153d80d --- /dev/null +++ b/.planning/phases/02-permissions/02-02-SUMMARY.md @@ -0,0 +1,152 @@ +--- +phase: 02-permissions +plan: 02 +subsystem: permissions +tags: [csom, sharepoint, permissions, scan-engine, pnp, c-sharp] + +# Dependency graph +requires: + - phase: 02-01 + provides: PermissionEntryHelper (IsExternalUser, FilterPermissionLevels, IsSharingLinksGroup) +provides: + - PermissionEntry record — flat data model for one permission assignment + - ScanOptions record — immutable scan configuration with IncludeInherited/ScanFolders/FolderDepth/IncludeSubsites + - IPermissionsService interface — contract enabling ViewModel mocking in tests + - PermissionsService implementation — full CSOM scan engine, port of PS Generate-PnPSitePermissionRpt +affects: + - 02-04 (PermissionsViewModel uses IPermissionsService) + - 02-05 (Export services work on IReadOnlyList) + - 02-06 (SitePickerDialog feeds site URLs into PermissionsService) + - 02-07 (Full integration wires PermissionsService into DI) + +# Tech tracking +tech-stack: + added: [] + patterns: + - "CSOM batched Include() load pattern — one round-trip per SecurableObject via ctx.Load + ExecuteQueryRetryHelper" + - "Async folder enumeration via SharePointPaginationHelper.GetAllItemsAsync (never raw CSOM list enumeration)" + - "HashSet ExcludedLists for O(1) system list filtering" + - "PrincipalType detection via PermissionEntryHelper.IsExternalUser before CSOM PrincipalType enum" + +key-files: + created: + - SharepointToolbox/Core/Models/PermissionEntry.cs + - SharepointToolbox/Core/Models/ScanOptions.cs + - SharepointToolbox/Services/IPermissionsService.cs + - SharepointToolbox/Services/PermissionsService.cs + modified: [] + +key-decisions: + - "Folder enumeration uses ListItem (SecurableObject) not Folder — Folder is not a SecurableObject in CSOM; ListItem.Folder provides metadata while ListItem itself holds role assignments" + - "Principal.Email excluded from CSOM Include — Principal base type has no Email property; only User subtype does; email not needed for PermissionEntry fields" + - "FolderDepth=999 is the sentinel for unlimited depth — avoids nullable int and matches PS reference behavior" + - "Subsite enumeration clones ClientContext via ctx.Clone(subweb.Url) — each subsite needs its own context for CSOM scoped operations" + +patterns-established: + - "CSOM batched load: always batch ctx.Load with all required sub-properties in one call before ExecuteQueryRetryAsync" + - "ExcludedLists HashSet: new service that filters SharePoint objects uses StringComparer.OrdinalIgnoreCase HashSet for O(1) exclusion" + - "ct.ThrowIfCancellationRequested() at the start of every private async method" + +requirements-completed: + - PERM-01 + - PERM-03 + - PERM-04 + - PERM-07 + +# Metrics +duration: 7min +completed: 2026-04-02 +--- + +# Phase 2 Plan 2: PermissionsService Scan Engine Summary + +**CSOM scan engine implementing all 5 SharePoint permission scan paths (site collection admins, web, lists, folders, subsites) as a faithful C# port of the PowerShell Generate-PnPSitePermissionRpt function** + +## Performance + +- **Duration:** 7 min +- **Started:** 2026-04-02T11:48:39Z +- **Completed:** 2026-04-02T11:54:58Z +- **Tasks:** 2 +- **Files modified:** 4 + +## Accomplishments + +- Defined PermissionEntry (9-field record), ScanOptions (4-field config record), and IPermissionsService interface — foundational contracts for all subsequent Phase 2 plans +- Implemented PermissionsService with full scan logic: site collection admins, web, lists, folders (via SharePointPaginationHelper), and subsites +- All CSOM round-trips use ExecuteQueryRetryHelper.ExecuteQueryRetryAsync; folder enumeration uses SharePointPaginationHelper.GetAllItemsAsync (never raw iteration) +- Limited Access filtering, sharing links group exclusion, external user detection, and 34-item ExcludedLists set all implemented + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Define data models and IPermissionsService interface** - `4a6594d` (feat) +2. **Task 2: Implement PermissionsService scan engine** - `9f2e2f9` (fix — linter auto-fixed CSOM type errors pre-commit) + +## Files Created/Modified + +- `SharepointToolbox/Core/Models/PermissionEntry.cs` — Flat record for one permission assignment (9 string/bool positional fields) +- `SharepointToolbox/Core/Models/ScanOptions.cs` — Immutable scan config: IncludeInherited, ScanFolders, FolderDepth, IncludeSubsites +- `SharepointToolbox/Services/IPermissionsService.cs` — Interface with ScanSiteAsync enabling ViewModel mocking +- `SharepointToolbox/Services/PermissionsService.cs` — Full CSOM engine: 340 lines, 5 private helpers, 34-item ExcludedLists + +## Decisions Made + +- `Folder` is not a `SecurableObject` in CSOM — folder permissions are extracted via `ListItem` (which IS a SecurableObject); `item.Folder` provides name/URL metadata only +- `Principal.Email` excluded from batched Include — `Principal` base type lacks Email; only `User` subtype has it; email was not needed for PermissionEntry fields +- `FolderDepth=999` used as sentinel for unlimited depth scanning +- Subsite enumeration clones ClientContext via `ctx.Clone(subweb.Url)` for proper CSOM scoping + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Principal.Email not available on RoleAssignment.Member** +- **Found during:** Task 2 (PermissionsService implementation) +- **Issue:** The plan's CSOM Include expression included `ra => ra.Member.Email` — Principal base type has no Email property (only User subtype does) +- **Fix:** Removed Email from the batched Include; email is not needed for any PermissionEntry field +- **Files modified:** SharepointToolbox/Services/PermissionsService.cs +- **Verification:** dotnet build passes with 0 errors +- **Committed in:** 9f2e2f9 + +**2. [Rule 1 - Bug] Folder is not a SecurableObject in CSOM** +- **Found during:** Task 2 (GetFolderPermissionsAsync) +- **Issue:** `ExtractPermissionsAsync(ctx, folder, ...)` failed — Folder does not inherit from SecurableObject in Microsoft.SharePoint.Client +- **Fix:** Changed to pass `item` (ListItem, which IS a SecurableObject) to ExtractPermissionsAsync; kept `item.Folder` load for ServerRelativeUrl/Name metadata only +- **Files modified:** SharepointToolbox/Services/PermissionsService.cs +- **Verification:** dotnet build passes with 0 errors +- **Committed in:** 9f2e2f9 + +--- + +**Total deviations:** 2 auto-fixed (2 Rule 1 bugs — CSOM API type constraints) +**Impact on plan:** Both fixes were necessary for correct CSOM usage. Folder permission extraction is semantically equivalent — ListItem holds the same role assignments as Folder. No scope creep. + +## Issues Encountered + +- Pre-existing test failures (6): CsvExportService and HtmlExportService tests throw NotImplementedException — these are intentional stubs from Plan 01 to be implemented in Plan 03. No regression introduced by this plan. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- PermissionEntry, ScanOptions, IPermissionsService, and PermissionsService are available for Plans 02-04 (ViewModel), 02-05 (Export), 02-06 (SitePicker), and 02-07 (full integration) +- All Phase 1 tests remain at 53 passing (plus 4 skipping, 6 pre-existing Plan 03 stubs failing) +- IPermissionsService is mockable — PermissionsViewModelTests can be unblocked in Plan 04 + +--- +*Phase: 02-permissions* +*Completed: 2026-04-02* + +## Self-Check: PASSED + +- FOUND: SharepointToolbox/Core/Models/PermissionEntry.cs +- FOUND: SharepointToolbox/Core/Models/ScanOptions.cs +- FOUND: SharepointToolbox/Services/IPermissionsService.cs +- FOUND: SharepointToolbox/Services/PermissionsService.cs +- FOUND: .planning/phases/02-permissions/02-02-SUMMARY.md +- FOUND: commit 4a6594d (feat(02-02): define models and interface) +- FOUND: commit 9f2e2f9 (fix(02-01): PermissionsService + export stubs)