3 Commits

Author SHA1 Message Date
romanett 5572107ab8 Push Model compliance: UpdateCertificate issuer check, TrustList audit events, NoConfiguration state, DefaultHttpsGroup (#4684)
# Description

Fixes found by a manual run of the OPC 10000-12 *Push Model for Global
Certificate and TrustList Management* test plan (183 test cases,
TC0–TC182) against the Reference Server. After these changes no test
case fails; the remaining non-pass verdicts are test-plan expectations
that contradict Part 12/Part 4/Part 20 (documented in the run report).

### Server behaviour changes

- **UpdateCertificate requires the issuer in the TrustList**
(`ConfigurationNodeManager.PushValidation.cs`). The validator classifies
an incomplete chain as suppressible; OPC 10000-4 Table 100 does not ("An
error during the chain creation may not be suppressed") and §7.10.5
requires the issuers to already be in the group's TrustList. A
certificate of an unknown CA is now rejected with
`Bad_CertificateChainIncomplete`. Expiry / host-name errors stay ignored
as §7.10.5 requires.
- **TrustList audit events reach the Server Object.**
`TrustListUpdateRequested`/`TrustListUpdated` were reported on the
TrustList node (`EventNotifier=0`, not linked to the Server), so no
client could receive them. `TrustList.SetAuditEventServer` routes them
through `IAuditEventServer`; wired for the Push TrustLists and the GDS
CertificateGroup TrustLists. New `AuditEvents` overloads keep the
node-based ones.
- **Duplicate AddCertificate** no longer makes the following
ApplyChanges fail with a bare `Bad` (store refused the second copy); the
commit is idempotent and its rollback keeps the pre-existing
certificate.
- **CloseAndUpdate validates CRLs** (incl. CRL-only updates): every CRL
must be signed by a CA of the new TrustList (§7.8.2.5). Previously an
unknown/forged CRL passed CloseAndUpdate and only failed ApplyChanges.
- **X.509 v1 certificates are rejected** by
CloseAndUpdate/AddCertificate/UpdateCertificate (OPC 10000-6 §6.2.2; new
`X509Utils.IsX509Version3`).
- **Application setup state (Annex G.2):** new
`StandardServer.StartupServerState` (virtual); the DI host returns
`NoConfiguration` when `ServerConfigurationOptions.InApplicationSetup ==
true`. `IsRunning` and `NodeManagerLifecycle` treat `NoConfiguration` as
operational. The Reference Server `--provision` mode starts in
`NoConfiguration` and exposes `InApplicationSetup=TRUE`.
- **DefaultHttpsGroup:** the group is only created when Https stores are
configured — the Reference Server config had none, so its
`opc.https`/`opc.wss` endpoints used the RSA application certificate
with an empty TLS trust list and the Https certificate could not be
pushed. The sample config now declares
`HttpsIssuerCertificates`/`TrustedHttpsCertificates` and an
`HttpsCertificateType` certificate (new `CertificateTypeString` value
`Https`). An Https certificate is assigned to DefaultHttpsGroup only (no
longer also listed in DefaultApplicationGroup). Fixed
`AddSecurityConfigurationHttpsStore`, which built the trusted Https
store path from the store *type*.
- Minor: AddCertificate logs a warning when it adds a CA without a CRL
while `RejectUnknownRevocationStatus` is enabled; UpdateCertificate
refused for an insufficient SecureChannel raises
`CertificateUpdateRequestedAuditEvent` with Status=false.

### Kept on purpose
- PEM certificates are accepted (converted to DER).
- `Bad_NotWritable` when opening an already open TrustList (OPC 10000-20
§4.2.2) and for AddCertificate while a read handle is open (§7.8.2.6).
- No validation rules for DefaultUserTokenGroup / DefaultHttpsGroup
TrustLists (Part 12 defines them only for ApplicationCertificateType
groups).

### Notes for reviewers
- Existing tests in `ConfigurationNodeManagerPushTests` and
`PushCertificateRequestRegressionTests` signed pushed certificates with
CAs that were not in the TrustList; they now trust their CA first
(behaviour change above).
- Adding the Https certificate to
`Quickstarts.ReferenceServer.Config.xml` gives the Reference Server's
opc.https/opc.wss endpoints their own TLS certificate.

### Tests
-
`tests/Opc.Ua.Server.Tests/Configuration/PushComplianceRegressionTests.cs`
— duplicate add/rollback, PEM, v1/v3, CRL issuer/forged/CRL-only, audit
routing.
- `tests/Opc.Ua.Server.Tests/ApplicationSetupServerStateTests.cs` —
NoConfiguration startup state.
- `tests/Opc.Ua.Gds.Tests/PushComplianceTests.cs` — end-to-end over a
secure channel: TrustList transaction lifecycle, open-handle rules,
masked reads, duplicate/PEM add, unknown CRL issuer, unknown certificate
issuer, cross-Session CSR key, nonce rules, RemoveCertificate chain/CRL
handling. Mutation-checked: reverting the issuer, duplicate-add and CRL
fixes makes the corresponding tests fail.
- Additional cases in `PushCertificateRequestRegressionTests`,
`CertificateIdentifierTests`, `ApplicationConfigurationBuilderTests`.

Local runs (net10.0): Opc.Ua.Gds.Tests 1257 passed / 0 failed / 56
skipped; Opc.Ua.Server.Tests push/TrustList/audit/lifecycle subset 1209
passed; Core and Configuration test classes passed. net48 build of the
affected test projects succeeds; `dotnet format whitespace` clean.

## Related Issues

- None (findings from a manual Push Model compliance run).

## Checklist

- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [ ] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 18:48:02 +00:00
Marc Schier 7d5f7a7076 Add shared stack APIs and fixes used by UaLens (#4464)
# Description

Extract shared stack, regression-test, sample and documentation changes
from draft #4462 into a separate prerequisite PR. The PR now contains
**19 shared paths**: the original eighteen extracted files plus a
focused user-management regression file added during review. It does not
include UaLens source, desktop dependencies, solution registrations or
application documentation.

## Changes

- Correct the V2 ConditionRefresh target and response validation. Bound
disposal-time remote subscription deletion with an independent
five-second TimeProvider-backed cancellation deadline, allowing local
cleanup after a server outage. This is a per-delete limit, not a global
application shutdown deadline.
- Preserve configured PubSub writer message/header settings, canonical
numeric JSON publisher matching, and flattened DataValue
quality/timestamps.
- Add asynchronous user-management client wrappers over the generated
standard proxy, and set file/directory method declaration IDs needed by
typed client workflows.
- Enable AI client NativeAOT compatibility and add focused AI client
regressions.
- Enable the ConsoleReferenceServer file-system node manager and
configure 1 ms publishing/sampling demonstrations. These sample defaults
do not promise hard real-time delivery.
- Document the subscription and PubSub behavior. Make the new
subscription/PubSub regression helpers portable to .NET Framework
without weakening their assertions.

## Review follow-up

Commit `812297ce1` aligns both public `ListUsersAsync` signatures with
`ValueTask<ArrayOf<UserManagementUser>>`. The implementation constructs
its result directly from an owned snapshot array, without an
intermediate list.

Five focused cases pass on **net10.0 and net48**:
`ListUsersReturnsArrayOfSnapshotsAsync` checks both interface and
concrete entry points and preserves snapshot order, values and flags;
`ListUsersReturnsEmptyArrayAsync` covers typed-empty and missing values;
`ListUsersPreservesBadReadStatusAsync` checks the exact server failure
status.

All **eight single-line XML summaries** were converted to multi-line
form. A complete scan of the fourteen changed C# files, including the
new test, finds zero remaining single-line summaries. Scoped
whitespace/line-ending verification passes.

The sole UaLens caller was adapted separately in draft #4462
(`4caf69f33`). A fresh combined dependency-graph build and all **924
UaLens net10.0 tests** pass with both changes. Each of the three review
threads received an action-taken reply and was resolved only after the
fixes were pushed and verified.

## Split boundary

The branch originated at `a45105375`; the subsequent master merge
`f209f3804` incorporates upstream `4633aa7a3`. The original shared
changes and review regression remain separate from the app. Draft #4462
retains `tools\Opc.Ua.Lens`, `tests\Opc.Ua.Lens.Tests`, and its six
solution/package/documentation integration files.

**Merge this prerequisite before the UaLens draft.** The draft consumes
the extracted APIs. Published history is preserved; neither PR has been
merged automatically.

## Initial split validation

The following results describe the initial split validation at
`fa7199afa`; current review-specific validation is recorded above.

| Scope | net10.0 | net48 |
|---|---:|---:|
| Client subscription/file-system tests | 168 passed | 168 passed |
| Server file-system/user-management tests | 244 passed | 244 passed |
| PubSub writer/reader regressions | 34 passed | 34 passed |
| ConsoleReferenceServer build | Passed | Passed |

The corrected portable regression helpers were rechecked on net10.0
after the net48 fixes. The combined UaLens application also passed its
924-case net10.0 suite before extraction.

**Focused NativeAOT: 16 passed, zero failed/skipped.** A session-local
TUnit runner linked the exact committed `AIClientAotTests.cs` and
referenced the real AI client project. It published without warnings or
suppressions and executed as a genuine Windows native binary with no CLR
header.

The canonical `Opc.Ua.Aot.Tests` project was blocked by pre-existing
CS8632 nullable-annotation errors in unchanged upstream
`SchemaAotTests.cs` (lines 82, 127, 129 and 132). Its existing nullable
configuration and all upstream tests were preserved; the focused result
is not a claim that the full AOT test project builds.

The net48 hosting selection uses a short NUnit WorkDirectory: the
existing fixture builds long PKI paths that otherwise exceed the
platform's path limit. The original full selection passed without test
exclusions, source changes or increased timeouts.

Scoped builds reported inherited warnings in unchanged upstream files:
CA1508 in `ReferenceBuilderExtensions.cs`, CA1861 in
`NodeSetImportIntegrationTests.cs`, and net48 CA1823 in
`DefaultNodeIdFactory.cs`. They were not suppressed or folded into this
split. Full-solution, coverage and CI/CodeQL gates remain unasserted.

## Related Issues

- Tracking/design review: #4461.
- Prerequisite extracted from draft #4462.

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [ ] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [ ] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [x] I have addressed **all** PR feedback received.

Tests and scoped results are documented above; measured coverage,
inherited-warning qualification, CLA status, full-solution results and
CI completion are not asserted by these checkboxes.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-09 19:22:06 +00:00
Copilot d525bc3f28 Bound reconnect cleanup and organize samples by domain (#4276)
# Description

This PR fixes bounded ManagedSession reconnect cleanup and reorganizes
the sample applications into domain-focused folders.

## Reconnect cleanup

- Bounds reconnect integration cleanup with independent 30-second close
and dispose limits.
- Sends `CloseSession` through the awaited transport request path during
`Session.DisposeAsync`.
- Preserves explicit `Session.CloseAsync` behavior that waits for or
cancels outstanding publish requests.
- Covers the disposal request path with a regression test.

## Sample organization

- `samples/OpenUsd`: `GeneratorServer`, `SiteCompositionServer`
- `samples/Redundancy`: `RedundantClient`, `RedundantServer`,
`RedundantPubSub`
- `samples/Isa95`: `MinimalIsa95Server`
- `samples/MinimalApi`: `MinimalBoilerServer`, `MinimalCalcServer`,
`MinimalClient`
- `samples/DI`: `PumpDeviceIntegrationServer`
- `samples/Lds`: `ConsoleLdsServer`
- `samples/Reference`: `ConsoleReferenceClient`,
`ConsoleReferenceServer`
- `samples/PubSub`: `ConsoleReferencePubSubClient`

`UA.slnx` now exposes the same requested domain groups. Project
references, tests, runtime sample probes, CI workflows, Dockerfiles,
Compose files, helper scripts, and documentation were updated to the new
paths. `samples/Robotics/MinimalRobotServer` remains under `Robotics`.

## Validation

- `dotnet build UA.slnx`
- 3 Redundancy short-haul sample tests
- 70 DI sample tests
- 914 OpenUSD tests
- 1 WotCon model-materialization test
- 137 AOT tests
- `docker compose config` for all 6 moved Redundancy Compose files

## Related Issues

- Fixes #4273

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce new build or
analyzer warnings.
- [x] I built the complete `UA.slnx` solution locally.
- [ ] I ran all tests locally against at least .NET Framework and .NET
10.
- [ ] I fixed all failing and flaky tests in the CI pipelines and all
CodeQL warnings.
- [x] I have addressed all PR feedback received.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>
Co-authored-by: Marc Schier <mschier@microsoft.com>
Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
Copilot-Session: a2c2838f-74ae-4997-a1d4-50574d9e0636
2026-08-21 04:35:47 +00:00