diff --git a/src/Backend/AutopilotMonitor.Functions/Functions/AnalyzeRulesFunction.cs b/src/Backend/AutopilotMonitor.Functions/Functions/AnalyzeRulesFunction.cs index 02bcd566..eb064f20 100644 --- a/src/Backend/AutopilotMonitor.Functions/Functions/AnalyzeRulesFunction.cs +++ b/src/Backend/AutopilotMonitor.Functions/Functions/AnalyzeRulesFunction.cs @@ -198,11 +198,31 @@ namespace AutopilotMonitor.Functions.Functions return unauthorized; } - var success = await _ruleService.DeleteRuleAsync(tenantId, ruleId); + // Galactic Admins can delete built-in rules globally + var globalDelete = req.Url.Query.Contains("global=true", StringComparison.OrdinalIgnoreCase); + if (globalDelete) + { + 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 for global rule deletion" }); + return forbidden; + } - var response = req.CreateResponse(success ? HttpStatusCode.OK : HttpStatusCode.InternalServerError); - await response.WriteAsJsonAsync(new { success, message = success ? "Rule deleted" : "Failed to delete rule" }); - return response; + var success = await _ruleService.DeleteGlobalRuleAsync(ruleId); + var response = req.CreateResponse(success ? HttpStatusCode.OK : HttpStatusCode.InternalServerError); + await response.WriteAsJsonAsync(new { success, message = success ? "Global rule deleted" : "Failed to delete global rule" }); + return response; + } + else + { + var success = await _ruleService.DeleteRuleAsync(tenantId, ruleId); + var response = req.CreateResponse(success ? HttpStatusCode.OK : HttpStatusCode.InternalServerError); + await response.WriteAsJsonAsync(new { success, message = success ? "Rule deleted" : "Failed to delete rule" }); + return response; + } } } } diff --git a/src/Backend/AutopilotMonitor.Functions/Services/AnalyzeRuleService.cs b/src/Backend/AutopilotMonitor.Functions/Services/AnalyzeRuleService.cs index ee0eee9c..3e456a10 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/AnalyzeRuleService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/AnalyzeRuleService.cs @@ -121,13 +121,22 @@ namespace AutopilotMonitor.Functions.Services } /// - /// Deletes a custom analyze rule (cannot delete built-in rules) + /// Deletes a custom analyze rule for a tenant /// public async Task DeleteRuleAsync(string tenantId, string ruleId) { return await _storageService.DeleteAnalyzeRuleAsync(tenantId, ruleId); } + /// + /// Deletes a global built-in analyze rule (Galactic Admin only) + /// + public async Task DeleteGlobalRuleAsync(string ruleId) + { + _logger.LogInformation($"Deleting global analyze rule: {ruleId}"); + return await _storageService.DeleteAnalyzeRuleAsync("global", ruleId); + } + /// /// Re-imports all built-in analyze rules into the global partition. /// Deletes old global built-in rules and writes current code definitions. diff --git a/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx b/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx index 554ce070..91fc134e 100644 --- a/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx +++ b/src/Web/autopilot-monitor-web/app/analyze-rules/page.tsx @@ -285,7 +285,9 @@ export default function AnalyzeRulesPage() { throw new Error("Failed to get access token"); } - const response = await fetch(`${API_BASE_URL}/api/analyze-rules/${encodeURIComponent(rule.ruleId)}`, { + const isGlobalDelete = rule.isBuiltIn && isGalacticAdmin; + const url = `${API_BASE_URL}/api/analyze-rules/${encodeURIComponent(rule.ruleId)}${isGlobalDelete ? "?global=true" : ""}`; + const response = await fetch(url, { method: "DELETE", headers: { Authorization: `Bearer ${token}`, @@ -955,9 +957,9 @@ export default function AnalyzeRulesPage() { {rule.isBuiltIn ? "Edit (Global)" : "Edit"} )} - {!rule.isBuiltIn && ( - )}