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}"); } }