From 2a74395874cb36a9567ee5a61b00c45b061458bc Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Mon, 19 Oct 2026 17:32:55 +0200 Subject: [PATCH] Dead delegated-admin and repository members go: status transition, single-row reads, MCP-user flag None of them had a production caller. DelegatedAdminService.GetAssignmentsForUpnAsync and GetAssigneesForTenantAsync, DelegatedAdminService.SetStatusAsync with IAdminRepository.SetDelegatedAdminStatusAsync (only a test called it; the operator revoke is RemoveAsync), and IAdminRepository.GetDelegatedAdminAsync and IsMcpUserAsync, which only had their own implementation. Revoke_InvalidatesCache now pins the revoke the operator route runs (RemoveAsync); Disable_InvalidatesCache covers SetEnabledAsync. Co-Authored-By: Claude Opus 5.5 --- .../DelegatedAdminServiceTests.cs | 20 +++++- .../TableStorage/TableAdminRepository.cs | 61 ------------------- .../Services/DelegatedAdminService.cs | 18 ------ .../DataAccess/IAdminRepository.cs | 3 - 4 files changed, 17 insertions(+), 85 deletions(-) diff --git a/src/Backend/AutopilotMonitor.Functions.Tests/DelegatedAdminServiceTests.cs b/src/Backend/AutopilotMonitor.Functions.Tests/DelegatedAdminServiceTests.cs index a0554a4c..516969bd 100644 --- a/src/Backend/AutopilotMonitor.Functions.Tests/DelegatedAdminServiceTests.cs +++ b/src/Backend/AutopilotMonitor.Functions.Tests/DelegatedAdminServiceTests.cs @@ -249,13 +249,27 @@ public class DelegatedAdminServiceTests [Fact] public async Task Revoke_InvalidatesCache() { + // RemoveAsync is the revoke the operator route runs (DelegatedAdminManagementFunction.RevokeCoreAsync). var (svc, repo) = Build(); Returns(repo, Row(TenantA)); - repo.Setup(r => r.SetDelegatedAdminStatusAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(true); + repo.Setup(r => r.RemoveDelegatedAdminAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); await svc.GetScopeAsync(Id(Upn)); - await svc.SetStatusAsync(Upn, TenantA, Constants.DelegatedStatus.Revoked); + await svc.RemoveAsync(Upn, TenantA); + await svc.GetScopeAsync(Id(Upn)); + + repo.Verify(r => r.GetDelegatedTenantsAsync(It.IsAny()), Times.Exactly(2)); + } + + [Fact] + public async Task Disable_InvalidatesCache() + { + var (svc, repo) = Build(); + Returns(repo, Row(TenantA)); + repo.Setup(r => r.SetDelegatedAdminEnabledAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); + + await svc.GetScopeAsync(Id(Upn)); + await svc.SetEnabledAsync(Upn, TenantA, false); await svc.GetScopeAsync(Id(Upn)); repo.Verify(r => r.GetDelegatedTenantsAsync(It.IsAny()), Times.Exactly(2)); diff --git a/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableAdminRepository.cs b/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableAdminRepository.cs index 5b2ab641..221ac7d8 100644 --- a/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableAdminRepository.cs +++ b/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableAdminRepository.cs @@ -152,29 +152,6 @@ namespace AutopilotMonitor.Functions.DataAccess.TableStorage // --- MCP Users --- - public async Task IsMcpUserAsync(string upn) - { - if (string.IsNullOrWhiteSpace(upn)) - return false; - - try - { - var normalizedUpn = upn.ToLowerInvariant(); - var entity = await _mcpUsersTableClient.GetEntityAsync( - "McpUsers", normalizedUpn); - return entity?.Value != null && entity.Value.IsEnabled; - } - catch (RequestFailedException ex) when (ex.Status == 404) - { - return false; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error checking MCP user status for {Upn}", upn); - return false; - } - } - public async Task GetMcpUserAsync(string upn) { if (string.IsNullOrWhiteSpace(upn)) @@ -338,23 +315,6 @@ namespace AutopilotMonitor.Functions.DataAccess.TableStorage return entries; } - public async Task GetDelegatedAdminAsync(string upn, string tenantId) - { - if (string.IsNullOrWhiteSpace(upn) || string.IsNullOrWhiteSpace(tenantId)) - return null; - - try - { - var result = await _delegatedAdminsTableClient.GetEntityAsync( - upn.ToLowerInvariant(), tenantId.ToLowerInvariant()); - return result.Value == null ? null : MapToDelegatedEntry(result.Value); - } - catch (RequestFailedException ex) when (ex.Status == 404) - { - return null; - } - } - public async Task UpsertDelegatedAdminAsync( string upn, string tenantId, string role, string status, string source, string grantedBy) { @@ -380,27 +340,6 @@ namespace AutopilotMonitor.Functions.DataAccess.TableStorage return true; } - public async Task SetDelegatedAdminStatusAsync(string upn, string tenantId, string status) - { - upn = upn.ToLowerInvariant(); - tenantId = tenantId.ToLowerInvariant(); - - try - { - var result = await _delegatedAdminsTableClient.GetEntityAsync(upn, tenantId); - var entity = result.Value; - if (entity == null) return false; - - entity.Status = status; - await _delegatedAdminsTableClient.UpdateEntityAsync(entity, ETag.All); - return true; - } - catch (RequestFailedException ex) when (ex.Status == 404) - { - return false; - } - } - public async Task SetDelegatedAdminEnabledAsync(string upn, string tenantId, bool isEnabled) { upn = upn.ToLowerInvariant(); diff --git a/src/Backend/AutopilotMonitor.Functions/Services/DelegatedAdminService.cs b/src/Backend/AutopilotMonitor.Functions/Services/DelegatedAdminService.cs index 5e43b08b..d330c80c 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/DelegatedAdminService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/DelegatedAdminService.cs @@ -231,16 +231,6 @@ public class DelegatedAdminService }; } - /// Transitions an assignment's status (e.g. PendingApproval → Active on approval, → Revoked). - public async Task SetStatusAsync(string upn, string tenantId, string status) - { - upn = upn.ToLowerInvariant(); - tenantId = tenantId.ToLowerInvariant(); - var ok = await _adminRepo.SetDelegatedAdminStatusAsync(upn, tenantId, status); - Invalidate(upn); - return ok; - } - public async Task SetEnabledAsync(string upn, string tenantId, bool isEnabled) { upn = upn.ToLowerInvariant(); @@ -263,14 +253,6 @@ public class DelegatedAdminService public Task> GetAllAsync() => _adminRepo.GetAllDelegatedAdminsAsync(); - /// All assignment rows for a UPN (any status) — for the operator/admin management UI. - public Task> GetAssignmentsForUpnAsync(string upn) - => _adminRepo.GetDelegatedTenantsAsync(upn.ToLowerInvariant()); - - /// All assignment rows targeting a tenant (any status) — for the customer "who manages me?" UI. - public Task> GetAssigneesForTenantAsync(string tenantId) - => _adminRepo.GetDelegatedAssigneesAsync(tenantId.ToLowerInvariant()); - // --- Tenant Groups (app-internal tenant bundles) --- // // ALL group mutations go through the service (never the repo directly) so the delegated-scope diff --git a/src/Shared/AutopilotMonitor.Shared/DataAccess/IAdminRepository.cs b/src/Shared/AutopilotMonitor.Shared/DataAccess/IAdminRepository.cs index 4ae0de2f..8af1bb00 100644 --- a/src/Shared/AutopilotMonitor.Shared/DataAccess/IAdminRepository.cs +++ b/src/Shared/AutopilotMonitor.Shared/DataAccess/IAdminRepository.cs @@ -23,7 +23,6 @@ namespace AutopilotMonitor.Shared.DataAccess Task DisableGlobalAdminAsync(string upn); // --- MCP Users --- - Task IsMcpUserAsync(string upn); Task GetMcpUserAsync(string upn); Task> GetAllMcpUsersAsync(); Task AddMcpUserAsync(string upn, string addedBy); @@ -41,10 +40,8 @@ namespace AutopilotMonitor.Shared.DataAccess /// All assignment rows targeting one tenant (cross-partition RowKey scan — admin UI, not hot path). /// Powers the customer's "who manages my tenant?" view including pending requests. Task> GetDelegatedAssigneesAsync(string tenantId); - Task GetDelegatedAdminAsync(string upn, string tenantId); /// Creates or replaces an assignment row (upsert on PK=upn, RK=tenantId). Task UpsertDelegatedAdminAsync(string upn, string tenantId, string role, string status, string source, string grantedBy); - Task SetDelegatedAdminStatusAsync(string upn, string tenantId, string status); Task SetDelegatedAdminEnabledAsync(string upn, string tenantId, bool isEnabled); Task RemoveDelegatedAdminAsync(string upn, string tenantId);