Read-only TechN0 users could see nav items for pages that immediately
return a WriteGuard notice (transfer, versions, templates, bulk members/
sites, folder structure), landing them on empty screens. Add a `write`
nav scope (HasProfile && Role >= TechN1) so those items no longer appear
for N0. The Bulk and Config section headers drop out automatically since
all their children are now write-scoped. Per-page guards remain intact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Standard technicians (TechN0/TechN1) are no longer auto-prompted for a
delegated SharePoint sign-in when selecting a profile — only admins are.
Techs operate under the profile's app (certificate) identity, so a profile
selection never forces them to authenticate.
To keep that usable, the admin profile list now shows a "No shared access"
badge on any profile that isn't certificate-configured, since standard
techs can't operate against those until an admin registers a cert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Feature work:
- Certificate (app-only) auth per profile: cert store, context/Graph client
factories, automated app-registration provisioning (delegated + application
permissions, admin consent), and a SessionManager seam that resolves the auth
model per profile.
- Scheduled reports: repositories, hosted service/runner/coordinator, report
pages, and email delivery (app-only Mail.Send).
- Tenant-wide user-access audit when no site is selected.
Audit fixes:
- Site enumeration: app-only discovery used Graph getAllSites (needs Graph
Sites.Read.All the cert app lacks) and silently returned empty. Switched to
the admin-host CSOM TenantSiteEnumerator, matching the scheduler; both auth
models now share one enumeration path.
- Group expansion: the scan records a SharePoint group as a single principal, so
user-centric audits found nothing for group-granted access. Resolve group
membership (shared by audit + scheduler) and attribute it to the target user.
- M365 group claims: the resolver only recognized AAD security groups
(c:0t.c|). Group-connected/Teams sites grant via the M365 group claim
(c:0o.c|…|<guid>[_o]); now expanded too, resolving owners for the "_o" claim.
- Provision Directory.Read.All as an application permission so M365/AAD group
expansion works under the cert identity.
Also: ignore data/appcerts/ (encrypted certificate key material).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add missing modal CSS (.modal-overlay/.modal-dialog/.modal-header):
the "Connect to Microsoft" auth modal was rendering unstyled inline
at the bottom of the page. Now a centered dialog with backdrop.
- Surface OAuth connect errors in the modal instead of silently
reopening it with no explanation.
- MainLayout: implement IDisposable so event handlers are actually
unsubscribed (Dispose existed but was never invoked).
- Wire up the Settings theme selector (was a dead control): drop the
unsupported Dark option, call sptb.setTheme on save and on load,
resolve System via prefers-color-scheme.
- Add branded 404 page via UseStatusCodePagesWithReExecute + Routes
<NotFound> (blank white page before).
- Add .progress-fill.indeterminate animation and .progress-panel.
- Home: replace inline JS hover handlers with a .feature-card CSS class.
- Define missing --surface-2 variable referenced by MainLayout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>