mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 04:32:32 +00:00
impelented PolicyEnforcementMiddleware
This commit is contained in:
@@ -1 +1 @@
|
||||
325
|
||||
331
|
||||
|
||||
@@ -75,7 +75,8 @@ public class BuiltInRulesTests
|
||||
"policiesDiscovered", "ignoreCompletedApp", "imeAgentVersion",
|
||||
"espTrackStatus", "updateName", "updateWin32AppState",
|
||||
"cancelStuckAndSetCurrent", "imeSessionChange", "imeImpersonation",
|
||||
"enrollmentCompleted"
|
||||
"enrollmentCompleted", "updateDoTelemetry", "scriptStarted",
|
||||
"scriptContext", "scriptExitCode", "scriptOutput", "scriptCompleted"
|
||||
};
|
||||
|
||||
var patterns = BuiltInImeLogPatterns.GetAll();
|
||||
|
||||
+1
-7
@@ -97,13 +97,7 @@ public class AutopilotDeviceValidationConsentFunction
|
||||
|
||||
private async Task<HttpResponseData?> EnsureAuthorizedTenantAdminAsync(HttpRequestData req, string tenantId)
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { error = "Authentication required." });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var authenticatedTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
|
||||
@@ -15,16 +15,13 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
private readonly ILogger<DeviceBlockFunction> _logger;
|
||||
private readonly BlockedDeviceService _blockedDeviceService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public DeviceBlockFunction(
|
||||
ILogger<DeviceBlockFunction> logger,
|
||||
BlockedDeviceService blockedDeviceService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
BlockedDeviceService blockedDeviceService)
|
||||
{
|
||||
_logger = logger;
|
||||
_blockedDeviceService = blockedDeviceService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
/// <summary>GET /api/devices/blocked?tenantId={tenantId} — list all active blocks for a tenant</summary>
|
||||
@@ -34,12 +31,7 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
return await UnauthorizedAsync(req);
|
||||
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
return await ForbiddenAsync(req);
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var tenantId = req.Query["tenantId"];
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
@@ -67,12 +59,8 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
return await UnauthorizedAsync(req);
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
return await ForbiddenAsync(req);
|
||||
|
||||
string body;
|
||||
using (var reader = new System.IO.StreamReader(req.Body))
|
||||
@@ -137,12 +125,8 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
return await UnauthorizedAsync(req);
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
return await ForbiddenAsync(req);
|
||||
|
||||
var tenantId = req.Query["tenantId"];
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
@@ -175,20 +159,6 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
// Helpers
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
private static async Task<HttpResponseData> UnauthorizedAsync(HttpRequestData req)
|
||||
{
|
||||
var r = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await r.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return r;
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseData> ForbiddenAsync(HttpRequestData req)
|
||||
{
|
||||
var r = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await r.WriteAsJsonAsync(new { success = false, message = "Access denied. Only Galactic Admins can manage device blocks." });
|
||||
return r;
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseData> BadRequestAsync(HttpRequestData req, string message)
|
||||
{
|
||||
var r = req.CreateResponse(HttpStatusCode.BadRequest);
|
||||
|
||||
+2
-20
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using AutopilotMonitor.Functions.Helpers;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
@@ -13,16 +12,13 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
private readonly ILogger<GetAllBlockedDevicesFunction> _logger;
|
||||
private readonly BlockedDeviceService _blockedDeviceService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetAllBlockedDevicesFunction(
|
||||
ILogger<GetAllBlockedDevicesFunction> logger,
|
||||
BlockedDeviceService blockedDeviceService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
BlockedDeviceService blockedDeviceService)
|
||||
{
|
||||
_logger = logger;
|
||||
_blockedDeviceService = blockedDeviceService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
/// <summary>GET /api/galactic/devices/blocked — list all active blocks across all tenants</summary>
|
||||
@@ -34,21 +30,7 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userEmail))
|
||||
{
|
||||
_logger.LogWarning("Non-Galactic Admin user {User} attempted to access GetAllBlockedDevices", userEmail);
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { success = false, message = "Access denied. Galactic Admin role required." });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var blocked = await _blockedDeviceService.GetAllBlockedDevicesAsync();
|
||||
|
||||
|
||||
@@ -26,19 +26,7 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated GetAuditLogs attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
string tenantId = TenantHelper.GetTenantId(req);
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
|
||||
+2
-31
@@ -11,16 +11,13 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
private readonly ILogger<GetGalacticAuditLogsFunction> _logger;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetGalacticAuditLogsFunction(
|
||||
ILogger<GetGalacticAuditLogsFunction> logger,
|
||||
TableStorageService storageService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TableStorageService storageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_storageService = storageService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetGalacticAuditLogs")]
|
||||
@@ -31,34 +28,8 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated GetGalacticAuditLogs attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Check if user is Galactic Admin
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userEmail} attempted to access GetGalacticAuditLogs");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Fetching all audit logs across all tenants (User: {userEmail})");
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
private readonly AnalyzeRuleService _analyzeRuleService;
|
||||
private readonly ImeLogPatternService _imeLogPatternService;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public ReseedFromGitHubFunction(
|
||||
ILogger<ReseedFromGitHubFunction> logger,
|
||||
@@ -27,8 +26,7 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
GatherRuleService gatherRuleService,
|
||||
AnalyzeRuleService analyzeRuleService,
|
||||
ImeLogPatternService imeLogPatternService,
|
||||
TableStorageService storageService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TableStorageService storageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_gitHubRepo = gitHubRepo;
|
||||
@@ -36,7 +34,6 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
_analyzeRuleService = analyzeRuleService;
|
||||
_imeLogPatternService = imeLogPatternService;
|
||||
_storageService = storageService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("ReseedFromGitHub")]
|
||||
@@ -45,20 +42,8 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var upn = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(upn))
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Galactic Admin privileges required" });
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
// Parse ?type= parameter (default: all)
|
||||
var typeParam = "all";
|
||||
|
||||
+12
-30
@@ -41,11 +41,8 @@ public class TenantAdminManagementFunction
|
||||
string tenantId,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser()!;
|
||||
|
||||
var userTenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
@@ -84,11 +81,8 @@ public class TenantAdminManagementFunction
|
||||
string tenantId,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser()!;
|
||||
|
||||
var userTenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
@@ -140,11 +134,8 @@ public class TenantAdminManagementFunction
|
||||
string adminUpn,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser()!;
|
||||
|
||||
var userTenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
@@ -202,11 +193,8 @@ public class TenantAdminManagementFunction
|
||||
string adminUpn,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser()!;
|
||||
|
||||
var userTenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
@@ -247,11 +235,8 @@ public class TenantAdminManagementFunction
|
||||
string adminUpn,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser()!;
|
||||
|
||||
var userTenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
@@ -291,11 +276,8 @@ public class TenantAdminManagementFunction
|
||||
string adminUpn,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser()!;
|
||||
|
||||
var userTenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
|
||||
@@ -51,11 +51,8 @@ public class TenantOffboardFunction
|
||||
string tenantId,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
// Authentication enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser()!;
|
||||
|
||||
var userTenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
|
||||
+2
-32
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using AutopilotMonitor.Functions.Extensions;
|
||||
using AutopilotMonitor.Functions.Helpers;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
@@ -17,16 +16,13 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
private readonly ILogger<TriggerMaintenanceFunction> _logger;
|
||||
private readonly MaintenanceService _maintenanceService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public TriggerMaintenanceFunction(
|
||||
ILogger<TriggerMaintenanceFunction> logger,
|
||||
MaintenanceService maintenanceService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
MaintenanceService maintenanceService)
|
||||
{
|
||||
_logger = logger;
|
||||
_maintenanceService = maintenanceService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -43,34 +39,8 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
_logger.LogInformation("Manual maintenance trigger requested");
|
||||
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated maintenance trigger attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Check if user is Galactic Admin via GalacticAdminService (Azure Table Storage)
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userEmail} attempted to trigger maintenance");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Maintenance trigger initiated by Galactic Admin: {userEmail}");
|
||||
|
||||
|
||||
@@ -15,16 +15,13 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
private readonly ILogger<VersionBlockFunction> _logger;
|
||||
private readonly BlockedVersionService _blockedVersionService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public VersionBlockFunction(
|
||||
ILogger<VersionBlockFunction> logger,
|
||||
BlockedVersionService blockedVersionService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
BlockedVersionService blockedVersionService)
|
||||
{
|
||||
_logger = logger;
|
||||
_blockedVersionService = blockedVersionService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
/// <summary>GET /api/versions/blocked — list all active version block rules</summary>
|
||||
@@ -34,12 +31,7 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
return await UnauthorizedAsync(req);
|
||||
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
return await ForbiddenAsync(req);
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var rules = await _blockedVersionService.GetBlockedVersionsAsync();
|
||||
|
||||
@@ -63,12 +55,8 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
return await UnauthorizedAsync(req);
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
return await ForbiddenAsync(req);
|
||||
|
||||
string body;
|
||||
using (var reader = new System.IO.StreamReader(req.Body))
|
||||
@@ -130,12 +118,8 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
return await UnauthorizedAsync(req);
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
return await ForbiddenAsync(req);
|
||||
|
||||
var versionPattern = Uri.UnescapeDataString(encodedPattern ?? string.Empty);
|
||||
if (string.IsNullOrEmpty(versionPattern))
|
||||
@@ -164,20 +148,6 @@ namespace AutopilotMonitor.Functions.Functions.Admin
|
||||
// Helpers
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
private static async Task<HttpResponseData> UnauthorizedAsync(HttpRequestData req)
|
||||
{
|
||||
var r = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await r.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return r;
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseData> ForbiddenAsync(HttpRequestData req)
|
||||
{
|
||||
var r = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await r.WriteAsJsonAsync(new { success = false, message = "Access denied. Only Galactic Admins can manage version blocks." });
|
||||
return r;
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseData> BadRequestAsync(HttpRequestData req, string message)
|
||||
{
|
||||
var r = req.CreateResponse(HttpStatusCode.BadRequest);
|
||||
|
||||
+2
-21
@@ -21,20 +21,17 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
private readonly ILogger<CreateBootstrapSessionFunction> _logger;
|
||||
private readonly BootstrapSessionService _bootstrapService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly TenantConfigurationService _configService;
|
||||
|
||||
public CreateBootstrapSessionFunction(
|
||||
ILogger<CreateBootstrapSessionFunction> logger,
|
||||
BootstrapSessionService bootstrapService,
|
||||
GalacticAdminService galacticAdminService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
TenantConfigurationService configService)
|
||||
{
|
||||
_logger = logger;
|
||||
_bootstrapService = bootstrapService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
@@ -44,13 +41,7 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauth = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauth.WriteAsJsonAsync(new { error = "Authentication required" });
|
||||
return unauth;
|
||||
}
|
||||
|
||||
// Authentication + BootstrapManagerOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var authenticatedTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
@@ -70,7 +61,7 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
// Use tenant from JWT if not specified in body
|
||||
var tenantId = !string.IsNullOrEmpty(request.TenantId) ? request.TenantId : authenticatedTenantId;
|
||||
|
||||
// Tenant boundary check
|
||||
// Cross-tenant boundary check — only Galactic Admins may operate on other tenants
|
||||
if (!string.Equals(authenticatedTenantId, tenantId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var isGalactic = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
@@ -82,16 +73,6 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
}
|
||||
}
|
||||
|
||||
// Admin or Operator with bootstrap permission check
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var canManageBootstrap = await _tenantAdminsService.CanManageBootstrapAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !canManageBootstrap)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { error = "Bootstrap token management access required" });
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
// Check if bootstrap token feature is enabled for this tenant
|
||||
var tenantConfig = await _configService.GetConfigurationAsync(tenantId);
|
||||
if (!tenantConfig.BootstrapTokenEnabled)
|
||||
|
||||
+2
-21
@@ -20,20 +20,17 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
private readonly ILogger<ListBootstrapSessionsFunction> _logger;
|
||||
private readonly BootstrapSessionService _bootstrapService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly TenantConfigurationService _configService;
|
||||
|
||||
public ListBootstrapSessionsFunction(
|
||||
ILogger<ListBootstrapSessionsFunction> logger,
|
||||
BootstrapSessionService bootstrapService,
|
||||
GalacticAdminService galacticAdminService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
TenantConfigurationService configService)
|
||||
{
|
||||
_logger = logger;
|
||||
_bootstrapService = bootstrapService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
@@ -43,19 +40,13 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauth = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauth.WriteAsJsonAsync(new { error = "Authentication required" });
|
||||
return unauth;
|
||||
}
|
||||
|
||||
// Authentication + BootstrapManagerOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var authenticatedTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
var tenantId = req.Query["tenantId"] ?? authenticatedTenantId;
|
||||
|
||||
// Tenant boundary check
|
||||
// Cross-tenant boundary check — only Galactic Admins may operate on other tenants
|
||||
if (!string.Equals(authenticatedTenantId, tenantId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var isGalactic = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
@@ -67,16 +58,6 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
}
|
||||
}
|
||||
|
||||
// Admin or Operator with bootstrap permission check
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var canManageBootstrap = await _tenantAdminsService.CanManageBootstrapAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !canManageBootstrap)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { error = "Bootstrap token management access required" });
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
// Check if bootstrap token feature is enabled for this tenant
|
||||
var tenantConfig = await _configService.GetConfigurationAsync(tenantId);
|
||||
if (!tenantConfig.BootstrapTokenEnabled)
|
||||
|
||||
+2
-21
@@ -18,20 +18,17 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
private readonly ILogger<RevokeBootstrapSessionFunction> _logger;
|
||||
private readonly BootstrapSessionService _bootstrapService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly TenantConfigurationService _configService;
|
||||
|
||||
public RevokeBootstrapSessionFunction(
|
||||
ILogger<RevokeBootstrapSessionFunction> logger,
|
||||
BootstrapSessionService bootstrapService,
|
||||
GalacticAdminService galacticAdminService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
TenantConfigurationService configService)
|
||||
{
|
||||
_logger = logger;
|
||||
_bootstrapService = bootstrapService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
@@ -42,19 +39,13 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauth = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauth.WriteAsJsonAsync(new { error = "Authentication required" });
|
||||
return unauth;
|
||||
}
|
||||
|
||||
// Authentication + BootstrapManagerOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var authenticatedTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
var tenantId = req.Query["tenantId"] ?? authenticatedTenantId;
|
||||
|
||||
// Tenant boundary check
|
||||
// Cross-tenant boundary check — only Galactic Admins may operate on other tenants
|
||||
if (!string.Equals(authenticatedTenantId, tenantId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var isGalactic = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
@@ -66,16 +57,6 @@ namespace AutopilotMonitor.Functions.Functions.Bootstrap
|
||||
}
|
||||
}
|
||||
|
||||
// Admin or Operator with bootstrap permission check
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var canManageBootstrap = await _tenantAdminsService.CanManageBootstrapAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !canManageBootstrap)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { error = "Bootstrap token management access required" });
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
// Check if bootstrap token feature is enabled for this tenant
|
||||
var tenantConfig = await _configService.GetConfigurationAsync(tenantId);
|
||||
if (!tenantConfig.BootstrapTokenEnabled)
|
||||
|
||||
+2
-32
@@ -13,16 +13,13 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
private readonly ILogger<GetAdminConfigurationFunction> _logger;
|
||||
private readonly AdminConfigurationService _adminConfigService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetAdminConfigurationFunction(
|
||||
ILogger<GetAdminConfigurationFunction> logger,
|
||||
AdminConfigurationService adminConfigService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
AdminConfigurationService adminConfigService)
|
||||
{
|
||||
_logger = logger;
|
||||
_adminConfigService = adminConfigService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetAdminConfiguration")]
|
||||
@@ -31,36 +28,9 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated GetAdminConfiguration attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Get tenant ID and user identifier from JWT token
|
||||
string tenantId = TenantHelper.GetTenantId(req);
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Validate Galactic Admin role
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userIdentifier} from tenant {tenantId} attempted to access admin configuration");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Only Galactic Admins can access global configuration."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"GetAdminConfiguration by Galactic Admin user {userIdentifier}");
|
||||
|
||||
var config = await _adminConfigService.GetConfigurationAsync();
|
||||
|
||||
+2
-33
@@ -13,16 +13,13 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
private readonly ILogger<GetAllTenantConfigurationsFunction> _logger;
|
||||
private readonly TenantConfigurationService _configService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetAllTenantConfigurationsFunction(
|
||||
ILogger<GetAllTenantConfigurationsFunction> logger,
|
||||
TenantConfigurationService configService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TenantConfigurationService configService)
|
||||
{
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetAllTenantConfigurations")]
|
||||
@@ -31,37 +28,9 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated GetAllTenantConfigurations attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Get user identifier
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Check if user is Galactic Admin
|
||||
bool isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
_logger.LogWarning($"User {userIdentifier} attempted to access all tenant configurations without Galactic Admin privileges");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. This endpoint requires Galactic Admin privileges."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"GetAllTenantConfigurations by Galactic Admin {userIdentifier}");
|
||||
|
||||
var configurations = await _configService.GetAllConfigurationsAsync();
|
||||
|
||||
+19
-24
@@ -13,13 +13,16 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
private readonly ILogger<GetTenantConfigurationFunction> _logger;
|
||||
private readonly TenantConfigurationService _configService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetTenantConfigurationFunction(
|
||||
ILogger<GetTenantConfigurationFunction> logger,
|
||||
TenantConfigurationService configService)
|
||||
TenantConfigurationService configService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
{
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetTenantConfiguration")]
|
||||
@@ -29,34 +32,26 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning($"Unauthenticated GetTenantConfiguration attempt for tenant {tenantId}");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Get tenant ID from JWT token and validate access
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
string authenticatedTenantId = TenantHelper.GetTenantId(req);
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Validate tenant access: User can only access their own tenant's configuration
|
||||
if (authenticatedTenantId != tenantId)
|
||||
// Validate tenant access: cross-tenant only for Galactic Admins
|
||||
if (!string.Equals(authenticatedTenantId, tenantId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_logger.LogWarning($"User {userIdentifier} from tenant {authenticatedTenantId} attempted to access configuration for tenant {tenantId}");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. You can only access your own tenant's configuration."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
_logger.LogWarning("User {User} from tenant {AuthTenant} attempted to access configuration for tenant {TargetTenant}",
|
||||
userIdentifier, authenticatedTenantId, tenantId);
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. You can only access your own tenant's configuration."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation($"GetTenantConfiguration: {tenantId} by user {userIdentifier}");
|
||||
|
||||
+2
-32
@@ -16,16 +16,13 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
private readonly ILogger<UpdateAdminConfigurationFunction> _logger;
|
||||
private readonly AdminConfigurationService _adminConfigService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public UpdateAdminConfigurationFunction(
|
||||
ILogger<UpdateAdminConfigurationFunction> logger,
|
||||
AdminConfigurationService adminConfigService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
AdminConfigurationService adminConfigService)
|
||||
{
|
||||
_logger = logger;
|
||||
_adminConfigService = adminConfigService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("UpdateAdminConfiguration")]
|
||||
@@ -34,36 +31,9 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated UpdateAdminConfiguration attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Get tenant ID and user identifier from JWT token
|
||||
string tenantId = TenantHelper.GetTenantId(req);
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Validate Galactic Admin role
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(userIdentifier))
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userIdentifier} from tenant {tenantId} attempted to update admin configuration");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Only Galactic Admins can modify global configuration."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"UpdateAdminConfiguration by Galactic Admin user {userIdentifier}");
|
||||
|
||||
// Parse request body
|
||||
|
||||
+9
-36
@@ -16,18 +16,15 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
private readonly ILogger<UpdateTenantConfigurationFunction> _logger;
|
||||
private readonly TenantConfigurationService _configService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public UpdateTenantConfigurationFunction(
|
||||
ILogger<UpdateTenantConfigurationFunction> logger,
|
||||
TenantConfigurationService configService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
{
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
@@ -38,27 +35,18 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning($"Unauthenticated UpdateTenantConfiguration attempt for tenant {tenantId}");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Get tenant ID from JWT token and validate access
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
string authenticatedTenantId = TenantHelper.GetTenantId(req);
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Validate tenant access: User can only update their own tenant's configuration
|
||||
if (authenticatedTenantId != tenantId)
|
||||
// GA check needed for: cross-tenant bypass + protected field filtering
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
|
||||
// Validate tenant access: cross-tenant only for Galactic Admins
|
||||
if (!isGalacticAdmin && !string.Equals(authenticatedTenantId, tenantId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_logger.LogWarning($"User {userIdentifier} from tenant {authenticatedTenantId} attempted to update configuration for tenant {tenantId}");
|
||||
_logger.LogWarning("User {User} from tenant {AuthTenant} attempted to update configuration for tenant {TargetTenant}",
|
||||
userIdentifier, authenticatedTenantId, tenantId);
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
@@ -68,22 +56,7 @@ namespace AutopilotMonitor.Functions.Functions.Config
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for configuration changes
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-admin user {userIdentifier} attempted to update configuration for tenant {tenantId}");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Tenant Admin or Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"UpdateTenantConfiguration: {tenantId} by user {userIdentifier}");
|
||||
_logger.LogInformation("UpdateTenantConfiguration: {TenantId} by user {User}", tenantId, userIdentifier);
|
||||
|
||||
// Parse request body
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues)
|
||||
|
||||
+1
-8
@@ -34,14 +34,7 @@ namespace AutopilotMonitor.Functions.Functions.Diagnostics
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var userTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
|
||||
@@ -217,21 +217,7 @@ public class AuthFunction
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "auth/galactic-admins")] HttpRequestData req,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
var isAdmin = await _galacticAdminService.IsGalacticAdminAsync(upn);
|
||||
|
||||
if (!isAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { error = "Only Galactic Admins can access this endpoint" });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var admins = await _galacticAdminService.GetAllGalacticAdminsAsync();
|
||||
|
||||
@@ -250,21 +236,9 @@ public class AuthFunction
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "auth/galactic-admins")] HttpRequestData req,
|
||||
FunctionContext context)
|
||||
{
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
var currentUpn = principal.GetUserPrincipalName();
|
||||
var isAdmin = await _galacticAdminService.IsGalacticAdminAsync(currentUpn);
|
||||
|
||||
if (!isAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { error = "Only Galactic Admins can add other admins" });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
var currentUpn = principal?.GetUserPrincipalName();
|
||||
|
||||
// Parse request body
|
||||
var body = await req.ReadFromJsonAsync<AddGalacticAdminRequest>();
|
||||
@@ -295,21 +269,9 @@ public class AuthFunction
|
||||
string upn,
|
||||
FunctionContext context)
|
||||
{
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
var currentUpn = principal.GetUserPrincipalName();
|
||||
var isAdmin = await _galacticAdminService.IsGalacticAdminAsync(currentUpn);
|
||||
|
||||
if (!isAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { error = "Only Galactic Admins can remove other admins" });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
var currentUpn = principal?.GetUserPrincipalName();
|
||||
|
||||
// Prevent self-removal
|
||||
if (upn.Equals(currentUpn, StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
+2
-36
@@ -1,6 +1,4 @@
|
||||
using System.Net;
|
||||
using AutopilotMonitor.Functions.Extensions;
|
||||
using AutopilotMonitor.Functions.Helpers;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
@@ -12,16 +10,13 @@ namespace AutopilotMonitor.Functions.Functions.Infrastructure
|
||||
{
|
||||
private readonly ILogger<HealthCheckFunction> _logger;
|
||||
private readonly HealthCheckService _healthCheckService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public HealthCheckFunction(
|
||||
ILogger<HealthCheckFunction> logger,
|
||||
HealthCheckService healthCheckService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
HealthCheckService healthCheckService)
|
||||
{
|
||||
_logger = logger;
|
||||
_healthCheckService = healthCheckService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,36 +52,7 @@ namespace AutopilotMonitor.Functions.Functions.Infrastructure
|
||||
{
|
||||
_logger.LogInformation("Detailed health check requested");
|
||||
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated detailed health check attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Check if user is Galactic Admin via GalacticAdminService (Azure Table Storage)
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userEmail} attempted to access detailed health check");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Detailed health check accessed by Galactic Admin: {userEmail}");
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
// Perform comprehensive health checks
|
||||
var healthCheckResult = await _healthCheckService.PerformAllChecksAsync();
|
||||
|
||||
+1
-8
@@ -28,14 +28,7 @@ namespace AutopilotMonitor.Functions.Functions.Infrastructure
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated AddToGroup attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return new AddToGroupOutput { HttpResponse = unauthorizedResponse };
|
||||
}
|
||||
// Authentication + AuthenticatedUser authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
// Parse request
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues)
|
||||
|
||||
+1
-8
@@ -28,14 +28,7 @@ namespace AutopilotMonitor.Functions.Functions.Infrastructure
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated RemoveFromGroup attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return new RemoveFromGroupOutput { HttpResponse = unauthorizedResponse };
|
||||
}
|
||||
// Authentication + AuthenticatedUser authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
// Parse request
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues)
|
||||
|
||||
@@ -24,14 +24,7 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
_logger.LogInformation($"Fetching app metrics for tenant {tenantId}");
|
||||
|
||||
|
||||
+2
-19
@@ -11,16 +11,13 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
private readonly ILogger<GetGalacticAppMetricsFunction> _logger;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetGalacticAppMetricsFunction(
|
||||
ILogger<GetGalacticAppMetricsFunction> logger,
|
||||
TableStorageService storageService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TableStorageService storageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_storageService = storageService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetGalacticAppMetrics")]
|
||||
@@ -29,22 +26,8 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { success = false, message = "Access denied. Galactic Admin role required." });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Fetching galactic app metrics (User: {userEmail})");
|
||||
|
||||
|
||||
+2
-19
@@ -11,16 +11,13 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
private readonly ILogger<GetGalacticGeographicMetricsFunction> _logger;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetGalacticGeographicMetricsFunction(
|
||||
ILogger<GetGalacticGeographicMetricsFunction> logger,
|
||||
TableStorageService storageService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TableStorageService storageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_storageService = storageService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetGalacticGeographicMetrics")]
|
||||
@@ -29,22 +26,8 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { success = false, message = "Access denied. Galactic Admin role required." });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Fetching galactic geographic metrics (User: {UserEmail})", userEmail);
|
||||
|
||||
|
||||
+2
-35
@@ -1,5 +1,4 @@
|
||||
using System.Net;
|
||||
using AutopilotMonitor.Functions.Helpers;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
@@ -15,16 +14,13 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
private readonly ILogger<GetGalacticPlatformMetricsFunction> _logger;
|
||||
private readonly PlatformMetricsService _platformMetricsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetGalacticPlatformMetricsFunction(
|
||||
ILogger<GetGalacticPlatformMetricsFunction> logger,
|
||||
PlatformMetricsService platformMetricsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
PlatformMetricsService platformMetricsService)
|
||||
{
|
||||
_logger = logger;
|
||||
_platformMetricsService = platformMetricsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetGalacticPlatformMetrics")]
|
||||
@@ -35,36 +31,7 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated platform metrics attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Check if user is Galactic Admin
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userEmail} attempted to access platform metrics");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Platform agent metrics accessed by Galactic Admin: {userEmail}");
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var metrics = await _platformMetricsService.ComputePlatformMetricsAsync();
|
||||
|
||||
|
||||
+1
-7
@@ -27,13 +27,7 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
_logger.LogInformation("Fetching geographic metrics for tenant {TenantId}", tenantId);
|
||||
|
||||
|
||||
+2
-35
@@ -1,5 +1,4 @@
|
||||
using System.Net;
|
||||
using AutopilotMonitor.Functions.Helpers;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
@@ -14,16 +13,13 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
{
|
||||
private readonly ILogger<PlatformUsageMetricsFunction> _logger;
|
||||
private readonly UsageMetricsService _usageMetricsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public PlatformUsageMetricsFunction(
|
||||
ILogger<PlatformUsageMetricsFunction> logger,
|
||||
UsageMetricsService usageMetricsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
UsageMetricsService usageMetricsService)
|
||||
{
|
||||
_logger = logger;
|
||||
_usageMetricsService = usageMetricsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,36 +35,7 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated platform usage metrics attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Check if user is Galactic Admin via GalacticAdminService (Azure Table Storage)
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userEmail} attempted to access platform usage metrics");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Platform usage metrics accessed by Galactic Admin: {userEmail}");
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var metrics = await _usageMetricsService.ComputeUsageMetricsAsync();
|
||||
|
||||
|
||||
@@ -36,19 +36,7 @@ namespace AutopilotMonitor.Functions.Functions.Metrics
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated UsageMetrics attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
string tenantId = TenantHelper.GetTenantId(req);
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
|
||||
+2
-27
@@ -41,19 +41,7 @@ public class ProgressPortalFunction
|
||||
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token.",
|
||||
count = 0,
|
||||
sessions = Array.Empty<object>()
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + AuthenticatedUser authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
_logger.LogInformation("ProgressGetSessions: Fetching sessions for tenant {TenantId}", tenantId);
|
||||
@@ -106,20 +94,7 @@ public class ProgressPortalFunction
|
||||
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token.",
|
||||
sessionId = sessionId,
|
||||
count = 0,
|
||||
events = Array.Empty<object>()
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + AuthenticatedUser authorization enforced by PolicyEnforcementMiddleware
|
||||
var userTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
|
||||
+1
-19
@@ -1,7 +1,6 @@
|
||||
using System.Net;
|
||||
using System.Web;
|
||||
using Azure.Storage.Blobs;
|
||||
using AutopilotMonitor.Functions.Helpers;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
@@ -17,17 +16,14 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
public class GetSessionReportDownloadUrlFunction
|
||||
{
|
||||
private readonly ILogger<GetSessionReportDownloadUrlFunction> _logger;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
private readonly string _blobConnectionString;
|
||||
private const string ContainerName = "session-reports";
|
||||
|
||||
public GetSessionReportDownloadUrlFunction(
|
||||
ILogger<GetSessionReportDownloadUrlFunction> logger,
|
||||
GalacticAdminService galacticAdminService,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_logger = logger;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
_blobConnectionString = configuration["AzureBlobStorageConnectionString"]
|
||||
?? throw new InvalidOperationException("AzureBlobStorageConnectionString is not configured");
|
||||
}
|
||||
@@ -38,21 +34,7 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { success = false, message = "Access denied. Galactic Admin role required." });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var query = HttpUtility.ParseQueryString(req.Url.Query);
|
||||
var blobName = query["blobName"];
|
||||
|
||||
+2
-31
@@ -1,5 +1,4 @@
|
||||
using System.Net;
|
||||
using AutopilotMonitor.Functions.Helpers;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
@@ -11,16 +10,13 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
{
|
||||
private readonly ILogger<GetSessionReportsFunction> _logger;
|
||||
private readonly SessionReportService _sessionReportService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetSessionReportsFunction(
|
||||
ILogger<GetSessionReportsFunction> logger,
|
||||
SessionReportService sessionReportService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
SessionReportService sessionReportService)
|
||||
{
|
||||
_logger = logger;
|
||||
_sessionReportService = sessionReportService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetSessionReports")]
|
||||
@@ -31,32 +27,7 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Require Galactic Admin only (this is admin-config data)
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var reports = await _sessionReportService.GetAllReportsAsync();
|
||||
|
||||
|
||||
+2
-29
@@ -13,7 +13,6 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
private readonly ILogger<SubmitSessionReportFunction> _logger;
|
||||
private readonly SessionReportService _sessionReportService;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
private readonly TelegramNotificationService _telegramNotificationService;
|
||||
|
||||
@@ -21,14 +20,12 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
ILogger<SubmitSessionReportFunction> logger,
|
||||
SessionReportService sessionReportService,
|
||||
TableStorageService storageService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
GalacticAdminService galacticAdminService,
|
||||
TelegramNotificationService telegramNotificationService)
|
||||
{
|
||||
_logger = logger;
|
||||
_sessionReportService = sessionReportService;
|
||||
_storageService = storageService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
_telegramNotificationService = telegramNotificationService;
|
||||
}
|
||||
@@ -42,36 +39,12 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated session report attempt for session {SessionId}", sessionId);
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
string tenantId = TenantHelper.GetTenantId(req);
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Require Tenant Admin or Galactic Admin
|
||||
// Still needed: GA check for audit log skip logic
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
_logger.LogWarning("Non-admin user {UserIdentifier} attempted to submit session report for {SessionId}", userIdentifier, sessionId);
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Tenant Admin or Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
// Parse request body
|
||||
var request = await req.ReadFromJsonAsync<SubmitSessionReportRequest>();
|
||||
|
||||
+2
-18
@@ -16,16 +16,13 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
{
|
||||
private readonly ILogger<UpdateSessionReportNoteFunction> _logger;
|
||||
private readonly SessionReportService _sessionReportService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public UpdateSessionReportNoteFunction(
|
||||
ILogger<UpdateSessionReportNoteFunction> logger,
|
||||
SessionReportService sessionReportService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
SessionReportService sessionReportService)
|
||||
{
|
||||
_logger = logger;
|
||||
_sessionReportService = sessionReportService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("UpdateSessionReportNote")]
|
||||
@@ -35,21 +32,8 @@ namespace AutopilotMonitor.Functions.Functions.Reports
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new { success = false, message = "Authentication required." });
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new { success = false, message = "Access denied. Galactic Admin role required." });
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(reportId))
|
||||
{
|
||||
|
||||
@@ -16,32 +16,21 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
{
|
||||
private readonly ILogger<AnalyzeRulesFunction> _logger;
|
||||
private readonly AnalyzeRuleService _ruleService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public AnalyzeRulesFunction(
|
||||
ILogger<AnalyzeRulesFunction> logger,
|
||||
AnalyzeRuleService ruleService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
AnalyzeRuleService ruleService)
|
||||
{
|
||||
_logger = logger;
|
||||
_ruleService = ruleService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetAnalyzeRules")]
|
||||
public async Task<HttpResponseData> GetRules(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "rules/analyze")] HttpRequestData req)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
var rules = await _ruleService.GetAllRulesForTenantAsync(tenantId);
|
||||
|
||||
@@ -54,24 +43,8 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
public async Task<HttpResponseData> CreateRule(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "rules/analyze")] HttpRequestData req)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for rule modifications
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Access denied. Tenant Admin or Galactic Admin role required." });
|
||||
return forbidden;
|
||||
}
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues)
|
||||
&& long.TryParse(clValues.FirstOrDefault(), out var contentLength)
|
||||
@@ -103,24 +76,8 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "rules/analyze/{ruleId}")] HttpRequestData req,
|
||||
string ruleId)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for rule modifications
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Access denied. Tenant Admin or Galactic Admin role required." });
|
||||
return forbidden;
|
||||
}
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues2)
|
||||
&& long.TryParse(clValues2.FirstOrDefault(), out var contentLength2)
|
||||
@@ -153,24 +110,8 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "rules/analyze/{ruleId}")] HttpRequestData req,
|
||||
string ruleId)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for rule modifications
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Access denied. Tenant Admin or Galactic Admin role required." });
|
||||
return forbidden;
|
||||
}
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
// Load the rule to determine its type (built-in/community vs. custom)
|
||||
var rules = await _ruleService.GetAllRulesForTenantAsync(tenantId);
|
||||
|
||||
@@ -16,32 +16,21 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
{
|
||||
private readonly ILogger<GatherRulesFunction> _logger;
|
||||
private readonly GatherRuleService _ruleService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GatherRulesFunction(
|
||||
ILogger<GatherRulesFunction> logger,
|
||||
GatherRuleService ruleService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
GatherRuleService ruleService)
|
||||
{
|
||||
_logger = logger;
|
||||
_ruleService = ruleService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetGatherRules")]
|
||||
public async Task<HttpResponseData> GetRules(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "rules/gather")] HttpRequestData req)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
var rules = await _ruleService.GetAllRulesForTenantAsync(tenantId);
|
||||
|
||||
@@ -54,24 +43,8 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
public async Task<HttpResponseData> CreateRule(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "rules/gather")] HttpRequestData req)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for rule modifications
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Access denied. Tenant Admin or Galactic Admin role required." });
|
||||
return forbidden;
|
||||
}
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues)
|
||||
&& long.TryParse(clValues.FirstOrDefault(), out var contentLength)
|
||||
@@ -103,24 +76,8 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "rules/gather/{ruleId}")] HttpRequestData req,
|
||||
string ruleId)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for rule modifications
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Access denied. Tenant Admin or Galactic Admin role required." });
|
||||
return forbidden;
|
||||
}
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues2)
|
||||
&& long.TryParse(clValues2.FirstOrDefault(), out var contentLength2)
|
||||
@@ -153,24 +110,8 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "rules/gather/{ruleId}")] HttpRequestData req,
|
||||
string ruleId)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for rule modifications
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Access denied. Tenant Admin or Galactic Admin role required." });
|
||||
return forbidden;
|
||||
}
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
// Load the rule to determine its type (built-in/community vs. custom)
|
||||
var rules = await _ruleService.GetAllRulesForTenantAsync(tenantId);
|
||||
|
||||
@@ -37,13 +37,7 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "sessions/{sessionId}/analysis")] HttpRequestData req,
|
||||
string sessionId)
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var userTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
|
||||
@@ -17,26 +17,19 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
{
|
||||
private readonly ILogger<ImeLogPatternsFunction> _logger;
|
||||
private readonly ImeLogPatternService _patternService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public ImeLogPatternsFunction(ILogger<ImeLogPatternsFunction> logger, ImeLogPatternService patternService, GalacticAdminService galacticAdminService)
|
||||
public ImeLogPatternsFunction(ILogger<ImeLogPatternsFunction> logger, ImeLogPatternService patternService)
|
||||
{
|
||||
_logger = logger;
|
||||
_patternService = patternService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetImeLogPatterns")]
|
||||
public async Task<HttpResponseData> GetPatterns(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "rules/ime-log-patterns")] HttpRequestData req)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
var patterns = await _patternService.GetAllPatternsForTenantAsync(tenantId);
|
||||
|
||||
@@ -50,29 +43,14 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "rules/ime-log-patterns/{patternId}")] HttpRequestData req,
|
||||
string patternId)
|
||||
{
|
||||
var tenantId = TenantHelper.IsAuthenticated(req) ? TenantHelper.GetTenantId(req) : null;
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
// Phase 1: Only global edits by Galactic Admins
|
||||
// Phase 1: Only global edits
|
||||
var globalEdit = req.Url.Query.Contains("global=true", StringComparison.OrdinalIgnoreCase);
|
||||
if (!globalEdit)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "IME log pattern edits require ?global=true and Galactic Admin privileges" });
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
var upn = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalactic = await _galacticAdminService.IsGalacticAdminAsync(upn);
|
||||
if (!isGalactic)
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Galactic Admin privileges required" });
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "IME log pattern edits require ?global=true" });
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
@@ -108,20 +86,8 @@ namespace AutopilotMonitor.Functions.Functions.Rules
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorized.WriteAsJsonAsync(new { success = false, message = "Authentication required" });
|
||||
return unauthorized;
|
||||
}
|
||||
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var upn = TenantHelper.GetUserIdentifier(req);
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(upn))
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { success = false, message = "Galactic Admin privileges required" });
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Reseed IME log patterns triggered by Galactic Admin {upn}");
|
||||
|
||||
|
||||
@@ -17,20 +17,17 @@ public class PreviewWhitelistFunction
|
||||
{
|
||||
private readonly ILogger<PreviewWhitelistFunction> _logger;
|
||||
private readonly PreviewWhitelistService _previewWhitelistService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
private readonly TenantConfigurationService _tenantConfigurationService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
|
||||
public PreviewWhitelistFunction(
|
||||
ILogger<PreviewWhitelistFunction> logger,
|
||||
PreviewWhitelistService previewWhitelistService,
|
||||
GalacticAdminService galacticAdminService,
|
||||
TenantConfigurationService tenantConfigurationService,
|
||||
TenantAdminsService tenantAdminsService)
|
||||
{
|
||||
_logger = logger;
|
||||
_previewWhitelistService = previewWhitelistService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
_tenantConfigurationService = tenantConfigurationService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
}
|
||||
@@ -45,17 +42,7 @@ public class PreviewWhitelistFunction
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "preview/whitelist")] HttpRequestData req,
|
||||
FunctionContext context)
|
||||
{
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(upn))
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { error = "Only Galactic Admins can access this endpoint" });
|
||||
return forbidden;
|
||||
}
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
var approved = await _previewWhitelistService.GetAllApprovedAsync();
|
||||
|
||||
@@ -75,17 +62,9 @@ public class PreviewWhitelistFunction
|
||||
string tenantId,
|
||||
FunctionContext context)
|
||||
{
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(upn))
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { error = "Only Galactic Admins can approve tenants" });
|
||||
return forbidden;
|
||||
}
|
||||
var upn = principal?.GetUserPrincipalName();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
@@ -156,17 +135,9 @@ public class PreviewWhitelistFunction
|
||||
string tenantId,
|
||||
FunctionContext context)
|
||||
{
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var principal = context.GetUser();
|
||||
if (principal == null)
|
||||
return req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
if (!await _galacticAdminService.IsGalacticAdminAsync(upn))
|
||||
{
|
||||
var forbidden = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbidden.WriteAsJsonAsync(new { error = "Only Galactic Admins can revoke tenant access" });
|
||||
return forbidden;
|
||||
}
|
||||
var upn = principal?.GetUserPrincipalName();
|
||||
|
||||
await _previewWhitelistService.RevokeAsync(tenantId);
|
||||
|
||||
|
||||
@@ -11,19 +11,13 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
{
|
||||
private readonly ILogger<DeleteSessionFunction> _logger;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public DeleteSessionFunction(
|
||||
ILogger<DeleteSessionFunction> logger,
|
||||
TableStorageService storageService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TableStorageService storageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_storageService = storageService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("DeleteSession")]
|
||||
@@ -35,37 +29,10 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning($"Unauthenticated DeleteSession attempt for session {sessionId}");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for destructive operations
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-admin user {userIdentifier} attempted to delete session {sessionId}");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Tenant Admin or Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Deleting session {sessionId} for tenant {tenantId} by user {userIdentifier}");
|
||||
|
||||
// Delete all related data for this session
|
||||
|
||||
+2
-31
@@ -11,16 +11,13 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
{
|
||||
private readonly ILogger<GetAllSessionsFunction> _logger;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public GetAllSessionsFunction(
|
||||
ILogger<GetAllSessionsFunction> logger,
|
||||
TableStorageService storageService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TableStorageService storageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_storageService = storageService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("GetAllSessions")]
|
||||
@@ -31,34 +28,8 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication and Galactic Admin role
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated GetAllSessions attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Check if user is Galactic Admin via GalacticAdminService (Azure Table Storage)
|
||||
// Authentication + GalacticAdminOnly authorization enforced by PolicyEnforcementMiddleware
|
||||
var userEmail = TenantHelper.GetUserIdentifier(req);
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userEmail);
|
||||
|
||||
if (!isGalacticAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-Galactic Admin user {userEmail} attempted to access GetAllSessions");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Galactic Admin role required."
|
||||
});
|
||||
return forbiddenResponse;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Fetching all sessions across all tenants (User: {userEmail})");
|
||||
|
||||
|
||||
+1
-15
@@ -41,21 +41,7 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning($"{sessionPrefix} Unauthenticated GetSessionEvents attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token.",
|
||||
sessionId = sessionId,
|
||||
count = 0,
|
||||
events = Array.Empty<object>()
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
|
||||
// Get user's tenant ID and identifier from JWT
|
||||
var userTenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
@@ -44,18 +44,7 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
|
||||
try
|
||||
{
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning($"{sessionPrefix} Unauthenticated GetSession attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var userTenantId = TenantHelper.GetTenantId(req);
|
||||
var userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
var query = HttpUtility.ParseQueryString(req.Url.Query);
|
||||
|
||||
@@ -26,21 +26,7 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning("Unauthenticated GetSessions attempt");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token.",
|
||||
count = 0,
|
||||
sessions = Array.Empty<object>()
|
||||
});
|
||||
return unauthorizedResponse;
|
||||
}
|
||||
|
||||
// Authentication + MemberRead authorization enforced by PolicyEnforcementMiddleware
|
||||
var tenantId = TenantHelper.GetTenantId(req);
|
||||
|
||||
_logger.LogInformation($"Fetching sessions for tenant {tenantId}");
|
||||
|
||||
+2
-35
@@ -13,19 +13,13 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
{
|
||||
private readonly ILogger<MarkSessionFailedFunction> _logger;
|
||||
private readonly TableStorageService _storageService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
public MarkSessionFailedFunction(
|
||||
ILogger<MarkSessionFailedFunction> logger,
|
||||
TableStorageService storageService,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
TableStorageService storageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_storageService = storageService;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
[Function("MarkSessionFailed")]
|
||||
@@ -37,37 +31,10 @@ namespace AutopilotMonitor.Functions.Functions.Sessions
|
||||
|
||||
try
|
||||
{
|
||||
// Validate authentication
|
||||
if (!TenantHelper.IsAuthenticated(req))
|
||||
{
|
||||
_logger.LogWarning($"Unauthenticated MarkSessionFailed attempt for session {sessionId}");
|
||||
var unauthorizedResponse = req.CreateResponse(HttpStatusCode.Unauthorized);
|
||||
await unauthorizedResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
return new MarkSessionFailedOutput { HttpResponse = unauthorizedResponse };
|
||||
}
|
||||
|
||||
// Authentication + TenantAdminOrGA authorization enforced by PolicyEnforcementMiddleware
|
||||
string tenantId = TenantHelper.GetTenantId(req);
|
||||
string userIdentifier = TenantHelper.GetUserIdentifier(req);
|
||||
|
||||
// Require Tenant Admin or Galactic Admin for status modifications
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(userIdentifier);
|
||||
var isTenantAdmin = await _tenantAdminsService.IsTenantAdminAsync(tenantId, userIdentifier);
|
||||
if (!isGalacticAdmin && !isTenantAdmin)
|
||||
{
|
||||
_logger.LogWarning($"Non-admin user {userIdentifier} attempted to mark session {sessionId} as failed");
|
||||
var forbiddenResponse = req.CreateResponse(HttpStatusCode.Forbidden);
|
||||
await forbiddenResponse.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
message = "Access denied. Tenant Admin or Galactic Admin role required."
|
||||
});
|
||||
return new MarkSessionFailedOutput { HttpResponse = forbiddenResponse };
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Marking session {sessionId} as failed for tenant {tenantId} by user {userIdentifier}");
|
||||
|
||||
// Update session status to Failed with manual failure reason
|
||||
|
||||
@@ -9,27 +9,6 @@ namespace AutopilotMonitor.Functions.Helpers;
|
||||
/// </summary>
|
||||
public static class TenantHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the request has an authenticated user
|
||||
/// Azure Functions Isolated Worker compatible
|
||||
/// </summary>
|
||||
/// <param name="req">The HTTP request</param>
|
||||
/// <returns>True if user is authenticated, false otherwise</returns>
|
||||
public static bool IsAuthenticated(HttpRequestData req)
|
||||
{
|
||||
// Azure Functions Isolated Worker: Try FunctionContext.Items first
|
||||
// This is set by AuthenticationMiddleware and is more reliable than httpContext.User
|
||||
if (req.FunctionContext.Items.TryGetValue("ClaimsPrincipal", out var principalObj)
|
||||
&& principalObj is ClaimsPrincipal principal)
|
||||
{
|
||||
return principal.Identity?.IsAuthenticated == true;
|
||||
}
|
||||
|
||||
// Fallback to HTTP context (may not work reliably in isolated worker)
|
||||
var httpContext = req.FunctionContext.GetHttpContext();
|
||||
return httpContext?.User?.Identity?.IsAuthenticated == true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the tenant ID from the authenticated user's JWT token claims.
|
||||
/// Uses the Azure AD tenant ID claim which identifies which customer/organization owns the data.
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using AutopilotMonitor.Functions.Extensions;
|
||||
using AutopilotMonitor.Functions.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Middleware;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AutopilotMonitor.Functions.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// Middleware that enforces tenant membership (Admin or Operator role) on protected routes.
|
||||
/// Runs after AuthenticationMiddleware. Non-members receive 403 and can only use the Progress Portal.
|
||||
/// </summary>
|
||||
public class MemberAuthorizationMiddleware : IFunctionsWorkerMiddleware
|
||||
{
|
||||
private readonly ILogger<MemberAuthorizationMiddleware> _logger;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
|
||||
/// <summary>
|
||||
/// Route prefixes that are exempt from the member check.
|
||||
/// These routes either have their own authorization, use device auth, or are open to any tenant user.
|
||||
/// </summary>
|
||||
private static readonly string[] _exemptPrefixes =
|
||||
{
|
||||
"/api/health", // Anonymous health check
|
||||
"/api/stats/", // Anonymous platform stats
|
||||
"/api/agent/", // Device cert auth, no JWT
|
||||
"/api/bootstrap/", // Bootstrap routes — device routes have no JWT, management routes have inline auth
|
||||
"/api/auth/", // Must work for any authenticated user (role info)
|
||||
"/api/progress/", // Progress Portal — open to any tenant user
|
||||
"/api/realtime/", // SignalR — tenant isolation via group validation
|
||||
"/api/galactic/", // Galactic Admin routes — have own GA checks
|
||||
"/api/global/", // Galactic Admin config — has own GA check
|
||||
"/api/versions/", // Galactic Admin — has own GA check
|
||||
"/api/preview/", // Preview whitelist — has own GA check
|
||||
};
|
||||
|
||||
public MemberAuthorizationMiddleware(
|
||||
ILogger<MemberAuthorizationMiddleware> logger,
|
||||
TenantAdminsService tenantAdminsService,
|
||||
GalacticAdminService galacticAdminService)
|
||||
{
|
||||
_logger = logger;
|
||||
_tenantAdminsService = tenantAdminsService;
|
||||
_galacticAdminService = galacticAdminService;
|
||||
}
|
||||
|
||||
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
|
||||
{
|
||||
var httpContext = context.GetHttpContext();
|
||||
if (httpContext == null)
|
||||
{
|
||||
// Non-HTTP trigger (e.g. timer, queue) — no auth needed
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
var requestPath = httpContext.Request.Path.Value ?? string.Empty;
|
||||
|
||||
// Skip exempt routes
|
||||
if (IsExemptRoute(requestPath))
|
||||
{
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the ClaimsPrincipal set by AuthenticationMiddleware
|
||||
if (!context.Items.TryGetValue("ClaimsPrincipal", out var principalObj)
|
||||
|| principalObj is not ClaimsPrincipal principal
|
||||
|| principal.Identity?.IsAuthenticated != true)
|
||||
{
|
||||
// Not authenticated — AuthenticationMiddleware will have already returned 401
|
||||
// or this is an anonymous route handled above. Just pass through.
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
var tenantId = principal.GetTenantId();
|
||||
var upn = principal.GetUserPrincipalName();
|
||||
|
||||
if (string.IsNullOrEmpty(tenantId) || string.IsNullOrEmpty(upn))
|
||||
{
|
||||
// Missing claims — let the function handle it
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Galactic Admins bypass member check
|
||||
var isGalacticAdmin = await _galacticAdminService.IsGalacticAdminAsync(upn);
|
||||
if (isGalacticAdmin)
|
||||
{
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user is a tenant member (Admin or Operator)
|
||||
var isMember = await _tenantAdminsService.IsTenantMemberAsync(tenantId, upn);
|
||||
if (isMember)
|
||||
{
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-member — block access
|
||||
_logger.LogWarning("[MemberAuth] Non-member {Upn} (tenant {TenantId}) blocked from {Path}", upn, tenantId, requestPath);
|
||||
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
httpContext.Response.ContentType = "application/json";
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
error = "MembershipRequired",
|
||||
message = "Access denied. You must be an Admin or Operator for this tenant. Use the Progress Portal for session monitoring."
|
||||
});
|
||||
}
|
||||
|
||||
private static bool IsExemptRoute(string path)
|
||||
{
|
||||
foreach (var prefix in _exemptPrefixes)
|
||||
{
|
||||
if (path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+51
-46
@@ -12,22 +12,22 @@ using Microsoft.Extensions.Logging;
|
||||
namespace AutopilotMonitor.Functions.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// Logging-only middleware that evaluates the EndpointAccessPolicyCatalog against each request
|
||||
/// and compares its decision with the actual pipeline outcome. Never blocks requests.
|
||||
/// Policy enforcement middleware that evaluates the EndpointAccessPolicyCatalog against each request
|
||||
/// and blocks requests that don't meet the required policy. Fail-closed: unregistered routes are denied.
|
||||
///
|
||||
/// Designed for Phase 2 of the auth refactor: deploy to production, observe mismatches,
|
||||
/// then switch to enforcement in Phase 3.
|
||||
/// Phase 3 of the auth refactor: replaces both PolicyAuditMiddleware (logging-only) and
|
||||
/// MemberAuthorizationMiddleware (coarse member check) with catalog-driven enforcement.
|
||||
///
|
||||
/// Middleware order: AuthenticationMiddleware → PolicyAuditMiddleware → MemberAuthorizationMiddleware
|
||||
/// Middleware order: AuthenticationMiddleware → PolicyEnforcementMiddleware → Function
|
||||
/// </summary>
|
||||
public class PolicyAuditMiddleware : IFunctionsWorkerMiddleware
|
||||
public class PolicyEnforcementMiddleware : IFunctionsWorkerMiddleware
|
||||
{
|
||||
private readonly ILogger<PolicyAuditMiddleware> _logger;
|
||||
private readonly ILogger<PolicyEnforcementMiddleware> _logger;
|
||||
private readonly GalacticAdminService _galacticAdminService;
|
||||
private readonly TenantAdminsService _tenantAdminsService;
|
||||
|
||||
public PolicyAuditMiddleware(
|
||||
ILogger<PolicyAuditMiddleware> logger,
|
||||
public PolicyEnforcementMiddleware(
|
||||
ILogger<PolicyEnforcementMiddleware> logger,
|
||||
GalacticAdminService galacticAdminService,
|
||||
TenantAdminsService tenantAdminsService)
|
||||
{
|
||||
@@ -41,6 +41,7 @@ public class PolicyAuditMiddleware : IFunctionsWorkerMiddleware
|
||||
var httpContext = context.GetHttpContext();
|
||||
if (httpContext == null)
|
||||
{
|
||||
// Non-HTTP trigger (e.g. timer, queue) — no auth needed
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
@@ -53,53 +54,57 @@ public class PolicyAuditMiddleware : IFunctionsWorkerMiddleware
|
||||
|
||||
if (catalogEntry == null)
|
||||
{
|
||||
_logger.LogWarning("[PolicyAudit] UNREGISTERED route: {Method} {Path} — not in catalog (fail-closed)",
|
||||
// Fail-closed: unregistered route → 403
|
||||
_logger.LogError("[PolicyEnforcement] BLOCKED unregistered route: {Method} {Path} — not in catalog (fail-closed)",
|
||||
httpMethod, requestPath);
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
httpContext.Response.ContentType = "application/json";
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
error = "Forbidden",
|
||||
message = "Access denied."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Evaluate the catalog policy for the current user
|
||||
var decision = await EvaluateCatalogPolicyAsync(context, catalogEntry);
|
||||
|
||||
if (decision.IsAllowed)
|
||||
{
|
||||
_logger.LogDebug("[PolicyEnforcement] ALLOW {Method} {Path} policy={Policy} user={User} role={Role}",
|
||||
httpMethod, requestPath, catalogEntry.Policy, decision.UserIdentifier, decision.UserRole);
|
||||
await next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Evaluate what the catalog WOULD decide for the current user
|
||||
var catalogDecision = await EvaluateCatalogPolicyAsync(context, catalogEntry);
|
||||
// Denied — determine 401 vs 403
|
||||
var statusCode = decision.Reason is "NoJWT" or "MissingClaims"
|
||||
? HttpStatusCode.Unauthorized
|
||||
: HttpStatusCode.Forbidden;
|
||||
|
||||
// Let the rest of the pipeline run (MemberAuth + function)
|
||||
await next(context);
|
||||
_logger.LogWarning("[PolicyEnforcement] DENIED {Method} {Path} policy={Policy} status={Status} user={User} role={Role} reason={Reason}",
|
||||
httpMethod, requestPath, catalogEntry.Policy, (int)statusCode,
|
||||
decision.UserIdentifier, decision.UserRole, decision.Reason);
|
||||
|
||||
// Compare catalog decision against actual outcome
|
||||
var actualStatusCode = httpContext.Response.StatusCode;
|
||||
var isAuthFailure = actualStatusCode == (int)HttpStatusCode.Unauthorized
|
||||
|| actualStatusCode == (int)HttpStatusCode.Forbidden;
|
||||
var actuallyAllowed = !isAuthFailure;
|
||||
httpContext.Response.StatusCode = (int)statusCode;
|
||||
httpContext.Response.ContentType = "application/json";
|
||||
|
||||
if (catalogDecision.IsAllowed == actuallyAllowed)
|
||||
if (statusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
// Decisions match — no mismatch
|
||||
_logger.LogDebug("[PolicyAudit] OK {Method} {Path} policy={Policy} catalogDecision={Decision} actualStatus={Status}",
|
||||
httpMethod, requestPath, catalogEntry.Policy, catalogDecision.IsAllowed ? "Allow" : "Deny", actualStatusCode);
|
||||
return;
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
error = "AuthenticationRequired",
|
||||
message = "Authentication required. Please provide a valid JWT token."
|
||||
});
|
||||
}
|
||||
|
||||
// Mismatch detected
|
||||
if (catalogDecision.IsAllowed && !actuallyAllowed)
|
||||
else
|
||||
{
|
||||
// Catalog says Allow but pipeline denied → pipeline is MORE restrictive than catalog
|
||||
// Expected for Viewer role (MemberAuth blocks, catalog would allow MemberRead)
|
||||
_logger.LogWarning(
|
||||
"[PolicyAudit] MISMATCH(TooRestrictive) {Method} {Path} " +
|
||||
"policy={Policy} catalogDecision=Allow actualStatus={Status} " +
|
||||
"user={User} role={Role} reason={Reason}",
|
||||
httpMethod, requestPath, catalogEntry.Policy, actualStatusCode,
|
||||
catalogDecision.UserIdentifier, catalogDecision.UserRole, catalogDecision.Reason);
|
||||
}
|
||||
else if (!catalogDecision.IsAllowed && actuallyAllowed)
|
||||
{
|
||||
// Catalog says Deny but pipeline allowed → SECURITY CONCERN: pipeline is too permissive
|
||||
_logger.LogError(
|
||||
"[PolicyAudit] MISMATCH(TooPermissive) {Method} {Path} " +
|
||||
"policy={Policy} catalogDecision=Deny actualStatus={Status} " +
|
||||
"user={User} role={Role} reason={Reason}",
|
||||
httpMethod, requestPath, catalogEntry.Policy, actualStatusCode,
|
||||
catalogDecision.UserIdentifier, catalogDecision.UserRole, catalogDecision.Reason);
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
error = "InsufficientPermissions",
|
||||
message = "Access denied. You do not have permission to access this resource."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +167,7 @@ public class PolicyAuditMiddleware : IFunctionsWorkerMiddleware
|
||||
if (role == null)
|
||||
return CatalogDecisionResult.Deny(userIdentifier, "NonMember", "NotInTenant");
|
||||
|
||||
// MemberRead allows Admin, Operator, AND Viewer (future Phase 3 behavior)
|
||||
// MemberRead allows Admin, Operator, AND Viewer
|
||||
return CatalogDecisionResult.Allow(userIdentifier, role.Role ?? "Admin", "TenantMember");
|
||||
}
|
||||
|
||||
@@ -19,10 +19,9 @@ var builder = FunctionsApplication.CreateBuilder(args);
|
||||
builder.ConfigureFunctionsWebApplication();
|
||||
|
||||
// Register middleware pipeline (Azure Functions .NET 8 isolated worker pattern)
|
||||
// Order matters: authentication → policy audit (logging-only) → member authorization
|
||||
// Order matters: authentication (401) → policy enforcement (403)
|
||||
builder.UseMiddleware<AuthenticationMiddleware>();
|
||||
builder.UseMiddleware<PolicyAuditMiddleware>();
|
||||
builder.UseMiddleware<MemberAuthorizationMiddleware>();
|
||||
builder.UseMiddleware<PolicyEnforcementMiddleware>();
|
||||
|
||||
// Configure JSON serialization to use camelCase
|
||||
builder.Services.Configure<JsonSerializerOptions>(options =>
|
||||
|
||||
Reference in New Issue
Block a user