mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-20 08:41:51 +08:00
23063afacb
## Proposed changes
`ApplicationConfiguration.TraceConfiguration` still existed as a live
configuration surface after `ITelemetryContext` was introduced, which
made the migration path unclear. This change makes that legacy surface
explicitly discoverable as transitional API and points consumers to the
telemetry-based replacement.
- **Public API guidance**
- Marks `ApplicationConfiguration.TraceConfiguration` as obsolete with a
message directing consumers to `ITelemetryContext` and `ILogger`-based
diagnostics.
- Keeps the property functional so existing configuration loading and
legacy trace application continue to work during migration.
- **Internal compatibility**
- Adds targeted `CS0618` suppressions at the remaining internal legacy
call sites that intentionally preserve `TraceConfiguration` behavior.
- Avoids broad warning suppression; only the compatibility path is
annotated.
- **Coverage**
- Adds a focused reflection-based test that verifies the property is
marked obsolete and that the migration message stays stable.
```csharp
[Obsolete("Use ITelemetryContext and ILogger-based diagnostics instead.")]
public TraceConfiguration TraceConfiguration { get; set; }
```
## Related Issues
## Types of changes
What types of changes does your code introduce?
_Put an `x` in the boxes that apply. You can also fill these out after
creating the PR._
- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] Enhancement (non-breaking change which adds functionality)
- [x] Test enhancement (non-breaking change to increase test coverage)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected, requires version increase of
Nuget packages)
- [ ] Documentation Update (if none of the other choices apply)
## Checklist
_Put an `x` in the boxes that apply. You can also fill these out after
creating the PR. If you're unsure about any of them, don't hesitate to
ask. We're here to help! This is simply a reminder of what we are going
to look for before merging your code._
- [ ] I have read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf).
- [x] I ran tests locally with my changes, all passed.
- [ ] I fixed all failing tests in the CI pipelines.
- [x] I fixed all introduced issues with CodeQL and LGTM.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [ ] I have added necessary documentation (if appropriate).
- [ ] Any dependent changes have been merged and published in downstream
modules.
## Further comments
This is intentionally narrow in scope: it clarifies the migration path
without removing the legacy trace pipeline yet. The change is limited to
surfacing the deprecation at the public configuration boundary and
isolating the remaining intentional legacy usages.
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixes #4056
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2955 lines
108 KiB
C#
2955 lines
108 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;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.Serialization;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using Microsoft.Extensions.Logging;
|
|
using Opc.Ua.Bindings;
|
|
using Opc.Ua.Security;
|
|
|
|
namespace Opc.Ua
|
|
{
|
|
/// <summary>
|
|
/// Stores the configurable configuration information for a UA application.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class ApplicationConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ApplicationConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ApplicationConfiguration(ITelemetryContext telemetry)
|
|
{
|
|
Initialize(telemetry);
|
|
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The constructor from a template.
|
|
/// </summary>
|
|
public ApplicationConfiguration(ApplicationConfiguration template)
|
|
{
|
|
ApplicationName = template.ApplicationName;
|
|
ApplicationType = template.ApplicationType;
|
|
ApplicationUri = template.ApplicationUri;
|
|
DiscoveryServerConfiguration = template.DiscoveryServerConfiguration;
|
|
m_securityConfiguration = template.m_securityConfiguration;
|
|
m_transportConfigurations = template.m_transportConfigurations;
|
|
ServerConfiguration = template.ServerConfiguration;
|
|
ClientConfiguration = template.ClientConfiguration;
|
|
DisableHiResClock = template.DisableHiResClock;
|
|
CertificateValidator = template.CertificateValidator;
|
|
TransportQuotas = template.TransportQuotas;
|
|
#pragma warning disable CS0618 // Type or member is obsolete
|
|
TraceConfiguration = template.TraceConfiguration;
|
|
#pragma warning restore CS0618 // Type or member is obsolete
|
|
m_extensions = template.m_extensions;
|
|
m_extensionObjects = template.m_extensionObjects;
|
|
SourceFilePath = template.SourceFilePath;
|
|
m_properties = template.m_properties;
|
|
m_telemetry = template.m_telemetry;
|
|
m_logger = template.m_logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
SourceFilePath = null;
|
|
m_securityConfiguration = new SecurityConfiguration();
|
|
m_transportConfigurations = [];
|
|
DisableHiResClock = false;
|
|
m_properties = [];
|
|
m_extensionObjects = [];
|
|
|
|
CertificateValidator ??= new CertificateValidator(m_telemetry);
|
|
m_logger ??= m_telemetry.CreateLogger<ApplicationConfiguration>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize telemetry context - after loading
|
|
/// </summary>
|
|
/// <param name="telemetry">The telemetry context to use to create obvservability instruments</param>
|
|
internal void Initialize(ITelemetryContext telemetry)
|
|
{
|
|
m_telemetry = telemetry;
|
|
m_logger = telemetry.CreateLogger<ApplicationConfiguration>();
|
|
CertificateValidator = new CertificateValidator(m_telemetry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
m_telemetry = AmbientMessageContext.Telemetry;
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an object used to synchronize access to the properties dictionary.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The object used to synchronize access to the properties dictionary.
|
|
/// </value>
|
|
public object PropertiesLock => m_properties;
|
|
|
|
/// <summary>
|
|
/// Gets a dictionary used to save state associated with the application.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The dictionary used to save state associated with the application.
|
|
/// </value>
|
|
public IDictionary<string, object> Properties => m_properties;
|
|
|
|
/// <summary>
|
|
/// Storage for decoded extensions of the application.
|
|
/// Used by ParseExtension if no matching XmlElement is found.
|
|
/// </summary>
|
|
public IList<object> ExtensionObjects => m_extensionObjects;
|
|
|
|
/// <summary>
|
|
/// A descriptive name for the application (not necessarily unique).
|
|
/// </summary>
|
|
/// <value>The name of the application.</value>
|
|
[DataMember(IsRequired = true, EmitDefaultValue = false, Order = 0)]
|
|
public string ApplicationName { get; set; }
|
|
|
|
/// <summary>
|
|
/// A unique identifier for the application instance.
|
|
/// </summary>
|
|
/// <value>The application URI.</value>
|
|
[DataMember(IsRequired = true, EmitDefaultValue = false, Order = 1)]
|
|
public string ApplicationUri { get; set; }
|
|
|
|
/// <summary>
|
|
/// A unique identifier for the product.
|
|
/// </summary>
|
|
/// <value>The product URI.</value>
|
|
[DataMember(IsRequired = false, Order = 2)]
|
|
public string ProductUri { get; set; }
|
|
|
|
/// <summary>
|
|
/// The type of application.
|
|
/// </summary>
|
|
/// <value>The type of the application.</value>
|
|
[DataMember(IsRequired = true, Order = 3)]
|
|
public ApplicationType ApplicationType { get; set; }
|
|
|
|
/// <summary>
|
|
/// The security configuration for the application.
|
|
/// </summary>
|
|
/// <value>The security configuration.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = true, Order = 4)]
|
|
public SecurityConfiguration SecurityConfiguration
|
|
{
|
|
get => m_securityConfiguration;
|
|
set => m_securityConfiguration = value ?? new SecurityConfiguration();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The transport configuration for the application.
|
|
/// </summary>
|
|
/// <value>The transport configurations.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = true, Order = 5)]
|
|
public TransportConfigurationCollection TransportConfigurations
|
|
{
|
|
get => m_transportConfigurations;
|
|
set => m_transportConfigurations = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The quotas that are used at the transport layer.
|
|
/// </summary>
|
|
/// <value>The transport quotas.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = true, Order = 6)]
|
|
public TransportQuotas TransportQuotas { get; set; }
|
|
|
|
/// <summary>
|
|
/// Additional configuration for server applications.
|
|
/// </summary>
|
|
/// <value>The server configuration.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 7)]
|
|
public ServerConfiguration ServerConfiguration { get; set; }
|
|
|
|
/// <summary>
|
|
/// Additional configuration for client applications.
|
|
/// </summary>
|
|
/// <value>The client configuration.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 8)]
|
|
public ClientConfiguration ClientConfiguration { get; set; }
|
|
|
|
/// <summary>
|
|
/// Additional configuration of the discovery server.
|
|
/// </summary>
|
|
/// <value>The discovery server configuration.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 9)]
|
|
public DiscoveryServerConfiguration DiscoveryServerConfiguration { get; set; }
|
|
|
|
/// <summary>
|
|
/// A bucket to store additional application specific configuration data.
|
|
/// </summary>
|
|
/// <value>The extensions.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 10)]
|
|
public XmlElementCollection Extensions
|
|
{
|
|
get => m_extensions;
|
|
set => m_extensions = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configuration of the trace and information about log file
|
|
/// </summary>
|
|
/// <value>The trace configuration.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 11)]
|
|
[Obsolete("Use ITelemetryContext and ILogger-based diagnostics instead.")]
|
|
public TraceConfiguration TraceConfiguration { get; set; }
|
|
|
|
/// <summary>
|
|
/// Disabling / enabling high resolution clock
|
|
/// </summary>
|
|
/// <value><c>true</c> if high resolution clock is disabled; otherwise, <c>false</c>.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 12)]
|
|
public bool DisableHiResClock { get; set; }
|
|
|
|
private ITelemetryContext m_telemetry;
|
|
private ILogger m_logger;
|
|
private SecurityConfiguration m_securityConfiguration;
|
|
private TransportConfigurationCollection m_transportConfigurations;
|
|
private XmlElementCollection m_extensions;
|
|
private List<object> m_extensionObjects;
|
|
private Dictionary<string, object> m_properties;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies various limits that apply to the transport or secure channel layers.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class TransportQuotas
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public TransportQuotas()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
// encoding limits
|
|
MaxMessageSize = DefaultEncodingLimits.MaxMessageSize;
|
|
MaxStringLength = DefaultEncodingLimits.MaxStringLength;
|
|
MaxByteStringLength = DefaultEncodingLimits.MaxByteStringLength;
|
|
MaxArrayLength = DefaultEncodingLimits.MaxArrayLength;
|
|
MaxEncodingNestingLevels = DefaultEncodingLimits.MaxEncodingNestingLevels;
|
|
MaxDecoderRecoveries = DefaultEncodingLimits.MaxDecoderRecoveries;
|
|
|
|
// message limits
|
|
MaxBufferSize = TcpMessageLimits.DefaultMaxBufferSize;
|
|
OperationTimeout = TcpMessageLimits.DefaultOperationTimeout;
|
|
ChannelLifetime = TcpMessageLimits.DefaultChannelLifetime;
|
|
SecurityTokenLifetime = TcpMessageLimits.DefaultSecurityTokenLifeTime;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default timeout to use when sending requests (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The operation timeout.</value>
|
|
[DataMember(IsRequired = false, Order = 0)]
|
|
public int OperationTimeout { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum length of string encoded in a message body.
|
|
/// </summary>
|
|
/// <value>The max length of the string.</value>
|
|
[DataMember(IsRequired = false, Order = 1)]
|
|
public int MaxStringLength { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum length of a byte string encoded in a message body.
|
|
/// </summary>
|
|
/// <value>The max length of the byte string.</value>
|
|
[DataMember(IsRequired = false, Order = 2)]
|
|
public int MaxByteStringLength { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum length of an array encoded in a message body.
|
|
/// </summary>
|
|
/// <value>The max length of the array.</value>
|
|
[DataMember(IsRequired = false, Order = 3)]
|
|
public int MaxArrayLength { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum length of a message body.
|
|
/// </summary>
|
|
/// <value>The max size of the message.</value>
|
|
[DataMember(IsRequired = false, Order = 4)]
|
|
public int MaxMessageSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum size of the buffer to use when sending messages.
|
|
/// </summary>
|
|
/// <value>The max size of the buffer.</value>
|
|
[DataMember(IsRequired = false, Order = 5)]
|
|
public int MaxBufferSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum nesting level accepted while encoding or decoding objects.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, Order = 6)]
|
|
public int MaxEncodingNestingLevels { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of times the decoder can recover from a decoder error
|
|
/// of an IEncodeable before throwing a decoder error.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, Order = 7)]
|
|
public int MaxDecoderRecoveries { get; set; }
|
|
|
|
/// <summary>
|
|
/// The lifetime of a secure channel (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The channel lifetime.</value>
|
|
[DataMember(IsRequired = false, Order = 8)]
|
|
public int ChannelLifetime { get; set; }
|
|
|
|
/// <summary>
|
|
/// The lifetime of a security token (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The security token lifetime.</value>
|
|
[DataMember(IsRequired = false, Order = 9)]
|
|
public int SecurityTokenLifetime { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies parameters used for tracing.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class TraceConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public TraceConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
OutputFilePath = null;
|
|
DeleteOnLoad = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The output file used to log the trace information.
|
|
/// </summary>
|
|
/// <value>The output file path.</value>
|
|
[DataMember(IsRequired = false, Order = 0)]
|
|
public string OutputFilePath { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the existing log file should be deleted when the application configuration is loaded.
|
|
/// </summary>
|
|
/// <value><c>true</c> if existing log file should be deleted when the application configuration is loaded; otherwise, <c>false</c>.</value>
|
|
[DataMember(IsRequired = false, Order = 1)]
|
|
public bool DeleteOnLoad { get; set; }
|
|
|
|
/// <summary>
|
|
/// The masks used to select what is written to the output
|
|
/// Masks supported by the trace feature:
|
|
/// - Do not output any messages -None = 0x0;
|
|
/// - Output error messages - Error = 0x1;
|
|
/// - Output informational messages - Information = 0x2;
|
|
/// - Output stack traces - StackTrace = 0x4;
|
|
/// - Output basic messages for service calls - Service = 0x8;
|
|
/// - Output detailed messages for service calls - ServiceDetail = 0x10;
|
|
/// - Output basic messages for each operation - Operation = 0x20;
|
|
/// - Output detailed messages for each operation - OperationDetail = 0x40;
|
|
/// - Output messages related to application initialization or shutdown - StartStop = 0x80;
|
|
/// - Output messages related to a call to an external system - ExternalSystem = 0x100;
|
|
/// - Output messages related to security. - Security = 0x200;
|
|
/// </summary>
|
|
/// <value>The trace masks.</value>
|
|
[DataMember(IsRequired = false, Order = 2)]
|
|
public int TraceMasks { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the configuration information for a transport protocol
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Each application is allows to have one transport configure per protocol type.
|
|
/// </remarks>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class TransportConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public TransportConfiguration()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
/// <param name="urlScheme">The URL scheme.</param>
|
|
/// <param name="type">The type.</param>
|
|
public TransportConfiguration(string urlScheme, Type type)
|
|
{
|
|
UriScheme = urlScheme;
|
|
TypeName = type.AssemblyQualifiedName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The URL prefix used by the application (http, opc.tcp, net.tpc, etc.).
|
|
/// </summary>
|
|
/// <value>The URI scheme.</value>
|
|
[DataMember(IsRequired = true, EmitDefaultValue = false, Order = 0)]
|
|
public string UriScheme { get; set; }
|
|
|
|
/// <summary>
|
|
/// The name of the class that defines the binding for the transport.
|
|
/// </summary>
|
|
/// <value>The name of the type.</value>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This can be any instance of the System.ServiceModel.Channels.Binding class
|
|
/// that implements these constructors:
|
|
/// </para>
|
|
/// <para>
|
|
/// XxxBinding(EndpointDescription description, EndpointConfiguration configuration);
|
|
/// XxxBinding(IList{EndpointDescription} descriptions, EndpointConfiguration configuration)
|
|
/// XxxBinding(EndpointConfiguration configuration)
|
|
/// </para>
|
|
/// </remarks>
|
|
[DataMember(IsRequired = true, EmitDefaultValue = false, Order = 1)]
|
|
public string TypeName { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of TransportConfiguration objects.
|
|
/// </summary>
|
|
[CollectionDataContract(
|
|
Name = "ListOfTransportConfiguration",
|
|
Namespace = Namespaces.OpcUaConfig,
|
|
ItemName = "TransportConfiguration"
|
|
)]
|
|
public class TransportConfigurationCollection : List<TransportConfiguration>
|
|
{
|
|
/// <summary>
|
|
/// Initializes an empty collection.
|
|
/// </summary>
|
|
public TransportConfigurationCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection from another collection.
|
|
/// </summary>
|
|
/// <param name="collection">A collection of values to add to this new collection</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// <paramref name="collection"/> is null.
|
|
/// </exception>
|
|
public TransportConfigurationCollection(IEnumerable<TransportConfiguration> collection)
|
|
: base(collection)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection with the specified capacity.
|
|
/// </summary>
|
|
/// <param name="capacity">The capacity.</param>
|
|
public TransportConfigurationCollection(int capacity)
|
|
: base(capacity)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A class that defines a group of security policies supported by the server.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class ServerSecurityPolicy
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ServerSecurityPolicy()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
SecurityMode = MessageSecurityMode.SignAndEncrypt;
|
|
SecurityPolicyUri = SecurityPolicies.Basic256Sha256;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obsolete version of CalculateSecurityLevel that does not take a logger.
|
|
/// </summary>
|
|
[Obsolete("Use CalculateSecurityLevel(MessageSecurityMode mode, string policyUri, ILogger logger) instead.")]
|
|
public static byte CalculateSecurityLevel(
|
|
MessageSecurityMode mode,
|
|
string policyUri)
|
|
{
|
|
return SecuredApplication.CalculateSecurityLevel(mode, policyUri);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the security level, given the security mode and policy
|
|
/// Invalid and none is discouraged
|
|
/// Just signing is always weaker than any use of encryption
|
|
/// </summary>
|
|
public static byte CalculateSecurityLevel(
|
|
MessageSecurityMode mode,
|
|
string policyUri,
|
|
ILogger logger)
|
|
{
|
|
return SecuredApplication.CalculateSecurityLevel(mode, policyUri, logger);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies whether the messages are signed and encrypted or simply signed
|
|
/// </summary>
|
|
/// <value>The security mode.</value>
|
|
[DataMember(IsRequired = false, Order = 1)]
|
|
public MessageSecurityMode SecurityMode { get; set; }
|
|
|
|
/// <summary>
|
|
/// The security policy to use.
|
|
/// </summary>
|
|
/// <value>The security policy URI.</value>
|
|
[DataMember(IsRequired = false, Order = 2)]
|
|
public string SecurityPolicyUri { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of ServerSecurityPolicy objects.
|
|
/// </summary>
|
|
[CollectionDataContract(
|
|
Name = "ListOfServerSecurityPolicy",
|
|
Namespace = Namespaces.OpcUaConfig,
|
|
ItemName = "ServerSecurityPolicy"
|
|
)]
|
|
public class ServerSecurityPolicyCollection : List<ServerSecurityPolicy>
|
|
{
|
|
/// <summary>
|
|
/// Initializes an empty collection.
|
|
/// </summary>
|
|
public ServerSecurityPolicyCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection from another collection.
|
|
/// </summary>
|
|
/// <param name="collection">A collection of values to add to this new collection</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// <paramref name="collection"/> is null.
|
|
/// </exception>
|
|
public ServerSecurityPolicyCollection(IEnumerable<ServerSecurityPolicy> collection)
|
|
: base(collection)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection with the specified capacity.
|
|
/// </summary>
|
|
/// <param name="capacity">The capacity.</param>
|
|
public ServerSecurityPolicyCollection(int capacity)
|
|
: base(capacity)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The security configuration for the application.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class SecurityConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public SecurityConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_applicationCertificates = [];
|
|
m_trustedIssuerCertificates = new CertificateTrustList();
|
|
m_trustedPeerCertificates = new CertificateTrustList();
|
|
NonceLength = 32;
|
|
MaxRejectedCertificates = 5;
|
|
AutoAcceptUntrustedCertificates = false;
|
|
RejectSHA1SignedCertificates = true;
|
|
RejectUnknownRevocationStatus = false;
|
|
MinimumCertificateKeySize = CertificateFactory.DefaultKeySize;
|
|
AddAppCertToTrustedStore = true;
|
|
SendCertificateChain = true;
|
|
SuppressNonceValidationErrors = false;
|
|
IsDeprecatedConfiguration = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The application instance certificate.
|
|
/// Kept for backward compatibility with configuration files which only support RSA certificates.
|
|
/// </summary>
|
|
/// <value>The application certificate.</value>
|
|
/// <remarks>
|
|
/// This certificate must contain the application uri.
|
|
/// For servers, URLs for each supported protocol must also be present.
|
|
/// </remarks>
|
|
[IgnoreDataMember]
|
|
public CertificateIdentifier ApplicationCertificate
|
|
{
|
|
get
|
|
{
|
|
if (m_applicationCertificates.Count > 0)
|
|
{
|
|
return m_applicationCertificates[0];
|
|
}
|
|
return null;
|
|
}
|
|
set
|
|
{
|
|
if (m_applicationCertificates.Count > 0)
|
|
{
|
|
if (value == null)
|
|
{
|
|
m_applicationCertificates.RemoveAt(0);
|
|
}
|
|
else
|
|
{
|
|
m_applicationCertificates[0] = value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_applicationCertificates.Add(value);
|
|
}
|
|
SupportedSecurityPolicies = BuildSupportedSecurityPolicies();
|
|
|
|
m_applicationCertificates[0].CertificateType = ObjectTypeIds
|
|
.RsaSha256ApplicationCertificateType;
|
|
IsDeprecatedConfiguration = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This private property exists solely to control serialization of the legacy single
|
|
/// certificate element. It is emitted only when the configuration was marked deprecated.
|
|
/// </summary>
|
|
[DataMember(Name = "ApplicationCertificate", IsRequired = false, EmitDefaultValue = false, Order = 0)]
|
|
private CertificateIdentifier ApplicationCertificateLegacy
|
|
{
|
|
get => IsDeprecatedConfiguration ? ApplicationCertificate : null;
|
|
set => ApplicationCertificate = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The application instance certificates in use for the application.
|
|
/// </summary>
|
|
[IgnoreDataMember]
|
|
public CertificateIdentifierCollection ApplicationCertificates
|
|
{
|
|
get => m_applicationCertificates;
|
|
set
|
|
{
|
|
if (value == null || value.Count == 0)
|
|
{
|
|
m_applicationCertificates = [];
|
|
return;
|
|
}
|
|
|
|
// If both legacy (<ApplicationCertificate>) and modern (<ApplicationCertificates>) elements
|
|
// are present during deserialization (as a consequence of previous serialization that included both unintentionally),
|
|
// prefer the modern representation and clear the
|
|
// deprecated flag when we process the collection below.
|
|
|
|
var newCertificates = new CertificateIdentifierCollection(value);
|
|
|
|
// Remove unsupported certificate types
|
|
for (int i = newCertificates.Count - 1; i >= 0; i--)
|
|
{
|
|
if (!Utils.IsSupportedCertificateType(newCertificates[i].CertificateType))
|
|
{
|
|
// TODO: Log when ITelemetry instance is available
|
|
newCertificates.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
// Remove any duplicates based on thumbprint
|
|
// Only perform duplicate detection if we have actual loaded certificates
|
|
for (int i = 0; i < newCertificates.Count; i++)
|
|
{
|
|
for (int j = newCertificates.Count - 1; j > i; j--)
|
|
{
|
|
bool isDuplicate = false;
|
|
|
|
// Only check for duplicates if both certificates are actually loaded
|
|
if (newCertificates[i].Certificate != null && newCertificates[j].Certificate != null)
|
|
{
|
|
// Compare by actual certificate thumbprint
|
|
isDuplicate = newCertificates[i].Certificate.Thumbprint.Equals(
|
|
newCertificates[j].Certificate.Thumbprint,
|
|
StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
// If certificates aren't loaded yet, compare by explicit thumbprint configuration
|
|
else if (!string.IsNullOrEmpty(newCertificates[i].Thumbprint) &&
|
|
!string.IsNullOrEmpty(newCertificates[j].Thumbprint))
|
|
{
|
|
isDuplicate = newCertificates[i].Thumbprint.Equals(
|
|
newCertificates[j].Thumbprint,
|
|
StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
if (isDuplicate)
|
|
{
|
|
newCertificates.RemoveAt(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_applicationCertificates = newCertificates;
|
|
|
|
// Presence of the modern collection takes precedence over legacy; clear the flag so
|
|
// hybrid configurations are treated as modern.
|
|
IsDeprecatedConfiguration = false;
|
|
SupportedSecurityPolicies = BuildSupportedSecurityPolicies();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This private property exists solely to control the serialization of the modern certificates collection.
|
|
/// Emit only when the configuration is not marked deprecated.
|
|
/// </summary>
|
|
[DataMember(Name = "ApplicationCertificates", IsRequired = false, EmitDefaultValue = false, Order = 1)]
|
|
private CertificateIdentifierCollection ApplicationCertificatesDataContract
|
|
{
|
|
get => IsDeprecatedConfiguration ? null : ApplicationCertificates;
|
|
set => ApplicationCertificates = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The store containing any additional issuer certificates.
|
|
/// </summary>
|
|
[DataMember(IsRequired = true, EmitDefaultValue = false, Order = 2)]
|
|
public CertificateTrustList TrustedIssuerCertificates
|
|
{
|
|
get => m_trustedIssuerCertificates;
|
|
set => m_trustedIssuerCertificates = value ?? new CertificateTrustList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The trusted certificate store.
|
|
/// </summary>
|
|
[DataMember(IsRequired = true, EmitDefaultValue = false, Order = 4)]
|
|
public CertificateTrustList TrustedPeerCertificates
|
|
{
|
|
get => m_trustedPeerCertificates;
|
|
set => m_trustedPeerCertificates = value ?? new CertificateTrustList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The length of nonce in the CreateSession service.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The length of nonce in the CreateSession service.
|
|
/// </value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 6)]
|
|
public int NonceLength { get; set; }
|
|
|
|
/// <summary>
|
|
/// A store where invalid certificates can be placed for later review by the administrator.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A store where invalid certificates can be placed for later review by the administrator.
|
|
/// </value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 7)]
|
|
public CertificateStoreIdentifier RejectedCertificateStore { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating how many certificates are kept
|
|
/// in the rejected store before the oldest is removed.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This value can be set by applications.
|
|
/// The number of certificates to keep in the rejected store before it is updated.
|
|
/// <see langword="0"/> to keep all rejected certificates.
|
|
/// A negative number to keep no history.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 8)]
|
|
public int MaxRejectedCertificates { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether untrusted certificates should be automatically accepted.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This flag can be set to by servers that allow anonymous clients or use user credentials for authentication.
|
|
/// It can be set by clients that connect to URLs specified in configuration rather than with user entry.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 9)]
|
|
public bool AutoAcceptUntrustedCertificates { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a directory which contains files representing users roles.
|
|
/// </summary>
|
|
[DataMember(Order = 10)]
|
|
public string UserRoleDirectory { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether SHA-1 signed certificates are accepted.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This flag can be set to false by servers that accept SHA-1 signed certificates.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 11)]
|
|
public bool RejectSHA1SignedCertificates { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether certificates with unavailable revocation lists are not accepted.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This flag can be set to true by servers that must have a revocation list for each CA (even if empty).
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 12)]
|
|
public bool RejectUnknownRevocationStatus { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating which minimum certificate key strength is accepted.
|
|
/// The value is ignored for certificates with a ECDSA signature.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This value can be set to 1024, 2048 or 4096 by servers
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 13)]
|
|
public ushort MinimumCertificateKeySize { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the Validator skips the full chain validation
|
|
/// for already validated or accepted certificates.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This flag can be set to true by applications.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 14)]
|
|
public bool UseValidatedCertificates { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the application cert should be copied to the trusted store.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is useful for client/server applications running on the same host and sharing the cert store to autotrust.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 15)]
|
|
public bool AddAppCertToTrustedStore { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the application should send the complete certificate chain.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// If set to true the complete certificate chain will be sent for CA signed certificates.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 16)]
|
|
public bool SendCertificateChain { get; set; }
|
|
|
|
/// <summary>
|
|
/// The store containing additional user issuer certificates.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 17)]
|
|
public CertificateTrustList UserIssuerCertificates
|
|
{
|
|
get => m_userIssuerCertificates;
|
|
set => m_userIssuerCertificates = value ?? new CertificateTrustList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The store containing trusted user certificates.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 18)]
|
|
public CertificateTrustList TrustedUserCertificates
|
|
{
|
|
get => m_trustedUserCertificates;
|
|
set => m_trustedUserCertificates = value ?? new CertificateTrustList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The store containing additional Https issuer certificates.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 19)]
|
|
public CertificateTrustList HttpsIssuerCertificates
|
|
{
|
|
get => m_httpsIssuerCertificates;
|
|
set => m_httpsIssuerCertificates = value ?? new CertificateTrustList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The store containing trusted Https certificates.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 20)]
|
|
public CertificateTrustList TrustedHttpsCertificates
|
|
{
|
|
get => m_trustedHttpsCertificates;
|
|
set => m_trustedHttpsCertificates = value ?? new CertificateTrustList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the server nonce validation errors should be suppressed.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Allows client interoperability with legacy servers which do not comply with the specification for nonce usage.
|
|
/// If set to true the server nonce validation errors are suppressed.
|
|
/// Please set this flag to true only in close and secured networks since it can cause security vulnerabilities.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 21)]
|
|
public bool SuppressNonceValidationErrors { get; set; }
|
|
|
|
/// <summary>
|
|
/// The type of Configuration (deprecated or not)
|
|
/// </summary>
|
|
public bool IsDeprecatedConfiguration { get; set; }
|
|
|
|
private CertificateIdentifierCollection m_applicationCertificates;
|
|
private CertificateTrustList m_trustedIssuerCertificates;
|
|
private CertificateTrustList m_trustedPeerCertificates;
|
|
private CertificateTrustList m_httpsIssuerCertificates;
|
|
private CertificateTrustList m_trustedHttpsCertificates;
|
|
private CertificateTrustList m_userIssuerCertificates;
|
|
private CertificateTrustList m_trustedUserCertificates;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A class that defines a group of sampling rates supported by the server.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class SamplingRateGroup
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public SamplingRateGroup()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a group with the specified settings.
|
|
/// </summary>
|
|
/// <param name="start">The start.</param>
|
|
/// <param name="increment">The increment.</param>
|
|
/// <param name="count">The count.</param>
|
|
public SamplingRateGroup(int start, int increment, int count)
|
|
{
|
|
Start = start;
|
|
Increment = increment;
|
|
Count = count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
Start = 1000;
|
|
Increment = 0;
|
|
Count = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The first sampling rate in the group (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The first sampling rate in the group (in milliseconds).</value>
|
|
[DataMember(IsRequired = false, Order = 1)]
|
|
public double Start { get; set; }
|
|
|
|
/// <summary>
|
|
/// The increment between sampling rates in the group (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The increment.</value>
|
|
/// <remarks>
|
|
/// An increment of 0 means the group only contains one sampling rate equal to the start.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, Order = 2)]
|
|
public double Increment { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of sampling rates in the group.
|
|
/// </summary>
|
|
/// <value>The count.</value>
|
|
/// <remarks>
|
|
/// A count of 0 means there is no limit.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, Order = 3)]
|
|
public int Count { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of SamplingRateGroup objects.
|
|
/// </summary>
|
|
[CollectionDataContract(
|
|
Name = "ListOfSamplingRateGroup",
|
|
Namespace = Namespaces.OpcUaConfig,
|
|
ItemName = "SamplingRateGroup"
|
|
)]
|
|
public class SamplingRateGroupCollection : List<SamplingRateGroup>
|
|
{
|
|
/// <summary>
|
|
/// Initializes an empty collection.
|
|
/// </summary>
|
|
public SamplingRateGroupCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection from another collection.
|
|
/// </summary>
|
|
/// <param name="collection">A collection of values to add to this new collection</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// <paramref name="collection"/> is null.
|
|
/// </exception>
|
|
public SamplingRateGroupCollection(IEnumerable<SamplingRateGroup> collection)
|
|
: base(collection)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection with the specified capacity.
|
|
/// </summary>
|
|
/// <param name="capacity">The capacity.</param>
|
|
public SamplingRateGroupCollection(int capacity)
|
|
: base(capacity)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the configuration for a server application.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class ServerBaseConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ServerBaseConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_baseAddresses = [];
|
|
m_alternateBaseAddresses = [];
|
|
m_securityPolicies = [];
|
|
MinRequestThreadCount = 10;
|
|
MaxRequestThreadCount = 100;
|
|
MaxQueuedRequestCount = 200;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove unsupported security policies and expand wild cards.
|
|
/// </summary>
|
|
[OnDeserialized]
|
|
private void ValidateSecurityPolicyCollection(StreamingContext context)
|
|
{
|
|
string[] supportedPolicies = Ua.SecurityPolicies.GetDisplayNames();
|
|
var newPolicies = new ServerSecurityPolicyCollection();
|
|
foreach (ServerSecurityPolicy securityPolicy in m_securityPolicies)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(securityPolicy.SecurityPolicyUri))
|
|
{
|
|
// add wild card policies
|
|
foreach (string policyUri in Ua.SecurityPolicies.GetDefaultUris())
|
|
{
|
|
var newPolicy = new ServerSecurityPolicy
|
|
{
|
|
SecurityMode = securityPolicy.SecurityMode,
|
|
SecurityPolicyUri = policyUri
|
|
};
|
|
if (newPolicies.Find(s =>
|
|
s.SecurityMode == newPolicy.SecurityMode &&
|
|
string.Equals(
|
|
s.SecurityPolicyUri,
|
|
newPolicy.SecurityPolicyUri,
|
|
StringComparison.Ordinal)
|
|
) == null)
|
|
{
|
|
newPolicies.Add(newPolicy);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < supportedPolicies.Length; i++)
|
|
{
|
|
if (securityPolicy.SecurityPolicyUri
|
|
.Contains(supportedPolicies[i], StringComparison.Ordinal))
|
|
{
|
|
if (newPolicies.Find(s =>
|
|
s.SecurityMode == securityPolicy.SecurityMode &&
|
|
string.Equals(
|
|
s.SecurityPolicyUri,
|
|
securityPolicy.SecurityPolicyUri,
|
|
StringComparison.Ordinal)
|
|
) == null)
|
|
{
|
|
newPolicies.Add(securityPolicy);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_securityPolicies = newPolicies;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The base addresses for the server.
|
|
/// </summary>
|
|
/// <value>The base addresses.</value>
|
|
/// <remarks>
|
|
/// The actually endpoints are constructed from the security policies.
|
|
/// On one base address per supported transport protocol is allowed.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, Order = 0)]
|
|
public StringCollection BaseAddresses
|
|
{
|
|
get => m_baseAddresses;
|
|
set => m_baseAddresses = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the alternate base addresses.
|
|
/// </summary>
|
|
/// <value>The alternate base addresses.</value>
|
|
/// <remarks>
|
|
/// These addresses are used to specify alternate paths to ther via firewalls, proxies
|
|
/// or similar network infrastructure. If these paths are specified in the configuration
|
|
/// file then the server will use the domain of the URL used by the client to determine
|
|
/// which, if any, or the alternate addresses to use instead of the primary addresses.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, Order = 1)]
|
|
public StringCollection AlternateBaseAddresses
|
|
{
|
|
get => m_alternateBaseAddresses;
|
|
set => m_alternateBaseAddresses = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The security policies supported by the server.
|
|
/// </summary>
|
|
/// <value>The security policies.</value>
|
|
/// <remarks>
|
|
/// An endpoint description is created for each combination of base address and security policy.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, Order = 2)]
|
|
public ServerSecurityPolicyCollection SecurityPolicies
|
|
{
|
|
get => m_securityPolicies;
|
|
set => m_securityPolicies = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The minimum number of threads assigned to processing requests.
|
|
/// </summary>
|
|
/// <value>The minimum request thread count.</value>
|
|
[DataMember(IsRequired = false, Order = 3)]
|
|
public int MinRequestThreadCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of threads assigned to processing requests.
|
|
/// </summary>
|
|
/// <value>The maximum request thread count.</value>
|
|
[DataMember(IsRequired = false, Order = 4)]
|
|
public int MaxRequestThreadCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of requests that will be queued waiting for a thread.
|
|
/// </summary>
|
|
/// <value>The maximum queued request count.</value>
|
|
[DataMember(IsRequired = false, Order = 5)]
|
|
public int MaxQueuedRequestCount { get; set; }
|
|
|
|
private StringCollection m_baseAddresses;
|
|
private StringCollection m_alternateBaseAddresses;
|
|
private ServerSecurityPolicyCollection m_securityPolicies;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the configuration for a server application.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class ServerConfiguration : ServerBaseConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ServerConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_userTokenPolicies = [];
|
|
DiagnosticsEnabled = false;
|
|
MaxSessionCount = 100;
|
|
MaxChannelCount = 1000;
|
|
MaxSessionTimeout = 3600000;
|
|
MinSessionTimeout = 10000;
|
|
MaxBrowseContinuationPoints = 10;
|
|
MaxQueryContinuationPoints = 10;
|
|
MaxHistoryContinuationPoints = 100;
|
|
MaxRequestAge = 600000;
|
|
MinPublishingInterval = 100;
|
|
MaxPublishingInterval = 3600000;
|
|
PublishingResolution = 100;
|
|
MinSubscriptionLifetime = 10000;
|
|
MaxSubscriptionLifetime = 3600000;
|
|
MaxMessageQueueSize = 10;
|
|
MaxNotificationQueueSize = 100;
|
|
MaxNotificationsPerPublish = 100;
|
|
MinMetadataSamplingInterval = 1000;
|
|
AvailableSamplingRates = [];
|
|
RegistrationEndpoint = null;
|
|
MaxRegistrationInterval = 30000;
|
|
MaxPublishRequestCount = 20;
|
|
MaxSubscriptionCount = 100;
|
|
MaxEventQueueSize = 10000;
|
|
// https://opcfoundation-onlineapplications.org/profilereporting/ for list of available profiles
|
|
m_serverProfileArray = new string[] {
|
|
"http://opcfoundation.org/UA-Profile/Server/StandardUA2017" };
|
|
ShutdownDelay = 5;
|
|
m_serverCapabilities = new string[] { "DA" };
|
|
m_supportedPrivateKeyFormats = new string[] { "PFX", "PEM" };
|
|
MaxTrustListSize = 0;
|
|
MultiCastDnsEnabled = false;
|
|
OperationLimits = new OperationLimits();
|
|
AuditingEnabled = false;
|
|
HttpsMutualTls = true;
|
|
DurableSubscriptionsEnabled = false;
|
|
MaxDurableNotificationQueueSize = 200000;
|
|
MaxDurableEventQueueSize = 200000;
|
|
MaxDurableSubscriptionLifetimeInHours = 10;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public new void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The user tokens accepted by the server.
|
|
/// </summary>
|
|
/// <value>The user token policies.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 3)]
|
|
public UserTokenPolicyCollection UserTokenPolicies
|
|
{
|
|
get => m_userTokenPolicies;
|
|
set => m_userTokenPolicies = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether diagnostics are enabled.
|
|
/// </summary>
|
|
/// <value><c>true</c> if diagnostic is enabled; otherwise, <c>false</c>.</value>
|
|
[DataMember(IsRequired = false, Order = 4)]
|
|
public bool DiagnosticsEnabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of open sessions.
|
|
/// </summary>
|
|
/// <value>The maximum session count.</value>
|
|
[DataMember(IsRequired = false, Order = 5)]
|
|
public int MaxSessionCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of supported secure channels.
|
|
/// </summary>
|
|
/// <value>The channel lifetime.</value>
|
|
[DataMember(IsRequired = false, Order = 6)]
|
|
public int MaxChannelCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// That minimum period of that a session is allowed to remain
|
|
/// open without communication from the client (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The minimum session timeout.</value>
|
|
[DataMember(IsRequired = false, Order = 7)]
|
|
public int MinSessionTimeout { get; set; }
|
|
|
|
/// <summary>
|
|
/// That maximum period of that a session is allowed to remain
|
|
/// open without communication from the client (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The maximum session timeout.</value>
|
|
[DataMember(IsRequired = false, Order = 8)]
|
|
public int MaxSessionTimeout { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of continuation points used for
|
|
/// Browse/BrowseNext operations.
|
|
/// </summary>
|
|
/// <value>The maximum number of continuation points used for Browse/BrowseNext operations</value>
|
|
[DataMember(IsRequired = false, Order = 9)]
|
|
public int MaxBrowseContinuationPoints { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of continuation points used for
|
|
/// Query/QueryNext operations.
|
|
/// </summary>
|
|
/// <value>The maximum number of query continuation points.</value>
|
|
[DataMember(IsRequired = false, Order = 10)]
|
|
public int MaxQueryContinuationPoints { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of continuation points used for HistoryRead operations.
|
|
/// </summary>
|
|
/// <value>The maximum number of history continuation points.</value>
|
|
[DataMember(IsRequired = false, Order = 11)]
|
|
public int MaxHistoryContinuationPoints { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum age of an incoming request (old requests are rejected) (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The maximum age of an incoming request.</value>
|
|
[DataMember(IsRequired = false, Order = 12)]
|
|
public int MaxRequestAge { get; set; }
|
|
|
|
/// <summary>
|
|
/// The minimum publishing interval supported by the server (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The minimum publishing interval.</value>
|
|
[DataMember(IsRequired = false, Order = 13)]
|
|
public int MinPublishingInterval { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum publishing interval supported by the server (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The maximum publishing interval.</value>
|
|
[DataMember(IsRequired = false, Order = 14)]
|
|
public int MaxPublishingInterval { get; set; }
|
|
|
|
/// <summary>
|
|
/// The minimum difference between supported publishing interval (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The publishing resolution.</value>
|
|
[DataMember(IsRequired = false, Order = 15)]
|
|
public int PublishingResolution { get; set; }
|
|
|
|
/// <summary>
|
|
/// How long the subscriptions will remain open without a publish from the client.
|
|
/// </summary>
|
|
/// <value>The maximum subscription lifetime.</value>
|
|
[DataMember(IsRequired = false, Order = 16)]
|
|
public int MaxSubscriptionLifetime { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of messages saved in the queue for each subscription.
|
|
/// </summary>
|
|
/// <value>The maximum size of the message queue.</value>
|
|
[DataMember(IsRequired = false, Order = 17)]
|
|
public int MaxMessageQueueSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of notificates saved in the queue for each monitored item.
|
|
/// </summary>
|
|
/// <value>The maximum size of the notification queue.</value>
|
|
[DataMember(IsRequired = false, Order = 18)]
|
|
public int MaxNotificationQueueSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of notifications per publish.
|
|
/// </summary>
|
|
/// <value>The maximum number of notifications per publish.</value>
|
|
[DataMember(IsRequired = false, Order = 19)]
|
|
public int MaxNotificationsPerPublish { get; set; }
|
|
|
|
/// <summary>
|
|
/// The minimum sampling interval for metadata.
|
|
/// </summary>
|
|
/// <value>The minimum sampling interval for metadata.</value>
|
|
[DataMember(IsRequired = false, Order = 20)]
|
|
public int MinMetadataSamplingInterval { get; set; }
|
|
|
|
/// <summary>
|
|
/// The available sampling rates.
|
|
/// </summary>
|
|
/// <value>The available sampling rates.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 21)]
|
|
public SamplingRateGroupCollection AvailableSamplingRates { get; set; }
|
|
|
|
/// <summary>
|
|
/// The endpoint description for the registration endpoint.
|
|
/// </summary>
|
|
/// <value>The registration endpoint.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 22)]
|
|
public EndpointDescription RegistrationEndpoint { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum time between registration attempts (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The maximum time between registration attempts (in milliseconds).</value>
|
|
[DataMember(IsRequired = false, Order = 23)]
|
|
public int MaxRegistrationInterval { get; set; }
|
|
|
|
/// <summary>
|
|
/// The path to the file containing nodes persisted by the core node manager.
|
|
/// </summary>
|
|
/// <value>The path to the file containing nodes persisted by the core node manager.</value>
|
|
[DataMember(IsRequired = false, Order = 24)]
|
|
public string NodeManagerSaveFile { get; set; }
|
|
|
|
/// <summary>
|
|
/// The minimum lifetime for a subscription (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The minimum lifetime for a subscription.</value>
|
|
[DataMember(IsRequired = false, Order = 25)]
|
|
public int MinSubscriptionLifetime { get; set; }
|
|
|
|
/// <summary>
|
|
/// The max publish request count.
|
|
/// </summary>
|
|
/// <value>The max publish request count.</value>
|
|
[DataMember(IsRequired = false, Order = 26)]
|
|
public int MaxPublishRequestCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The max subscription count.
|
|
/// </summary>
|
|
/// <value>The max subscription count.</value>
|
|
[DataMember(IsRequired = false, Order = 27)]
|
|
public int MaxSubscriptionCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The max size of the event queue.
|
|
/// </summary>
|
|
/// <value>The max size of the event queue.</value>
|
|
[DataMember(IsRequired = false, Order = 28)]
|
|
public int MaxEventQueueSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// The server profile array.
|
|
/// </summary>
|
|
/// <value>The array of server profiles.</value>
|
|
[DataMember(IsRequired = false, Order = 29)]
|
|
public StringCollection ServerProfileArray
|
|
{
|
|
get => m_serverProfileArray;
|
|
set => m_serverProfileArray = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The server shutdown delay.
|
|
/// </summary>
|
|
/// <value>The number of seconds to delay the shutdown if a client is connected.</value>
|
|
[DataMember(IsRequired = false, Order = 30)]
|
|
public int ShutdownDelay { get; set; }
|
|
|
|
/// <summary>
|
|
/// The server capabilities.
|
|
/// The latest set of server capabilities is listed
|
|
/// <see href="http://www.opcfoundation.org/UA/schemas/1.05/ServerCapabilities.csv">here.</see>
|
|
/// </summary>
|
|
/// <value>The array of server capabilites.</value>
|
|
[DataMember(IsRequired = false, Order = 31)]
|
|
public StringCollection ServerCapabilities
|
|
{
|
|
get => m_serverCapabilities;
|
|
set => m_serverCapabilities = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the supported private key format.
|
|
/// </summary>
|
|
/// <value>The array of server profiles.</value>
|
|
[DataMember(IsRequired = false, Order = 32)]
|
|
public StringCollection SupportedPrivateKeyFormats
|
|
{
|
|
get => m_supportedPrivateKeyFormats;
|
|
set => m_supportedPrivateKeyFormats = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the max size of the trust list.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, Order = 33)]
|
|
public int MaxTrustListSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets if multicast DNS is enabled.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, Order = 34)]
|
|
public bool MultiCastDnsEnabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets reverse connect server configuration.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, Order = 35)]
|
|
public ReverseConnectServerConfiguration ReverseConnect { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the operation limits of the OPC UA Server.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, Order = 36)]
|
|
public OperationLimits OperationLimits { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether auditing is enabled.
|
|
/// </summary>
|
|
/// <value><c>true</c> if auditing is enabled; otherwise, <c>false</c>.</value>
|
|
[DataMember(IsRequired = false, Order = 37)]
|
|
public bool AuditingEnabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether mTLS is required/enforced by the HttpsTransportListener
|
|
/// </summary>
|
|
/// <value><c>true</c> if mutual TLS is enabled; otherwise, <c>false</c>.</value>
|
|
[DataMember(IsRequired = false, Order = 38)]
|
|
public bool HttpsMutualTls { get; set; }
|
|
|
|
/// <summary>
|
|
/// Enable / disable support for durable subscriptions
|
|
/// </summary>
|
|
/// <value><c>true</c> if durable subscriptions are enabled; otherwise, <c>false</c>.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 39)]
|
|
public bool DurableSubscriptionsEnabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum number of notifications saved in the durable queue for each monitored item.
|
|
/// </summary>
|
|
/// <value>The maximum size of the durable notification queue.</value>
|
|
[DataMember(IsRequired = false, Order = 40)]
|
|
public int MaxDurableNotificationQueueSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// The max size of the durable event queue.
|
|
/// </summary>
|
|
/// <value>The max size of the durable event queue.</value>
|
|
[DataMember(IsRequired = false, Order = 41)]
|
|
public int MaxDurableEventQueueSize { get; set; }
|
|
|
|
/// <summary>
|
|
/// How long the durable subscriptions will remain open without a publish from the client.
|
|
/// </summary>
|
|
/// <value>The maximum durable subscription lifetime.</value>
|
|
[DataMember(IsRequired = false, Order = 42)]
|
|
public int MaxDurableSubscriptionLifetimeInHours { get; set; }
|
|
|
|
private UserTokenPolicyCollection m_userTokenPolicies;
|
|
private StringCollection m_serverProfileArray;
|
|
private StringCollection m_serverCapabilities;
|
|
private StringCollection m_supportedPrivateKeyFormats;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores the configuration of the reverse connections.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class ReverseConnectServerConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ReverseConnectServerConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
private void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
ConnectInterval = 15000;
|
|
ConnectTimeout = 30000;
|
|
RejectTimeout = 60000;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of reverse connect clients.
|
|
/// </summary>
|
|
[DataMember(Order = 10)]
|
|
public ReverseConnectClientCollection Clients { get; set; }
|
|
|
|
/// <summary>
|
|
/// The interval after which a new reverse connection is attempted.
|
|
/// </summary>
|
|
[DataMember(Order = 20)]
|
|
public int ConnectInterval { get; set; }
|
|
|
|
/// <summary>
|
|
/// The default timeout to wait for a response to a reverse connection.
|
|
/// </summary>
|
|
[DataMember(Order = 30)]
|
|
public int ConnectTimeout { get; set; }
|
|
|
|
/// <summary>
|
|
/// The timeout to wait to establish a new reverse
|
|
/// connection after a rejected attempt.
|
|
/// </summary>
|
|
[DataMember(Order = 40)]
|
|
public int RejectTimeout { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores the operation limits of a OPC UA Server.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class OperationLimits
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public OperationLimits()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
private void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
MaxNodesPerRead = 0;
|
|
MaxNodesPerHistoryReadData = 0;
|
|
MaxNodesPerHistoryReadEvents = 0;
|
|
MaxNodesPerWrite = 0;
|
|
MaxNodesPerHistoryUpdateData = 0;
|
|
MaxNodesPerHistoryUpdateEvents = 0;
|
|
MaxNodesPerMethodCall = 0;
|
|
MaxNodesPerBrowse = 0;
|
|
MaxNodesPerRegisterNodes = 0;
|
|
MaxNodesPerTranslateBrowsePathsToNodeIds = 0;
|
|
MaxNodesPerNodeManagement = 0;
|
|
MaxMonitoredItemsPerCall = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the nodesToRead array when a Client calls the Read Service.
|
|
/// </summary>
|
|
[DataMember(Order = 10)]
|
|
public uint MaxNodesPerRead { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the nodesToRead array when a Client calls the HistoryRead
|
|
/// Service using the historyReadDetails RAW, PROCESSED, MODIFIED or ATTIME.
|
|
/// </summary>
|
|
[DataMember(Order = 20)]
|
|
public uint MaxNodesPerHistoryReadData { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the nodesToRead array when a Client calls the HistoryRead
|
|
/// Service using the historyReadDetails EVENTS.
|
|
/// </summary>
|
|
[DataMember(Order = 30)]
|
|
public uint MaxNodesPerHistoryReadEvents { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the nodesToWrite array when a Client calls the Write Service.
|
|
/// </summary>
|
|
[DataMember(Order = 40)]
|
|
public uint MaxNodesPerWrite { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the historyUpdateDetails array supported by the Server
|
|
/// when a Client calls the HistoryUpdate Service.
|
|
/// </summary>
|
|
[DataMember(Order = 50)]
|
|
public uint MaxNodesPerHistoryUpdateData { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the historyUpdateDetails array
|
|
/// when a Client calls the HistoryUpdate Service.
|
|
/// </summary>
|
|
[DataMember(Order = 60)]
|
|
public uint MaxNodesPerHistoryUpdateEvents { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the methodsToCall array when a Client calls the Call Service.
|
|
/// </summary>
|
|
[DataMember(Order = 70)]
|
|
public uint MaxNodesPerMethodCall { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the nodesToBrowse array when calling the Browse Service
|
|
/// or the continuationPoints array when a Client calls the BrowseNext Service.
|
|
/// </summary>
|
|
[DataMember(Order = 80)]
|
|
public uint MaxNodesPerBrowse { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the nodesToRegister array when a Client calls the RegisterNodes Service
|
|
/// and the maximum size of the nodesToUnregister when calling the UnregisterNodes Service.
|
|
/// </summary>
|
|
[DataMember(Order = 90)]
|
|
public uint MaxNodesPerRegisterNodes { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the browsePaths array when a Client calls the TranslateBrowsePathsToNodeIds Service.
|
|
/// </summary>
|
|
[DataMember(Order = 100)]
|
|
public uint MaxNodesPerTranslateBrowsePathsToNodeIds { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the nodesToAdd array when a Client calls the AddNodes Service,
|
|
/// the maximum size of the referencesToAdd array when a Client calls the AddReferences Service,
|
|
/// the maximum size of the nodesToDelete array when a Client calls the DeleteNodes Service,
|
|
/// and the maximum size of the referencesToDelete array when a Client calls the DeleteReferences Service.
|
|
/// </summary>
|
|
[DataMember(Order = 110)]
|
|
public uint MaxNodesPerNodeManagement { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicates the maximum size of the itemsToCreate array when a Client calls the CreateMonitoredItems Service,
|
|
/// the maximum size of the itemsToModify array when a Client calls the ModifyMonitoredItems Service,
|
|
/// the maximum size of the monitoredItemIds array when a Client calls the SetMonitoringMode Service or the DeleteMonitoredItems Service,
|
|
/// the maximum size of the sum of the linksToAdd and linksToRemove arrays when a Client calls the SetTriggering Service.
|
|
/// </summary>
|
|
[DataMember(Order = 120)]
|
|
public uint MaxMonitoredItemsPerCall { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores the configuration of the reverse connections.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class ReverseConnectClient
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ReverseConnectClient()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
private void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
Enabled = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The endpoint Url of the reverse connect client endpoint.
|
|
/// </summary>
|
|
[DataMember(Order = 10)]
|
|
public string EndpointUrl { get; set; }
|
|
|
|
/// <summary>
|
|
/// The timeout to wait for a response to a reverse connection.
|
|
/// Overrides the default reverse connection setting.
|
|
/// </summary>
|
|
[DataMember(Order = 20)]
|
|
public int Timeout { get; set; }
|
|
|
|
/// <summary>
|
|
/// The maximum count of active reverse connect sessions.
|
|
/// 0 or undefined means unlimited number of sessions.
|
|
/// 1 means a single connection is created at a time.
|
|
/// n disables reverse hello once the total number of sessions
|
|
/// in the server reaches n.
|
|
/// </summary>
|
|
[DataMember(Order = 30)]
|
|
public int MaxSessionCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies whether the sending of reverse connect attempts is enabled.
|
|
/// </summary>
|
|
[DataMember(Order = 40)]
|
|
public bool Enabled { get; set; } = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of reverse connect clients.
|
|
/// </summary>
|
|
[CollectionDataContract(
|
|
Name = "ListOfReverseConnectClient",
|
|
Namespace = Namespaces.OpcUaConfig,
|
|
ItemName = "ReverseConnectClient"
|
|
)]
|
|
public class ReverseConnectClientCollection : List<ReverseConnectClient>
|
|
{
|
|
/// <summary>
|
|
/// Initializes an empty collection.
|
|
/// </summary>
|
|
public ReverseConnectClientCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection from another collection.
|
|
/// </summary>
|
|
/// <param name="collection">A collection of values to add to this new collection</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// <paramref name="collection"/> is null.
|
|
/// </exception>
|
|
public ReverseConnectClientCollection(IEnumerable<ReverseConnectClient> collection)
|
|
: base(collection)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection with the specified capacity.
|
|
/// </summary>
|
|
/// <param name="capacity">The capacity.</param>
|
|
public ReverseConnectClientCollection(int capacity)
|
|
: base(capacity)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The configuration for a client application.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class ClientConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ClientConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
DefaultSessionTimeout = 60000;
|
|
MinSubscriptionLifetime = 10000;
|
|
m_wellKnownDiscoveryUrls = [];
|
|
m_discoveryServers = [];
|
|
OperationLimits = new OperationLimits();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default session timeout (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The default session timeout.</value>
|
|
[DataMember(IsRequired = false, Order = 0)]
|
|
public int DefaultSessionTimeout { get; set; }
|
|
|
|
/// <summary>
|
|
/// The well known URLs for the local discovery servers.
|
|
/// </summary>
|
|
/// <value>The well known discovery URLs.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 1)]
|
|
public StringCollection WellKnownDiscoveryUrls
|
|
{
|
|
get => m_wellKnownDiscoveryUrls;
|
|
set => m_wellKnownDiscoveryUrls = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The endpoint descriptions for central discovery servers.
|
|
/// </summary>
|
|
/// <value>The endpoint descriptions for central discovery servers.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 2)]
|
|
public EndpointDescriptionCollection DiscoveryServers
|
|
{
|
|
get => m_discoveryServers;
|
|
set => m_discoveryServers = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The path to the file containing the cached endpoints.
|
|
/// </summary>
|
|
/// <value>The path to the file containing the cached endpoints.</value>
|
|
[DataMember(IsRequired = false, Order = 3)]
|
|
public string EndpointCacheFilePath { get; set; }
|
|
|
|
/// <summary>
|
|
/// The minimum lifetime for a subscription (in milliseconds).
|
|
/// </summary>
|
|
/// <value>The minimum lifetime for a subscription.</value>
|
|
[DataMember(IsRequired = false, Order = 4)]
|
|
public int MinSubscriptionLifetime { get; set; }
|
|
|
|
/// <summary>
|
|
/// The reverse connect Client configuration.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, Order = 5)]
|
|
public ReverseConnectClientConfiguration ReverseConnect { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default operation limits of the OPC UA client.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Values not equal to zero are overwritten with smaller values set by the server.
|
|
/// The values are used to limit client service calls.
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, Order = 6)]
|
|
public OperationLimits OperationLimits { get; set; }
|
|
|
|
private StringCollection m_wellKnownDiscoveryUrls;
|
|
private EndpointDescriptionCollection m_discoveryServers;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores the configuration of the reverse connections.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class ReverseConnectClientConfiguration
|
|
{
|
|
/// <summary>
|
|
/// A collection of reverse connect client endpoints.
|
|
/// </summary>
|
|
[DataMember(Order = 10, IsRequired = false)]
|
|
public ReverseConnectClientEndpointCollection ClientEndpoints { get; set; }
|
|
|
|
/// <summary>
|
|
/// The time a reverse hello port is held open to wait for a
|
|
/// reverse connection until the request is rejected.
|
|
/// </summary>
|
|
[DataMember(Order = 20, IsRequired = false)]
|
|
public int HoldTime { get; set; } = 15000;
|
|
|
|
/// <summary>
|
|
/// The timeout to wait for a reverse hello message.
|
|
/// </summary>
|
|
[DataMember(Order = 30, IsRequired = false)]
|
|
public int WaitTimeout { get; set; } = 20000;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores the configuration of the reverse connections.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class ReverseConnectClientEndpoint
|
|
{
|
|
/// <summary>
|
|
/// The endpoint Url of a reverse connect client.
|
|
/// </summary>
|
|
[DataMember(Order = 1, IsRequired = false)]
|
|
public string EndpointUrl { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of reverse connect client endpoints.
|
|
/// </summary>
|
|
[CollectionDataContract(
|
|
Name = "ListOfReverseConnectClientEndpoint",
|
|
Namespace = Namespaces.OpcUaConfig,
|
|
ItemName = "ClientEndpoint"
|
|
)]
|
|
public class ReverseConnectClientEndpointCollection : List<ReverseConnectClientEndpoint>
|
|
{
|
|
/// <summary>
|
|
/// Initializes an empty collection.
|
|
/// </summary>
|
|
public ReverseConnectClientEndpointCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection from another collection.
|
|
/// </summary>
|
|
/// <param name="collection">A collection of values to add to this new collection</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// <paramref name="collection"/> is null.
|
|
/// </exception>
|
|
public ReverseConnectClientEndpointCollection(
|
|
IEnumerable<ReverseConnectClientEndpoint> collection)
|
|
: base(collection)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection with the specified capacity.
|
|
/// </summary>
|
|
/// <param name="capacity">The capacity.</param>
|
|
public ReverseConnectClientEndpointCollection(int capacity)
|
|
: base(capacity)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the configuration for a discovery server application.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class DiscoveryServerConfiguration : ServerBaseConfiguration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public DiscoveryServerConfiguration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_serverNames = [];
|
|
ServerRegistrations = [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public new void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The localized names for the discovery server.
|
|
/// </summary>
|
|
/// <value>The server names.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 2)]
|
|
public LocalizedTextCollection ServerNames
|
|
{
|
|
get => m_serverNames;
|
|
set => m_serverNames = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The path to the file containing servers saved by the discovery server.
|
|
/// </summary>
|
|
/// <value>The discovery server cache file.</value>
|
|
[DataMember(IsRequired = false, Order = 3)]
|
|
public string DiscoveryServerCacheFile { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the server registrations associated with the discovery server.
|
|
/// </summary>
|
|
/// <value>The server registrations.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 4)]
|
|
public ServerRegistrationCollection ServerRegistrations { get; set; }
|
|
|
|
private LocalizedTextCollection m_serverNames;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the configuration for a discovery server application.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class ServerRegistration
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ServerRegistration()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
ApplicationUri = null;
|
|
m_alternateDiscoveryUrls = [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the application URI of the server which the registration applies to.
|
|
/// </summary>
|
|
/// <value>The application uri.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 1)]
|
|
public string ApplicationUri { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the alternate discovery urls.
|
|
/// </summary>
|
|
/// <value>The alternate discovery urls.</value>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// These addresses are used to specify alternate paths to ther via firewalls, proxies
|
|
/// or similar network infrastructure. If these paths are specified in the configuration
|
|
/// file then the server will use the domain of the URL used by the client to determine
|
|
/// which, if any, or the alternate addresses to use instead of the primary addresses.
|
|
/// </para>
|
|
/// <para>
|
|
/// In the ideal world the server would provide these URLs during registration but this
|
|
/// table allows the administrator to provide the information to the discovery server
|
|
/// directly without requiring a patch to the server.
|
|
/// </para>
|
|
/// </remarks>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 2)]
|
|
public StringCollection AlternateDiscoveryUrls
|
|
{
|
|
get => m_alternateDiscoveryUrls;
|
|
set => m_alternateDiscoveryUrls = value ?? [];
|
|
}
|
|
|
|
private StringCollection m_alternateDiscoveryUrls;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A collection of AdditionalServerRegistrationInfo objects.
|
|
/// </summary>
|
|
[CollectionDataContract(
|
|
Name = "ListOfServerRegistration",
|
|
Namespace = Namespaces.OpcUaConfig,
|
|
ItemName = "ServerRegistration"
|
|
)]
|
|
public class ServerRegistrationCollection : List<ServerRegistration>
|
|
{
|
|
/// <summary>
|
|
/// Initializes an empty collection.
|
|
/// </summary>
|
|
public ServerRegistrationCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection from another collection.
|
|
/// </summary>
|
|
/// <param name="collection">A collection of values to add to this new collection</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// <paramref name="collection"/> is null.
|
|
/// </exception>
|
|
public ServerRegistrationCollection(IEnumerable<ServerRegistration> collection)
|
|
: base(collection)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection with the specified capacity.
|
|
/// </summary>
|
|
/// <param name="capacity">The capacity.</param>
|
|
public ServerRegistrationCollection(int capacity)
|
|
: base(capacity)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Describes a certificate store.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class CertificateStoreIdentifier
|
|
{
|
|
/// <summary>
|
|
/// The type of certificate store.
|
|
/// </summary>
|
|
/// <value>
|
|
/// If the StoreName is not empty, the CertificateStoreType.X509Store is returned, otherwise the StoreType is returned.
|
|
/// </value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 0)]
|
|
public string StoreType { get; set; }
|
|
|
|
/// <summary>
|
|
/// The path that identifies the certificate store.
|
|
/// </summary>
|
|
/// <value>
|
|
/// If the StoreName is not empty and the StoreLocation is empty, the Utils.Format("CurrentUser\\{0}", m_storeName) is returned.
|
|
/// If the StoreName is not empty and the StoreLocation is not empty, the Utils.Format("{1}\\{0}", m_storeName, m_storeLocation) is returned.
|
|
/// If the StoreName is empty, the m_storePath is returned.
|
|
/// </value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 1)]
|
|
public string StorePath
|
|
{
|
|
get => m_storePath;
|
|
set
|
|
{
|
|
m_storePath = value;
|
|
|
|
if (!string.IsNullOrEmpty(m_storePath) && string.IsNullOrEmpty(StoreType))
|
|
{
|
|
StoreType = DetermineStoreType(m_storePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Options that can be used to suppress certificate validation errors.
|
|
/// </summary>
|
|
[DataMember(
|
|
Name = "ValidationOptions",
|
|
IsRequired = false,
|
|
EmitDefaultValue = false,
|
|
Order = 4)]
|
|
internal int XmlEncodedValidationOptions
|
|
{
|
|
get => (int)ValidationOptions;
|
|
set => ValidationOptions = (CertificateValidationOptions)value;
|
|
}
|
|
|
|
private string m_storePath;
|
|
}
|
|
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
[KnownType(typeof(CertificateTrustList))]
|
|
public partial class CertificateTrustList : CertificateStoreIdentifier
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public CertificateTrustList()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_trustedCertificates = [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The list of trusted certificates.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The list of trusted certificates is set when TrustedCertificates is not a null value,
|
|
/// otherwise new CertificateIdentifierCollection is set.
|
|
/// </value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 3)]
|
|
public CertificateIdentifierCollection TrustedCertificates
|
|
{
|
|
get => m_trustedCertificates;
|
|
set => m_trustedCertificates = value ?? [];
|
|
}
|
|
|
|
private CertificateIdentifierCollection m_trustedCertificates;
|
|
}
|
|
|
|
[CollectionDataContract(
|
|
Name = "ApplicationCertificates",
|
|
Namespace = Namespaces.OpcUaConfig,
|
|
ItemName = "CertificateIdentifier"
|
|
)]
|
|
public partial class CertificateIdentifierCollection : List<CertificateIdentifier>
|
|
{
|
|
/// <summary>
|
|
/// Initializes an empty collection.
|
|
/// </summary>
|
|
public CertificateIdentifierCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection from another collection.
|
|
/// </summary>
|
|
/// <param name="collection">A collection of values to add to this new collection</param>
|
|
public CertificateIdentifierCollection(IEnumerable<CertificateIdentifier> collection)
|
|
: base(collection)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the collection with the specified capacity.
|
|
/// </summary>
|
|
public CertificateIdentifierCollection(int capacity)
|
|
: base(capacity)
|
|
{
|
|
}
|
|
}
|
|
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class CertificateIdentifier
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public CertificateIdentifier()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the identifier with the raw data from a certificate.
|
|
/// </summary>
|
|
public CertificateIdentifier(X509Certificate2 certificate)
|
|
{
|
|
Certificate = certificate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the identifier with the raw data from a certificate.
|
|
/// </summary>
|
|
public CertificateIdentifier(
|
|
X509Certificate2 certificate,
|
|
CertificateValidationOptions validationOptions)
|
|
{
|
|
Certificate = certificate;
|
|
ValidationOptions = validationOptions;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the identifier with the raw data from a certificate.
|
|
/// </summary>
|
|
public CertificateIdentifier(byte[] rawData)
|
|
{
|
|
Certificate = CertificateFactory.Create(rawData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The type of certificate store.
|
|
/// </summary>
|
|
/// <value>The type of the store - defined in the <see cref="CertificateStoreType"/>.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 10)]
|
|
public string StoreType { get; set; }
|
|
|
|
/// <summary>
|
|
/// The path that identifies the certificate store.
|
|
/// </summary>
|
|
/// <value>The store path in the form <c>StoreName\\Store Location</c> .</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 15)]
|
|
public string StorePath
|
|
{
|
|
get => m_storePath;
|
|
set
|
|
{
|
|
m_storePath = value;
|
|
|
|
if (!string.IsNullOrEmpty(m_storePath) && string.IsNullOrEmpty(StoreType))
|
|
{
|
|
StoreType = CertificateStoreIdentifier.DetermineStoreType(m_storePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The certificate's subject name - the distinguished name of an X509 certificate.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The distinguished name of an X509 certificate acording to the Abstract Syntax Notation One (ASN.1) syntax.
|
|
/// </value>
|
|
/// <remarks> The subject field identifies the entity associated with the public key stored in the subject public
|
|
/// key field. The subject name MAY be carried in the subject field and/or the subjectAltName extension.
|
|
/// Where it is non-empty, the subject field MUST contain an X.500 distinguished name (DN).
|
|
/// Name is defined by the following ASN.1 structures:
|
|
/// Name ::= CHOICE {RDNSequence }
|
|
/// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
|
|
/// RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
|
|
/// AttributeTypeAndValue ::= SEQUENCE {type AttributeType, value AttributeValue }
|
|
/// AttributeType ::= OBJECT IDENTIFIER
|
|
/// AttributeValue ::= ANY DEFINED BY AttributeType
|
|
/// DirectoryString ::= CHOICE {
|
|
/// teletexString TeletexString (SIZE (1..MAX)),
|
|
/// printableString PrintableString (SIZE (1..MAX)),
|
|
/// universalString UniversalString (SIZE (1..MAX)),
|
|
/// utf8String UTF8String (SIZE (1..MAX)),
|
|
/// bmpString BMPString (SIZE (1..MAX)) }
|
|
/// The Name describes a hierarchical name composed of attributes, such as country name, and
|
|
/// corresponding values, such as US. The type of the component AttributeValue is determined by
|
|
/// the AttributeType; in general it will be a DirectoryString.
|
|
/// String X.500 AttributeType:
|
|
/// <list type="bullet">
|
|
/// <item>CN commonName</item>
|
|
/// <item>L localityName</item>
|
|
/// <item>ST stateOrProvinceName</item>
|
|
/// <item>O organizationName</item>
|
|
/// <item>OU organizationalUnitName</item>
|
|
/// <item>C countryName</item>
|
|
/// <item>STREET streetAddress</item>
|
|
/// <item>DC domainComponent</item>
|
|
/// <item>UID userid</item>
|
|
/// </list>
|
|
/// <para>
|
|
/// This notation is designed to be convenient for common forms of name. This section gives a few
|
|
/// examples of distinguished names written using this notation. First is a name containing three relative
|
|
/// distinguished names (RDNs):
|
|
/// <c>CN=Steve Kille,O=Isode Limited,C=GB</c>
|
|
/// </para>
|
|
/// <para>
|
|
/// RFC 3280 Internet X.509 Public Key Infrastructure, April 2002
|
|
/// RFC 2253 LADPv3 Distinguished Names, December 1997
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <seealso cref="X500DistinguishedName"/>
|
|
/// <seealso cref="System.Security.Cryptography.AsnEncodedData"/>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 40)]
|
|
public string SubjectName
|
|
{
|
|
get
|
|
{
|
|
if (m_certificate == null)
|
|
{
|
|
return m_subjectName;
|
|
}
|
|
|
|
return m_certificate.Subject;
|
|
}
|
|
set
|
|
{
|
|
if (m_certificate != null &&
|
|
!string.IsNullOrEmpty(value) &&
|
|
m_certificate.Subject != value)
|
|
{
|
|
throw new ArgumentException(
|
|
"SubjectName does not match the SubjectName of the current certificate.");
|
|
}
|
|
|
|
m_subjectName = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The certificate's thumbprint.
|
|
/// </summary>
|
|
/// <value>The thumbprint of a certificate..</value>
|
|
/// <seealso cref="X509Certificate2"/>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 50)]
|
|
public string Thumbprint
|
|
{
|
|
get
|
|
{
|
|
if (m_certificate == null)
|
|
{
|
|
return m_thumbprint;
|
|
}
|
|
|
|
return m_certificate.Thumbprint;
|
|
}
|
|
set
|
|
{
|
|
if (m_certificate != null &&
|
|
!string.IsNullOrEmpty(value) &&
|
|
m_certificate.Thumbprint != value)
|
|
{
|
|
throw new ArgumentException(
|
|
"Thumbprint does not match the thumbprint of the current certificate.");
|
|
}
|
|
|
|
m_thumbprint = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the DER encoded certificate data or create embedded in this instance certificate using the DER encoded certificate data.
|
|
/// </summary>
|
|
/// <value>A byte array containing the X.509 certificate data.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 60)]
|
|
public byte[] RawData
|
|
{
|
|
get
|
|
{
|
|
if (m_certificate == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return m_certificate.RawData;
|
|
}
|
|
set
|
|
{
|
|
if (value == null || value.Length == 0)
|
|
{
|
|
m_certificate = null;
|
|
return;
|
|
}
|
|
|
|
m_certificate = CertificateFactory.Create(value);
|
|
m_subjectName = m_certificate.Subject;
|
|
m_thumbprint = m_certificate.Thumbprint;
|
|
CertificateType = GetCertificateType(m_certificate);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the XML encoded validation options - use to serialize the validation options.
|
|
/// </summary>
|
|
/// <value>The XML encoded validation options.</value>
|
|
[DataMember(
|
|
Name = "ValidationOptions",
|
|
IsRequired = false,
|
|
EmitDefaultValue = false,
|
|
Order = 70)]
|
|
internal int XmlEncodedValidationOptions
|
|
{
|
|
get => (int)ValidationOptions;
|
|
set => ValidationOptions = (CertificateValidationOptions)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the certificate type.
|
|
/// </summary>
|
|
/// <value>The NodeId of the certificate type, e.g. EccNistP256ApplicationCertificateType.</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 80)]
|
|
public NodeId CertificateType { get; set; }
|
|
|
|
/// <summary>
|
|
/// The string representation of the certificate
|
|
/// </summary>
|
|
/// <value>Rsa, RsaMin, RsaSha256, NistP256, NistP384, BrainpoolP256r1, BrainpoolP384r1, Curve25519, Curve448</value>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 90)]
|
|
public string CertificateTypeString
|
|
{
|
|
get => EncodeCertificateType(CertificateType);
|
|
set => CertificateType = DecodeCertificateType(value);
|
|
}
|
|
|
|
private string m_storePath;
|
|
private string m_subjectName;
|
|
private string m_thumbprint;
|
|
private X509Certificate2 m_certificate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores a list of cached endpoints.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public partial class ConfiguredEndpointCollection
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ConfiguredEndpointCollection()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the .NET framework during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_knownHosts = [];
|
|
m_discoveryUrls = [.. Utils.DiscoveryUrls];
|
|
m_endpoints = [];
|
|
DefaultConfiguration = EndpointConfiguration.Create();
|
|
}
|
|
|
|
/// <summary>
|
|
/// A list of known hosts that can be used for discovery.
|
|
/// </summary>
|
|
[DataMember(Name = "KnownHosts", IsRequired = false, Order = 1)]
|
|
public StringCollection KnownHosts
|
|
{
|
|
get => m_knownHosts;
|
|
set => m_knownHosts = value ?? [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default configuration to use when connecting to an endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "Endpoints", IsRequired = false, Order = 2)]
|
|
public List<ConfiguredEndpoint> Endpoints
|
|
{
|
|
get => m_endpoints;
|
|
private set
|
|
{
|
|
m_endpoints = value ?? [];
|
|
|
|
foreach (ConfiguredEndpoint endpoint in m_endpoints)
|
|
{
|
|
endpoint.Collection = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The URL of the UA TCP proxy server.
|
|
/// </summary>
|
|
[DataMember(Name = "TcpProxyUrl", EmitDefaultValue = false, Order = 3)]
|
|
public Uri TcpProxyUrl { get; set; }
|
|
|
|
private string m_filepath;
|
|
private StringCollection m_knownHosts;
|
|
private StringCollection m_discoveryUrls;
|
|
private List<ConfiguredEndpoint> m_endpoints;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores the configuration information for an endpoint.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
[KnownType(typeof(UserNameIdentityToken))]
|
|
[KnownType(typeof(X509IdentityToken))]
|
|
[KnownType(typeof(IssuedIdentityToken))]
|
|
public partial class ConfiguredEndpoint
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ConfiguredEndpoint()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the .NET framework during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_collection = null;
|
|
m_description = new EndpointDescription();
|
|
m_configuration = null;
|
|
UpdateBeforeConnect = true;
|
|
BinaryEncodingSupport = BinaryEncodingSupport.Optional;
|
|
SelectedUserTokenPolicyIndex = 0;
|
|
UserIdentity = null;
|
|
ReverseConnect = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The description for the endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "Endpoint", Order = 1, IsRequired = true)]
|
|
public EndpointDescription Description
|
|
{
|
|
get => m_description;
|
|
private set => m_description = value ?? new EndpointDescription();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The configuration to use when connecting to an endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "Configuration", Order = 2, IsRequired = false)]
|
|
public EndpointConfiguration Configuration
|
|
{
|
|
get => m_configuration;
|
|
set
|
|
{
|
|
m_configuration = value;
|
|
|
|
// copy default configuration if not already set.
|
|
if (m_configuration == null)
|
|
{
|
|
if (m_collection != null)
|
|
{
|
|
Update(m_collection.DefaultConfiguration);
|
|
}
|
|
else
|
|
{
|
|
Update(EndpointConfiguration.Create());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the endpoint information should be updated before connecting to the server.
|
|
/// </summary>
|
|
[DataMember(Name = "UpdateBeforeConnect", Order = 3, IsRequired = false)]
|
|
public bool UpdateBeforeConnect { get; set; }
|
|
|
|
/// <summary>
|
|
/// The user identity to use when connecting to the endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "BinaryEncodingSupport", Order = 4, IsRequired = false)]
|
|
public BinaryEncodingSupport BinaryEncodingSupport { get; set; }
|
|
|
|
/// <summary>
|
|
/// The user identity to use when connecting to the endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "SelectedUserTokenPolicy", Order = 5, IsRequired = false)]
|
|
public int SelectedUserTokenPolicyIndex { get; set; }
|
|
|
|
/// <summary>
|
|
/// The user identity to use when connecting to the endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "UserIdentity", Order = 6, IsRequired = false)]
|
|
public UserIdentityToken UserIdentity { get; set; }
|
|
|
|
/// <summary>
|
|
/// The reverse connect information.
|
|
/// </summary>
|
|
[DataMember(Name = "ReverseConnect", Order = 8, IsRequired = false)]
|
|
public ReverseConnectEndpoint ReverseConnect { get; set; }
|
|
|
|
/// <summary>
|
|
/// A bucket to store additional application specific configuration data.
|
|
/// </summary>
|
|
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 9)]
|
|
public XmlElementCollection Extensions
|
|
{
|
|
get => m_extensions;
|
|
set => m_extensions = value;
|
|
}
|
|
|
|
private ConfiguredEndpointCollection m_collection;
|
|
private EndpointDescription m_description;
|
|
private EndpointConfiguration m_configuration;
|
|
private XmlElementCollection m_extensions;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The type of binary encoding support allowed by a channel.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public enum BinaryEncodingSupport
|
|
{
|
|
/// <summary>
|
|
/// The UA binary encoding may be used.
|
|
/// </summary>
|
|
[EnumMember]
|
|
Optional,
|
|
|
|
/// <summary>
|
|
/// The UA binary encoding must be used.
|
|
/// </summary>
|
|
[EnumMember]
|
|
Required,
|
|
|
|
/// <summary>
|
|
/// The UA binary encoding may not be used.
|
|
/// </summary>
|
|
[EnumMember]
|
|
None
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores the reverse connect information for an endpoint.
|
|
/// </summary>
|
|
[DataContract(Namespace = Namespaces.OpcUaConfig)]
|
|
public class ReverseConnectEndpoint
|
|
{
|
|
/// <summary>
|
|
/// The default constructor.
|
|
/// </summary>
|
|
public ReverseConnectEndpoint()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the .NET framework during deserialization.
|
|
/// </summary>
|
|
[OnDeserializing]
|
|
public void Initialize(StreamingContext context)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets private members to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
Enabled = false;
|
|
ServerUri = null;
|
|
Thumbprint = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether reverse connect is enabled for the endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "Enabled", Order = 1, IsRequired = false)]
|
|
public bool Enabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// The server Uri of the endpoint.
|
|
/// </summary>
|
|
[DataMember(Name = "ServerUri", Order = 2, IsRequired = false)]
|
|
public string ServerUri { get; set; }
|
|
|
|
/// <summary>
|
|
/// The thumbprint of the certificate which contains
|
|
/// the server Uri.
|
|
/// </summary>
|
|
[DataMember(Name = "Thumbprint", Order = 3, IsRequired = false)]
|
|
public string Thumbprint { get; set; }
|
|
}
|
|
}
|