using Microsoft.AspNetCore.Authorization; namespace ZB.MOM.WW.MxGateway.Server.Dashboard; /// /// Requirement satisfied ONLY by an authenticated principal holding one of /// as a real role claim. Unlike /// there is no environment bypass: it is a /// distinct requirement type, so — /// an AuthorizationHandler<DashboardAuthorizationRequirement> — is never invoked for /// it and its Authentication:Mode=Disabled / anonymous-localhost arms cannot reach it. /// The type distinction is the enforcement; adding a bypass arm to the other handler cannot leak /// into this one by accident. /// /// /// Introduced to gate the alarm feed (AlarmsHub and the /alarms page), /// which is gateway-central and session-less and therefore carries no per-session provenance the /// tag ACL could scope it by. Requiring a real dashboard role claim is the narrowing that /// is actually available: it keeps galaxy-wide alarm metadata — alarm reference (a full tag path), /// state, severity, category, area, acknowledging operator — away from the read-only environment /// bypasses, which exist for local development and unauthenticated deployments rather than as a /// grant of plant-wide visibility. The same pattern the shared Secrets UI relies on: a policy the /// dashboard bypass handler cannot satisfy because the requirement type differs. /// /// Roles any one of which satisfies the requirement. public sealed class AuthenticatedDashboardRoleRequirement(IReadOnlyList requiredRoles) : IAuthorizationRequirement { /// Gets the roles, any one of which satisfies this requirement. public IReadOnlyList RequiredRoles { get; } = requiredRoles; /// /// Gets the requirement satisfied by an authenticated caller holding either dashboard role. /// Same role set as ; the /// difference is entirely that no environment bypass applies. /// public static AuthenticatedDashboardRoleRequirement AnyDashboardRole { get; } = new([DashboardRoles.Admin, DashboardRoles.Viewer]); }