Skip to content

Conversation

@raserle
Copy link

@raserle raserle commented Feb 6, 2026

Proposed changes

Change generation of UserTokenPolicyId's
Add IEquatable to UserTokenPolicy.cs

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

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • 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.

@CLAassistant
Copy link

CLAassistant commented Feb 6, 2026

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
❌ raserle
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 59.98%. Comparing base (d25caff) to head (9ab4404).
⚠️ Report is 111 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3525      +/-   ##
==========================================
+ Coverage   51.86%   59.98%   +8.11%     
==========================================
  Files         370      378       +8     
  Lines       78618    78978     +360     
  Branches    13650    13812     +162     
==========================================
+ Hits        40779    47376    +6597     
+ Misses      33705    27180    -6525     
- Partials     4134     4422     +288     

☔ 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.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request addresses issue #3522, which involves incorrect UserTokenPolicyId generation when an OPC UA server listens on multiple IP addresses. The fix changes the ID generation strategy from using a simple global counter to using semantic equality-based deduplication of UserTokenPolicy objects.

Changes:

  • Adds IEquatable<UserTokenPolicy> implementation to enable semantic equality comparison based on TokenType, SecurityPolicyUri, IssuedTokenType, and IssuerEndpointUrl (intentionally excluding PolicyId)
  • Modifies ServerBase.GetUserTokenPolicies to maintain a server-wide collection of unique policies and reuse existing PolicyIds for semantically equivalent policies
  • Ensures PolicyIds remain consistent regardless of the order in which endpoints are processed

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
Stack/Opc.Ua.Core/Stack/Types/UserTokenPolicy.cs Implements IEquatable with Equals, GetHashCode, and operator overloads to enable semantic equality comparison
Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs Adds UserTokenPolicys collection for deduplicating policies and modifies GetUserTokenPolicies to assign consistent PolicyIds based on semantic equality

Comment on lines 71 to 126
public override bool Equals(object obj)
{
return Equals(obj as UserTokenPolicy);
}

/// <inheritdoc/>
public bool Equals(UserTokenPolicy other)
{

if (ReferenceEquals(null, other))
{
return false;
}

if (ReferenceEquals(this, other))
{
return true;
}

return
TokenType == other.TokenType &&
string.Equals(SecurityPolicyUri, other.SecurityPolicyUri, StringComparison.Ordinal) &&
string.Equals(IssuedTokenType, other.IssuedTokenType, StringComparison.Ordinal) &&
string.Equals(IssuerEndpointUrl, other.IssuerEndpointUrl, StringComparison.Ordinal);
}

/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(
TokenType,
SecurityPolicyUri,
IssuedTokenType,
IssuerEndpointUrl);
}

/// <inheritdoc/>
public static bool operator ==(UserTokenPolicy left, UserTokenPolicy right)
{
if (ReferenceEquals(left, right))
{
return true;
}

if (ReferenceEquals(left, null))
{
return false;
}

return left.Equals(right);
}

/// <inheritdoc/>
public static bool operator !=(UserTokenPolicy left, UserTokenPolicy right)
{
return !(left == right);
}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new Equals, GetHashCode, and operator overloads added to UserTokenPolicy lack test coverage. Given that this implementation is critical for the bug fix (ensuring correct PolicyId generation), tests should be added to verify:

  1. Equality comparison works correctly for policies with the same semantic properties (TokenType, SecurityPolicyUri, IssuedTokenType, IssuerEndpointUrl) but different PolicyIds
  2. Hash codes are consistent for equal objects
  3. Operator overloads (== and !=) work as expected
  4. Edge cases like null values for optional string properties

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines 915 to 926
var existingPolicy = UserTokenPolicys.FirstOrDefault(o => o.Equals(policy));

// Ensure each policy has a unique ID within the context of the Server
if (existingPolicy == null)
{
clone.PolicyId = Utils.Format("{0}", UserTokenPolicys.Count + 1);
UserTokenPolicys.Add(clone);
existingPolicy = clone;
}

policies.Add(clone);
policies.Add(existingPolicy);
}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modified logic in GetUserTokenPolicies that uses the UserTokenPolicys collection to deduplicate policies based on semantic equality should have test coverage. Tests should verify:

  1. That policies with the same semantic properties receive the same PolicyId
  2. That PolicyIds are stable across multiple calls to GetUserTokenPolicies with different endpoint descriptions
  3. That the SecurityPolicyUri modifications (lines 906, 911) are handled correctly
  4. That the fix resolves the original issue where servers with multiple IP addresses had inconsistent PolicyIds

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines 915 to 926
var existingPolicy = UserTokenPolicys.FirstOrDefault(o => o.Equals(policy));

// Ensure each policy has a unique ID within the context of the Server
if (existingPolicy == null)
{
clone.PolicyId = Utils.Format("{0}", UserTokenPolicys.Count + 1);
UserTokenPolicys.Add(clone);
existingPolicy = clone;
}

policies.Add(clone);
policies.Add(existingPolicy);
}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UserTokenPolicys collection uses IList<UserTokenPolicy> which is not thread-safe. While GetUserTokenPolicies is likely only called during server initialization, there's no explicit documentation or synchronization to prevent concurrent calls. The code uses FirstOrDefault (read), Count (read), and Add (write) operations on lines 915, 920-921 which could result in race conditions if called concurrently.

Consider either:

  1. Documenting that this method should only be called during initialization, or
  2. Adding proper synchronization (e.g., using a concurrent collection or locking) if concurrent access is possible.

Copilot uses AI. Check for mistakes.
/// Defines constants for key user token policies.
/// </summary>
public partial class UserTokenPolicy : IFormattable
public partial class UserTokenPolicy : IFormattable, IEquatable<UserTokenPolicy>
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class 'UserTokenPolicy' implements 'ICloneable'.

Copilot uses AI. Check for mistakes.
@romanett romanett changed the title add Equals, change id generation [Server] Fix generation of UserTokenPolicyIDs Feb 6, 2026
@raserle raserle force-pushed the incorrect_usertokenpolicyid_generation branch 3 times, most recently from 3abd8a2 to 810e06d Compare February 6, 2026 14:04
@raserle raserle force-pushed the incorrect_usertokenpolicyid_generation branch from 810e06d to 823cd17 Compare February 6, 2026 14:05
@romanett
Copy link
Contributor

romanett commented Feb 6, 2026

@raserle Thank you very much for your fix, with this proper Ids are generated and the ctt can properly run tests requiring user auth. Also GetEndpoints returns the same Ids for same UserTokenPolicies

@raserle
Copy link
Author

raserle commented Feb 7, 2026

The changes have already been approved by another reviewer, but it seems that the license agreement is still not being recognized.
I’m not sure if any additional action is required from my side, or if this will resolve automatically.
Could you please let me know how to proceed?

@romanett
Copy link
Contributor

romanett commented Feb 8, 2026

@raserle you need to register the Email configured in your local git as commit Email in your GitHub Account. Then the license cla will work

@romanett
Copy link
Contributor

romanett commented Feb 8, 2026

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@raserle
Copy link
Author

raserle commented Feb 8, 2026

I can only approve and accept the CLA using my GitHub account raserle, which is associated with the verified email address.
I confirm that I am the author of the changes in this pull request and that I agree to the license terms.

@romanett
Copy link
Contributor

romanett commented Feb 9, 2026

@raserle some Tests in the Opc.Ua.Core.Stack.Server area are now failing (e.g. TranslateEndpointDescriptionsTest), can you check it out?

@romanett
Copy link
Contributor

@raserle I provided a fix, so we can get this into the next update

@romanett romanett requested a review from mrsuciu February 10, 2026 16:11
@romanett
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

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.

Incorrect UserTokenPolicyId generation on servers listening on multiple IP addresses

4 participants