reuse usertokenpolicy used in connect for Reconnect / "ReactivateSession" (#3581)

* reuse usertokenpolicy

* change assert not null to assert ignore when null

* reportauditevent -> Utils.Clone(session?.IdentityToken?.Token)

* use standard credentials

* only assign to endpoint when not null, in UpdateSession -> also update m_userTokenSecurityPolicyUri, change endpoint configuration only on cloned object

* keep using m_userTokenSecurityPolicyUri in Encrypt

* use 'cleaner clone'
This commit is contained in:
KircMax
2026-03-10 06:55:47 +01:00
committed by GitHub
parent a255facf68
commit 36dc85b525
3 changed files with 72 additions and 8 deletions
+12 -7
View File
@@ -1283,6 +1283,8 @@ namespace Opc.Ua.Client
previousServerNonce, previousServerNonce,
m_endpoint.Description.SecurityMode); m_endpoint.Description.SecurityMode);
m_userTokenSecurityPolicyUri = tokenSecurityPolicyUri;
// sign data with user token. // sign data with user token.
SignatureData userTokenSignature = identityToken.Sign( SignatureData userTokenSignature = identityToken.Sign(
dataToSign, dataToSign,
@@ -2324,7 +2326,7 @@ namespace Opc.Ua.Client
UserTokenPolicy identityPolicy = endpoint.FindUserTokenPolicy( UserTokenPolicy identityPolicy = endpoint.FindUserTokenPolicy(
m_identity.TokenType, m_identity.TokenType,
m_identity.IssuedTokenType, m_identity.IssuedTokenType,
endpoint.SecurityPolicyUri); m_userTokenSecurityPolicyUri ?? endpoint.SecurityPolicyUri);
if (identityPolicy == null) if (identityPolicy == null)
{ {
@@ -2337,18 +2339,21 @@ namespace Opc.Ua.Client
} }
// select the security policy for the user token. // select the security policy for the user token.
string? tokenSecurityPolicyUri = identityPolicy.SecurityPolicyUri; if (m_userTokenSecurityPolicyUri == null)
if (string.IsNullOrEmpty(tokenSecurityPolicyUri))
{ {
tokenSecurityPolicyUri = endpoint.SecurityPolicyUri; string? tokenSecurityPolicyUri = identityPolicy.SecurityPolicyUri;
if (string.IsNullOrEmpty(tokenSecurityPolicyUri))
{
tokenSecurityPolicyUri = endpoint.SecurityPolicyUri;
}
m_userTokenSecurityPolicyUri = tokenSecurityPolicyUri;
} }
m_userTokenSecurityPolicyUri = tokenSecurityPolicyUri;
// validate server nonce and security parameters for user identity. // validate server nonce and security parameters for user identity.
ValidateServerNonce( ValidateServerNonce(
m_identity, m_identity,
m_serverNonce, m_serverNonce,
tokenSecurityPolicyUri, m_userTokenSecurityPolicyUri,
m_previousServerNonce, m_previousServerNonce,
m_endpoint.Description.SecurityMode); m_endpoint.Description.SecurityMode);
@@ -2357,7 +2362,7 @@ namespace Opc.Ua.Client
identityToken.UpdatePolicy(identityPolicy); identityToken.UpdatePolicy(identityPolicy);
SignatureData userTokenSignature = identityToken.Sign( SignatureData userTokenSignature = identityToken.Sign(
dataToSign, dataToSign,
tokenSecurityPolicyUri); m_userTokenSecurityPolicyUri);
// encrypt token. // encrypt token.
identityToken.Encrypt( identityToken.Encrypt(
@@ -1072,7 +1072,7 @@ namespace Opc.Ua.Server
e.SetChildValue( e.SetChildValue(
systemContext, systemContext,
BrowseNames.UserIdentityToken, BrowseNames.UserIdentityToken,
Utils.Clone(session?.IdentityToken.Token), Utils.Clone(session?.IdentityToken?.Token),
false); false);
server.ReportAuditEvent(systemContext, e); server.ReportAuditEvent(systemContext, e);
+59
View File
@@ -956,6 +956,65 @@ namespace Opc.Ua.Client.Tests
Utils.SilentDispose(session2); Utils.SilentDispose(session2);
} }
/// <summary>
/// Open a session on a channel using a usertokenpolicy from another endpoint, then reconnect (activate)
/// </summary>
[Test]
[Order(260)]
[TestCase(SecurityPolicies.Basic256Sha256, SecurityPolicies.Basic128Rsa15)]
public async Task ReconnectSession_ReuseUsertokenPolicyAsync(
string securityPolicy, string userTokenPolicy)
{
UserIdentity userIdentity = new UserIdentity("user1", "password"u8);
// the first channel determines the endpoint
ConfiguredEndpoint endpoint = await ClientFixture
.GetEndpointAsync(ServerUrl, securityPolicy, Endpoints)
.ConfigureAwait(false);
if (endpoint == null)
{
NUnit.Framework.Assert.Ignore(
$"No endpoint found for {securityPolicy}");
}
endpoint = new ConfiguredEndpoint(
null,
(EndpointDescription)endpoint.Description.MemberwiseClone(),
endpoint.Configuration);
endpoint.Description.SecurityMode = MessageSecurityMode.Sign;
ConfiguredEndpoint tokenPolicyEndpoint = await ClientFixture
.GetEndpointAsync(ServerUrl, userTokenPolicy, Endpoints)
.ConfigureAwait(false);
Assert.NotNull(tokenPolicyEndpoint);
UserTokenPolicy identityPolicy = endpoint.Description.FindUserTokenPolicy(
userIdentity.TokenType,
userIdentity.IssuedTokenType,
tokenPolicyEndpoint.Description.SecurityPolicyUri);
if (identityPolicy == null)
{
NUnit.Framework.Assert.Ignore(
$"No UserTokenPolicy found for {userIdentity.TokenType}" +
$" / {userIdentity.IssuedTokenType}");
}
userIdentity.PolicyId = identityPolicy.PolicyId;
// the active channel
ISession session1 = await ClientFixture.ConnectAsync(endpoint, userIdentity)
.ConfigureAwait(false);
Assert.NotNull(session1);
try
{
await session1.ReconnectAsync(null, null).ConfigureAwait(false);
}
finally
{
session1.DeleteSubscriptionsOnClose = true;
await session1.CloseAsync(1000).ConfigureAwait(false);
Utils.SilentDispose(session1);
}
}
/// <summary> /// <summary>
/// Open a session on a channel, then recreate using the session as a template, /// Open a session on a channel, then recreate using the session as a template,
/// verify the renewUserIdentityHandler is brought to the new session and called /// verify the renewUserIdentityHandler is brought to the new session and called