9.8 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 07-user-access-audit | 01 | execute | 1 |
|
true |
|
|
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
<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>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/07-user-access-audit/07-CONTEXT.md 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: " 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):
public record ScanOptions(bool IncludeInherited, bool ScanFolders, int FolderDepth, bool IncludeSubsites);
```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
cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5
UserAccessEntry.cs and AccessType enum exist in Core/Models/, compile without errors, contain all 12 fields.
Task 2: Create IUserAccessAuditService and IGraphUserSearchService interfaces
SharepointToolbox/Services/IUserAccessAuditService.cs, SharepointToolbox/Services/IGraphUserSearchService.cs
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);
```
cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5
Both interface files exist in Services/, compile without errors, IUserAccessAuditService.AuditUsersAsync and IGraphUserSearchService.SearchUsersAsync are defined with correct signatures.
- `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
<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>
After completion, create `.planning/phases/07-user-access-audit/07-01-SUMMARY.md`