Skip to content

Enhance role-based access control and add permission tests - #3653

Merged
marcschier merged 5 commits into
masterfrom
romanett/fixIdentityAndPermissionsCache
Apr 8, 2026
Merged

marcschier merged 5 commits into
masterfrom
romanett/fixIdentityAndPermissionsCache

Conversation

@romanett

@romanett romanett commented Apr 2, 2026 •

Copy link
Copy Markdown
Contributor

Improves role-based access control in the OPC UA server stack: fixes ValidateRolePermissions logic, expands default role assignments, refines RoleBasedIdentity merging, and adds comprehensive unit tests. Also fixes a regression that broke GDS ApplicationSelfAdmin sessions.

Proposed changes

RoleBasedIdentity constructor

  • Merges roles from a wrapped RoleBasedIdentity inner identity (avoids losing roles on re-wrap)
  • Deduplicates GrantedRoleIds when composing from inner identity + new roles
  • Removed erroneous !role.RoleId.IsNull filter that broke GdsRole.ApplicationSelfAdmin: that role uses RoleId = ExpandedNodeId.Null intentionally, and OnAddSelfAdminUserRolePermissions relies on GrantedRoleIds.Contains(NodeId.Null) to detect self-admin sessions

UserIdentity

  • Default GrantedRoleIds now returns [WellKnownRole_Anonymous] instead of empty, preventing ValidateRolePermissions from failing with "no granted role" for unauthenticated sessions

ValidateRolePermissions (MasterNodeManager)

  • Stores full permissions per role (not pre-masked by requestedPermission); applies the mask at match-time — logically equivalent but consistent across multi-entry roles
  • Added ILogger parameter for access-denial diagnostics
  • Fixed PrepareValidationCache<T> to correctly handle BrowseDescription and CallMethodRequest node IDs (previously only ReadValueId was handled, others threw on null nodeId)

Role additions

  • Added Role.TrustedApplication and Role.AuthenticatedUser static properties
  • SessionManager assigns TrustedApplication role when the session has a client certificate and SecurityMode >= Sign

GDS fix

  • Removed System.Data stray using directive

Tests

  • New unit tests in Opc.Ua.Server.Tests covering ValidateRolePermissions scenarios: no permissions defined, anonymous access, role match/mismatch, and logger output on denial

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

The GDS regression root cause: RoleBasedIdentity was filtering role.RoleId.IsNull before converting to NodeId, so ApplicationSelfAdmin (intentionally null RoleId) never contributed NodeId.Null to GrantedRoleIds. The GDS OnAddSelfAdminUserRolePermissions callback uses GrantedRoleIds.Contains(NodeId.Null) as the signal to inject Call permission into UserRolePermissions — without it, ValidateRolePermissions in CustomNodeManager.CallInternalAsync returned BadUserAccessDenied before the method handler was reached.

- Improve ValidateRolePermissions logic and add ILogger support for detailed access denial logging
- Expand default roles (add AuthenticatedUser, TrustedApplication)
- Refine RoleBasedIdentity to merge roles and avoid duplicates
- Set UserIdentity default GrantedRoleIds to Anonymous
- Fix MasterNodeManager PrepareValidationCache
- Add comprehensive unit tests for role permission validation and logging
@codecov

codecov Bot commented Apr 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.79487% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.03%. Comparing base (3826c47) to head (c0db6f5).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...ies/Opc.Ua.Server/NodeManager/MasterNodeManager.cs 75.00% 6 Missing and 2 partials ⚠️
...erver/RoleBasedUserManagement/RoleBasedIdentity.cs 33.33% 1 Missing and 1 partial ⚠️
...pc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3653      +/-   ##
==========================================
+ Coverage   69.94%   70.03%   +0.08%     
==========================================
  Files         477      477              
  Lines      103319   103336      +17     
  Branches    17402    17411       +9     
==========================================
+ Hits        72266    72367     +101     
+ Misses      25891    25827      -64     
+ Partials     5162     5142      -20     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@romanett

romanett commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please fix the failing Opc.Ua.Gds.Tests

…plicationSelfAdmin

The RoleBasedIdentity constructor was filtering out roles with null RoleIds
(!role.RoleId.IsNull), which broke the GDS ApplicationSelfAdmin mechanism.
The GDS OnAddSelfAdminUserRolePermissions relies on GrantedRoleIds.Contains(NodeId.Null)
to detect self-admin users (since GdsRole.ApplicationSelfAdmin has RoleId = ExpandedNodeId.Null).

Also remove unused System.Data using statement.

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/3db86efa-c6d6-4e63-947f-156a88210b58

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

Copilot AI commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

@copilot Please fix the failing Opc.Ua.Gds.Tests

Fixed in commit 62760c1. The root cause was in RoleBasedIdentity constructor: the !role.RoleId.IsNull filter I added was blocking GdsRole.ApplicationSelfAdmin (which has RoleId = ExpandedNodeId.Null) from being included in GrantedRoleIds. The GDS OnAddSelfAdminUserRolePermissions relies on GrantedRoleIds.Contains(NodeId.Null) to detect self-admin sessions — without NodeId.Null present, the Call permission was never added to UserRolePermissions, causing ValidateRolePermissions to return BadUserAccessDenied before the method handler was even reached. Removed the null-RoleId filter; all 177 GDS tests now pass locally.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ romanett
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

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.

4 participants