Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/Opc.Ua.Bindings.Https/Https/HttpsServiceHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,11 @@ public async ValueTask<List<EndpointDescription>> CreateServiceHostAsync(
description);
description.TransportProfileUri = TransportProfileUri;

// if no mutual TLS authentication is used, anonymous user tokens are not allowed
if (!httpsMutualTls)
// Keep the HTTPS authentication policy that requires an
// authenticated user when mutual TLS is disabled. WSS carries
// normal UASC sessions, so it uses the server's configured OPC UA
// user-token policies just like UA TCP, including Anonymous.
if (!httpsMutualTls && !Profiles.IsWssBinary(TransportProfileUri))
{
description.UserIdentityTokens = description.UserIdentityTokens
.Filter(token => token.TokenType != UserTokenType.Anonymous);
Expand Down
4 changes: 3 additions & 1 deletion src/Opc.Ua.Client/Session/ChannelManagerSessionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,9 @@ private async Task<ServiceMessageContext> PrepareEndpointAndManagerAsync(

if (updateBeforeConnect && connection == null)
{
await endpoint.UpdateFromServerAsync(messageContext.Telemetry, ct).ConfigureAwait(false);
await endpoint
.UpdateFromServerAsync(configuration, messageContext.Telemetry, ct)
.ConfigureAwait(false);
}

if (checkDomain && endpoint.Description.ServerCertificate.Length > 0)
Expand Down
4 changes: 3 additions & 1 deletion src/Opc.Ua.Client/Session/DefaultSessionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,9 @@ public virtual async Task<ITransportChannel> CreateChannelAsync(
// update endpoint description using the discovery endpoint.
if (endpoint.UpdateBeforeConnect && connection == null)
{
await endpoint.UpdateFromServerAsync(messageContext.Telemetry, ct).ConfigureAwait(false);
await endpoint
.UpdateFromServerAsync(configuration, messageContext.Telemetry, ct)
.ConfigureAwait(false);
endpointDescription = endpoint.Description;
// UpdateFromServerAsync re-reads Configuration from the discovery response;
// it is set whenever the description was updated successfully.
Expand Down
2 changes: 1 addition & 1 deletion src/Opc.Ua.Client/Session/Session.ChannelManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ public static async Task<Session> CreateAsync(
if (updateBeforeConnect)
{
await endpoint
.UpdateFromServerAsync(probeContext.Telemetry, ct)
.UpdateFromServerAsync(configuration, probeContext.Telemetry, ct)
.ConfigureAwait(false);
}

Expand Down
58 changes: 56 additions & 2 deletions src/Opc.Ua.Core/Stack/Configuration/ConfiguredEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,13 +1173,59 @@ public Task UpdateFromServerAsync(
/// <summary>
/// Updates an endpoint with information from the server's discovery endpoint.
/// </summary>
public async Task UpdateFromServerAsync(
/// <remarks>
/// The application configuration supplies the certificate validator required by
/// secure discovery transports such as WSS.
/// </remarks>
public Task UpdateFromServerAsync(
ApplicationConfiguration applicationConfiguration,
ITelemetryContext? telemetry,
CancellationToken ct = default)
{
if (applicationConfiguration == null)
{
throw new ArgumentNullException(nameof(applicationConfiguration));
}

return UpdateFromServerCoreAsync(
applicationConfiguration,
EndpointUrl!,
connection: null,
m_description.SecurityMode,
m_description.SecurityPolicyUri!,
telemetry,
ct);
}
Comment thread
marcschier marked this conversation as resolved.

/// <summary>
/// Updates an endpoint with information from the server's discovery endpoint.
/// </summary>
public Task UpdateFromServerAsync(
Uri endpointUrl,
ITransportWaitingConnection? connection,
MessageSecurityMode securityMode,
string securityPolicyUri,
ITelemetryContext? telemetry,
CancellationToken ct = default)
{
return UpdateFromServerCoreAsync(
applicationConfiguration: null,
endpointUrl,
connection,
securityMode,
securityPolicyUri,
telemetry,
ct);
}

private async Task UpdateFromServerCoreAsync(
ApplicationConfiguration? applicationConfiguration,
Uri endpointUrl,
ITransportWaitingConnection? connection,
MessageSecurityMode securityMode,
string securityPolicyUri,
ITelemetryContext? telemetry,
CancellationToken ct)
{
// get the a discovery url.
Uri? discoveryUrl = GetDiscoveryUrl(endpointUrl);
Expand All @@ -1188,7 +1234,15 @@ public async Task UpdateFromServerAsync(
DiscoveryClient? client = null;
try
{
if (connection != null)
if (applicationConfiguration != null)
{
client = await DiscoveryClient.CreateAsync(
applicationConfiguration,
discoveryUrl,
m_configuration,
ct: ct).ConfigureAwait(false);
}
else if (connection != null)
{
client = await DiscoveryClient.CreateAsync(
connection,
Expand Down
17 changes: 17 additions & 0 deletions tests/Opc.Ua.Core.Tests/Stack/Client/ConfiguredEndpointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,23 @@ public void NeedUpdateFromServerReturnsFalseWithNoneSecurity()
Assert.That(endpoint.NeedUpdateFromServer(), Is.False);
}

[Test]
public void UpdateFromServerWithNullApplicationConfigurationThrowsArgumentNullException()
{
var endpoint = new ConfiguredEndpoint(null, new EndpointDescription
{
EndpointUrl = "opc.wss://localhost:4840",
SecurityMode = MessageSecurityMode.None,
SecurityPolicyUri = SecurityPolicies.None
});

ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
endpoint.UpdateFromServerAsync(
(ApplicationConfiguration)null!,
telemetry: null));
Assert.That(ex.ParamName, Is.EqualTo("applicationConfiguration"));
}

[Test]
public void GetDiscoveryUrlWithHttpScheme()
{
Expand Down
19 changes: 19 additions & 0 deletions tests/Opc.Ua.Sessions.Tests/WssJsonTransportIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,25 @@ public void ServerExposesWssEndpointWithSecurityNone()
"Reference server did not advertise an unsecured WSS endpoint - JSON sub-protocol requires SM None.");
}

[Test]
public async Task AnonymousSessionOverWssBinaryOpensWithoutMutualTlsAsync()
{
EndpointDescription wss = m_server.GetEndpoints()
.ToArray()
.First(ep =>
string.Equals(ep.TransportProfileUri, Profiles.UaWssTransport, StringComparison.Ordinal) &&
ep.SecurityMode == MessageSecurityMode.None);
Assert.That(
wss.UserIdentityTokens.ToArray(),
Has.Some.Matches<UserTokenPolicy>(token => token.TokenType == UserTokenType.Anonymous));

using var session = await m_clientFixture
.ConnectAsync(m_endpointUrl.ToString())
.ConfigureAwait(false);
Assert.That(session.Connected, Is.True);
await session.CloseAsync().ConfigureAwait(false);
}
Comment thread
marcschier marked this conversation as resolved.

[Test]
public async Task GetEndpointsOverWssJsonReturnsServerEndpointsAsync()
{
Expand Down
23 changes: 23 additions & 0 deletions tests/Opc.Ua.Sessions.Tests/WssTransportIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,29 @@ public async Task ConnectAndBrowseServerNodeAsync()

await session.CloseAsync().ConfigureAwait(false);
}

[Test]
public async Task UpdateBeforeConnectUsesApplicationCertificateValidationAsync()
{
ConfiguredEndpoint endpoint = await m_clientFixture
.GetEndpointAsync(m_endpointUrl, SecurityPolicies.Basic256Sha256)
.ConfigureAwait(false);

using ISession session = await m_clientFixture.SessionFactory
.CreateAsync(
m_clientFixture.Config,
endpoint,
updateBeforeConnect: true,
checkDomain: false,
nameof(UpdateBeforeConnectUsesApplicationCertificateValidationAsync),
m_clientFixture.SessionTimeout,
identity: null,
preferredLocales: default)
.ConfigureAwait(false);

Assert.That(session.Connected, Is.True);
await session.CloseAsync().ConfigureAwait(false);
}
Comment thread
marcschier marked this conversation as resolved.
}
}

Expand Down
Loading