test(opcua): flag-OFF HistoryUpdate over the wire - Cluster HistoryUpdate grant allowed, WriteOperate alone denied
The rig's key roles are now settable; the test derives them with the production ApiKeyInterimRoleMapper from the same grants it installs. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -46,6 +46,13 @@ internal sealed class ServerEventRig : IAsyncDisposable
|
||||
|
||||
public OtOpcUaNodeManager Nm => _server.NodeManager!;
|
||||
|
||||
/// <summary>
|
||||
/// The roles the <c>apikey</c> session activates with (read at each activation). Defaults to the two
|
||||
/// server-wide roles; a flag-off test sets it from <c>ApiKeyInterimRoleMapper.Map</c> over its own grants, as
|
||||
/// the production authenticator does.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> KeyRoles { get; set; } = [OpcUaDataPlaneRoles.AlarmAck, OpcUaDataPlaneRoles.WriteOperate];
|
||||
|
||||
public SdkAddressSpaceSink Sink { get; private set; } = null!;
|
||||
|
||||
public static async Task<ServerEventRig> StartAsync(string name, bool encrypted, CancellationToken ct)
|
||||
@@ -83,7 +90,7 @@ internal sealed class ServerEventRig : IAsyncDisposable
|
||||
}
|
||||
|
||||
private OpcUaApplicationHost NewHost() =>
|
||||
new(_hostOptions, NullLogger<OpcUaApplicationHost>.Instance, new KeyPresentingAuthenticator());
|
||||
new(_hostOptions, NullLogger<OpcUaApplicationHost>.Instance, new KeyPresentingAuthenticator(() => KeyRoles));
|
||||
|
||||
/// <summary>
|
||||
/// Stops the server and boots a fresh one on the SAME port, application URI and PKI store (a process
|
||||
@@ -300,18 +307,17 @@ internal sealed class ServerEventRig : IAsyncDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Admits <c>apikey</c> as <see cref="KeyPrincipal"/> carrying the two server-wide roles (used only
|
||||
/// while enforcement is off); anonymous falls through to the <c>anonymous</c> principal.
|
||||
/// Admits <c>apikey</c> as <see cref="KeyPrincipal"/> carrying <see cref="KeyRoles"/> (used only while
|
||||
/// enforcement is off); anonymous falls through to the <c>anonymous</c> principal.
|
||||
/// </summary>
|
||||
private sealed class KeyPresentingAuthenticator : IOpcUaUserAuthenticator
|
||||
private sealed class KeyPresentingAuthenticator(Func<IReadOnlyList<string>> roles) : IOpcUaUserAuthenticator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<OpcUaUserAuthResult> AuthenticateUserNameAsync(string username, string password, CancellationToken ct) =>
|
||||
Task.FromResult(
|
||||
string.Equals(username, "apikey", StringComparison.Ordinal)
|
||||
&& password.StartsWith($"otua_{KeyId}_", StringComparison.Ordinal)
|
||||
? OpcUaUserAuthResult.AllowKey(
|
||||
KeyPrincipal, [OpcUaDataPlaneRoles.AlarmAck, OpcUaDataPlaneRoles.WriteOperate], KeyId)
|
||||
? OpcUaUserAuthResult.AllowKey(KeyPrincipal, roles(), KeyId)
|
||||
: OpcUaUserAuthResult.Deny("Invalid API key."));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user