Added new feature : display the file/folder and link of a SharingLink object in the permissions reports.
This commit is contained in:
@@ -48,6 +48,54 @@ public class CsvExportServiceTests
|
||||
Assert.Contains("Object", csv);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildCsv_WithResolvedTarget_IncludesTargetColumns()
|
||||
{
|
||||
var entry = new PermissionEntry(
|
||||
ObjectType: "Site",
|
||||
Title: "HR",
|
||||
Url: "https://contoso.sharepoint.com/sites/HR",
|
||||
HasUniquePermissions: true,
|
||||
Users: "Limited Access System Group For List b20e3b22-2b09-4c99-9ba4-37b42f3a12dc",
|
||||
UserLogins: "Limited Access System Group For List b20e3b22-2b09-4c99-9ba4-37b42f3a12dc",
|
||||
PermissionLevels: "Limited Access: Edit",
|
||||
GrantedThrough: "SharePoint Group: Limited Access System Group For List b20e3b22-2b09-4c99-9ba4-37b42f3a12dc",
|
||||
PrincipalType: "SharePointGroup",
|
||||
TargetUrl: "https://contoso.sharepoint.com/sites/HR/Lists/Payroll",
|
||||
TargetLabel: "Payroll");
|
||||
|
||||
var svc = new CsvExportService();
|
||||
var csv = svc.BuildCsv(new[] { entry });
|
||||
|
||||
Assert.Contains("TargetLabel", csv);
|
||||
Assert.Contains("TargetUrl", csv);
|
||||
Assert.Contains("Payroll", csv);
|
||||
Assert.Contains("https://contoso.sharepoint.com/sites/HR/Lists/Payroll", csv);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildCsv_WithSharingLink_IncludesLinkType()
|
||||
{
|
||||
var entry = new PermissionEntry(
|
||||
ObjectType: "Folder", Title: "Reports", Url: "https://contoso.sharepoint.com/sites/HR/Docs",
|
||||
HasUniquePermissions: true,
|
||||
Users: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
UserLogins: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PermissionLevels: "Contribute",
|
||||
GrantedThrough: "SharePoint Group: SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PrincipalType: "SharePointGroup",
|
||||
TargetUrl: "https://contoso.sharepoint.com/sites/HR/Docs/Q4.xlsx",
|
||||
TargetLabel: "Q4.xlsx",
|
||||
SharingLinkType: "OrganizationEdit");
|
||||
|
||||
var svc = new CsvExportService();
|
||||
var csv = svc.BuildCsv(new[] { entry });
|
||||
|
||||
Assert.Contains("SharingLinkType", csv);
|
||||
Assert.Contains("OrganizationEdit", csv);
|
||||
Assert.Contains("Q4.xlsx", csv);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildCsv_WithDuplicateUserPermissionGrantedThrough_MergesLocations()
|
||||
{
|
||||
|
||||
@@ -156,6 +156,146 @@ public class HtmlExportServiceTests
|
||||
Assert.Contains("getAttribute('data-group-target')", html);
|
||||
}
|
||||
|
||||
// ── System-group target rendering (Limited Access / SharingLinks) ────────
|
||||
|
||||
[Fact]
|
||||
public void BuildHtml_WithResolvedListTarget_RendersClickableLink()
|
||||
{
|
||||
var entry = new PermissionEntry(
|
||||
ObjectType: "Site",
|
||||
Title: "Contoso HR",
|
||||
Url: "https://contoso.sharepoint.com/sites/HR",
|
||||
HasUniquePermissions: true,
|
||||
Users: "Limited Access System Group For List b20e3b22-2b09-4c99-9ba4-37b42f3a12dc",
|
||||
UserLogins: "Limited Access System Group For List b20e3b22-2b09-4c99-9ba4-37b42f3a12dc",
|
||||
PermissionLevels: "Limited Access: Edit",
|
||||
GrantedThrough: "SharePoint Group: Limited Access System Group For List b20e3b22-2b09-4c99-9ba4-37b42f3a12dc",
|
||||
PrincipalType: "SharePointGroup",
|
||||
TargetUrl: "https://contoso.sharepoint.com/sites/HR/Lists/Payroll",
|
||||
TargetLabel: "Payroll");
|
||||
|
||||
var svc = new HtmlExportService();
|
||||
var html = svc.BuildHtml(new[] { entry });
|
||||
|
||||
Assert.Contains("https://contoso.sharepoint.com/sites/HR/Lists/Payroll", html);
|
||||
Assert.Contains(">Payroll</a>", html);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildHtml_WithSharingLinkTarget_RendersLinkTypeBadgeAndTarget()
|
||||
{
|
||||
var entry = new PermissionEntry(
|
||||
ObjectType: "Folder",
|
||||
Title: "Reports",
|
||||
Url: "https://contoso.sharepoint.com/sites/HR/Shared%20Documents/Reports",
|
||||
HasUniquePermissions: true,
|
||||
Users: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
UserLogins: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PermissionLevels: "Contribute",
|
||||
GrantedThrough: "SharePoint Group: SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PrincipalType: "SharePointGroup",
|
||||
TargetUrl: "https://contoso.sharepoint.com/sites/HR/Shared%20Documents/Reports/Q4.xlsx",
|
||||
TargetLabel: "Q4.xlsx",
|
||||
SharingLinkType: "OrganizationEdit");
|
||||
|
||||
var svc = new HtmlExportService();
|
||||
var html = svc.BuildHtml(new[] { entry });
|
||||
|
||||
// Friendly label, raw code preserved as tooltip
|
||||
Assert.Contains("Org link", html);
|
||||
Assert.Contains("Edit", html);
|
||||
Assert.Contains("title=\"OrganizationEdit\"", html);
|
||||
Assert.Contains("Q4.xlsx", html);
|
||||
Assert.Contains("https://contoso.sharepoint.com/sites/HR/Shared%20Documents/Reports/Q4.xlsx", html);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("OrganizationView", "Org link", "View")]
|
||||
[InlineData("AnonymousEdit", "Anyone", "Edit")]
|
||||
[InlineData("Direct", "Specific people", null)]
|
||||
[InlineData("Flexible", "Custom link", null)]
|
||||
public void BuildHtml_FriendlyLinkLabel_ReplacesRawType(string raw, string expectedPart, string? expectedSecond)
|
||||
{
|
||||
var entry = new PermissionEntry(
|
||||
ObjectType: "File", Title: "Doc.docx",
|
||||
Url: "https://contoso.sharepoint.com/sites/HR/Docs/Doc.docx",
|
||||
HasUniquePermissions: true,
|
||||
Users: $"SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.{raw}.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
UserLogins: $"SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.{raw}.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PermissionLevels: "Contribute",
|
||||
GrantedThrough: $"SharePoint Group: SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.{raw}.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PrincipalType: "SharePointGroup",
|
||||
TargetUrl: "https://contoso.sharepoint.com/sites/HR/Docs/Doc.docx",
|
||||
TargetLabel: "Doc.docx",
|
||||
SharingLinkType: raw);
|
||||
|
||||
var svc = new HtmlExportService();
|
||||
var html = svc.BuildHtml(new[] { entry }, null, null, hideSystemGroupRaw: true);
|
||||
|
||||
Assert.Contains(expectedPart, html);
|
||||
if (expectedSecond is not null)
|
||||
Assert.Contains(expectedSecond, html);
|
||||
Assert.Contains($"title=\"{raw}\"", html);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildHtml_WithSharingLinkAndHideRaw_OmitsRawGroupString()
|
||||
{
|
||||
var entry = new PermissionEntry(
|
||||
ObjectType: "File", Title: "Q4.xlsx",
|
||||
Url: "https://contoso.sharepoint.com/sites/HR/Docs/Q4.xlsx",
|
||||
HasUniquePermissions: true,
|
||||
Users: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
UserLogins: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PermissionLevels: "Contribute",
|
||||
GrantedThrough: "SharePoint Group: SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PrincipalType: "SharePointGroup",
|
||||
TargetUrl: "https://contoso.sharepoint.com/sites/HR/Docs/Q4.xlsx",
|
||||
TargetLabel: "Q4.xlsx",
|
||||
SharingLinkType: "OrganizationEdit");
|
||||
|
||||
var svc = new HtmlExportService();
|
||||
var html = svc.BuildHtml(new[] { entry }, null, null, hideSystemGroupRaw: true);
|
||||
|
||||
// Raw "SharingLinks.{guid}..." text suppressed when target resolved + flag on
|
||||
Assert.DoesNotContain("SharingLinks.e686221e", html);
|
||||
// Target link still rendered
|
||||
Assert.Contains("Q4.xlsx", html);
|
||||
Assert.Contains("OrganizationEdit", html);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildHtml_WithSharingLinkButNoTargetAndHideRaw_KeepsRawAsFallback()
|
||||
{
|
||||
// Target resolution failed (deleted item) — flag on, but no TargetUrl → keep raw text.
|
||||
var entry = new PermissionEntry(
|
||||
ObjectType: "File", Title: "Q4.xlsx",
|
||||
Url: "https://contoso.sharepoint.com/sites/HR/Docs/Q4.xlsx",
|
||||
HasUniquePermissions: true,
|
||||
Users: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
UserLogins: "SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PermissionLevels: "Contribute",
|
||||
GrantedThrough: "SharePoint Group: SharingLinks.e686221e-d1cb-43c5-8c68-04aa7f90f329.OrganizationEdit.64c27910-66ea-421d-b0f0-8f0f72dcfaf6",
|
||||
PrincipalType: "SharePointGroup",
|
||||
SharingLinkType: "OrganizationEdit");
|
||||
|
||||
var svc = new HtmlExportService();
|
||||
var html = svc.BuildHtml(new[] { entry }, null, null, hideSystemGroupRaw: true);
|
||||
|
||||
Assert.Contains("SharingLinks.e686221e", html);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildHtml_WithoutTarget_RendersGrantedThroughTextOnly()
|
||||
{
|
||||
// Standard SP group with no resolved target — no extra link should appear.
|
||||
var entry = MakeEntry("Owners", "ownersgroup", principalType: "SharePointGroup");
|
||||
var svc = new HtmlExportService();
|
||||
var html = svc.BuildHtml(new[] { entry });
|
||||
|
||||
Assert.DoesNotContain("→", html);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildHtml_Simplified_WithGroupMembers_RendersExpandablePill()
|
||||
{
|
||||
|
||||
@@ -86,20 +86,15 @@ public class UserAccessCsvExportServiceTests
|
||||
var svc = new UserAccessCsvExportService();
|
||||
var csv = svc.BuildCsv("Alice", "alice@contoso.com", new[] { DefaultEntry });
|
||||
|
||||
// Find the header row and count its quoted comma-separated fields
|
||||
// Header is: "Site","Object Type","Object","URL","Permission Level","Access Type","Granted Through"
|
||||
// That is 7 fields.
|
||||
// Header: Site, Object Type, Object, URL, Permission Level, Access Type,
|
||||
// Granted Through, TargetLabel, TargetUrl, SharingLinkType → 10 fields.
|
||||
var lines = csv.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Find a data row (after the blank line separating summary from data)
|
||||
// Data rows contain the entry content (not the header line itself)
|
||||
// We want to count fields in the header row:
|
||||
var headerLine = lines.FirstOrDefault(l => l.Contains("\"Site\",\"Object Type\""));
|
||||
Assert.NotNull(headerLine);
|
||||
|
||||
// Count comma-separated quoted fields: split by "," boundary
|
||||
var fields = CountCsvFields(headerLine!);
|
||||
Assert.Equal(7, fields);
|
||||
Assert.Equal(10, fields);
|
||||
}
|
||||
|
||||
// ── Test 5: WriteSingleFileAsync includes entries for all users ───────────
|
||||
@@ -190,8 +185,8 @@ public class UserAccessCsvExportServiceTests
|
||||
await svc.WriteSingleFileAsync(entries, tmpFile, CancellationToken.None, mergePermissions: true);
|
||||
var content = await File.ReadAllTextAsync(tmpFile);
|
||||
|
||||
// Header must contain consolidated columns
|
||||
Assert.Contains("\"User\",\"User Login\",\"Permission Level\",\"Access Type\",\"Granted Through\",\"Locations\",\"Location Count\"", content);
|
||||
// Header must contain consolidated columns (now includes Target* + SharingLinkType)
|
||||
Assert.Contains("\"User\",\"User Login\",\"Permission Level\",\"Access Type\",\"Granted Through\",\"TargetLabel\",\"TargetUrl\",\"SharingLinkType\",\"Locations\",\"Location Count\"", content);
|
||||
|
||||
// Alice's two entries merged — locations column contains both site titles
|
||||
Assert.Contains("Contoso", content);
|
||||
|
||||
Reference in New Issue
Block a user