Files
Sharepoint-Toolbox/.planning/phases/02-permissions/02-02-SUMMARY.md
Dev ac86bbc302 docs(02-02): complete PermissionsService plan — models, interface, scan engine
- 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
2026-04-02 13:56:53 +02:00

7.6 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
02-permissions 02 permissions
csom
sharepoint
permissions
scan-engine
pnp
c-sharp
phase provides
02-01 PermissionEntryHelper (IsExternalUser, FilterPermissionLevels, IsSharingLinksGroup)
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
02-04 (PermissionsViewModel uses IPermissionsService)
02-05 (Export services work on IReadOnlyList<PermissionEntry>)
02-06 (SitePickerDialog feeds site URLs into PermissionsService)
02-07 (Full integration wires PermissionsService into DI)
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<string> ExcludedLists for O(1) system list filtering
PrincipalType detection via PermissionEntryHelper.IsExternalUser before CSOM PrincipalType enum
created modified
SharepointToolbox/Core/Models/PermissionEntry.cs
SharepointToolbox/Core/Models/ScanOptions.cs
SharepointToolbox/Services/IPermissionsService.cs
SharepointToolbox/Services/PermissionsService.cs
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
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
PERM-01
PERM-03
PERM-04
PERM-07
7min 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)