mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-19 07:02:16 +08:00
2966f693b2
Introduces `OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer` — a single NuGet package that ships **three Roslyn components + a runtime shim** to make the 1.5.378 → 2.0 upgrade as close to "install package, run `dotnet format`, remove package" as possible: | DLL | Where | Purpose | |-----|-------|---------| | `Opc.Ua.MigrationAnalyzer.dll` | `analyzers/dotnet/cs/` | 19 Roslyn analyzers (`UA0001`–`UA0022`) covering every breaking-change pattern in [`Docs/MigrationGuide.md`](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/Docs/MigrationGuide.md). Built against the stable Roslyn 4.14 analyzer API — loads cleanly in csc.exe (no `Microsoft.CodeAnalysis.Workspaces` reference). | | `Opc.Ua.MigrationAnalyzer.CodeFixer.dll` | `analyzers/dotnet/cs/` | 14 companion `CodeFixProvider`s; loaded only by Workspaces-aware hosts (Visual Studio, `dotnet format analyzers`). | | `Opc.Ua.MigrationAnalyzer.Generator.dll` | `analyzers/dotnet/cs/` | Roslyn `IIncrementalGenerator` that emits per-consumer `internal sealed [Obsolete] class <Name>Collection : List<TElement>` shims for every `<Type>Collection` wrapper the consumer references — including **arbitrary model-compiled `<UserType>Collection` patterns**, not just the 30 well-known built-in ones. | | `Opc.Ua.MigrationAnalyzer.Core.dll` | `lib/<tfm>/` for `net472, net48, netstandard2.1, net8.0, net9.0, net10.0` | 1.5.378 → 2.0 runtime compatibility shim. Re-exposes the obsolete extension surface so 1.5.378-style call sites keep compiling against 2.0 with warnings instead of errors. | ### Migration workflow the package enables 1. **Bump every `OPCFoundation.NetStandard.Opc.Ua.*` PackageReference to `2.0.*-*`.** 2. **Add the migration package** as a build-only dependency: ```xml <PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer" Version="2.0.*-*" PrivateAssets="all" /> ``` 3. **Run `dotnet build`.** The Core shim DLL re-supplies obsolete extension members; the source generator covers `<Type>Collection` references; what remains are `[Obsolete]` warnings + `UA00xx` analyzer diagnostics + (occasionally) `MIG01` for unresolvable model-compiled element types. 4. **Apply auto-fixes** in the IDE or from the command line: ```bash dotnet format analyzers <solution>.sln \ --diagnostics UA0002 UA0003 UA0004 UA0005 UA0006 UA0007 UA0008 \ UA0009 UA0010 UA0012 UA0014 UA0019 UA0020 UA0022 \ --severity warn ``` 5. **Walk the residual** `UA0001` / `UA0011` / `UA0015` / `UA0018` / `UA0021` diagnostics manually — they cover patterns that require human judgement. 6. **Remove the migration package.** You are now on clean 2.0 with no shim dependency. ### Source generator (`Opc.Ua.MigrationAnalyzer.Generator.dll`) Per-consumer Roslyn `IIncrementalGenerator` that emits `.g.cs` files for every `<Name>Collection` reference in the consumer's compilation that fails to bind: - **Catalog override (30 entries)** — pins element types that renamed across the 1.5.378 → 2.0 boundary: `DateTime`→`DateTimeUtc`, `Guid`→`Uuid`, `byte[]`→`ByteString`, `XmlElement`→`Opc.Ua.XmlElement`, plus the primitives and built-in opc types whose element name didn't rename. - **Semantic lookup** — for model-compiled `<UserType>Collection` patterns the model compiler historically emitted for any user-defined complex type. Strips the `Collection` suffix, runs `Compilation.GetSymbolsWithName` against the unique element short name, emits the shim with the fully-qualified type reference. - **`MIG01` diagnostic** — fires when the element type can't be uniquely resolved (zero or multiple candidates). User adds the missing `using` or migrates the site manually. Each generated file emits a class shaped exactly like the 1.5.378 wrapper: ```csharp [global::System.Obsolete("'Int32Collection' was removed in 2.0. Use 'List<int>' or 'ArrayOf<int>' instead. (UA0002)")] internal sealed class Int32Collection : global::System.Collections.Generic.List<int> { public Int32Collection() { } public Int32Collection(int capacity) : base(capacity) { } public Int32Collection(global::System.Collections.Generic.IEnumerable<int> collection) : base(collection) { } public static implicit operator global::Opc.Ua.ArrayOf<int>(Int32Collection? value) => value is null ? default : value.ToArrayOf(); } ``` **Design choice — `internal` (not `public`):** the generated shim never leaks through the consumer's public API surface. Consumers with *public* methods or properties returning a `<Type>Collection` will hit `CS0050: Inconsistent accessibility` — the intended signal that the public surface must migrate to `List<T>` / `ArrayOf<T>` first; internal call sites keep compiling under the shim so iteration is incremental. ### 19-rule analyzer reference | Rule | Replaces | Default | Auto-fix | |------|----------|---------|----------| | `UA0001` | `Utils.Trace` / `Utils.LogX` | Info | No (telemetry plumbing requires manual review) | | `UA0002` | Removed `<Type>Collection` wrappers | Warning | Yes (→ `List<T>` / `ArrayOf<T>`); the source generator keeps the call site compiling in the meantime | | `UA0003` | `x == null` on now-struct built-ins | Warning | Yes (→ `x.IsNull`) | | `UA0004` | `?.` on now-struct built-ins | Warning | Yes (drop the `?`) | | `UA0005` | `byte[]` where `ByteString` is expected | Warning | Yes (append `.ToByteString()`) | | `UA0006` | `new Variant(object\|DateTime\|Guid\|byte[])` | Warning | Yes (→ `Variant.From(...)`) | | `UA0007` | `new NodeId(string)` / `new ExpandedNodeId(string)` | Warning | Yes (→ `Parse`) | | `UA0008` | `Session.Call(..., params object[])` | Warning | Yes (wrap with `Variant.From`) | | `UA0009` | `[DataContract]` / `[DataMember]` on config extensions | Warning | Yes (→ `[DataType]` / `[DataTypeField]`) | | `UA0010` | `using` / `Dispose` on `CertificateIdentifier` / `UserIdentity` / `IUserIdentityTokenHandler` | Warning | Yes (drop the disposable) | | `UA0011` | Sync `IUserIdentityTokenHandler.{Encrypt,Decrypt,Sign,Verify}` | Info | No (async promotion required) | | `UA0012` | `CertificateFactory.*` static helpers | Warning | Yes (→ instance methods) | | `UA0014` | `DataValue.IsGood(dv)` static helper | Warning | Yes (→ `dv.IsGood`) | | `UA0015` | Sync / APM members on GDS / LDS clients | Info | No (async promotion required) | | `UA0018` | `CertificateIdentifier.Certificate` getter | Info | No (→ `LoadCertificate2Async`) | | `UA0019` | `new DataValue(StatusCode[, ts])` | Warning | Yes (→ object-initializer form) | | `UA0020` | `EncodeableFactory.GlobalFactory` / `Create()` | Warning | Yes (→ `ServiceMessageContext.Factory` / `Fork()`) | | `UA0021` | `CertificateValidator` / `CertificateValidationEventArgs` (structural rename) | Info | No (see migration guide §Certificate validation) | | `UA0022` | `ApplicationConfiguration.CertificateValidator` / `ServerBase.CertificateValidator` property rename | Warning | Yes (→ `.CertificateManager`) | Plus one generator-only diagnostic: | Rule | Source | Default | Triggers | |------|--------|---------|----------| | `MIG01` | Generator | Warning | Generator can't uniquely resolve the element type for a `<Foo>Collection` reference (zero or multiple candidates). The user adds a `using` for the namespace defining `Foo`, or migrates the call site manually. | ### Runtime shim coverage (`Opc.Ua.MigrationAnalyzer.Core.dll`) C# 14 `extension` members so 1.5.378 call sites continue to compile: - **Moved obsolete extensions** the 2.0 libraries no longer carry inline: `NodeId` / `Variant` / `DataValue` null-check helpers, `Session` sync helpers, `Subscription` sync helpers, `ApplicationInstance` helpers, `ServerBase.Start` / `Stop`, `TransportChannel` APM, `ChannelBase` static factory methods, etc. - **New shims for genuinely-removed members**: `EncodeableFactory.GlobalFactory`, `CertificateIdentifier.Certificate` (throws), sync wrappers for `IUserIdentityTokenHandler.{Encrypt,Decrypt,Sign,Verify}`, sync + APM wrappers for the GDS / LDS client APIs, `GlobalDiscoverySampleServer` 1.5.378-shape ctor (in-tree `Libraries/Opc.Ua.Gds.Server.Common/`). > ⚠ The sync shims wrap their `*Async` counterparts via `Task.Run(…).GetAwaiter().GetResult()`. Intended as a **migration aid only** — port to `await` before shipping. ### Repo layout ``` Tools/Opc.Ua.MigrationAnalyzer/ # analyzer DLL Analyzers/UA00xxAnalyzer.cs # 19 DiagnosticAnalyzer impls Diagnostics/{DiagnosticIds,Descriptors,WellKnownProperties}.cs Helpers/{SymbolExtensions,UaSymbols}.cs Opc.Ua.MigrationAnalyzer.nuspec # ships all 3 analyzer DLLs + 6-TFM Core OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer.props Tools/Opc.Ua.MigrationAnalyzer.CodeFixer/ # code-fix DLL CodeFixes/UA00xxCodeFix.cs # 14 CodeFixProvider impls Tools/Opc.Ua.MigrationAnalyzer.Generator/ # source generator DLL (NEW) MigrationGenerator.cs # IIncrementalGenerator CollectionShimCatalog.cs # 30-entry element-type overrides Diagnostics/GeneratorDiagnostics.cs # MIG01 Tools/Opc.Ua.MigrationAnalyzer.Core/ # runtime shim (multi-TFM) Client/, Configuration/, Core/, Gds.Client.Common/, Types/, Marker/ Tools/Roslyn.slnx # source-gen + migration projects Tests/Opc.Ua.MigrationAnalyzer.Tests/ # 142 tests Analyzers/UA00xxTests.cs # 107 analyzer + code-fix tests Generators/MigrationGeneratorTests.cs # 35 generator tests (NEW) Tests/Opc.Ua.MigrationAnalyzer.Core.Tests/ # 13 runtime-shim tests ``` ### Documentation - [`Docs/MigrationGuide.md`](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/Docs/MigrationGuide.md) — §3 Collection Type Migration gains a tip pointing at the generator so CS0246 is replaced with `[Obsolete]` warnings + UA0002. - [`.github/agents/opcua-v20-migration.agent.md`](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/.github/agents/opcua-v20-migration.agent.md) — Strategy section mentions the three Roslyn components + runtime shim explicitly, plus the MIG01 diagnostic and the "internal-by-design" CS0050 hint. ### Central package pinning `Directory.Packages.props` pins `Microsoft.CodeAnalysis.*` (Common, CSharp, CSharp.Workspaces, Workspaces.Common) to **4.14.0** — the stable analyzer API surface. The earlier 5.3 pin was the *csc-internal* version, which is not safe for analyzer hosts. ### Dogfood story The full 42-sample dogfood against `OPCFoundation/UA-.NETStandard-Samples` surfaced a **critical analyzer-loader bug** in an earlier iteration: the analyzer DLL initially co-shipped its code-fix providers in one assembly, which transitively referenced `Microsoft.CodeAnalysis.Workspaces.dll`. csc.exe's analyzer host ships only `Microsoft.CodeAnalysis.dll` + `CSharp.dll` in its bincore and **silently swallows** load failures — net result: zero diagnostics across all samples. Fix landed in commit `861fa6ee1`: 1. **Split** the assembly: analyzer in `Opc.Ua.MigrationAnalyzer.dll` (Workspaces-free, csc-safe), code-fixes in `Opc.Ua.MigrationAnalyzer.CodeFixer.dll` (Workspaces-aware, IDE-only). 2. **Downgrade** to Roslyn 4.14 — the stable analyzer API. 3. `/p:ReportAnalyzer=true` confirms all 19 analyzers initialise and execute on real consumer code. This PR's new generator was smoke-tested against a minimal consumer that exercises all 30 catalog entries + a model-compiled element type + the unresolvable case: - 30 catalog patterns + model-compiled `MyThingCollection` → all emit shims, fire CS0618 with `(UA0002)` marker - Element-type renames verified: `DateTimeCollection` → `DateTimeUtc`, `ByteStringCollection` → `ByteString`, `GuidCollection` → `Uuid`, etc. - `NeverHeardOfItCollection` → `MIG01` fires with help link to the migration guide Known compatibility gaps (documented in the migration agent): - Legacy WinForms `.Net4` sample projects use pre-SDK MSBuild XML and **ignore** `Directory.Build.targets` `<PackageReference>` injection — consumers in this format must add the migration package to their csproj inline. ### Packaging design notes - Hand-authored nuspec ships 3 DLLs in `analyzers/dotnet/cs/` + 6 TFMs of Core (shim) DLLs under `lib/`. All 6 OPC UA runtime package dependencies are declared per-TFM-group. - `build/OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer.props` is auto-imported by NuGet (file name must match package id exactly) and injects all three analyzer DLLs as `<Analyzer>` items. - `DiagnosticIds.cs` / `DiagnosticDescriptors.cs` / `WellKnownProperties.cs` / `Helpers/*.cs` are **shared via `<Compile Link=…>`** between the analyzer and code-fixer projects to break what would otherwise be a NuGet restore cycle. The generator has no compile dependency on the analyzer; the 30-entry override table is duplicated in `CollectionShimCatalog.cs` (the analyzer table also drives diagnostic messages, so the duplication is small and intentional). - `Opc.Ua.MigrationAnalyzer.Core` is multi-TFM (`net472, net48, netstandard2.1, net8.0, net9.0, net10.0`) and **non-packable on its own**; the parent migration package's nuspec consumes each TFM's DLL by path.
1494 lines
58 KiB
C#
1494 lines
58 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.Diagnostics.CodeAnalysis;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
|
|
namespace Opc.Ua.Configuration
|
|
{
|
|
/// <summary>
|
|
/// A class that builds a configuration for a UA application.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This builder uses a fluent staged-interface pattern: callers progress through
|
|
/// <see cref="ApplicationInstance.Build(string, string)"/> (which initializes
|
|
/// <see cref="ApplicationConfiguration.TransportQuotas"/> and
|
|
/// <see cref="ApplicationConfiguration.TraceConfiguration"/>),
|
|
/// then through <see cref="AsClient"/>/<see cref="AsServer(string[], string[])"/>
|
|
/// (which initialize <see cref="ApplicationConfiguration.ClientConfiguration"/>
|
|
/// and <see cref="ApplicationConfiguration.ServerConfiguration"/> respectively),
|
|
/// before reaching the per-section setter methods. As a result the
|
|
/// <c>!</c> operators on accesses to those nullable sub-configuration objects in
|
|
/// this class are guaranteed to be non-null at the time the methods are invoked.
|
|
/// </remarks>
|
|
public class ApplicationConfigurationBuilder : IApplicationConfigurationBuilder
|
|
{
|
|
/// <summary>
|
|
/// Create the application instance builder.
|
|
/// </summary>
|
|
public ApplicationConfigurationBuilder(ApplicationInstance applicationInstance)
|
|
{
|
|
ApplicationInstance = applicationInstance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The application instance used to build the configuration.
|
|
/// </summary>
|
|
public IApplicationInstance ApplicationInstance { get; }
|
|
|
|
/// <summary>
|
|
/// The application configuration.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This builder is constructed by <see cref="ApplicationInstance.Build"/>, which assigns
|
|
/// <see cref="IApplicationInstance.ApplicationConfiguration"/> before returning the builder,
|
|
/// so the underlying configuration is guaranteed to be non-null.
|
|
/// </remarks>
|
|
public ApplicationConfiguration ApplicationConfiguration
|
|
=> ApplicationInstance.ApplicationConfiguration!;
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderGlobalConfiguration SetHiResClockDisabled(
|
|
bool hiResClockDisabled)
|
|
{
|
|
ApplicationConfiguration.DisableHiResClock = hiResClockDisabled;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderClientSelected AsClient()
|
|
{
|
|
switch (ApplicationInstance.ApplicationType)
|
|
{
|
|
case ApplicationType.Client:
|
|
case ApplicationType.ClientAndServer:
|
|
break;
|
|
case ApplicationType.Server:
|
|
ApplicationInstance.ApplicationType = m_typeSelected
|
|
? ApplicationType.ClientAndServer
|
|
: ApplicationType.Client;
|
|
break;
|
|
case ApplicationType.DiscoveryServer:
|
|
throw new ArgumentException("Discovery server type is not allowed as client.");
|
|
default:
|
|
throw new ArgumentException("Invalid application type for client.");
|
|
}
|
|
|
|
m_typeSelected = true;
|
|
|
|
ApplicationConfiguration.ClientConfiguration = new ClientConfiguration();
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions AddSecurityConfiguration(
|
|
string subjectName,
|
|
string? pkiRoot = null,
|
|
string? appRoot = null,
|
|
string? rejectedRoot = null)
|
|
{
|
|
pkiRoot = DefaultPKIRoot(pkiRoot);
|
|
appRoot = appRoot == null ? pkiRoot : DefaultPKIRoot(appRoot);
|
|
rejectedRoot = rejectedRoot == null ? pkiRoot : DefaultPKIRoot(rejectedRoot);
|
|
string appStoreType = CertificateStoreIdentifier.DetermineStoreType(appRoot);
|
|
string pkiRootType = CertificateStoreIdentifier.DetermineStoreType(pkiRoot);
|
|
string rejectedRootType = CertificateStoreIdentifier.DetermineStoreType(rejectedRoot);
|
|
ApplicationConfiguration.SecurityConfiguration = new SecurityConfiguration
|
|
{
|
|
// app cert store
|
|
ApplicationCertificate = new CertificateIdentifier
|
|
{
|
|
StoreType = appStoreType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Application, appRoot),
|
|
SubjectName = Utils.ReplaceDCLocalhost(subjectName)
|
|
},
|
|
// App trusted & issuer
|
|
TrustedPeerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Trusted, pkiRoot)
|
|
},
|
|
TrustedIssuerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Issuer, pkiRoot)
|
|
},
|
|
// Https trusted & issuer
|
|
TrustedHttpsCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.TrustedHttps, pkiRoot)
|
|
},
|
|
HttpsIssuerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.IssuerHttps, pkiRoot)
|
|
},
|
|
// User trusted & issuer
|
|
TrustedUserCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.TrustedUser, pkiRoot)
|
|
},
|
|
UserIssuerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.IssuerUser, pkiRoot)
|
|
},
|
|
// rejected store
|
|
RejectedCertificateStore = new CertificateStoreIdentifier
|
|
{
|
|
StoreType = rejectedRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Rejected, rejectedRoot)
|
|
}
|
|
};
|
|
SetSecureDefaults(ApplicationConfiguration.SecurityConfiguration);
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions AddSecurityConfiguration(
|
|
ArrayOf<CertificateIdentifier> certIdList,
|
|
string? pkiRoot = null,
|
|
string? rejectedRoot = null)
|
|
{
|
|
pkiRoot = DefaultPKIRoot(pkiRoot);
|
|
rejectedRoot = rejectedRoot == null ? pkiRoot : DefaultPKIRoot(rejectedRoot);
|
|
string pkiRootType = CertificateStoreIdentifier.DetermineStoreType(pkiRoot);
|
|
string rejectedRootType = CertificateStoreIdentifier.DetermineStoreType(rejectedRoot);
|
|
ApplicationConfiguration.SecurityConfiguration = new SecurityConfiguration
|
|
{
|
|
// app cert store
|
|
ApplicationCertificates = certIdList,
|
|
// App trusted & issuer
|
|
TrustedPeerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Trusted, pkiRoot)
|
|
},
|
|
TrustedIssuerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Issuer, pkiRoot)
|
|
},
|
|
// Https trusted & issuer
|
|
TrustedHttpsCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.TrustedHttps, pkiRoot)
|
|
},
|
|
HttpsIssuerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.IssuerHttps, pkiRoot)
|
|
},
|
|
// User trusted & issuer
|
|
TrustedUserCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.TrustedUser, pkiRoot)
|
|
},
|
|
UserIssuerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = pkiRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.IssuerUser, pkiRoot)
|
|
},
|
|
// rejected store
|
|
RejectedCertificateStore = new CertificateStoreIdentifier
|
|
{
|
|
StoreType = rejectedRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Rejected, rejectedRoot)
|
|
}
|
|
};
|
|
SetSecureDefaults(ApplicationConfiguration.SecurityConfiguration);
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptionStores AddSecurityConfigurationStores(
|
|
string subjectName,
|
|
string appRoot,
|
|
string trustedRoot,
|
|
string issuerRoot,
|
|
string? rejectedRoot = null)
|
|
{
|
|
string appStoreType = CertificateStoreIdentifier.DetermineStoreType(appRoot);
|
|
string issuerRootType = CertificateStoreIdentifier.DetermineStoreType(issuerRoot);
|
|
string trustedRootType = CertificateStoreIdentifier.DetermineStoreType(trustedRoot);
|
|
rejectedRoot ??= DefaultPKIRoot(null);
|
|
string rejectedRootType = CertificateStoreIdentifier.DetermineStoreType(rejectedRoot);
|
|
ApplicationConfiguration.SecurityConfiguration = new SecurityConfiguration
|
|
{
|
|
// app cert store
|
|
ApplicationCertificate = new CertificateIdentifier
|
|
{
|
|
StoreType = appStoreType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Application, appRoot),
|
|
SubjectName = Utils.ReplaceDCLocalhost(subjectName)
|
|
},
|
|
// App trusted & issuer
|
|
TrustedPeerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = trustedRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Trusted, trustedRoot)
|
|
},
|
|
TrustedIssuerCertificates = new CertificateTrustList
|
|
{
|
|
StoreType = issuerRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Issuer, issuerRoot)
|
|
},
|
|
// rejected store
|
|
RejectedCertificateStore = new CertificateStoreIdentifier
|
|
{
|
|
StoreType = rejectedRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.Rejected, rejectedRoot)
|
|
}
|
|
};
|
|
SetSecureDefaults(ApplicationConfiguration.SecurityConfiguration);
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptionStores AddSecurityConfigurationUserStore(
|
|
string trustedRoot,
|
|
string issuerRoot)
|
|
{
|
|
string trustedRootType = CertificateStoreIdentifier.DetermineStoreType(trustedRoot);
|
|
string issuerRootType = CertificateStoreIdentifier.DetermineStoreType(issuerRoot);
|
|
|
|
// User trusted & issuer
|
|
ApplicationConfiguration.SecurityConfiguration.TrustedUserCertificates
|
|
= new CertificateTrustList
|
|
{
|
|
StoreType = trustedRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.TrustedUser, trustedRoot)
|
|
};
|
|
ApplicationConfiguration.SecurityConfiguration.UserIssuerCertificates
|
|
= new CertificateTrustList
|
|
{
|
|
StoreType = issuerRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.IssuerUser, issuerRoot)
|
|
};
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptionStores AddSecurityConfigurationHttpsStore(
|
|
string trustedRoot,
|
|
string issuerRoot)
|
|
{
|
|
string trustedRootType = CertificateStoreIdentifier.DetermineStoreType(trustedRoot);
|
|
string issuerRootType = CertificateStoreIdentifier.DetermineStoreType(issuerRoot);
|
|
|
|
// Https trusted & issuer
|
|
ApplicationConfiguration.SecurityConfiguration.TrustedHttpsCertificates
|
|
= new CertificateTrustList
|
|
{
|
|
StoreType = trustedRootType,
|
|
StorePath = DefaultCertificateStorePath(
|
|
TrustlistType.TrustedHttps,
|
|
trustedRootType)
|
|
};
|
|
ApplicationConfiguration.SecurityConfiguration.HttpsIssuerCertificates
|
|
= new CertificateTrustList
|
|
{
|
|
StoreType = issuerRootType,
|
|
StorePath = DefaultCertificateStorePath(TrustlistType.IssuerHttps, issuerRoot)
|
|
};
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<ApplicationConfiguration> CreateAsync(CancellationToken ct = default)
|
|
{
|
|
// sanity checks
|
|
if (ApplicationInstance.ApplicationType is
|
|
ApplicationType.Server or ApplicationType.ClientAndServer &&
|
|
ApplicationConfiguration.ServerConfiguration == null)
|
|
{
|
|
throw new ArgumentException("ApplicationType Server is not configured.");
|
|
}
|
|
if (ApplicationInstance.ApplicationType is
|
|
ApplicationType.Client or ApplicationType.ClientAndServer &&
|
|
ApplicationConfiguration.ClientConfiguration == null)
|
|
{
|
|
throw new ArgumentException("ApplicationType Client is not configured.");
|
|
}
|
|
|
|
// ensure for a user token policy
|
|
if (ApplicationConfiguration.ServerConfiguration?.UserTokenPolicies.Count == 0)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.UserTokenPolicies +=
|
|
new UserTokenPolicy(UserTokenType.Anonymous);
|
|
}
|
|
|
|
// ensure for secure transport profiles
|
|
if (ApplicationConfiguration.ServerConfiguration?.SecurityPolicies.Count == 0)
|
|
{
|
|
AddSecurityPolicies();
|
|
}
|
|
|
|
// Legacy Utils trace pipeline; kept for 1.5.378 -> 2.0 migration.
|
|
#pragma warning disable CS0618 // Type or member is obsolete
|
|
ApplicationConfiguration.TraceConfiguration?.ApplySettings();
|
|
#pragma warning restore CS0618 // Type or member is obsolete
|
|
|
|
await ApplicationConfiguration.ValidateAsync(ApplicationInstance.ApplicationType, ct)
|
|
.ConfigureAwait(false);
|
|
|
|
return ApplicationConfiguration;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AsServer(
|
|
string[] baseAddresses,
|
|
string[]? alternateBaseAddresses = null)
|
|
{
|
|
switch (ApplicationInstance.ApplicationType)
|
|
{
|
|
case ApplicationType.Client:
|
|
ApplicationInstance.ApplicationType = m_typeSelected
|
|
? ApplicationType.ClientAndServer
|
|
: ApplicationType.Server;
|
|
break;
|
|
case ApplicationType.Server:
|
|
case ApplicationType.ClientAndServer:
|
|
break;
|
|
case ApplicationType.DiscoveryServer:
|
|
throw new ArgumentException("Discovery server type is not allowed as server.");
|
|
default:
|
|
throw new ArgumentException("Invalid application type for server.");
|
|
}
|
|
|
|
m_typeSelected = true;
|
|
|
|
// configure a server
|
|
var serverConfiguration = new ServerConfiguration
|
|
{
|
|
// by default disable LDS registration
|
|
MaxRegistrationInterval = 0
|
|
};
|
|
|
|
// base addresses
|
|
foreach (string baseAddress in baseAddresses)
|
|
{
|
|
// ReplaceLocalhost only returns null when its input is null; the foreach variable is non-null.
|
|
serverConfiguration.BaseAddresses +=
|
|
Utils.ReplaceLocalhost(baseAddress)!;
|
|
}
|
|
|
|
// alternate base addresses
|
|
if (alternateBaseAddresses != null)
|
|
{
|
|
foreach (string alternateBaseAddress in alternateBaseAddresses)
|
|
{
|
|
// ReplaceLocalhost only returns null when its input is null; the foreach variable is non-null.
|
|
serverConfiguration.AlternateBaseAddresses +=
|
|
Utils.ReplaceLocalhost(alternateBaseAddress)!;
|
|
}
|
|
}
|
|
|
|
// add container for policies
|
|
serverConfiguration.SecurityPolicies = [];
|
|
|
|
// add user token policy container and Anonymous
|
|
serverConfiguration.UserTokenPolicies = [];
|
|
|
|
ApplicationConfiguration.ServerConfiguration = serverConfiguration;
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddUnsecurePolicyNone(
|
|
bool addPolicy = true)
|
|
{
|
|
if (addPolicy)
|
|
{
|
|
var policies = ApplicationConfiguration
|
|
.ServerConfiguration!
|
|
.SecurityPolicies.ToList();
|
|
InternalAddPolicy(policies, MessageSecurityMode.None, SecurityPolicies.None);
|
|
ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddSignPolicies(
|
|
bool addPolicies = true)
|
|
{
|
|
if (addPolicies)
|
|
{
|
|
AddSecurityPolicies(true, false, false);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddSignAndEncryptPolicies(
|
|
bool addPolicies = true)
|
|
{
|
|
if (addPolicies)
|
|
{
|
|
AddSecurityPolicies(false, false, false);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddEccSignPolicies()
|
|
{
|
|
AddEccSecurityPolicies(true);
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddEccSignAndEncryptPolicies()
|
|
{
|
|
AddEccSecurityPolicies(false);
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddPolicy(
|
|
MessageSecurityMode securityMode,
|
|
string securityPolicy)
|
|
{
|
|
if (!SecurityPolicies.IsValidSecurityPolicyUri(securityPolicy))
|
|
{
|
|
throw new ArgumentException("Unknown security policy", nameof(securityPolicy));
|
|
}
|
|
|
|
if (securityMode == MessageSecurityMode.None ||
|
|
securityPolicy.Equals(SecurityPolicies.None, StringComparison.Ordinal))
|
|
{
|
|
throw new ArgumentException("Use AddUnsecurePolicyNone to add no security policy.");
|
|
}
|
|
|
|
var policies = ApplicationConfiguration.ServerConfiguration!.SecurityPolicies.ToList();
|
|
InternalAddPolicy(
|
|
policies,
|
|
securityMode,
|
|
securityPolicy);
|
|
ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf();
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddUserTokenPolicy(
|
|
UserTokenType userTokenType)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.UserTokenPolicies +=
|
|
new UserTokenPolicy(userTokenType);
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerSelected AddUserTokenPolicy(
|
|
UserTokenPolicy userTokenPolicy)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.UserTokenPolicies += userTokenPolicy ?? throw new ArgumentNullException(nameof(userTokenPolicy));
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetApplicationCertificates(
|
|
ArrayOf<CertificateIdentifier> certIdList)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.ApplicationCertificates = certIdList;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetMaxRejectedCertificates(
|
|
int maxRejectedCertificates)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.MaxRejectedCertificates
|
|
= maxRejectedCertificates;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetAutoAcceptUntrustedCertificates(
|
|
bool autoAccept)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.AutoAcceptUntrustedCertificates
|
|
= autoAccept;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetAddAppCertToTrustedStore(
|
|
bool addToTrustedStore)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.AddAppCertToTrustedStore
|
|
= addToTrustedStore;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetRejectSHA1SignedCertificates(
|
|
bool rejectSHA1Signed)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.RejectSHA1SignedCertificates
|
|
= rejectSHA1Signed;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetRejectUnknownRevocationStatus(
|
|
bool rejectUnknownRevocationStatus)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.RejectUnknownRevocationStatus =
|
|
rejectUnknownRevocationStatus;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetUseValidatedCertificates(
|
|
bool useValidatedCertificates)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.UseValidatedCertificates
|
|
= useValidatedCertificates;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetSuppressNonceValidationErrors(
|
|
bool suppressNonceValidationErrors)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.SuppressNonceValidationErrors =
|
|
suppressNonceValidationErrors;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetSendCertificateChain(
|
|
bool sendCertificateChain)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.SendCertificateChain
|
|
= sendCertificateChain;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions SetMinimumCertificateKeySize(
|
|
ushort keySize)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.MinimumCertificateKeySize = keySize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderSecurityOptions AddCertificatePasswordProvider(
|
|
ICertificatePasswordProvider certificatePasswordProvider)
|
|
{
|
|
ApplicationConfiguration.SecurityConfiguration.CertificatePasswordProvider
|
|
= certificatePasswordProvider;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotasSet SetTransportQuotas(
|
|
TransportQuotas transportQuotas)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas = transportQuotas;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetOperationTimeout(
|
|
int operationTimeout)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.OperationTimeout = operationTimeout;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetMaxStringLength(
|
|
int maxStringLength)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.MaxStringLength = maxStringLength;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetMaxByteStringLength(
|
|
int maxByteStringLength)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.MaxByteStringLength = maxByteStringLength;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetMaxArrayLength(int maxArrayLength)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.MaxArrayLength = maxArrayLength;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetMaxMessageSize(int maxMessageSize)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.MaxMessageSize = maxMessageSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetMaxBufferSize(int maxBufferSize)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.MaxBufferSize = maxBufferSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetChannelLifetime(
|
|
int channelLifetime)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.ChannelLifetime = channelLifetime;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetSecurityTokenLifetime(
|
|
int securityTokenLifetime)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.SecurityTokenLifetime = securityTokenLifetime;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetMaxEncodingNestingLevels(
|
|
int maxEncodingNestingLevels)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.MaxEncodingNestingLevels
|
|
= maxEncodingNestingLevels;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTransportQuotas SetMaxDecoderRecoveries(
|
|
int maxDecoderRecoveries)
|
|
{
|
|
ApplicationConfiguration.TransportQuotas!.MaxDecoderRecoveries = maxDecoderRecoveries;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMinRequestThreadCount(
|
|
int minRequestThreadCount)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MinRequestThreadCount
|
|
= minRequestThreadCount;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxRequestThreadCount(
|
|
int maxRequestThreadCount)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxRequestThreadCount
|
|
= maxRequestThreadCount;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxQueuedRequestCount(
|
|
int maxQueuedRequestCount)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxQueuedRequestCount
|
|
= maxQueuedRequestCount;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetDiagnosticsEnabled(
|
|
bool diagnosticsEnabled)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.DiagnosticsEnabled = diagnosticsEnabled;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxSessionCount(int maxSessionCount)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxSessionCount = maxSessionCount;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxChannelCount(int maxChannelCount)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxChannelCount = maxChannelCount;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMinSessionTimeout(
|
|
int minSessionTimeout)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MinSessionTimeout = minSessionTimeout;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxSessionTimeout(
|
|
int maxSessionTimeout)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxSessionTimeout = maxSessionTimeout;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxBrowseContinuationPoints(
|
|
int maxBrowseContinuationPoints)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxBrowseContinuationPoints
|
|
= maxBrowseContinuationPoints;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxQueryContinuationPoints(
|
|
int maxQueryContinuationPoints)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxQueryContinuationPoints
|
|
= maxQueryContinuationPoints;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxHistoryContinuationPoints(
|
|
int maxHistoryContinuationPoints)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxHistoryContinuationPoints
|
|
= maxHistoryContinuationPoints;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxRequestAge(int maxRequestAge)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxRequestAge = maxRequestAge;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMinPublishingInterval(
|
|
int minPublishingInterval)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MinPublishingInterval
|
|
= minPublishingInterval;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxPublishingInterval(
|
|
int maxPublishingInterval)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxPublishingInterval
|
|
= maxPublishingInterval;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetPublishingResolution(
|
|
int publishingResolution)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.PublishingResolution
|
|
= publishingResolution;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxSubscriptionLifetime(
|
|
int maxSubscriptionLifetime)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxSubscriptionLifetime
|
|
= maxSubscriptionLifetime;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxMessageQueueSize(
|
|
int maxMessageQueueSize)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxMessageQueueSize = maxMessageQueueSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxNotificationQueueSize(
|
|
int maxNotificationQueueSize)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxNotificationQueueSize
|
|
= maxNotificationQueueSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxNotificationsPerPublish(
|
|
int maxNotificationsPerPublish)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxNotificationsPerPublish
|
|
= maxNotificationsPerPublish;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMinMetadataSamplingInterval(
|
|
int minMetadataSamplingInterval)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MinMetadataSamplingInterval
|
|
= minMetadataSamplingInterval;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetAvailableSamplingRates(
|
|
ArrayOf<SamplingRateGroup> availableSampleRates)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.AvailableSamplingRates
|
|
= availableSampleRates;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetRegistrationEndpoint(
|
|
EndpointDescription registrationEndpoint)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.RegistrationEndpoint
|
|
= registrationEndpoint;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxRegistrationInterval(
|
|
int maxRegistrationInterval)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxRegistrationInterval
|
|
= maxRegistrationInterval;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetNodeManagerSaveFile(
|
|
string nodeManagerSaveFile)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.NodeManagerSaveFile = nodeManagerSaveFile;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMinSubscriptionLifetime(
|
|
int minSubscriptionLifetime)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MinSubscriptionLifetime
|
|
= minSubscriptionLifetime;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxPublishRequestCount(
|
|
int maxPublishRequestCount)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxPublishRequestCount
|
|
= maxPublishRequestCount;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxSubscriptionCount(
|
|
int maxSubscriptionCount)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxSubscriptionCount
|
|
= maxSubscriptionCount;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxEventQueueSize(
|
|
int setMaxEventQueueSize)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxEventQueueSize = setMaxEventQueueSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions AddServerProfile(string serverProfile)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.ServerProfileArray += serverProfile;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetShutdownDelay(int shutdownDelay)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.ShutdownDelay = shutdownDelay;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions AddServerCapabilities(
|
|
string serverCapability)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.ServerCapabilities += serverCapability;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetSupportedPrivateKeyFormats(
|
|
ArrayOf<string> supportedPrivateKeyFormats)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.SupportedPrivateKeyFormats
|
|
= supportedPrivateKeyFormats;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxTrustListSize(
|
|
int maxTrustListSize)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxTrustListSize = maxTrustListSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMultiCastDnsEnabled(
|
|
bool multiCastDnsEnabled)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MultiCastDnsEnabled = multiCastDnsEnabled;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetReverseConnect(
|
|
ReverseConnectServerConfiguration reverseConnectConfiguration)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.ReverseConnect
|
|
= reverseConnectConfiguration;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetOperationLimits(
|
|
OperationLimits operationLimits)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.OperationLimits = operationLimits;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetAuditingEnabled(
|
|
bool auditingEnabled)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.AuditingEnabled = auditingEnabled;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetHttpsMutualTls(
|
|
bool mTlsEnabled)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.HttpsMutualTls = mTlsEnabled;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderClientOptions SetDefaultSessionTimeout(
|
|
int defaultSessionTimeout)
|
|
{
|
|
ApplicationConfiguration.ClientConfiguration!.DefaultSessionTimeout
|
|
= defaultSessionTimeout;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderClientOptions AddWellKnownDiscoveryUrls(
|
|
string wellKnownDiscoveryUrl)
|
|
{
|
|
ApplicationConfiguration.ClientConfiguration!.WellKnownDiscoveryUrls += wellKnownDiscoveryUrl;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderClientOptions AddDiscoveryServer(
|
|
EndpointDescription discoveryServer)
|
|
{
|
|
ApplicationConfiguration.ClientConfiguration!.DiscoveryServers += discoveryServer;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderClientOptions SetEndpointCacheFilePath(
|
|
string endpointCacheFilePath)
|
|
{
|
|
ApplicationConfiguration.ClientConfiguration!.EndpointCacheFilePath
|
|
= endpointCacheFilePath;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
IApplicationConfigurationBuilderClientOptions IApplicationConfigurationBuilderClientOptions.SetMinSubscriptionLifetime(
|
|
int minSubscriptionLifetime)
|
|
{
|
|
ApplicationConfiguration.ClientConfiguration!.MinSubscriptionLifetime
|
|
= minSubscriptionLifetime;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderClientOptions SetReverseConnect(
|
|
ReverseConnectClientConfiguration reverseConnect)
|
|
{
|
|
ApplicationConfiguration.ClientConfiguration!.ReverseConnect = reverseConnect;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderClientOptions SetClientOperationLimits(
|
|
OperationLimits operationLimits)
|
|
{
|
|
ApplicationConfiguration.ClientConfiguration!.OperationLimits = operationLimits;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTraceConfiguration SetOutputFilePath(
|
|
string outputFilePath)
|
|
{
|
|
ApplicationConfiguration.TraceConfiguration!.OutputFilePath = outputFilePath;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTraceConfiguration SetDeleteOnLoad(bool deleteOnLoad)
|
|
{
|
|
ApplicationConfiguration.TraceConfiguration!.DeleteOnLoad = deleteOnLoad;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderTraceConfiguration SetTraceMasks(int traceMasks)
|
|
{
|
|
ApplicationConfiguration.TraceConfiguration!.TraceMasks = traceMasks;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
[Experimental("UA_NETStandard_1")]
|
|
public IApplicationConfigurationBuilderExtension AddExtension<T>(
|
|
XmlQualifiedName elementName,
|
|
T value,
|
|
Action<IEncoder, T> encoderFunc)
|
|
{
|
|
ApplicationConfiguration.UpdateExtension(elementName, value, encoderFunc);
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderExtension AddExtension<T>(
|
|
XmlQualifiedName? elementName,
|
|
T value)
|
|
where T : IEncodeable
|
|
{
|
|
ApplicationConfiguration.UpdateExtension(elementName, value);
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetDurableSubscriptionsEnabled(
|
|
bool durableSubscriptionsEnabled)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.DurableSubscriptionsEnabled
|
|
= durableSubscriptionsEnabled;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxDurableNotificationQueueSize(
|
|
int maxDurableNotificationQueueSize)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxDurableNotificationQueueSize =
|
|
maxDurableNotificationQueueSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxDurableEventQueueSize(
|
|
int maxDurableEventQueueSize)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxDurableEventQueueSize
|
|
= maxDurableEventQueueSize;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IApplicationConfigurationBuilderServerOptions SetMaxDurableSubscriptionLifetime(
|
|
int maxDurableSubscriptionLifetimeInHours)
|
|
{
|
|
ApplicationConfiguration.ServerConfiguration!.MaxDurableSubscriptionLifetimeInHours =
|
|
maxDurableSubscriptionLifetimeInHours;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create ApplicationCertificates from a PKI root.
|
|
/// </summary>
|
|
/// <param name="subjectName">The subject name.</param>
|
|
/// <param name="storeType">The cert store type: ex: "Directory"</param>
|
|
/// <param name="storePath">The PKI root.</param>
|
|
/// <returns>The application certificates.</returns>
|
|
public static ArrayOf<CertificateIdentifier> CreateDefaultApplicationCertificates(
|
|
string subjectName,
|
|
string? storeType = null,
|
|
string? storePath = null)
|
|
{
|
|
var certificateIdentifiers = new List<CertificateIdentifier>
|
|
{
|
|
new() {
|
|
StoreType = storeType,
|
|
StorePath = storePath,
|
|
SubjectName = subjectName,
|
|
CertificateType = ObjectTypeIds.RsaSha256ApplicationCertificateType
|
|
},
|
|
new() {
|
|
StoreType = storeType,
|
|
StorePath = storePath,
|
|
SubjectName = subjectName,
|
|
CertificateType = ObjectTypeIds.EccNistP256ApplicationCertificateType
|
|
},
|
|
new() {
|
|
StoreType = storeType,
|
|
StorePath = storePath,
|
|
SubjectName = subjectName,
|
|
CertificateType = ObjectTypeIds.EccNistP384ApplicationCertificateType
|
|
}
|
|
};
|
|
|
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
{
|
|
certificateIdentifiers.AddRange(
|
|
[
|
|
new CertificateIdentifier
|
|
{
|
|
StoreType = storeType,
|
|
StorePath = storePath,
|
|
SubjectName = subjectName,
|
|
CertificateType = ObjectTypeIds
|
|
.EccBrainpoolP256r1ApplicationCertificateType
|
|
},
|
|
new CertificateIdentifier
|
|
{
|
|
StoreType = storeType,
|
|
StorePath = storePath,
|
|
SubjectName = subjectName,
|
|
CertificateType = ObjectTypeIds
|
|
.EccBrainpoolP384r1ApplicationCertificateType
|
|
}
|
|
]);
|
|
}
|
|
return certificateIdentifiers.ToArrayOf();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal enumeration of supported trust lists.
|
|
/// </summary>
|
|
private enum TrustlistType
|
|
{
|
|
Application,
|
|
Trusted,
|
|
Issuer,
|
|
TrustedHttps,
|
|
IssuerHttps,
|
|
TrustedUser,
|
|
IssuerUser,
|
|
Rejected
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the default PKI root path if root is unspecified, directory or X509Store.
|
|
/// </summary>
|
|
/// <param name="root">A real root path or the store type.</param>
|
|
private static string DefaultPKIRoot(string? root)
|
|
{
|
|
if (root == null ||
|
|
root.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return CertificateStoreIdentifier.DefaultPKIRoot;
|
|
}
|
|
else if (root.Equals(
|
|
CertificateStoreType.X509Store,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return CertificateStoreIdentifier.CurrentUser;
|
|
}
|
|
return root;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determine the default store path for a given trust list type.
|
|
/// </summary>
|
|
/// <param name="trustListType">The trust list type.</param>
|
|
/// <param name="pkiRoot">A PKI root for which the store path is needed.</param>
|
|
/// <exception cref="NotSupportedException"></exception>
|
|
private static string DefaultCertificateStorePath(
|
|
TrustlistType trustListType,
|
|
string pkiRoot)
|
|
{
|
|
string pkiRootType = CertificateStoreIdentifier.DetermineStoreType(pkiRoot);
|
|
if (pkiRootType.Equals(
|
|
CertificateStoreType.Directory,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// see https://reference.opcfoundation.org/v104/GDS/docs/F.1/
|
|
string leafPath;
|
|
switch (trustListType)
|
|
{
|
|
case TrustlistType.Application:
|
|
leafPath = "own";
|
|
break;
|
|
case TrustlistType.Trusted:
|
|
leafPath = "trusted";
|
|
break;
|
|
case TrustlistType.Issuer:
|
|
leafPath = "issuer";
|
|
break;
|
|
case TrustlistType.TrustedHttps:
|
|
leafPath = "trustedHttps";
|
|
break;
|
|
case TrustlistType.IssuerHttps:
|
|
leafPath = "issuerHttps";
|
|
break;
|
|
case TrustlistType.TrustedUser:
|
|
leafPath = "trustedUser";
|
|
break;
|
|
case TrustlistType.IssuerUser:
|
|
leafPath = "issuerUser";
|
|
break;
|
|
case TrustlistType.Rejected:
|
|
leafPath = "rejected";
|
|
break;
|
|
default:
|
|
leafPath = string.Empty;
|
|
break;
|
|
}
|
|
// Caller may have already provided the leaf path, then no need to add.
|
|
int startIndex = pkiRoot.Length - leafPath.Length;
|
|
char lastChar = pkiRoot[^1];
|
|
if (lastChar == Path.DirectorySeparatorChar ||
|
|
lastChar == Path.AltDirectorySeparatorChar)
|
|
{
|
|
startIndex--;
|
|
}
|
|
if (startIndex > 0 &&
|
|
pkiRoot
|
|
.Substring(startIndex, leafPath.Length)
|
|
.Equals(leafPath, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return pkiRoot;
|
|
}
|
|
return Path.Combine(pkiRoot, leafPath);
|
|
}
|
|
else if (pkiRootType.Equals(
|
|
CertificateStoreType.X509Store,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
switch (trustListType)
|
|
{
|
|
case TrustlistType.Application:
|
|
#if !NETFRAMEWORK
|
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
|
pkiRoot.StartsWith(
|
|
CertificateStoreIdentifier.CurrentUser,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return pkiRoot + "My";
|
|
}
|
|
#endif
|
|
return pkiRoot + "UA_MachineDefault";
|
|
case TrustlistType.Trusted:
|
|
return pkiRoot + "UA_Trusted";
|
|
case TrustlistType.Issuer:
|
|
return pkiRoot + "UA_Issuer";
|
|
case TrustlistType.TrustedHttps:
|
|
return pkiRoot + "UA_Trusted_Https";
|
|
case TrustlistType.IssuerHttps:
|
|
return pkiRoot + "UA_Issuer_Https";
|
|
case TrustlistType.TrustedUser:
|
|
return pkiRoot + "UA_Trusted_User";
|
|
case TrustlistType.IssuerUser:
|
|
return pkiRoot + "UA_Issuer_User";
|
|
case TrustlistType.Rejected:
|
|
return pkiRoot + "UA_Rejected";
|
|
default:
|
|
throw new NotSupportedException("Unsupported store type.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// return custom root store
|
|
return pkiRoot;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add specified groups of security policies and security modes.
|
|
/// </summary>
|
|
/// <param name="includeSign">Include the Sign only policies.</param>
|
|
/// <param name="deprecated">Include the deprecated policies.</param>
|
|
/// <param name="policyNone">Include policy 'None'. (no security!)</param>
|
|
private void AddSecurityPolicies(
|
|
bool includeSign = false,
|
|
bool deprecated = false,
|
|
bool policyNone = false)
|
|
{
|
|
// create list of supported policies
|
|
List<string> defaultPolicyUris = [.. SecurityPolicies
|
|
.GetDefaultUris()];
|
|
if (deprecated)
|
|
{
|
|
defaultPolicyUris.AddRange(SecurityPolicies.GetDefaultDeprecatedUris());
|
|
}
|
|
|
|
#if NET5_0_OR_GREATER
|
|
foreach (MessageSecurityMode securityMode in Enum.GetValues<MessageSecurityMode>())
|
|
#else
|
|
foreach (MessageSecurityMode securityMode in typeof(MessageSecurityMode)
|
|
.GetEnumValues())
|
|
#endif
|
|
{
|
|
var policies = ApplicationConfiguration
|
|
.ServerConfiguration!
|
|
.SecurityPolicies.ToList();
|
|
if (policyNone && securityMode == MessageSecurityMode.None)
|
|
{
|
|
InternalAddPolicy(policies, MessageSecurityMode.None, SecurityPolicies.None);
|
|
}
|
|
else if (securityMode >= MessageSecurityMode.SignAndEncrypt ||
|
|
(includeSign && securityMode == MessageSecurityMode.Sign))
|
|
{
|
|
foreach (string policyUri in defaultPolicyUris)
|
|
{
|
|
InternalAddPolicy(policies, securityMode, policyUri);
|
|
}
|
|
}
|
|
ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add default Ecc policies.
|
|
/// </summary>
|
|
private void AddEccSecurityPolicies(bool sign = false)
|
|
{
|
|
// create list of supported policies
|
|
string[] defaultPolicyUris = SecurityPolicies.GetDefaultEccUris();
|
|
MessageSecurityMode securityMode = sign
|
|
? MessageSecurityMode.Sign
|
|
: MessageSecurityMode.SignAndEncrypt;
|
|
{
|
|
var policies = ApplicationConfiguration
|
|
.ServerConfiguration!
|
|
.SecurityPolicies.ToList();
|
|
foreach (string policyUri in defaultPolicyUris)
|
|
{
|
|
InternalAddPolicy(policies, securityMode, policyUri);
|
|
}
|
|
ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set secure defaults for flags.
|
|
/// </summary>
|
|
private static void SetSecureDefaults(SecurityConfiguration securityConfiguration)
|
|
{
|
|
// ensure secure default settings
|
|
securityConfiguration.AutoAcceptUntrustedCertificates = false;
|
|
securityConfiguration.AddAppCertToTrustedStore = false;
|
|
securityConfiguration.RejectSHA1SignedCertificates = true;
|
|
securityConfiguration.RejectUnknownRevocationStatus = true;
|
|
securityConfiguration.SuppressNonceValidationErrors = false;
|
|
securityConfiguration.SendCertificateChain = true;
|
|
securityConfiguration.MinimumCertificateKeySize = CertificateFactory.DefaultKeySize;
|
|
securityConfiguration.MaxRejectedCertificates = 5;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add security policy if it doesn't exist yet.
|
|
/// </summary>
|
|
/// <param name="policies">The list to which the policies are added.</param>
|
|
/// <param name="securityMode">The message security mode.</param>
|
|
/// <param name="policyUri">The security policy Uri.</param>
|
|
/// <exception cref="ArgumentException"><paramref name="securityMode"/></exception>
|
|
private static bool InternalAddPolicy(
|
|
List<ServerSecurityPolicy> policies,
|
|
MessageSecurityMode securityMode,
|
|
string policyUri)
|
|
{
|
|
if (securityMode == MessageSecurityMode.Invalid)
|
|
{
|
|
throw new ArgumentException("Invalid security mode selected", nameof(securityMode));
|
|
}
|
|
|
|
var newPolicy = new ServerSecurityPolicy
|
|
{
|
|
SecurityMode = securityMode,
|
|
SecurityPolicyUri = policyUri
|
|
};
|
|
if (policies.Find(s =>
|
|
s.SecurityMode == newPolicy.SecurityMode &&
|
|
string.Equals(
|
|
s.SecurityPolicyUri,
|
|
newPolicy.SecurityPolicyUri,
|
|
StringComparison.Ordinal)
|
|
) == null)
|
|
{
|
|
policies.Add(newPolicy);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool m_typeSelected;
|
|
}
|
|
}
|