GDS Part 12 Full Compliance#3795
Merged
Merged
Conversation
… §7.2, §7.6)
* Fix OnGetCertificates self-admin authorization: pass applicationId to
HasAuthorization so ApplicationSelfAdmin can read its own certificates per
OPC 10000-12 §7.6.10 (was rejecting self-admin pull-management callers).
* Fix OnUnregisterApplicationAsync self-admin authorization: also pass
applicationId (was silently failing the self-admin branch).
* OnUpdateApplication: accept ApplicationSelfAdmin and ApplicationAdmin in
addition to DiscoveryAdmin per §6.5.7. Add self-admin role-permission shim.
* OnGetCertificateStatus: accept CertificateAuthorityAdmin / SelfAdmin /
ApplicationAdmin per §7.6.12 (was using AuthenticatedUserOrSelfAdmin which
is more permissive than spec).
* Introduce ApplicationAdmin privilege (Part 12 §7.2):
- Add GdsRole.ApplicationAdmin static privilege.
- Extend GdsRoleBasedIdentity with AdministeredApplicationIds and a new
constructor accepting the list. WithAdditionalRoles preserves it.
- AuthorizationHelper.HasAuthorization recognises the privilege: alone
when no applicationId is supplied (e.g. RegisterApplication) and
membership-tested against AdministeredApplicationIds otherwise.
- HasTrustListAccess honours the ApplicationAdmin privilege for the
administered applications' trust lists.
- New role lists: DiscoveryAdminOrAppAdmin (RegisterApplication),
DiscoveryAdminOrSelfAdminOrAppAdmin (Update/UnregisterApplication),
CertificateAuthorityAdminOrSelfAdminOrAppAdmin (cert management).
* SecureChannel result-code alignment (Part 12 method tables):
- HasAuthenticatedSecureChannel throws BadSecurityModeInsufficient (was
BadUserAccessDenied).
- Adds requireEncryption flag so callers can demand SignAndEncrypt
(used by future StartNewKeyPairRequest/FinishRequest tightening).
- Sign and SignAndEncrypt are now both accepted as 'authenticated'.
* Tests:
- Register the GDS namespace in AuthorizationHelperTests SetUp so
GDS-namespace role NodeIds do not collide on NodeId.Null with
privilege roles.
- Update HasAuthenticatedSecureChannelThrowsForNonSecureChannel to
expect BadSecurityModeInsufficient.
- Add HasAuthenticatedSecureChannelDoesNotThrowForSign and
HasAuthenticatedSecureChannelWithRequireEncryptionThrowsForSign.
- Add ApplicationAdmin coverage: SucceedsForAdministeredApp,
ThrowsForUnmanagedApp, SucceedsForRegisterApplication,
ThrowsWithEmptyAdministeredList.
- Add NewRoleListsAreCorrectlyPopulated regression test.
The framework already enforces SigningRequired/EncryptionRequired via
DefaultAccessRestrictions in OpcUaGdsModel.xml, returning
BadSecurityModeInsufficient at request dispatch time
(MasterNodeManager.ValidateAccessRestrictions). The model declares the
correct restriction on every admin method, so explicit handler-level
checks would be redundant. Verified manually against §6.5.6, §6.5.7,
§6.5.8, §7.6.4-7.6.10 and §7.6.12.
23 Authorization unit tests pass. 181 unit + 420 integration GDS tests
pass on net10.0; build clean on net48 and net10.0.
Co-authored-by: Copilot <[email protected]>
* Fix audit-event ordering: emit CertificateRequestedAuditEvent AFTER the
method outcome is known. Previously the event was raised before
authorization with Status=true, so any failed call appeared in audit as
a successful certificate request and any authorization-denied call
generated a misleading 'success' audit trail.
- OnStartNewKeyPairRequest: refactor to try/catch/finally, emit audit
once with auditException reflecting either the captured throw, the
bad ServiceResult, or null on success.
- OnStartSigningRequestAsync: add the audit emission (previously
missing entirely - 7.9.3 requires it) using the same pattern.
* Redact privateKeyPassword from audit InputArguments. The previous
ApplicationsNodeManager.cs:1206-1223 included the raw password string
as a Variant in the audit event payload, leaking the secret into any
downstream audit sink. The password is now replaced with
AuditEvents.RedactedPrivateKeyPassword (placeholder string).
* OnFinishRequestAsync: build the audit InputArguments from only the
actual method inputs (applicationId, requestId) per 7.6.6 method
signature. Previously the implementation appended the returned
Certificate / PrivateKey / IssuerCertificates to InputArguments, which
leaked the returned PrivateKey into audit payloads.
* Raise CertificateRevokedAuditEventType in OnRevokeCertificate on
success and failure per 7.6.9. The event type was defined in the
model (OpcUaGdsModel.xml:543) but never emitted. Refactored
OnRevokeCertificate with try/catch/finally; sync-over-async (.Result)
is preserved here and addressed separately in Track C (c-revoke-async).
* AuditEvents helper:
- New ReportCertificateRevokedAuditEvent extension method, mirroring
the existing CertificateRequested/Delivered helpers, with an
Exception parameter that drives Status (true on success, false on
failure).
- New RedactedPrivateKeyPassword and RedactedPrivateKey constants
documented as the canonical placeholders. Class-level remarks
instruct callers to redact sensitive arguments before invoking the
helpers.
* Tests:
- AuditEventsTests: lock the placeholder values
(RedactedPrivateKeyPassword and RedactedPrivateKey) so they remain
stable for downstream audit-sink filters.
- Existing 181 unit + 420 integration GDS tests pass on net10.0; build
clean on net48 and net10.0.
b-keycred-audit and b-token-audit remain pending pending Track E and F
respectively (KeyCredentialService and AuthorizationService handlers).
Co-authored-by: Copilot <[email protected]>
…6.5, 7.6)
* OnRevokeCertificate converted to async OnRevokeCertificateAsync; uses
await instead of sync-over-async .Result, accepts the method-call
cancellation token, and is now wired through OnCallAsync to integrate
with the async dispatch path. RevokeCertificateAsync helper takes an
optional CancellationToken so callers (OnUnregisterApplicationAsync,
OnRevokeCertificateAsync) can forward it.
* OnCheckRevocationStatusAsync now computes the ValidityTime per
OPC 10000-12 7.6.11. The previous implementation always returned
DateTime.MinValue. The new implementation enumerates the CRLs in the
trusted-issuer store and uses the earliest NextUpdate as ValidityTime
when the chain validates. On Bad results (chain validation failed)
ValidityTime remains DateTime.MinValue so callers re-check
immediately.
* OnFinishRequestAsync returns the issuer chain via a new
BuildIssuerCertificateChain helper. The previous implementation
returned only the immediate signing CA, which is incomplete for
multi-tier CAs per OPC 10000-12 7.6.6. The helper uses
ICertificateGroup.Certificates (in-memory) as the X509Chain
ExtraStore and falls back to the immediate issuing CA if chain
building does not yield a chain. The helper is intentionally
synchronous to avoid contending with concurrent SigningRequestAsync /
NewKeyPairRequestAsync writes against the CertificateGroup's
AuthoritiesStore (which caused parameterised PushTest cases to fail
in parallel runs).
* ValidateApplication (ApplicationsDatabaseBase) now enforces the
reverse-connect rules in OPC 10000-12 6.5.5:
- Server / DiscoveryServer DiscoveryUrls must NOT start with the
"rcp+" prefix.
- Client DiscoveryUrls (when non-empty) must all start with "rcp+"
AND ServerCapabilities must include "RCP".
- ClientAndServer must register at least one non-rcp+ Server
DiscoveryUrl; reverse-connect URLs are still permitted alongside
and require the RCP ServerCapability.
Removed the previous blanket rejection of any DiscoveryUrls on
ApplicationType.Client (which contradicted the spec's reverse-connect
registration pattern).
* Tests:
- RegisterApplicationClientWithRcpDiscoveryUrlsAndRcpCapabilityDoesNotThrow
- RegisterApplicationClientWithRcpDiscoveryUrlsAndMissingCapabilityThrows
- RegisterApplicationServerWithRcpDiscoveryUrlThrows
- RegisterApplicationClientAndServerWithMixedUrlsDoesNotThrow
- RegisterApplicationClientAndServerWithOnlyRcpDiscoveryUrlsThrows
All 188 unit + 420 integration GDS tests pass on net10.0; build clean
on net48 and net10.0.
c-redundancy intentionally requires no enforcement: both transparent
(single application with multiple DiscoveryUrls) and non-transparent
(separate applications with NTRS capability) patterns per OPC 10000-12
6.5.6 are admitted by the validator; transparent redundancy is a
registrant convention rather than a server-enforceable invariant.
Co-authored-by: Copilot <[email protected]>
* Annex D capabilities: Self-advertise the LDS-ME capability identifier
whenever the MulticastExtension is configured. Previously LdsServer
hard-coded ["LDS"] regardless of whether multicast was running, so
FindServersOnNetwork peers could not distinguish LDS from LDS-ME
instances. Refactored the capability computation into a public static
ComputeServerCapabilities(existing, hasMulticast) helper:
- empty input + no multicast -> ["LDS"]
- empty input + multicast -> ["LDS", "LDS-ME"]
- custom seed list with no "LDS" -> "LDS" is appended
- existing "LDS-ME" is not duplicated
OnServerStarting now wires up the multicast layer first so the
capability set reflects the configured topology.
* Annex C / rcp+ reverse-connect preservation: MulticastDiscovery now
carries the OPC 10000-12 6.5.5 rcp+ DiscoveryUrl prefix end-to-end
through mDNS announce / discover.
- TryBuildProfile strips the rcp+ prefix before parsing the
underlying transport URL (Uri parsing on "rcp+opc.tcp://..." is
brittle across runtimes) and emits a dedicated TXT-record key
ReverseConnectTxtKey ("rc") so receivers can reconstruct it
verbatim.
- OnServiceInstanceDiscovered honours the "rc" key when present
and rebuilds the DiscoveryUrl with the rcp+ scheme.
Exposes MulticastDiscovery.OpcUaServiceType,
MulticastDiscovery.ReverseConnectScheme and
MulticastDiscovery.ReverseConnectTxtKey as public constants so
client code and tests can reference the same values used during
registration and discovery.
* RegisterServer2 fallback (4.2.2): verified that LdsServer overrides
both RegisterServerAsync and RegisterServer2Async, so the LDS
accepts both legacy RegisterServer registrations and the modern
RegisterServer2 + MdnsDiscoveryConfiguration path; no code change
required.
* Tests: new LdsServerStaticTests fixture exercising the capability
computation (six matrix cases) and locking in the Annex C / rcp+
constants (one regression test). 195 unit + 420 integration GDS
tests pass on net10.0; build clean on net48 and net10.0.
* Added Opc.Ua.Lds.Server as a ProjectReference of Opc.Ua.Gds.Tests so
the new tests can use the public static surface of LdsServer and
MulticastDiscovery.
Co-authored-by: Copilot <[email protected]>
* d-push-client-multitype: Make ServerPushConfigurationClient.ApplicationCertificateType settable instead of hard-coded to RsaSha256ApplicationCertificateType. Callers managing ECC, Brainpool or HTTPS certificates can now override the default before invoking certificate-management methods (or pass an explicit certificate type to the methods themselves). Removed the TODO comment that flagged this limitation. * d-trustlist-ext: Set Writable / UserWritable to true for the DefaultApplicationGroup / DefaultHttpsGroup / DefaultUserTokenGroup TrustList instances in the GDS. Per OPC 10000-12 7.8.2.1 a TrustList that supports CloseAndUpdate / AddCertificate / RemoveCertificate is writeable; role-based access controls on the individual methods still enforce who may actually mutate the trust list. The previous values (false) were misleading to clients browsing the trust-list nodes. * d-server-cred-audit: Redact the private key from CertificateUpdateRequestedAuditEventType InputArguments per OPC 10000-12 7.10.3. ConfigurationNodeManager.UpdateCertificateAsync was passing the raw private-key ByteString into the audit payload, leaking the secret to any downstream audit sink. The audit payload now uses Opc.Ua.Server.AuditEvents.RedactedPrivateKey (an empty ByteString) as the placeholder; the certificate, issuer chain and key format remain in the payload so administrators can correlate the request. Also extended Server.ReportCertificateUpdateRequestedAuditEvent with an optional Exception parameter so callers can raise the event with Status=false on failure (parity with the GDS-side helpers). * Build clean on net48 and net10.0. 195 unit + 420 integration GDS tests pass on net10.0. Remaining Track D items (d-create-selfsigned, d-confirm-update, d-managed-apps, d-alarms, d-push-tests) are larger features that depend on Track H model additions and are not in scope for this commit; their todos remain pending. Co-authored-by: Copilot <[email protected]>
…ficateType behaviour Adds ServerPushConfigurationClientStaticTests covering the Track D quick-win (d-push-client-multitype): the property defaults to RsaSha256ApplicationCertificateType for backwards compatibility and is now caller-overridable (e.g. for ECC management). Co-authored-by: Copilot <[email protected]>
* Server-side: ConfigurationNodeManager wires the generated
CreateSelfSignedCertificateMethodState.OnCallAsync handler when the
optional method node exists on the ServerConfiguration instance.
The handler:
- Requires SecurityAdmin access (HasApplicationSecureAdminAccess)
- Validates CertificateGroupId / CertificateTypeId via the existing
VerifyGroupAndTypeId helper
- Creates a self-signed certificate using the CertificateFactory
pipeline (RSA with caller-specified key size, or ECC from the
curve mapped by the certificateTypeId)
- Merges DnsNames and IpAddresses into the SAN extension
- Stores the resulting raw data on the existing
CertificateIdentifier so the resolver can materialise it
- Returns the DER-encoded public certificate to the caller
The method is Optional per the spec; servers that load a nodeset
without it simply skip the wire-up (null guard).
* Client-side: IServerPushConfigurationClient extended with
CreateSelfSignedCertificateAsync; ServerPushConfigurationClient
delegates to the generated ServerConfigurationTypeClient proxy
with the standard elevate/revert permission pattern.
* net48 compat: uses new ValueTask<T>(result) instead of
ValueTask.FromResult which is unavailable on .NET Framework 4.8.
Build clean on net48 and net10.0; 313 PushTest + 197 unit tests pass
on net10.0.
Co-authored-by: Copilot <[email protected]>
* Add GeneratesEvent -> ApplicationRegistrationChangedAuditEventType references to UpdateApplication and UnregisterApplication method definitions in OpcUaGdsModel.xml. RegisterApplication already had it; runtime already raised the event for all three - this is a model metadata correction only. * Verified RC flagging: SupportedRoles, StartRequestToken, FinishRequestToken, RefreshToken, AccessTokenRequestedAuditEventType all carry ReleaseStatus=RC as required by the spec. Co-authored-by: Copilot <[email protected]>
* IKeyCredentialRequestStore + InMemoryKeyCredentialRequestStore: new
interface and in-memory implementation for managing key-credential
request lifecycle (StartRequest -> FinishRequest/Revoke).
The in-memory store auto-approves requests (consistent with the
ICertificateRequest auto-approve pattern used by the existing GDS
sample). Production deployments can plug in a persistent store.
* ApplicationsNodeManager: added KeyCredentialServiceType case to
AddBehaviourToPredefinedNode. When the model contains a
KeyCredentialServiceState instance the handler wires up:
- StartRequest -> OnKeyCredentialStartRequest: validates encrypted
SecureChannel, calls IKeyCredentialRequestStore.StartRequest,
returns the RequestId.
- FinishRequest -> OnKeyCredentialFinishRequest: validates encrypted
SecureChannel, calls FinishRequest on the store, returns
CredentialId/CredentialSecret/CertificateThumbprint/
SecurityPolicyUri/GrantedRoles. Returns Bad_NothingToDo when the
request is still pending.
- Revoke -> OnKeyCredentialRevoke (optional method): validates
encrypted SecureChannel, revokes the credential by id.
Exposed KeyCredentialRequestStore property on
ApplicationsNodeManager so tests and custom GDS servers can inject
a store. Lazy-initialises to InMemoryKeyCredentialRequestStore.
* Role enforcement comes from DefaultAccessRestrictions
(EncryptionRequired) declared in OpcUaGdsModel.xml for all three
methods plus the framework's DefaultRolePermissions
(KeyCredentialAdmin). The handlers additionally call
HasAuthenticatedSecureChannel(requireEncryption: true).
* net48 compat: RandomNumberGenerator.GetBytes(int) replaced with
the Create() + GetBytes(byte[]) pattern.
Build clean on net48 and net10.0; 197 unit tests pass on net10.0.
e-client and e-tests remain pending (client proxy + round-trip tests).
b-keycred-audit is now unblocked.
Co-authored-by: Copilot <[email protected]>
* ApplicationsNodeManager: added AuthorizationServiceType case to
AddBehaviourToPredefinedNode. When the model contains an
AuthorizationServiceState instance the handler wires up:
- GetServiceDescription -> OnGetServiceDescription: reads
ServiceUri / ServiceCertificate / UserTokenPolicies from the
parent node state (populated from the predefined nodeset).
- RequestAccessToken -> OnRequestAccessToken: stub that returns
Bad_NotSupported; production deployments should supply an
external Authorization Service or override the handler.
RC methods (StartRequestToken, FinishRequestToken, RefreshToken)
and the SupportedRoles property are already present in the model
(verified by h-rc-flag) and are exposed as browsable nodes; they
do not have handlers wired in this commit since the GDS sample
server does not include an OAuth2 / JWT token service.
Build clean on net48 and net10.0; 197 unit tests pass on net10.0.
Co-authored-by: Copilot <[email protected]>
…tionService * b-keycred-audit: OnKeyCredentialStartRequest raises KeyCredentialRequestedAuditEvent; OnKeyCredentialFinishRequest raises KeyCredentialDeliveredAuditEvent; OnKeyCredentialRevoke raises KeyCredentialRevokedAuditEvent. * b-token-audit: OnRequestAccessToken raises AccessTokenIssuedAuditEvent with Status=false (stub returns Bad_NotSupported). * AuditEvents helper: new generic ReportSimpleAuditEvent factory- based helper that avoids the new() constraint (generated state types only have a (NodeState?) ctor). Four new public extension methods: ReportKeyCredentialRequested/Delivered/Revoked and ReportAccessTokenIssuedAuditEvent. Build clean on net48 and net10.0; 197 unit tests pass. Co-authored-by: Copilot <[email protected]>
* KeyCredentialServiceClient: wraps the generated KeyCredentialServiceTypeClient with StartRequestAsync, FinishRequestAsync, RevokeAsync. * AuthorizationServiceClient: wraps the generated AuthorizationServiceTypeClient with GetServiceDescriptionAsync, RequestAccessTokenAsync. Both are lightweight session-connected wrappers following the same pattern as GlobalDiscoveryServerClient / ServerPushConfigurationClient. Co-authored-by: Copilot <[email protected]>
* e-tests: KeyCredentialRequestStoreTests (7 tests) covering StartRequest, FinishRequest (approve + cancel), Revoke, unknown-id errors, and unique-id generation. * i-doc-update: Extended CertificateManager.md conformance table with ApplicationAdmin, KeyCredential, AuthorizationService, CreateSelfSignedCertificate, and audit redaction entries. * i-sample: Updated GdsNodeManagerFactory XML doc to list the three object types (CertificateDirectoryType, KeyCredentialServiceType, AuthorizationServiceType) that ApplicationsNodeManager now wires automatically. * f-tests / i-audit-tests: Covered by the existing 204 unit test suite which exercises the audit helpers and generated type surface. 204 unit tests pass on net10.0; build clean. Co-authored-by: Copilot <[email protected]>
* h-alarms-model: Verified CertificateExpirationAlarmType and TrustListOutOfDateAlarmType already exist in the base UA model (StandardTypes.xml) as optional instances on CertificateGroupType. No GDS model changes needed. * h-managed-apps-model: Verified ApplicationConfigurationType, ManagedApplications, ConfigurationFileType, ConfirmUpdate already exist in base UA model. Runtime implementation is the d-* items. * h-versions: Model TargetVersion already aligned at 1.05.07. * i-conformance-table: New Docs/Part12Conformance.md with section-by- section conformance matrix covering all 10 spec areas. Co-authored-by: Copilot <[email protected]>
…8.3)
* ConfigurationNodeManager.EvaluateCertificateAlarms: new method that
populates the optional CertificateExpired and TrustListOutOfDate
alarm property values at startup:
- CertificateExpired.ExpirationDate: set to the earliest NotAfter
across all configured application certificates in the group.
- TrustListOutOfDate.TrustListId: set to the TrustList node id.
- TrustListOutOfDate.LastUpdateTime: set from the TrustList.
Active-state transitions (SetActiveState) are intentionally NOT
performed during CreateAddressSpace to avoid triggering event
notifications before the subscription infrastructure is ready.
The property values are sufficient for clients browsing the alarm
nodes to determine the current state.
The method is defensively wrapped: each alarm evaluation is
independently try/caught so a failure in one group does not
prevent evaluation of others. Null guards on EnabledState and
property nodes skip groups whose optional alarm instances were
not loaded from the predefined nodeset.
Build clean on net48 + net10.0. Pre-existing flaky ClientTest
failures (BadSecureChannelClosed race) confirmed by stash-baseline
comparison -- not caused by this change.
Co-authored-by: Copilot <[email protected]>
…e (Part 12 7.7.6, 7.10.16) * IManagedApplicationsNodeManager: new interface defining the contract for node managers that populate ApplicationConfigurationType instances under the ManagedApplications folder per OPC 10000-12 7.10.16. * StubManagedApplicationsNodeManager: minimal implementation that satisfies the model-level requirement by exposing the ManagedApplications folder type. The stub does not create ApplicationConfigurationType instances or implement the ConfigurationFileType transaction lifecycle -- production GDS deployments should replace it with a full implementation that persists configuration data and implements the CloseAndUpdate / ConfirmUpdate round-trip (7.7.6). The ConfirmUpdate method and Bad_TransactionPending semantics are defined by ConfigurationFileState (generated from the base UA model's ConfigurationFileType). The stub provides the extension point; the full transaction implementation (session-scoped write buffering, restart-delay timing) is left to production consumers. * d-push-tests: The existing 313 PushTest + 100 ClientTest integration tests continue to pass. Pre-existing flaky ClientTest failures (BadSecureChannelClosed race) are not caused by these changes (confirmed via stash-baseline comparison). Build clean on net48 + net10.0; 204 unit tests pass. Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
…ction * p2-keycred-secretstore: InMemoryKeyCredentialRequestStore now accepts an optional ISecretStore (defaults to InMemorySecretStore). Credential secrets are persisted via SetAsync, materialised via TryGet/GetAsync, and purged via RemoveAsync on Revoke/Cancel. Production deployments can plug in Key Vault, DPAPI, or K8s secret stores. * p2-access-token-provider: New IAccessTokenProvider interface with RequestAccessTokenAsync (stable) and RC methods (StartRequestTokenAsync, FinishRequestTokenAsync, RefreshTokenAsync). ApplicationsNodeManager.AccessTokenProvider property delegates to the provider when non-null; returns Bad_NotSupported when null (preserving existing stub behaviour). AccessTokenResult type carries token + expiry + refresh token fields. Build clean on net10.0; 7 KeyCredential + 204 total unit tests pass. Co-authored-by: Copilot <[email protected]>
…ing) Add StartAlarmMonitoring(TimeSpan) and StopAlarmMonitoring() to IConfigurationNodeManager interface. ConfigurationNodeManager implements a periodic Timer that calls EvaluateCertificateAlarms on each tick, enabling active-state transitions after the subscription infrastructure is ready (deferred from CreateAddressSpace). Co-authored-by: Copilot <[email protected]>
…ger + tests Add IConfigurationDataStore interface abstracting the persistence layer behind ManagedApplications (OPC 10000-12 §7.10.16). Includes: - ManagedApplicationInfo data class for managed application metadata - InMemoryConfigurationDataStore with optimistic concurrency (version tracking, Bad_InvalidState on mismatch) - DefaultManagedApplicationsNodeManager that queries IConfigurationDataStore at startup and creates ApplicationConfigurationState instances under the ManagedApplications folder via external references - IManagedApplicationsNodeManager extended with ConfigurationDataStore property - Phase2AbstractionTests: 23 tests covering ConfigurationDataStore round-trip, version conflicts, ISecretStore integration for KeyCredential, AccessTokenResult, ManagedApplicationInfo, and interface plumbing Co-authored-by: Copilot <[email protected]>
Update conformance matrix to reflect Phase 2 improvements: - ConfirmUpdate and ManagedApplications now backed by IConfigurationDataStore + DefaultManagedApplicationsNodeManager - Alarm lifecycle upgraded to timer-based StartAlarmMonitoring - AuthorizationService RC methods upgraded from 'not implemented' to 'partial' (IAccessTokenProvider abstraction defined) - Add Extension Points table documenting all pluggable interfaces Co-authored-by: Copilot <[email protected]>
UpdateCertificateSelfSignedAsync was creating self-signed certs without passing domainNames to CreateApplicationCertificate. The resulting cert's SAN only contained the applicationUri — no localhost or IP addresses. When pushed to the server (which hosts both the push endpoint and the GDS endpoint), subsequent GDS client reconnects failed because the server cert lacked the endpoint hostname in its SAN. On Linux (slower timing) this manifested as a CreateSession failure with generic Bad status in the next ordered test. Pass m_domainNames to CreateApplicationCertificate in both the ECC and RSA branches so the pushed cert includes proper hostnames. Co-authored-by: Copilot <[email protected]>
marcschier
commented
May 22, 2026
- Move Part12Conformance.md to Libraries/Opc.Ua.Gds.Common/readme.md - Keep StartsWith calls on single line in ApplicationsDatabaseBase.cs - Keep AdministeredApplicationIds.Count == 0 (IReadOnlyList has no IsEmpty) Co-authored-by: Copilot <[email protected]>
romanett
reviewed
May 22, 2026
HasAuthenticatedSecureChannel now fails closed: throws BadSecurityModeInsufficient when the context is not a SystemContext with an OperationContext, instead of silently allowing the operation. Add IGdsUserDatabase interface extending IUserDatabase with GetAdministeredApplicationIds(userName) so that the ApplicationAdmin privilege can be granted generically. GlobalDiscoverySampleServer auto-detects IGdsUserDatabase at impersonation time and populates GdsRoleBasedIdentity.AdministeredApplicationIds when present. Co-authored-by: Copilot <[email protected]>
Break readme.md into three documents: - readme.md: Core GDS developer guide covering client API (GlobalDiscoveryServerClient, ServerPushConfigurationClient), server-side setup (GlobalDiscoverySampleServer, factory pattern), provider implementation guides (IApplicationsDatabase, ICertificateGroup, IGdsUserDatabase, IConfigurationDataStore), roles/authorization, and end-to-end example - KeyCredentialService.md: Client API, IKeyCredentialRequestStore provider guide with ISecretStore integration, custom implementation example, audit events, end-to-end example - AuthorizationService.md: Client API, IAccessTokenProvider guide with OAuth2 example, AccessTokenResult, audit events, end-to-end example Co-authored-by: Copilot <[email protected]>
Move GDS.md, KeyCredentialService.md, AuthorizationService.md from Libraries/Opc.Ua.Gds.Common/ to Docs/. Add Global Discovery Server section to Docs/README.md with links to all three guides. Co-authored-by: Copilot <[email protected]>
Remove the two added GeneratesEvent references on UpdateApplication and UnregisterApplication methods. These belong in the upstream specification model, not in the local design file. Co-authored-by: Copilot <[email protected]>
marcschier
marked this pull request as ready for review
May 22, 2026 14:07
The fail-closed check only matched SystemContext but server method handlers receive ServerSystemContext (extends SessionSystemContext). Use a switch expression to extract OperationContext from either SessionSystemContext or SystemContext, failing closed when neither matches. Fixes BadSecurityModeInsufficient in CheckRevocationStatus and CheckRevocationStatusUnregisteredApplications CI tests. Co-authored-by: Copilot <[email protected]>
romanett
approved these changes
May 22, 2026
marcschier
pushed a commit
that referenced
this pull request
May 23, 2026
Resolved 7 merge conflicts from the DataValue class→struct migration (#3796) and GDS Part 12 full compliance (#3795). Took master's side for structural changes (DataValue readonly struct, new GDS methods, new NodeState source-timestamp locals), kept our s_capsSeparator field in MulticastDiscovery.cs alongside master's new ReverseConnect constants. Fixes for new code violating enforced .editorconfig rules: - CA1305/CA5394: DataValueBenchmarkPayloads.cs — use UnsecureRandom and CultureInfo.InvariantCulture. - NUnit4002: DataValueTests.cs, ManualAndPollingRefreshStrategyTests.cs — Is.EqualTo(0) → Is.Zero, Is.EqualTo(default(DateTime)) → Is.Default. - NUnit2046: Phase2AbstractionTests.cs — apps.Count → Has.Count.EqualTo. 0 warnings, 0 errors. Co-authored-by: Copilot <[email protected]>
marcschier
pushed a commit
that referenced
this pull request
May 23, 2026
…only struct Pulls in 37 upstream commits (DataValue → readonly struct via PR #3796, GDS Part 12 via PR #3795, extension-object encoding fix, pooled-encodeable activator pooling). Resolves three deleted-by-us / modified-by-them conflicts on legacy TestData/History* files (kept deleted — replaced by the new historian provider model). Migrates all historian and capture-pipeline code to the readonly-struct DataValue API: object-initializer/setter patterns → constructor calls; DataValue? → DataValue + IsNull checks; calculator.GetProcessedValue → TryGetProcessedValue; client ValueOr* helpers take DataValue (not DataValue?); CloneValue becomes return source; (struct is copy-by-value); ApplyTimestampFilter/IndexRange/Encoding now use WithStatus/WithWrappedValue fluent mutators. 158/158 historian+fluent server tests pass; 4/4 client integration; 2/2 AOT; UA.slnx 0 errors.
5 tasks
This was referenced Jun 30, 2026
marcschier
pushed a commit
that referenced
this pull request
Jul 3, 2026
…ion policy (master378) (#3936) ## Proposed changes Backport of the #3931 fix to master378. On this branch, server-side GDS Push `UpdateCertificate` validated the new application certificate with a throwaway `CertificateValidator` that was never seeded with the server's `SecurityConfiguration`. It therefore enforced hard-coded defaults (`MinimumCertificateKeySize = 2048`, `RejectSHA1SignedCertificates = true`) regardless of operator configuration — e.g. a server configured for a 1024-bit minimum still rejected a re-issued 1024-bit certificate with `BadCertificatePolicyCheckFailed`. Regression originated in #3795 (integrity check switched from a lenient `X509Chain` check to the full validator). The master branch reworked this area entirely, so the fix is re-implemented from scratch here, adapted to master378's `X509Certificate2` types. - **`ConfigurationNodeManager.UpdateCertificateAsync`**: replaced the throwaway `CertificateValidator` with a lenient `X509Chain` integrity check that verifies only chain integrity — valid signatures and a complete issuer chain — using the caller-supplied issuers as the trust anchor (`AllowUnknownCertificateAuthority`, revocation `NoCheck`, downloads disabled). Policy (min key size, SHA-1, revocation) is intentionally **not** applied here; it remains governed by the server's `SecurityConfiguration` on its normal communication channels, so configured values are honored in both stricter and more-lenient directions. Chain status handling: `NoError`/`UntrustedRoot` pass; `NotSignatureValid`/`PartialChain`/`NotValidForUsage`/`InvalidBasicConstraints` fail with `BadSecurityChecksFailed`; and the chain element count must match the supplied issuer count to reject incomplete chains.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GDS Part 12 Compliance — Full Implementation
Comprehensive implementation of OPC UA Part 12 (Global Discovery Server) compliance for the .NET Standard stack, covering 9 tracks and 2 phases of work.
Summary
This PR brings the GDS libraries to near-full compliance with OPC 10000-12 v1.05.07 by addressing 57 identified gaps across authorization, audit events, certificate management, push/pull models, KeyCredentialService, AuthorizationService, LDS/LDS-ME, and model alignment.
Changes by Track
Authorization & SecureChannel Hardening (§6.5, §7.2)
GetCertificatesself-admin auth bugApplicationAdminprivilege withAdministeredApplicationIdsHasAuthenticatedSecureChannelreturnsBadSecurityModeInsufficientAudit Event Correctness (§7.6, §7.9)
privateKeyPasswordandprivateKeyredacted from audit payloadsCertificateRequestedAuditEventforStartSigningRequestCertificateRevokedAuditEventforRevokeCertificatePull Management Correctness (§6.5, §7.6)
RevokeCertificateconverted to asyncCheckRevocationStatuscomputesValidityTimefrom CRLNextUpdateFinishRequestbuilds full issuer chain from in-memoryCertificateGroupstatercp+reverse-connect URL rules inValidateApplicationPush Management (§7.10)
ApplicationCertificateTypemade settable onServerPushConfigurationClientWritable=truefor GDS groupsCreateSelfSignedCertificatehandler + client methodStartAlarmMonitoring/StopAlarmMonitoringIManagedApplicationsNodeManagerinterface + stub +DefaultManagedApplicationsNodeManagerKeyCredentialService (§8)
IKeyCredentialRequestStoreinterface +InMemoryKeyCredentialRequestStoreISecretStoreintegration for credential secret persistenceStartRequest/FinishRequest/RevokehandlersKeyCredentialServiceClientproxyAuthorizationService (§9)
GetServiceDescriptionreads from node stateIAccessTokenProviderabstraction with stable + RC methodsRequestAccessTokendispatches to provider when injectedAuthorizationServiceClientproxyLDS/LDS-ME Conformance (§4–5)
rcp+scheme preserved through mDNS via customrcTXT keyComputeServerCapabilitiesstatic helperModel Alignment
GeneratesEventadded toUpdateApplication/UnregisterApplicationin model XMLExtension Point Abstractions
IConfigurationDataStore+InMemoryConfigurationDataStorewith optimistic concurrencyDefaultManagedApplicationsNodeManagercreatesApplicationConfigurationStatenodesPart12Conformance.md