using System.Net; using AutopilotMonitor.Functions.Extensions; using AutopilotMonitor.Functions.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; namespace AutopilotMonitor.Functions.Functions; /// /// CRUD endpoints for managing the Private Preview tenant whitelist. /// All endpoints are Galactic Admin only. /// Temporary — remove after GA. /// public class PreviewWhitelistFunction { private readonly ILogger _logger; private readonly PreviewWhitelistService _previewWhitelistService; private readonly GalacticAdminService _galacticAdminService; public PreviewWhitelistFunction( ILogger logger, PreviewWhitelistService previewWhitelistService, GalacticAdminService galacticAdminService) { _logger = logger; _previewWhitelistService = previewWhitelistService; _galacticAdminService = galacticAdminService; } /// /// GET /api/preview-whitelist /// Returns all approved tenants. /// [Function("GetPreviewWhitelist")] [Authorize] public async Task GetAll( [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; } var approved = await _previewWhitelistService.GetAllApprovedAsync(); var response = req.CreateResponse(HttpStatusCode.OK); await response.WriteAsJsonAsync(new { tenants = approved }); return response; } /// /// POST /api/preview-whitelist/{tenantId} /// Approves a tenant for Private Preview. /// [Function("ApprovePreviewTenant")] [Authorize] public async Task Approve( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "preview-whitelist/{tenantId}")] HttpRequestData req, string tenantId, 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 approve tenants" }); return forbidden; } if (string.IsNullOrWhiteSpace(tenantId)) { var bad = req.CreateResponse(HttpStatusCode.BadRequest); await bad.WriteAsJsonAsync(new { error = "tenantId is required" }); return bad; } await _previewWhitelistService.ApproveAsync(tenantId, upn!); _logger.LogInformation("Preview tenant approved: {TenantId} by {Upn}", tenantId, upn); var response = req.CreateResponse(HttpStatusCode.Created); await response.WriteAsJsonAsync(new { message = "Tenant approved for preview", tenantId }); return response; } /// /// DELETE /api/preview-whitelist/{tenantId} /// Revokes a tenant's Private Preview access. /// [Function("RevokePreviewTenant")] [Authorize] public async Task Revoke( [HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "preview-whitelist/{tenantId}")] HttpRequestData req, string tenantId, 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 revoke tenant access" }); return forbidden; } await _previewWhitelistService.RevokeAsync(tenantId); _logger.LogInformation("Preview tenant revoked: {TenantId} by {Upn}", tenantId, upn); var response = req.CreateResponse(HttpStatusCode.OK); await response.WriteAsJsonAsync(new { message = "Tenant removed from preview", tenantId }); return response; } }