Files
Marc Schier 296a904f34 Add full OPC UA Part 18 (Role management) support (#3778)
* role management compliant with OPC UA Part 18

Server-side:
- New IRoleManager / RoleManager with spec-correct status codes
  (Bad_AlreadyExists, Bad_NodeIdUnknown, Bad_RequestNotAllowed,
  Bad_NotFound) for every mutator per Part 18 4.2 / 4.4.
- Pre-populate default identities for the three reserved well-known
  roles per Part 18 4.3 (Anonymous, AuthenticatedUser,
  TrustedApplication).
- Reject mutations on reserved roles with Bad_RequestNotAllowed.
- Validate IdentityMappingRuleType per Part 18 4.4.3 (empty criteria
  for Anonymous/AuthenticatedUser/TrustedApplication; upper-case hex
  for Thumbprint; Name=Value grammar for X509Subject).
- Honour Part 18 4.4.2 default-field-as-wildcard EndpointType
  comparisons; tighten TrustedApplication/Application rules to
  require trusted certificate + signed channel.
- Introduce CustomConfiguration flag per Part 18 4.4.1.
- Add IRoleManager.RoleConfigurationChanged event raised after every
  successful mutation per Part 18 4.4.1.
- Make RoleEntry an immutable snapshot.

Address-space binding (typed proxies):
- RoleStateBinding switched from stringly-typed OnCallMethod2 plumbing
  to the source-generated typed proxies (RoleSetState, RoleState,
  AddRoleMethodState, AddIdentityMethodState, ...).
- DiagnosticsNodeManager.AddBehaviourToPredefinedNodeAsync upgrades
  passive BaseObjectState nodes of RoleSetType / RoleType to the
  typed RoleSetState / RoleState proxies at predefined-node load time.
- New RoleAuthorizationGate.CheckAdmin centralizes SecurityAdmin role
  plus SignAndEncrypt enforcement per Part 18 4.2 / 4.4 / 5.2.
- Bound writable ApplicationsExclude / EndpointsExclude /
  CustomConfiguration with the same auth gate.
- Raise RoleMappingRuleChangedAuditEventType from every successful
  mutator (Part 18 4.5) via the existing AuditEvents helper.

Client library:
- New Opc.Ua.Client.Roles.RoleManagementClient + IRoleManagementClient
  + RoleInfo + SessionRoleExtensions.OpenRoleManagementClient.
- Covers list / read / add / remove role, identity, application,
  endpoint operations plus Set*Exclude via Write Service.

Tests:
- New RoleManagerTests.cs - 37 NUnit tests for pre-populated roles,
  reserved-role immutability, spec-correct status codes, criteria
  validation, ResolveGrantedRoles, exclude semantics,
  CustomConfiguration, AddRole well-known NodeId reuse, and
  RoleConfigurationChanged event.

Documentation:
- Rewrite Docs/RoleBasedUserManagement.md as a Part 18 compliance
  reference (status-code matrix, client examples, known limitations).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add Part 18 5 UserManagement implementation

Server-side:
- New IUserManagement interface (Opc.Ua.Server.UserManagement) modelling
  Part 18 5.2.1 surface: PasswordLength, PasswordOptions, PasswordRestrictions,
  SnapshotUsers, AddUser, ModifyUser, RemoveUser, ChangePassword,
  MustChangePassword, IsUserActive, UserDeactivated event.
- Default UserManagement implementation wraps the existing IUserDatabase
  and stores per-user UserConfigurationMask + description in memory.
  Validates passwords against PasswordLength + character-class flags
  from PasswordOptions; honours Part 18 5.2 result codes
  (Bad_AlreadyExists, Bad_NotFound, Bad_OutOfRange, Bad_NotSupported,
  Bad_ConfigurationError, Bad_InvalidSelfReference,
  Bad_IdentityTokenInvalid).
- ChangePassword clears the MustChangePassword bit on success.
- New UserManagementBinding wires the typed source-generated proxies
  (UserManagementState, AddUserMethodState, ModifyUserMethodState,
  RemoveUserMethodState, ChangePasswordMethodState) - typed OnCallAsync
  delegates everywhere; admin gate via RoleAuthorizationGate.CheckAdmin;
  ChangePassword gated via RoleAuthorizationGate.CheckSelfUserName;
  closes active sessions for deactivated users via ISessionManager.
- ConfigurationNodeManager.AddBehaviourToPredefinedNodeAsync upgrades
  the passive UserManagement object to the typed UserManagementState
  proxy at predefined-node load time; CreateServerConfiguration invokes
  UserManagementBinding.Bind when an IUserManagement was injected.
- IServerInternal gains UserManagement getter + SetUserManagement
  injector.

Tests:
- New Tests/Opc.Ua.Server.Tests/UserManagementTests.cs - 18 NUnit tests
  covering AddUser happy path, duplicate / password-length / config-flag
  validation, RemoveUser non-existent / self-reference / NoDelete,
  ModifyUser self-disable / disable-other UserDeactivated event,
  ChangePassword happy path / same-as-old / wrong-old /
  MustChangePassword bit clearing / NoChangeByUser rejection,
  IsUserActive on disabled vs active users.

Documentation:
- Update Docs/RoleBasedUserManagement.md with a User Management section
  including the spec result-code matrix, example wiring code, and
  reduce known limitations - UserManagement is no longer a limitation.
2026-05-20 05:51:17 +02:00

135 lines
5.2 KiB
C#

/* ========================================================================
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System.Collections.Generic;
namespace Opc.Ua.Server
{
/// <summary>
/// Immutable snapshot of a role's configuration per OPC UA Part 18 §4.4.
/// </summary>
/// <remarks>
/// Returned by <see cref="IRoleManager"/> methods that expose role state to
/// the address-space binding (<see cref="RoleStateBinding"/>) and to test
/// code. The lists are caller-owned copies; mutating them does not affect
/// the manager.
/// </remarks>
public sealed class RoleEntry
{
internal RoleEntry(
NodeId roleId,
string? browseName,
ushort namespaceIndex,
bool isReserved,
bool isWellKnown,
IReadOnlyList<IdentityMappingRuleType> identities,
IReadOnlyList<string> applications,
bool applicationsExclude,
IReadOnlyList<EndpointType> endpoints,
bool endpointsExclude,
bool customConfiguration)
{
RoleId = roleId;
BrowseName = browseName;
NamespaceIndex = namespaceIndex;
IsReserved = isReserved;
IsWellKnown = isWellKnown;
Identities = identities;
Applications = applications;
ApplicationsExclude = applicationsExclude;
Endpoints = endpoints;
EndpointsExclude = endpointsExclude;
CustomConfiguration = customConfiguration;
}
/// <summary>
/// The role's NodeId (e.g. <c>Opc.Ua.ObjectIds.WellKnownRole_Observer</c>).
/// </summary>
public NodeId RoleId { get; }
/// <summary>
/// Browse name of the role; <c>null</c> for unnamed dynamic roles where
/// the manager only tracks the NodeId.
/// </summary>
public string? BrowseName { get; }
/// <summary>
/// Namespace index for the role's NodeId.
/// </summary>
public ushort NamespaceIndex { get; }
/// <summary>
/// True if the role is one of the three reserved well-known roles
/// (<c>Anonymous</c>, <c>AuthenticatedUser</c>, <c>TrustedApplication</c>)
/// that the spec forbids from being modified or removed (Part 18 §4.3).
/// </summary>
public bool IsReserved { get; }
/// <summary>
/// True if the role is one of the nine well-known roles defined in
/// OPC UA Part 3 §4.9.2 (Anonymous, AuthenticatedUser, TrustedApplication,
/// Observer, Operator, Engineer, Supervisor, ConfigureAdmin, SecurityAdmin).
/// </summary>
public bool IsWellKnown { get; }
/// <summary>
/// Snapshot of identity-mapping rules per Part 18 §4.4.3. Treat as
/// read-only — modifications are not reflected back into the manager.
/// </summary>
public IReadOnlyList<IdentityMappingRuleType> Identities { get; }
/// <summary>
/// Snapshot of ApplicationUris per Part 18 §4.4.1.
/// </summary>
public IReadOnlyList<string> Applications { get; }
/// <summary>
/// True if <see cref="Applications"/> is an exclude list.
/// </summary>
public bool ApplicationsExclude { get; }
/// <summary>
/// Snapshot of endpoints per Part 18 §4.4.1.
/// </summary>
public IReadOnlyList<EndpointType> Endpoints { get; }
/// <summary>
/// True if <see cref="Endpoints"/> is an exclude list.
/// </summary>
public bool EndpointsExclude { get; }
/// <summary>
/// True if the role uses vendor-specific configuration (Part 18 §4.4.1).
/// When <c>true</c>, the role may be granted even when
/// <see cref="Identities"/> is empty.
/// </summary>
public bool CustomConfiguration { get; }
}
}