The role-grants page authored LdapGroupRoleMapping rows but nothing consumed them — sign-in only read the static appsettings GroupToRole dictionary. Wire the DB-backed grants into the auth path. - AdminRoleGrantResolver merges the static bootstrap dictionary (always fleet-wide, lock-out-proof) with DB grants; system-wide rows fold into fleet roles, cluster-scoped rows become (cluster, role) grants. - Login emits a ClaimTypes.Role claim per fleet role and a cluster_role claim per cluster-scoped grant; lock-out check spans both scopes. - ClusterRoleClaims + ClaimsPrincipal extensions resolve the effective role for a cluster (highest of fleet-wide and cluster-scoped). - ClusterAuthorizeView gates cluster pages: ClusterDetail (view + ConfigEditor draft actions), DraftEditor (ConfigEditor / FleetAdmin publish), DiffViewer (ConfigViewer), ImportEquipment (ConfigEditor). - RoleGrants page is now FleetAdmin-only; Account surfaces fleet-wide and cluster-scoped grants separately. Control-plane only — decision #150 holds, NodeAcl is untouched. Tests: AdminRoleGrantResolverTests + ClusterRoleClaimsTests (22). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.7 KiB
Plaintext
45 lines
1.7 KiB
Plaintext
@* Cluster-scoped counterpart of <AuthorizeView>. Renders Authorized/ChildContent only when the
|
|
signed-in user's effective role for ClusterId meets MinRole; otherwise renders NotAuthorized.
|
|
Effective role combines fleet-wide and cluster-scoped grants — see ClaimsPrincipalClusterExtensions. *@
|
|
@using System.Security.Claims
|
|
@using ZB.MOM.WW.OtOpcUa.Admin.Security
|
|
@using ZB.MOM.WW.OtOpcUa.Configuration.Enums
|
|
|
|
@if (_authorized)
|
|
{
|
|
@(Authorized ?? ChildContent)
|
|
}
|
|
else
|
|
{
|
|
@NotAuthorized
|
|
}
|
|
|
|
@code {
|
|
[CascadingParameter] private Task<AuthenticationState>? AuthState { get; set; }
|
|
|
|
/// <summary>Cluster the grant is evaluated against.</summary>
|
|
[Parameter, EditorRequired] public string ClusterId { get; set; } = string.Empty;
|
|
|
|
/// <summary>Minimum effective role required to render the authorized content.</summary>
|
|
[Parameter] public AdminRole MinRole { get; set; } = AdminRole.ConfigViewer;
|
|
|
|
/// <summary>Content shown when authorized (alias-friendly: use this or <see cref="ChildContent"/>).</summary>
|
|
[Parameter] public RenderFragment? Authorized { get; set; }
|
|
|
|
/// <summary>Default content slot — shown when authorized if <see cref="Authorized"/> is unset.</summary>
|
|
[Parameter] public RenderFragment? ChildContent { get; set; }
|
|
|
|
/// <summary>Content shown when the user lacks the required role; renders nothing when unset.</summary>
|
|
[Parameter] public RenderFragment? NotAuthorized { get; set; }
|
|
|
|
private bool _authorized;
|
|
|
|
protected override async Task OnParametersSetAsync()
|
|
{
|
|
_authorized = false;
|
|
if (AuthState is null) return;
|
|
var user = (await AuthState).User;
|
|
_authorized = user.HasClusterRole(ClusterId, MinRole);
|
|
}
|
|
}
|