c1619d95f5
Standardize the control-plane admin role VALUES on the canonical six
(ZB.MOM.WW.Auth CanonicalRole). OtOpcUa uses four:
ConfigViewer -> Viewer
ConfigEditor -> Designer
FleetAdmin -> Administrator
DriverOperator -> Operator (appsettings-only string role)
This is a rename, not a permission change: enforcement semantics are
preserved (whoever could deploy/administer/operate before still can).
- AdminRole enum members renamed (persisted as string names via
HasConversion<string>); RoleGrants.razor dropdown default updated.
- EF DATA migration CanonicalizeAdminRoles rewrites existing
LdapGroupRoleMapping.Role rows old->new (Up) and back (Down); schema /
model snapshot byte-identical (no pending model changes).
- Enforcement role STRINGS canonicalized:
* Security policies keep their NAMES ("DriverOperator"/"FleetAdmin")
but require canonical roles: RequireRole("Operator","Administrator")
and RequireRole("Administrator").
* Deployments.razor [Authorize(Roles="Administrator,Designer")].
* DevStub now grants "Administrator"; LdapOptions/doc-comment examples
canonicalized.
- Data-plane authorization (NodePermissions/NodeAcl/IPermissionEvaluator/
TriePermissionEvaluator/UserAuthorizationState) UNTOUCHED.
- New CanonicalAdminRolesTests pins canonical claim values end-to-end and
the real registered policies; existing role-string tests updated.
107 lines
5.2 KiB
C#
107 lines
5.2 KiB
C#
using ZB.MOM.WW.Auth.Abstractions.Ldap;
|
|
using LibLdapOptions = ZB.MOM.WW.Auth.Abstractions.Ldap.LdapOptions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Security.Ldap;
|
|
|
|
/// <summary>
|
|
/// LDAP + role-mapping configuration for the Admin UI. Bound from <c>appsettings.json</c>
|
|
/// <c>Security:Ldap</c> section. Defaults point at the local GLAuth dev instance (see
|
|
/// <c>C:\publish\glauth\auth.md</c>).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Carries both the wire fields the shared <c>ZB.MOM.WW.Auth.Ldap</c> directory client needs
|
|
/// (<see cref="Server"/>/<see cref="Port"/>/<see cref="Transport"/>/…) AND the app-only concerns
|
|
/// the shared library has no notion of (<see cref="Enabled"/> master switch,
|
|
/// <see cref="DevStubMode"/> dev bypass, <see cref="GroupToRole"/> appsettings role baseline).
|
|
/// The app wrapper (<c>OtOpcUaLdapAuthService</c>) projects this onto the library's
|
|
/// <see cref="LibLdapOptions"/> at construction; see <see cref="ToLibraryOptions"/>.
|
|
/// </remarks>
|
|
public sealed class LdapOptions
|
|
{
|
|
public const string SectionName = "Security:Ldap";
|
|
|
|
/// <summary>Gets or sets a value indicating whether LDAP authentication is enabled.</summary>
|
|
public bool Enabled { get; set; } = true;
|
|
|
|
/// <summary>Gets or sets the LDAP server hostname.</summary>
|
|
public string Server { get; set; } = "localhost";
|
|
|
|
/// <summary>Gets or sets the LDAP server port.</summary>
|
|
public int Port { get; set; } = 3893;
|
|
|
|
/// <summary>
|
|
/// Transport security for the LDAP connection — <see cref="LdapTransport.Ldaps"/> (implicit
|
|
/// TLS), <see cref="LdapTransport.StartTls"/> (upgrade), or <see cref="LdapTransport.None"/>
|
|
/// (plaintext, dev/test only — requires <see cref="AllowInsecure"/>). Replaces the former
|
|
/// <c>UseTls</c> bool (Task 1.4): <c>true</c>→<see cref="LdapTransport.Ldaps"/>,
|
|
/// <c>false</c>→<see cref="LdapTransport.None"/>.
|
|
/// </summary>
|
|
public LdapTransport Transport { get; set; } = LdapTransport.None;
|
|
|
|
/// <summary>Dev-only escape hatch — must be <c>false</c> in production. Maps to the shared
|
|
/// library's <see cref="LibLdapOptions.AllowInsecure"/> (renamed from <c>AllowInsecureLdap</c>).</summary>
|
|
public bool AllowInsecure { get; set; }
|
|
|
|
/// <summary>
|
|
/// Dev-only stub: when <c>true</c>, <see cref="OtOpcUaLdapAuthService"/> bypasses the real LDAP
|
|
/// bind and accepts any non-empty username/password, returning a single Administrator role
|
|
/// so the operator can navigate the full Admin UI. MUST be <c>false</c> in production.
|
|
/// </summary>
|
|
public bool DevStubMode { get; set; }
|
|
|
|
/// <summary>Gets or sets the LDAP search base DN.</summary>
|
|
public string SearchBase { get; set; } = "dc=zb,dc=local";
|
|
|
|
/// <summary>
|
|
/// Service-account DN used for search-then-bind. When empty, a direct-bind with
|
|
/// <c>cn={user},{SearchBase}</c> is attempted.
|
|
/// </summary>
|
|
public string ServiceAccountDn { get; set; } = string.Empty;
|
|
|
|
/// <summary>Gets or sets the service account password for LDAP authentication.</summary>
|
|
public string ServiceAccountPassword { get; set; } = string.Empty;
|
|
|
|
/// <summary>Gets or sets the LDAP attribute name for user display name.</summary>
|
|
public string DisplayNameAttribute { get; set; } = "cn";
|
|
|
|
/// <summary>Gets or sets the LDAP attribute name for group membership.</summary>
|
|
public string GroupAttribute { get; set; } = "memberOf";
|
|
|
|
/// <summary>
|
|
/// Attribute the service-account search matches the login name against to resolve the
|
|
/// user's DN. <c>cn</c> for GLAuth (the dev default); set <c>sAMAccountName</c> for
|
|
/// Active Directory.
|
|
/// </summary>
|
|
public string UserNameAttribute { get; set; } = "cn";
|
|
|
|
/// <summary>
|
|
/// Maps LDAP group name → Admin role. Group match is case-insensitive. A user gets every
|
|
/// role whose source group is in their membership list. Values are the canonical control-plane
|
|
/// roles (Task 1.7). Example dev mapping:
|
|
/// <code>"ReadOnly":"Viewer","ReadWrite":"Designer","AlarmAck":"Administrator"</code>
|
|
/// </summary>
|
|
public Dictionary<string, string> GroupToRole { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Projects the wire fields onto the shared <c>ZB.MOM.WW.Auth.Ldap</c>
|
|
/// <see cref="LibLdapOptions"/> the directory client consumes. App-only concerns
|
|
/// (<see cref="DevStubMode"/>, <see cref="GroupToRole"/>) have no library counterpart and are
|
|
/// handled by the app wrapper around the library service; <see cref="Enabled"/> is carried
|
|
/// through so the library's own feature gate stays consistent with the app master switch.
|
|
/// </summary>
|
|
public LibLdapOptions ToLibraryOptions() => new()
|
|
{
|
|
Enabled = Enabled,
|
|
Server = Server,
|
|
Port = Port,
|
|
Transport = Transport,
|
|
AllowInsecure = AllowInsecure,
|
|
SearchBase = SearchBase,
|
|
ServiceAccountDn = ServiceAccountDn,
|
|
ServiceAccountPassword = ServiceAccountPassword,
|
|
UserNameAttribute = UserNameAttribute,
|
|
DisplayNameAttribute = DisplayNameAttribute,
|
|
GroupAttribute = GroupAttribute,
|
|
};
|
|
}
|