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

147 lines
5.5 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;
using System.Threading;
using System.Threading.Tasks;
namespace Opc.Ua.Client.Roles
{
/// <summary>
/// Strongly-typed client over the OPC UA Part 18 §4 role-management
/// methods on the server's <c>RoleSet</c> and <c>RoleType</c> objects.
/// </summary>
/// <remarks>
/// All mutator operations require the calling session to hold the
/// <c>SecurityAdmin</c> role and to use a <c>SignAndEncrypt</c> secure
/// channel; otherwise the server returns
/// <c>Bad_UserAccessDenied</c> / <c>Bad_SecurityModeInsufficient</c>
/// which surfaces as a <see cref="ServiceResultException"/>.
/// </remarks>
public interface IRoleManagementClient
{
/// <summary>
/// Browses the server's <c>RoleSet</c> and returns a snapshot of
/// every role exposed by it.
/// </summary>
ValueTask<IReadOnlyList<RoleInfo>> ListRolesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Reads a single role by NodeId.
/// </summary>
ValueTask<RoleInfo> ReadRoleAsync(NodeId roleId, CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleSet.AddRole</c> per Part 18 §4.2.2 and returns the
/// new role's NodeId.
/// </summary>
ValueTask<NodeId> AddRoleAsync(
string roleName,
string? namespaceUri = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleSet.RemoveRole</c> per Part 18 §4.2.3.
/// </summary>
ValueTask RemoveRoleAsync(NodeId roleId, CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleType.AddIdentity</c> per Part 18 §4.4.5.
/// </summary>
ValueTask AddIdentityAsync(
NodeId roleId,
IdentityMappingRuleType rule,
CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleType.RemoveIdentity</c> per Part 18 §4.4.6.
/// </summary>
ValueTask RemoveIdentityAsync(
NodeId roleId,
IdentityMappingRuleType rule,
CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleType.AddApplication</c> per Part 18 §4.4.7.
/// </summary>
ValueTask AddApplicationAsync(
NodeId roleId,
string applicationUri,
CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleType.RemoveApplication</c> per Part 18 §4.4.8.
/// </summary>
ValueTask RemoveApplicationAsync(
NodeId roleId,
string applicationUri,
CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleType.AddEndpoint</c> per Part 18 §4.4.9.
/// </summary>
ValueTask AddEndpointAsync(
NodeId roleId,
EndpointType endpoint,
CancellationToken cancellationToken = default);
/// <summary>
/// Invokes <c>RoleType.RemoveEndpoint</c> per Part 18 §4.4.10.
/// </summary>
ValueTask RemoveEndpointAsync(
NodeId roleId,
EndpointType endpoint,
CancellationToken cancellationToken = default);
/// <summary>
/// Writes the <c>ApplicationsExclude</c> property on a role.
/// </summary>
ValueTask SetApplicationsExcludeAsync(
NodeId roleId,
bool value,
CancellationToken cancellationToken = default);
/// <summary>
/// Writes the <c>EndpointsExclude</c> property on a role.
/// </summary>
ValueTask SetEndpointsExcludeAsync(
NodeId roleId,
bool value,
CancellationToken cancellationToken = default);
/// <summary>
/// Writes the <c>CustomConfiguration</c> property on a role.
/// </summary>
ValueTask SetCustomConfigurationAsync(
NodeId roleId,
bool value,
CancellationToken cancellationToken = default);
}
}