/* ========================================================================
* 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;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Opc.Ua.Client;
using Opc.Ua.Client.TestFramework;
namespace Opc.Ua.Core.Security.Tests
{
///
/// compliance tests for Security – endpoint security
/// policies, user tokens, and secure connections.
///
[TestFixture]
[Category("Conformance")]
[Category("Security")]
public class SecurityTests : TestFixture
{
[Test]
public async Task VerifyServerAdvertisesSecureEndpointsAsync()
{
ArrayOf endpoints =
await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
bool hasSecure = false;
foreach (EndpointDescription e in endpoints)
{
if (e.SecurityMode != MessageSecurityMode.None)
{
hasSecure = true;
break;
}
}
Assert.That(hasSecure, Is.True,
"Server should advertise at least one secure endpoint.");
}
[Test]
public async Task VerifySecurityPolicyUriIsValidAsync()
{
ArrayOf endpoints =
await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode != MessageSecurityMode.None)
{
Assert.That(
ep.SecurityPolicyUri,
Is.Not.Null.And.Not.Empty);
Assert.That(
ep.SecurityPolicyUri,
Does.StartWith("http://opcfoundation.org/UA/"));
}
}
}
[Test]
public async Task VerifyAnonymousUserTokenOnEndpointAsync()
{
ArrayOf endpoints =
await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
bool hasAnonymous = false;
foreach (EndpointDescription e in endpoints)
{
if (e.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in e.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.Anonymous)
{
hasAnonymous = true;
break;
}
}
}
if (hasAnonymous)
{
break;
}
}
Assert.That(hasAnonymous, Is.True);
}
[Test]
public async Task VerifyUsernameUserTokenOnEndpointAsync()
{
ArrayOf endpoints =
await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
bool hasUsername = false;
foreach (EndpointDescription e in endpoints)
{
if (e.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in e.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.UserName)
{
hasUsername = true;
break;
}
}
}
if (hasUsername)
{
break;
}
}
if (!hasUsername)
{
Assert.Fail(
"Server does not advertise Username user token.");
}
}
[Test]
public void ConnectWithSecurityModeNone()
{
Assert.That(Session.Connected, Is.True);
Assert.That(
Session.Endpoint.SecurityMode,
Is.EqualTo(MessageSecurityMode.None));
}
[Test]
public async Task ActivateWithAnonymousIdentityAsync()
{
ISession additionalSession = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None)
.ConfigureAwait(false);
try
{
Assert.That(additionalSession.Connected, Is.True);
}
finally
{
await additionalSession.CloseAsync(5000, true)
.ConfigureAwait(false);
additionalSession.Dispose();
}
}
[Test]
public void SessionSecurityModeIsNone()
{
Assert.That(
Session.Endpoint.SecurityMode,
Is.EqualTo(MessageSecurityMode.None));
}
[Test]
public async Task VerifyEndpointServerCertificateOnSecureEndpointAsync()
{
ArrayOf endpoints =
await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode != MessageSecurityMode.None)
{
Assert.That(
ep.ServerCertificate.Length,
Is.GreaterThan(0),
"Secure endpoint should have a server certificate.");
}
}
}
[Test]
public async Task VerifyEndpointSecurityLevelOrderingAsync()
{
ArrayOf endpoints =
await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
bool hasPositiveLevel = false;
foreach (EndpointDescription e in endpoints)
{
if (e.SecurityMode != MessageSecurityMode.None &&
e.SecurityLevel > 0)
{
hasPositiveLevel = true;
break;
}
}
Assert.That(hasPositiveLevel, Is.True,
"At least one secure endpoint should have SecurityLevel > 0.");
}
[Test]
public async Task ConnectSecondSessionVerifyIndependentSecurityAsync()
{
ISession second = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None)
.ConfigureAwait(false);
try
{
Assert.That(Session.Connected, Is.True);
Assert.That(second.Connected, Is.True);
Assert.That(
Session.SessionId,
Is.Not.EqualTo(second.SessionId));
}
finally
{
await second.CloseAsync(5000, true)
.ConfigureAwait(false);
second.Dispose();
}
}
[Test]
public async Task ConnectWithEmptyUsernameReturnsBadIdentityTokenInvalidAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
EndpointDescription? usernameEp = null;
foreach (EndpointDescription ep in endpoints)
{
if (ep.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in ep.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.UserName)
{
usernameEp = ep;
break;
}
}
}
if (usernameEp != null)
{
break;
}
}
if (usernameEp == null)
{
Assert.Fail(
"Server does not advertise Username user token.");
}
try
{
ISession session = await OpenAuxSessionAsync(
userIdentity: new UserIdentity(string.Empty, ""u8))
.ConfigureAwait(false);
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
Assert.Fail("Expected ServiceResultException.");
}
catch (ServiceResultException sre)
{
Assert.That(
sre.StatusCode == StatusCodes.BadIdentityTokenInvalid ||
sre.StatusCode == StatusCodes.BadIdentityTokenRejected ||
sre.StatusCode == StatusCodes.BadUserAccessDenied,
Is.True,
$"Expected identity rejection, got {sre.StatusCode}");
}
}
[Test]
public async Task ConnectWithWrongPasswordReturnsBadIdentityTokenRejectedAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
bool hasUsername = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in ep.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.UserName)
{
hasUsername = true;
break;
}
}
}
if (hasUsername)
{
break;
}
}
if (!hasUsername)
{
Assert.Fail(
"Server does not advertise Username user token.");
}
try
{
ISession session = await OpenAuxSessionAsync(
userIdentity: new UserIdentity("sysadmin", "WRONG_PASSWORD_12345"u8))
.ConfigureAwait(false);
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
Assert.Fail("Expected ServiceResultException.");
}
catch (ServiceResultException sre)
{
Assert.That(
sre.StatusCode == StatusCodes.BadIdentityTokenRejected ||
sre.StatusCode == StatusCodes.BadUserAccessDenied,
Is.True,
$"Expected rejection, got {sre.StatusCode}");
}
}
[Test]
public async Task ConnectWithSysadminCredentialsAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
bool hasUsername = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in ep.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.UserName)
{
hasUsername = true;
break;
}
}
}
if (hasUsername)
{
break;
}
}
if (!hasUsername)
{
Assert.Fail(
"Server does not advertise Username user token.");
}
try
{
ISession session = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None,
userIdentity: new UserIdentity("sysadmin", "demo"u8))
.ConfigureAwait(false);
Assert.That(session.Connected, Is.True);
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
}
catch (ServiceResultException sre)
{
Assert.Fail(
$"Server rejected sysadmin credentials: {sre.StatusCode}");
}
}
[Test]
public async Task ConnectWithAppuserCredentialsAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
bool hasUsername = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in ep.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.UserName)
{
hasUsername = true;
break;
}
}
}
if (hasUsername)
{
break;
}
}
if (!hasUsername)
{
Assert.Ignore(
"Server does not advertise Username user token.");
}
try
{
ISession session = await OpenAuxSessionAsync(
userIdentity: new UserIdentity("appuser", "demo"u8))
.ConfigureAwait(false);
Assert.That(session.Connected, Is.True);
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
}
catch (ServiceResultException sre)
{
Assert.Ignore(
$"Server rejected appuser credentials: {sre.StatusCode}");
}
}
[Test]
public async Task ConnectWithSpecialCharsUsernameAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
bool hasUsername = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in ep.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.UserName)
{
hasUsername = true;
break;
}
}
}
if (hasUsername)
{
break;
}
}
if (!hasUsername)
{
Assert.Fail(
"Server does not advertise Username user token.");
}
// Expect rejection — the point is the server handles it
try
{
ISession session = await OpenAuxSessionAsync(
userIdentity: new UserIdentity("user@#$%^&*()", "demo"u8))
.ConfigureAwait(false);
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
}
catch (ServiceResultException)
{
// Any rejection is acceptable
}
}
[Test]
public async Task VerifyEndpointListsUserIdentityTokenTypesAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
bool anyHasTokens = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.UserIdentityTokens != default! &&
ep.UserIdentityTokens.Count > 0)
{
anyHasTokens = true;
break;
}
}
Assert.That(anyHasTokens, Is.True,
"At least one endpoint should support identity tokens.");
}
[Test]
public async Task VerifySecureEndpointsHaveNonEmptyServerCertificateAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode != MessageSecurityMode.None)
{
Assert.That(
ep.ServerCertificate, Is.Not.Null,
"Secure endpoint should have ServerCertificate.");
Assert.That(
ep.ServerCertificate.Length, Is.GreaterThan(0),
"Secure endpoint ServerCertificate should not be empty.");
}
}
}
[Test]
public async Task VerifySecurityPolicyUriStartsWithOpcFoundationAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
foreach (EndpointDescription ep in endpoints)
{
Assert.That(
ep.SecurityPolicyUri, Is.Not.Null.And.Not.Empty,
"SecurityPolicyUri should not be null or empty.");
Assert.That(
Uri.IsWellFormedUriString(
ep.SecurityPolicyUri, UriKind.Absolute),
Is.True,
$"SecurityPolicyUri '{ep.SecurityPolicyUri}' should be valid URI.");
}
}
[Test]
public async Task VerifySecurityLevelHigherForMoreSecureAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
byte noneLevel = 0;
byte secureLevel = 0;
bool foundNone = false;
bool foundSecure = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode == MessageSecurityMode.None && !foundNone)
{
noneLevel = ep.SecurityLevel;
foundNone = true;
}
else if (ep.SecurityMode != MessageSecurityMode.None &&
!foundSecure)
{
secureLevel = ep.SecurityLevel;
foundSecure = true;
}
}
if (foundNone && foundSecure)
{
Assert.That(secureLevel, Is.GreaterThanOrEqualTo(noneLevel),
"Secure endpoint SecurityLevel should be >= None.");
}
}
[Test]
public async Task ConnectWithSignSecurityModeAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
EndpointDescription? signEp = null;
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode == MessageSecurityMode.Sign)
{
signEp = ep;
break;
}
}
if (signEp == null)
{
Assert.Fail("No Sign endpoint available.");
}
ISession session = await ClientFixture
.ConnectAsync(ServerUrl, signEp!.SecurityPolicyUri!)
.ConfigureAwait(false);
try
{
Assert.That(session.Connected, Is.True);
}
finally
{
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
}
}
[Test]
public async Task ConnectWithSignAndEncryptSecurityModeAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
EndpointDescription? encryptEp = null;
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode == MessageSecurityMode.SignAndEncrypt)
{
encryptEp = ep;
break;
}
}
if (encryptEp == null)
{
Assert.Fail("No SignAndEncrypt endpoint available.");
}
ISession session = await ClientFixture
.ConnectAsync(ServerUrl, encryptEp!.SecurityPolicyUri!)
.ConfigureAwait(false);
try
{
Assert.That(session.Connected, Is.True);
}
finally
{
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
}
}
[Test]
public async Task VerifyMinimumKeyLengthOnCertificatesAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode != MessageSecurityMode.None &&
!ep.ServerCertificate.IsEmpty)
{
X509Certificate2 cert =
X509CertificateLoader.LoadCertificate(ep.ServerCertificate.ToArray());
int keySize;
using (RSA rsa = cert.GetRSAPublicKey()!)
using (ECDsa ecdsa = (rsa is null ? cert.GetECDsaPublicKey() : null)!)
{
keySize = rsa?.KeySize ?? ecdsa?.KeySize ?? 0;
}
Assert.That(keySize, Is.GreaterThanOrEqualTo(256),
"Server certificate key should be at least 256 bits.");
cert.Dispose();
}
}
}
[Test]
public async Task VerifyNoneEndpointHasZeroSecurityLevelAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode == MessageSecurityMode.None)
{
Assert.That(ep.SecurityLevel, Is.Zero,
"None endpoint should have SecurityLevel=0.");
}
}
}
[Test]
public async Task VerifyServerCertificateSubjectDNAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode != MessageSecurityMode.None &&
!ep.ServerCertificate.IsEmpty)
{
X509Certificate2 cert =
X509CertificateLoader.LoadCertificate(ep.ServerCertificate.ToArray());
Assert.That(cert.Subject, Is.Not.Null.And.Not.Empty,
"Secure endpoint certificate should have a valid Subject.");
cert.Dispose();
return;
}
}
Assert.Fail("No secure endpoint with certificate found.");
}
[Test]
public async Task ConnectWithSysadminWriteToNodeAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
bool hasUsername = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.UserIdentityTokens != default!)
{
foreach (UserTokenPolicy t in ep.UserIdentityTokens)
{
if (t.TokenType == UserTokenType.UserName)
{
hasUsername = true;
break;
}
}
}
if (hasUsername)
{
break;
}
}
if (!hasUsername)
{
Assert.Fail(
"Server does not advertise Username user token.");
}
ISession session;
try
{
session = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None,
userIdentity: new UserIdentity("sysadmin", "demo"u8))
.ConfigureAwait(false);
}
catch (ServiceResultException sre)
{
Assert.Fail(
$"Server rejected sysadmin credentials: {sre.StatusCode}");
return;
}
try
{
NodeId nodeId = ToNodeId(Constants.ScalarStaticInt32);
WriteResponse writeResp = await session.WriteAsync(
null,
new WriteValue[]
{
new() {
NodeId = nodeId,
AttributeId = Attributes.Value,
Value = new DataValue(Variant.From(42))
}
}.ToArrayOf(),
CancellationToken.None).ConfigureAwait(false);
Assert.That(writeResp.Results.Count, Is.EqualTo(1));
Assert.That(
writeResp.Results[0].Code,
Is.EqualTo(StatusCodes.Good));
}
finally
{
await session.CloseAsync(5000, true).ConfigureAwait(false);
session.Dispose();
}
}
[Test]
public async Task VerifySecureEndpointHasUserTokenPoliciesAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
bool found = false;
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode != MessageSecurityMode.None)
{
Assert.That(
ep.UserIdentityTokens, Is.Not.Null,
"Secure endpoint should have UserIdentityTokens.");
Assert.That(
ep.UserIdentityTokens.Count, Is.GreaterThan(0),
"Secure endpoint should have at least one token policy.");
found = true;
break;
}
}
if (!found)
{
Assert.Fail("No secure endpoint found.");
}
}
[Test]
public async Task VerifyEndpointApplicationUriMatchesServerAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
Assert.That(endpoints.Count, Is.GreaterThan(0));
string firstUri = endpoints[0].Server.ApplicationUri!;
Assert.That(firstUri, Is.Not.Null.And.Not.Empty);
foreach (EndpointDescription ep in endpoints)
{
Assert.That(ep.Server.ApplicationUri,
Is.EqualTo(firstUri),
"All endpoints should share the same ApplicationUri.");
}
}
[Test]
public async Task VerifyTransportProfileUriAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
foreach (EndpointDescription ep in endpoints)
{
Assert.That(ep.TransportProfileUri,
Is.Not.Null.And.Not.Empty,
"TransportProfileUri should be set on every endpoint.");
}
}
[Test]
public async Task VerifyEndpointServerDescriptionIsServerAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
foreach (EndpointDescription ep in endpoints)
{
Assert.That(
ep.Server.ApplicationType,
Is.EqualTo(ApplicationType.Server)
.Or.EqualTo(ApplicationType.ClientAndServer),
"Endpoint Server field should be Server or ClientAndServer.");
}
}
[Test]
public async Task VerifyEndpointsHaveConsistentServerCertificateAsync()
{
ArrayOf endpoints = await GetEndpointsAsync().ConfigureAwait(false);
var certsByPolicy = new System.Collections.Generic.Dictionary();
foreach (EndpointDescription ep in endpoints)
{
if (ep.SecurityMode == MessageSecurityMode.None)
{
continue;
}
string key = ep.SecurityPolicyUri!;
byte[] certBytes = ep.ServerCertificate.ToArray();
if (certsByPolicy.TryGetValue(key!, out byte[]? existing))
{
Assert.That(certBytes, Is.EqualTo(existing),
$"Endpoints with policy '{key}' should have same cert.");
}
else
{
certsByPolicy[key] = certBytes;
}
}
}
[Test]
public async Task NoneSession001InsecureWithCertsAndNonces()
{
// Connect on an insecure channel (SecurityMode.None)
// and verify CreateSession / ActivateSession succeeds
// when both client certificate and nonces are present.
ISession session;
try
{
session = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None)
.ConfigureAwait(false);
}
catch (ServiceResultException sre) when (sre.StatusCode == StatusCodes.BadUserAccessDenied &&
sre.Message.Contains("Too many failed authentication attempts", StringComparison.Ordinal))
{
Assert.Fail("Account locked out from prior negative tests; cannot authenticate");
return;
}
try
{
Assert.That(session.Connected, Is.True);
Assert.That(
session.Endpoint.SecurityMode,
Is.EqualTo(MessageSecurityMode.None));
// Server should have returned a session id and nonce
Assert.That(
session.SessionId, Is.Not.Null,
"Session must have a valid SessionId.");
Assert.That(
session.SessionId, Is.Not.EqualTo(NodeId.Null),
"Server should supply a valid SessionId.");
}
finally
{
await session.CloseAsync(5000, true)
.ConfigureAwait(false);
session.Dispose();
}
}
[Test]
public async Task NoneSession002InsecureNoCerts()
{
// Connect on an insecure channel without a client
// certificate. The server should still accept the
// session when SecurityMode is None.
ISession session;
try
{
session = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None)
.ConfigureAwait(false);
}
catch (ServiceResultException sre) when (sre.StatusCode == StatusCodes.BadUserAccessDenied &&
sre.Message.Contains("Too many failed authentication attempts", StringComparison.Ordinal))
{
Assert.Fail("Account locked out from prior negative tests; cannot authenticate");
return;
}
try
{
Assert.That(session.Connected, Is.True);
Assert.That(
session.Endpoint.SecurityMode,
Is.EqualTo(MessageSecurityMode.None));
Assert.That(
session.SessionId, Is.Not.Null,
"Session must have a valid SessionId.");
}
finally
{
await session.CloseAsync(5000, true)
.ConfigureAwait(false);
session.Dispose();
}
}
[Test]
public async Task NoneSession003InsecureWithCertsNoNonce()
{
// Connect on an insecure channel with client certificate
// present but verify session works even when the server
// does not strictly require a nonce on None channels.
ISession session;
try
{
session = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None)
.ConfigureAwait(false);
}
catch (ServiceResultException sre) when (sre.StatusCode == StatusCodes.BadUserAccessDenied &&
sre.Message.Contains("Too many failed authentication attempts", StringComparison.Ordinal))
{
Assert.Fail("Account locked out from prior negative tests; cannot authenticate");
return;
}
try
{
Assert.That(session.Connected, Is.True);
Assert.That(
session.Endpoint.SecurityMode,
Is.EqualTo(MessageSecurityMode.None));
Assert.That(
session.Endpoint.SecurityPolicyUri,
Is.EqualTo(SecurityPolicies.None));
}
finally
{
await session.CloseAsync(5000, true)
.ConfigureAwait(false);
session.Dispose();
}
}
[Test]
public async Task NoneSession004InsecureNoCertsNoNonce()
{
// Connect on an insecure channel with neither client
// certificate nor nonce requirement. Verify that the
// session is fully functional.
ISession session;
try
{
session = await ClientFixture
.ConnectAsync(ServerUrl, SecurityPolicies.None)
.ConfigureAwait(false);
}
catch (ServiceResultException sre) when (sre.StatusCode == StatusCodes.BadUserAccessDenied &&
sre.Message.Contains("Too many failed authentication attempts", StringComparison.Ordinal))
{
Assert.Fail("Account locked out from prior negative tests; cannot authenticate");
return;
}
try
{
Assert.That(session.Connected, Is.True);
Assert.That(
session.Endpoint.SecurityMode,
Is.EqualTo(MessageSecurityMode.None));
// Verify the session is functional by reading
// the server status current time node.
DataValue timeValue = await session.ReadValueAsync(
VariableIds.Server_ServerStatus_CurrentTime,
CancellationToken.None).ConfigureAwait(false);
Assert.That(
StatusCode.IsGood(timeValue.StatusCode), Is.True,
"Should be able to read server time on " +
"None session.");
}
finally
{
await session.CloseAsync(5000, true)
.ConfigureAwait(false);
session.Dispose();
}
}
private async Task> GetEndpointsAsync()
{
var endpointConfiguration =
EndpointConfiguration.Create(ClientFixture.Config);
using DiscoveryClient client = await DiscoveryClient.CreateAsync(
ServerUrl,
endpointConfiguration,
Telemetry,
ct: CancellationToken.None).ConfigureAwait(false);
return await client.GetEndpointsAsync(
default, CancellationToken.None).ConfigureAwait(false);
}
}
}