Skip to content

Extend the RoleManagement sample to the Part 18 items #836 recommends - #864

Merged
romanett merged 2 commits into
masterfrom
romanett/ua-netstandard-836-8b642f
Sep 4, 2026
Merged

romanett merged 2 commits into
masterfrom
romanett/ua-netstandard-836-8b642f

Conversation

@romanett

@romanett romanett commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

Issue #836 is a menu of nine self-contained increments to Workshop/RoleManagement, written
down after #828 landed so that the gaps were a choice on record. This takes the four its own
Suggested order names — 5 (AccessRestrictions), 7 (auditing), 2 (endpoint-filtered
Role and CustomConfiguration) and 1 limited to Thumbprint / X509Subject — and gives
each of them a demonstration in the sample client and a fixture in the tests. Items 3 (the
§5 user management model, which wants its own sample) and 9 (a vendor-defined Role, blocked
on OPCFoundation/UA-.NETStandard#4361) stay open, as the issue asks.

AccessRestrictions — the half of the access story the sample was missing

Calibration carries EncryptionRequired, MaintenanceNote adds ApplyRestrictionsToBrowse.
An Engineer who holds Browse, Read and Write on both is refused with
BadSecurityModeInsufficient on the unsecured endpoint, and the distinction is the point:
BadUserAccessDenied means sign in as somebody else, BadSecurityModeInsufficient means
reconnect with security. The client shows the attribute in a new column.

An endpoint filter, and the flag that goes with it

ConfigureAdmin is restricted to the encrypted endpoints, which Part 18 §4.4.1 evaluates
before any identity rule. Its CustomConfiguration flag is deliberately left false and the
client's new Toggle CustomConfiguration button flips it — that flag is the only thing which
lets a Role with an empty Identities list be granted at all.

A Role earned by a certificate rather than by an account

ConfigureAdmin is granted by an X509Subject rule naming the certificate the sample client
creates for itself, so an anonymous Session from that client holds a Role no account can
earn. One new node, ServiceCode, is there to observe it. The client's identity criteria
drop-down adds Thumbprint and X509Subject beside UserName and fills the box with what it
would present.

Auditing

The server sets AuditingEnabled and the client subscribes to AuditEventType on the Server
object — see the caveat below.

Three things the stack does differently from what the issue assumed

All measured against 2.0.0-preview.4, and all recorded in the README and in code comments so
the next reader does not have to rediscover them:

  1. Thumbprint and X509Subject match the client's application instance certificate, not
    a user certificate — SessionManager passes session.ClientCertificate to
    ResolveGrantedRoles. So no X509 user token policy is needed, and such a Role belongs to the
    workstation: every Session it opens holds it, signed in or not. The issue assumed the other
    reading, which is why it estimated this item as needing a user certificate.
  2. IRoleManager.AddEndpoint refuses an entry whose EndpointUrl is empty, although
    EndpointTypeComparer.Matches treats a default-valued field as a wildcard per §4.4.2 — so
    { SecurityMode = SignAndEncrypt } cannot be stored. The server copies the endpoint
    descriptions it actually advertises instead, which is why that part of the Role configuration
    runs in OnServerStarted rather than in CreateRoleManager. Filed upstream as
    IRoleManager.AddEndpoint refuses the wildcard endpoint filter that EndpointTypeComparer is written to match UA-.NETStandard#4412.
  3. No audit event of any type reaches a subscriber in 2.0.0-preview.4. Server.Auditing
    reads true, AddIdentity answers Good, and a GeneralModelChangeEvent from the same
    Server object arrives on the same subscription — audit events do not. Reproduced on
    RoleManagement and on NodeManagement, with nothing logged server-side. The tier 1.5
    fixture is therefore written the right way round and wrapped in KnownIssueAsync, so it
    turns into a failure asking for the note to be removed the moment the stack delivers them.
    A separate investigation into the root cause is under way; if it turns out to be sample
    misconfiguration rather than a stack defect, that note and the matching README paragraph
    are what have to change.

A smaller one, also in the README: ApplyRestrictionsToBrowse covers a Browse of the restricted
node itself and a TranslateBrowsePathsToNodeIds starting there, but not the reference to it in
its parent's browse result — that per-reference filter applies role permissions only.

Related Issues

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which adds functionality)
  • Test enhancement (non-breaking change to increase test coverage)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected, requires version increase of Nuget packages)
  • Documentation Update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc.
  • I have signed the CLA.
  • I ran tests locally with my changes, all passed.
  • I fixed all failing tests in the CI pipelines.
  • I fixed all introduced issues with CodeQL and LGTM.
  • I have added tests that prove my fix is effective or that my feature works and increased code coverage.
  • I have added necessary documentation (if appropriate).
  • Any dependent changes have been merged and published in downstream modules.

Further comments

Why the demonstrations are laid out the way they are

Each feature is observed through a Role and a node which the other features do not touch, so a
failing assertion names one cause. MaintenanceNote and Calibration are observed through
Roles which are not endpoint-filtered; ServiceCode, which the endpoint-filtered Role owns,
carries no AccessRestrictions of its own. Without that separation a refusal on the unsecured
endpoint would be over-determined and the fixtures would stop teaching anything.

Test results

Run locally on Windows against 2.0.0-preview.4:

  • tier 1 Tests/SampleServers.Tests — 115 passed
  • tier 1.5 Tests/SampleNodeManagers.Tests — 164 passed, 4 skipped (one of them the recorded
    audit known issue; the other three pre-date this change)
  • tier 2 Tests/SampleClients.Tests — 63 passed

Two of the tier 2 fixtures are new. TheCertificateOfThisClientEarnsTheServiceCode is the one
worth knowing about: it holds the server's hard-coded X509Subject criteria to the certificate
the client's own configuration file actually produces, so it fails if either is edited without
the other.

Shared test helper

TestClient gains an application certificate subject and thumbprint, and a connect overload
which takes a certificate subject, so a test can assert what a server matched a client by. Two
clients built with the same subject still differ by thumbprint, which is exactly the contrast
between the two criteria.

🤖 Generated with Claude Code

romanett and others added 2 commits September 4, 2026 08:43
Issue #836 is a menu of nine self-contained increments to Workshop/RoleManagement.
This takes the four its own "suggested order" names, and gives each of them a
demonstration in the sample client and a fixture in the tests.

**AccessRestrictions (item 5).** Calibration carries EncryptionRequired and
MaintenanceNote adds ApplyRestrictionsToBrowse, so an Engineer who holds Browse,
Read and Write on both is refused with BadSecurityModeInsufficient on the
unsecured endpoint. That is a different fix from BadUserAccessDenied - reconnect
with security rather than sign in as somebody else - and the client shows the
attribute in a new column.

**Endpoint filter and CustomConfiguration (item 2).** The ConfigureAdmin Role is
restricted to the encrypted endpoints, which Part 18 4.4.1 evaluates before any
identity rule. Its CustomConfiguration flag stays false and the client's new
button flips it, which is what lets a Role with an empty Identities list be
granted at all.

**Certificate criteria (item 1).** ConfigureAdmin is granted for an X509Subject
rule naming the certificate the sample client creates for itself, so an anonymous
Session from that client holds a Role no account can earn - and a new ServiceCode
node is there to observe it. The client's criteria drop down adds Thumbprint and
X509Subject beside UserName and fills the box with what it would present.

**Auditing (item 7).** The server sets AuditingEnabled and the client subscribes
to AuditEventType on the Server object.

Three things the stack does rather than what the issue assumed, all measured:

* Thumbprint and X509Subject match the client's *application instance*
  certificate, not a user certificate, so no X509 user token policy is needed.
* IRoleManager.AddEndpoint refuses an entry with an empty EndpointUrl although
  4.4.2 treats a default field as a wildcard, so the endpoints the server
  advertises are copied in OnServerStarted instead.
* No audit event of any type reaches a subscriber in 2.0.0-preview.4:
  Server.Auditing reads true and a GeneralModelChangeEvent from the same object
  does arrive. The fixture is written the right way round and recorded with
  KnownIssueAsync, so it fails and asks for the note when that is fixed.

TestClient gains an application certificate subject and thumbprint, so a test can
assert what a server matched it by.

Items 3 (the section 5 user management model) and 9 (a vendor-defined Role, which
waits on UA-.NETStandard#4361) stay open, as #836 asks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ver surface

#861 landed on master while this was open and deleted every per-sample
StandardServer subclass, RoleManagementServer.cs among them, in favour of
registrations on the server builder. The conflict is a real one rather than
textual: this branch had added a Role configuration and an OnServerStarted
override to exactly the class master removed.

Resolved by keeping master's shape and porting the additions onto the new seams:

* the X509Subject rule which grants ConfigureAdmin for the certificate of the
  maintenance workstation joins the other role mappings in
  SampleUsers.ConfigureRoles, as a RoleDefinitionOptions on the well known Role;
  WorkstationCertificateSubject and WorkstationRoleId move there with it.
* the Endpoints filter becomes WorkstationEndpoints, an IServerStartupTask
  registered with AddStartupTask<T>. It still cannot be declared with the rest of
  the role configuration: AddEndpoint refuses an entry with an empty EndpointUrl
  (UA-.NETStandard#4412), so the endpoints the server advertises have to be
  copied, and those are only known once it has started. Declaring the wildcard in
  RoleConfigurationOptions would be worse than the startup task, because the
  stack applies those entries without looking at what AddEndpoint answered - the
  Role would end up granted on every endpoint.

The tier 1.5 fixture follows the constant to SampleUsers; the README and the node
manager comment follow the code.

Tier 1 116 passed, tier 1.5 170 passed with 4 skipped (one of them the recorded
audit known issue), tier 2 63 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@romanett
romanett merged commit 5a51c09 into master Sep 4, 2026
9 checks passed
romanett added a commit that referenced this pull request Sep 4, 2026
Master extended four of the clients this branch had already split, so their new
features are ported into the models rather than back into the windows:

- RoleManagement (#864): the identity criteria (UserName/Thumbprint/X509Subject) and
  the criteria strings this client can be matched by, the CustomConfiguration flag,
  the AccessRestrictions and Endpoints columns, and the audit trail stream all live in
  RoleManagementClientModel; the window renders them.
- StateMachines (#860): the materialized Part 16 model (available states and
  transitions), the sub state machine below Running with its StartBatch cause, and the
  effective state stream live in StateMachinesClientModel.
- NodeManagement: the hand written model change pump is replaced by the stack's
  ModelChangeTracker inside the model.
- UserAuthentication: the Kerberos and SAML stubs are gone from the model and the
  window, as on master.

The model fixtures gained cases for the new surface; the testing guide keeps the
model fixture column with master's updated StateMachines row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Role management sample: Part 18 functionality not yet demonstrated

1 participant