mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-26 21:47:13 +00:00
2d3853c38f
GlobalReader = cross-tenant READ visibility like a Global Admin, but no mutating operations and no platform settings. Provisioned exactly like a GA: a GlobalAdmins table row with Role=GlobalReader (empty/legacy Role = GlobalAdmin, back-compat). Semantics are ADDITIVE — a user who is also their own tenant's Admin keeps edit rights on that tenant; a pure GlobalReader is read-only everywhere. Backend: - GlobalAdmins.Role column + GlobalAdminService.GetGlobalRoleAsync; IsGlobalAdminAsync (incl. TableAdminRepository) delegates so a GlobalReader row never reads back as admin - RequestContext.IsGlobalReader + HasGlobalScope - Split GlobalAdminOnly into GlobalReadOrAdmin (cross-tenant reads) vs GlobalAdminOnly (writes/settings) + TenantAdminOrGlobalReader for config-read - PolicyEnforcementMiddleware: extracted testable DecideAsync seam; cross-tenant gate keys off HasGlobalScope; additive own-tenant role carried so IsTenantAdmin stays correct - Config secret redaction for readers (RedactedCopyForReader) + save-time placeholder restore (RestoreRedactedSecretsFrom) so a redacted view can't blank real secrets - raw table/log tools stay GlobalAdminOnly; SignalR global-admins read group admits HasGlobalScope - /auth/me emits isGlobalReader (+ preview-gate bypass, no auto-admin); /auth/mcp emits globalRole MCP: hasGlobalScope (GA or Reader) drives routing + tool catalog; raw secret-bearing tools (query_table/list_tables/query_backend_logs) gated strict-GA only. Web: AuthContext isGlobalReader + hasGlobalScope; reader reaches /dashboard + cross-tenant views; platform Settings/Ops/Backups/Software GA-only (nav-hidden + requireGlobalAdmin route layouts); mutating controls on mixed data pages disabled via useCanMutatePlatform; rules pages read-only on foreign tenants; additive own-tenant edit preserved. Tests: backend 2369, MCP 252, web 469 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
67 lines
2.5 KiB
C#
67 lines
2.5 KiB
C#
using System.Net;
|
|
using AutopilotMonitor.Functions.Helpers;
|
|
using AutopilotMonitor.Shared.DataAccess;
|
|
using Microsoft.Azure.Functions.Worker;
|
|
using Microsoft.Azure.Functions.Worker.Http;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace AutopilotMonitor.Functions.Functions.Metrics
|
|
{
|
|
/// <summary>
|
|
/// Returns all known IME agent versions with first/last seen dates and session counts.
|
|
/// Permanent archive that survives data retention — tracks Microsoft IME releases over time.
|
|
/// Access: MemberRead (any tenant member can see version/dates/counts).
|
|
/// FirstSeenTenantId and FirstSeenSessionId are only visible to Global Admins.
|
|
/// </summary>
|
|
public class GetImeVersionHistoryFunction
|
|
{
|
|
private readonly ILogger<GetImeVersionHistoryFunction> _logger;
|
|
private readonly ISessionRepository _sessionRepo;
|
|
|
|
public GetImeVersionHistoryFunction(ILogger<GetImeVersionHistoryFunction> logger, ISessionRepository sessionRepo)
|
|
{
|
|
_logger = logger;
|
|
_sessionRepo = sessionRepo;
|
|
}
|
|
|
|
[Function("GetImeVersionHistory")]
|
|
public async Task<HttpResponseData> Run(
|
|
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "metrics/ime-versions")] HttpRequestData req)
|
|
{
|
|
try
|
|
{
|
|
var requestCtx = req.GetRequestContext();
|
|
var versions = await _sessionRepo.GetImeVersionHistoryAsync();
|
|
|
|
var response = req.CreateResponse(HttpStatusCode.OK);
|
|
|
|
if (requestCtx.HasGlobalScope)
|
|
{
|
|
await response.WriteAsJsonAsync(versions);
|
|
}
|
|
else
|
|
{
|
|
// Strip sensitive cross-tenant data for non-Global Admins
|
|
var redacted = versions.Select(v => new
|
|
{
|
|
v.Version,
|
|
v.FirstSeenAt,
|
|
v.LastSeenAt,
|
|
v.SessionCount
|
|
});
|
|
await response.WriteAsJsonAsync(redacted);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error fetching IME version history");
|
|
var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError);
|
|
await errorResponse.WriteAsJsonAsync(new { error = "Failed to retrieve IME version history" });
|
|
return errorResponse;
|
|
}
|
|
}
|
|
}
|
|
}
|