e4125c6643
A SharePoint admin reported the grant runs without a logged error yet the account never appears as site-collection admin on Group/Teams sites. The failure was invisible: ElevateAsync called ExecuteQueryAsync directly (no enrichment/logging) and the coordinator only surfaced elevate failures on the page, not to Serilog. - Route the admin-endpoint ExecuteQuery through ExecuteQueryRetryHelper so a denial there is enriched (serverErrorType/httpStatus) and logged. - Log the resolved login and SetSiteAdmin acceptance in OwnershipElevationService. - Log elevate failures to Serilog in the coordinator. - Add a post-elevation verify that reads CurrentUser.IsSiteAdmin on the target site so logs distinguish a failed/no-op grant from a scan failing for another reason. Diagnostic only; never throws into the operation flow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
38 lines
1.6 KiB
C#
38 lines
1.6 KiB
C#
using Microsoft.Online.SharePoint.TenantAdministration;
|
|
using Microsoft.SharePoint.Client;
|
|
using Serilog;
|
|
using SharepointToolbox.Web.Core.Helpers;
|
|
using SharepointToolbox.Web.Services.Audit;
|
|
|
|
namespace SharepointToolbox.Web.Services;
|
|
|
|
public class OwnershipElevationService : IOwnershipElevationService
|
|
{
|
|
private readonly IAuditService _audit;
|
|
|
|
public OwnershipElevationService(IAuditService audit) { _audit = audit; }
|
|
|
|
public async Task ElevateAsync(ClientContext tenantAdminCtx, string siteUrl, string loginName, CancellationToken ct)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(loginName))
|
|
{
|
|
tenantAdminCtx.Load(tenantAdminCtx.Web.CurrentUser, u => u.LoginName);
|
|
await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(tenantAdminCtx, null, ct);
|
|
loginName = tenantAdminCtx.Web.CurrentUser.LoginName;
|
|
}
|
|
|
|
Log.Information("SetSiteAdmin: granting {Login} site-collection admin on {Site} via admin endpoint {Admin}",
|
|
loginName, siteUrl, tenantAdminCtx.Url);
|
|
|
|
var tenant = new Tenant(tenantAdminCtx);
|
|
tenant.SetSiteAdmin(siteUrl, loginName, isSiteAdmin: true);
|
|
// Route through the enricher so a denial on the admin endpoint surfaces the real reason
|
|
// (and gets logged) instead of a bare 403 the caller has to guess at.
|
|
await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(tenantAdminCtx, null, ct);
|
|
|
|
Log.Information("SetSiteAdmin call accepted for {Site} (login {Login})", siteUrl, loginName);
|
|
await _audit.LogAsync("ElevateOwnership", tenantAdminCtx.Url, new[] { siteUrl },
|
|
$"Site admin granted to {loginName}");
|
|
}
|
|
}
|