233 lines
9.8 KiB
Markdown
233 lines
9.8 KiB
Markdown
---
|
|
phase: 07-user-access-audit
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- SharepointToolbox/Core/Models/UserAccessEntry.cs
|
|
- SharepointToolbox/Services/IUserAccessAuditService.cs
|
|
- SharepointToolbox/Services/IGraphUserSearchService.cs
|
|
autonomous: true
|
|
requirements:
|
|
- UACC-01
|
|
- UACC-02
|
|
must_haves:
|
|
truths:
|
|
- "UserAccessEntry record exists with all fields needed for audit results display and export"
|
|
- "IUserAccessAuditService interface defines the contract for scanning permissions filtered by user"
|
|
- "IGraphUserSearchService interface defines the contract for Graph API people-picker autocomplete"
|
|
- "AccessType enum distinguishes Direct, Group, and Inherited access"
|
|
artifacts:
|
|
- path: "SharepointToolbox/Core/Models/UserAccessEntry.cs"
|
|
provides: "Data model for user-centric audit results"
|
|
contains: "record UserAccessEntry"
|
|
- path: "SharepointToolbox/Services/IUserAccessAuditService.cs"
|
|
provides: "Service contract for user access auditing"
|
|
contains: "interface IUserAccessAuditService"
|
|
- path: "SharepointToolbox/Services/IGraphUserSearchService.cs"
|
|
provides: "Service contract for Graph API user search"
|
|
contains: "interface IGraphUserSearchService"
|
|
key_links: []
|
|
---
|
|
|
|
<objective>
|
|
Define the data models and service interfaces that all subsequent plans depend on. This is the Wave 0 contract layer: UserAccessEntry record, AccessType enum, IUserAccessAuditService, and IGraphUserSearchService.
|
|
|
|
Purpose: Every other plan in this phase imports these types. Defining them first prevents circular dependencies and gives executors concrete contracts.
|
|
Output: UserAccessEntry.cs, IUserAccessAuditService.cs, IGraphUserSearchService.cs
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
|
|
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/07-user-access-audit/07-CONTEXT.md
|
|
|
|
<interfaces>
|
|
<!-- Existing models this builds alongside -->
|
|
From SharepointToolbox/Core/Models/PermissionEntry.cs:
|
|
```csharp
|
|
namespace SharepointToolbox.Core.Models;
|
|
|
|
public record PermissionEntry(
|
|
string ObjectType, // "Site Collection" | "Site" | "List" | "Folder"
|
|
string Title,
|
|
string Url,
|
|
bool HasUniquePermissions,
|
|
string Users, // Semicolon-joined display names
|
|
string UserLogins, // Semicolon-joined login names
|
|
string PermissionLevels, // Semicolon-joined role names
|
|
string GrantedThrough, // "Direct Permissions" | "SharePoint Group: <name>"
|
|
string PrincipalType // "SharePointGroup" | "User" | "External User"
|
|
);
|
|
```
|
|
|
|
From SharepointToolbox/Core/Models/SiteInfo.cs:
|
|
```csharp
|
|
namespace SharepointToolbox.Core.Models;
|
|
public record SiteInfo(string Url, string Title);
|
|
```
|
|
|
|
From SharepointToolbox/Core/Models/ScanOptions.cs (inferred from usage):
|
|
```csharp
|
|
public record ScanOptions(bool IncludeInherited, bool ScanFolders, int FolderDepth, bool IncludeSubsites);
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create UserAccessEntry model and AccessType enum</name>
|
|
<files>SharepointToolbox/Core/Models/UserAccessEntry.cs</files>
|
|
<action>
|
|
Create `SharepointToolbox/Core/Models/UserAccessEntry.cs` with:
|
|
|
|
```csharp
|
|
namespace SharepointToolbox.Core.Models;
|
|
|
|
/// <summary>
|
|
/// Classifies how a user received a permission assignment.
|
|
/// </summary>
|
|
public enum AccessType
|
|
{
|
|
/// <summary>User is directly assigned a role on the object.</summary>
|
|
Direct,
|
|
/// <summary>User is a member of a SharePoint group that has the role.</summary>
|
|
Group,
|
|
/// <summary>Permission is inherited from a parent object (not unique).</summary>
|
|
Inherited
|
|
}
|
|
|
|
/// <summary>
|
|
/// One row in the User Access Audit results grid.
|
|
/// Represents a single permission that a specific user holds on a specific object.
|
|
/// </summary>
|
|
public record UserAccessEntry(
|
|
string UserDisplayName, // e.g. "Alice Smith"
|
|
string UserLogin, // e.g. "alice@contoso.com" or "i:0#.f|membership|alice@contoso.com"
|
|
string SiteUrl, // The site collection URL where this permission exists
|
|
string SiteTitle, // The site collection title
|
|
string ObjectType, // "Site Collection" | "Site" | "List" | "Folder"
|
|
string ObjectTitle, // Name of the list/folder/site
|
|
string ObjectUrl, // URL of the specific object
|
|
string PermissionLevel, // e.g. "Full Control", "Contribute"
|
|
AccessType AccessType, // Direct | Group | Inherited
|
|
string GrantedThrough, // "Direct Permissions" | "SharePoint Group: Members" etc.
|
|
bool IsHighPrivilege, // True for Full Control, Site Collection Administrator
|
|
bool IsExternalUser // True if login contains #EXT#
|
|
);
|
|
```
|
|
|
|
Design notes:
|
|
- Each row is one user + one object + one permission level (fully denormalized for DataGrid binding)
|
|
- IsHighPrivilege pre-computed during scan for warning icon display without re-evaluation
|
|
- IsExternalUser pre-computed using PermissionEntryHelper.IsExternalUser pattern
|
|
- SiteUrl + SiteTitle included so results can group by site across multi-site scans
|
|
</action>
|
|
<verify>
|
|
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>UserAccessEntry.cs and AccessType enum exist in Core/Models/, compile without errors, contain all 12 fields.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Create IUserAccessAuditService and IGraphUserSearchService interfaces</name>
|
|
<files>SharepointToolbox/Services/IUserAccessAuditService.cs, SharepointToolbox/Services/IGraphUserSearchService.cs</files>
|
|
<action>
|
|
Create `SharepointToolbox/Services/IUserAccessAuditService.cs`:
|
|
|
|
```csharp
|
|
using SharepointToolbox.Core.Models;
|
|
|
|
namespace SharepointToolbox.Services;
|
|
|
|
/// <summary>
|
|
/// Scans permissions across selected sites and filters results to show
|
|
/// only what specific user(s) can access.
|
|
/// </summary>
|
|
public interface IUserAccessAuditService
|
|
{
|
|
/// <summary>
|
|
/// Scans all selected sites for permissions, then filters results to entries
|
|
/// matching the specified user logins. Returns a flat list of UserAccessEntry
|
|
/// records suitable for DataGrid binding and export.
|
|
/// </summary>
|
|
/// <param name="sessionManager">Session manager for creating authenticated contexts.</param>
|
|
/// <param name="targetUserLogins">Login names (emails) of users to audit.</param>
|
|
/// <param name="sites">Sites to scan.</param>
|
|
/// <param name="options">Scan depth options (inherited, folders, subsites).</param>
|
|
/// <param name="progress">Progress reporter.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Flat list of access entries for the target users.</returns>
|
|
Task<IReadOnlyList<UserAccessEntry>> AuditUsersAsync(
|
|
ISessionManager sessionManager,
|
|
IReadOnlyList<string> targetUserLogins,
|
|
IReadOnlyList<SiteInfo> sites,
|
|
ScanOptions options,
|
|
IProgress<OperationProgress> progress,
|
|
CancellationToken ct);
|
|
}
|
|
```
|
|
|
|
Create `SharepointToolbox/Services/IGraphUserSearchService.cs`:
|
|
|
|
```csharp
|
|
namespace SharepointToolbox.Services;
|
|
|
|
/// <summary>
|
|
/// Searches tenant users via Microsoft Graph API for the people-picker autocomplete.
|
|
/// </summary>
|
|
public interface IGraphUserSearchService
|
|
{
|
|
/// <summary>
|
|
/// Searches for users in the tenant whose display name or email matches the query.
|
|
/// Returns up to <paramref name="maxResults"/> matches.
|
|
/// </summary>
|
|
/// <param name="clientId">The Azure AD app client ID for Graph authentication.</param>
|
|
/// <param name="query">Partial name or email to search for.</param>
|
|
/// <param name="maxResults">Maximum number of results to return (default 10).</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>List of (DisplayName, Email/UPN) tuples.</returns>
|
|
Task<IReadOnlyList<GraphUserResult>> SearchUsersAsync(
|
|
string clientId,
|
|
string query,
|
|
int maxResults = 10,
|
|
CancellationToken ct = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a user returned by the Graph API people search.
|
|
/// </summary>
|
|
public record GraphUserResult(string DisplayName, string UserPrincipalName, string? Mail);
|
|
```
|
|
</action>
|
|
<verify>
|
|
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>Both interface files exist in Services/, compile without errors, IUserAccessAuditService.AuditUsersAsync and IGraphUserSearchService.SearchUsersAsync are defined with correct signatures.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors
|
|
- UserAccessEntry.cs contains record with 12 fields and AccessType enum
|
|
- IUserAccessAuditService.cs contains AuditUsersAsync method signature
|
|
- IGraphUserSearchService.cs contains SearchUsersAsync method signature and GraphUserResult record
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
All three files compile cleanly. The contracts are established: downstream plans (07-02 through 07-08) can import UserAccessEntry, AccessType, IUserAccessAuditService, IGraphUserSearchService, and GraphUserResult without ambiguity.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/07-user-access-audit/07-01-SUMMARY.md`
|
|
</output>
|