Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDS: implement ApplicationSelfAdmin privilege in GlobalDiscoverySampleServer #2338

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
214ede9
implement ApplicationSelfAdmin privilege in GlobalDiscoverySampleServer
romanett Oct 11, 2023
7e6bcde
improve documentation
romanett Oct 11, 2023
8d50547
fix: enforce ApplicationSelfAdminPrivilege
romanett Oct 17, 2023
8d4671d
add Test for ApplicationSelf Admin with disabled flag
romanett Nov 17, 2023
75dcf94
Merge pull request #5 from OPCFoundation/master
romanett Nov 17, 2023
78d3a42
Merge remote-tracking branch 'origin/master' into ImplementApplicatio…
mregen Dec 7, 2023
5dcf8ec
update tests with template for additional required tests
mregen Dec 7, 2023
516c83b
Merge branch 'OPCFoundation:master' into ImplementApplicationSelfAdmi…
romanett Dec 7, 2023
dd4bb7f
prepate Testing of application self admin privilege
romanett Jan 14, 2024
47e3e7a
Set new ApplicationCertificate
romanett Jan 15, 2024
2ca22ee
prepare merge
romanett Jan 18, 2024
a328c6d
merge master
romanett Jan 18, 2024
f82296a
fix issues from merge
romanett Jan 18, 2024
d8abd53
fix regression from merging master (unintentionally inverted if in Ap…
romanett Jan 20, 2024
dd2f7f0
use correct DC for Subject of Certificate
romanett Jan 21, 2024
cf3d367
fix regression from merge, to many roles were allowed
romanett Jan 21, 2024
1a51b43
add Test Cases for KeyPair & Sign with SelfAdminPrivilege
romanett Jan 23, 2024
252ad31
fix access rights of OnGetCertificateGroups method
romanett Jan 24, 2024
7e74111
use hostname for certificate, enable cert validation
romanett Jan 27, 2024
9115f57
refactor to 3 separate tests
romanett Jan 27, 2024
deba6a9
add Test to verify unregistered application is denied
romanett Jan 28, 2024
a3a4cac
validate client cert URI does match applicationUri
romanett Jan 28, 2024
0bcf343
add TrustList Access, still need CertificateStores or change of Appli…
romanett Jan 29, 2024
e550bad
change LinqApplicationsDatabase
romanett Jan 29, 2024
0787a35
cleanup
romanett Jan 29, 2024
a920cd7
Merge remote-tracking branch 'origin/master' into ImplementApplicatio…
romanett Feb 10, 2024
4d9d1fb
fix failing tests
romanett Feb 12, 2024
d8e522f
Merge remote-tracking branch 'origin/master' into ImplementApplicatio…
mregen Feb 14, 2024
ff098a3
add well known roles
romanett Feb 14, 2024
d955bf0
add missing roles to standard gds users to fix failing tests
romanett Feb 14, 2024
c9eb214
Merge pull request #6 from romanett/wellKnownRoles
romanett Feb 14, 2024
57e561a
add new Standard Users
romanett Feb 16, 2024
ecb565e
Merge branch 'wellKnownRoles' into ImplementApplicationSelfAdminPrivi…
romanett Feb 16, 2024
2e68d73
minor optimizations & cleanup & documentation improvements
romanett Feb 18, 2024
6e43018
Merge branch 'master' into ImplementApplicationSelfAdminPrivilege
romanett Feb 18, 2024
283a9c2
Merge remote-tracking branch 'origin/master' into ImplementApplicatio…
mregen Feb 23, 2024
6ef6414
push minor code cleanup
mregen Feb 26, 2024
5449305
check on some warnings
mregen Feb 26, 2024
80cc82b
use discard for unused parameter
romanett Feb 26, 2024
c213b0d
small cleanup
mregen Feb 26, 2024
c9047e7
Merge branch 'ImplementApplicationSelfAdminPrivilege' of https://gith…
mregen Feb 26, 2024
5b00002
fix GDSTestClient
romanett Feb 26, 2024
065b392
improve calls to authorizationHelper to be cleaner
romanett Feb 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,29 @@
}
}

/// <summary>
/// checks if the given Application can be modified with the current context
/// </summary>
/// <param name="context">the current context</param>
/// <param name="applicationId">the application to modify</param>
private void HasApplicationSelfAdminPrivilege(ISystemContext context, NodeId applicationId)
{
HasApplicationUserAccess(context);
if (context != null)
{
RoleBasedIdentity identity = context.UserIdentity as RoleBasedIdentity;
//administrator has full access
if (identity.Role == GdsRole.ApplicationAdmin)
return;
//not administrator only has access to specified application
if (identity.ApplicationId != applicationId)
{
throw new ServiceResultException(StatusCodes.BadUserAccessDenied, "Application Self Admin Privielge or " +
"Application Administrator access required.");

Check warning on line 209 in Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs#L208-L209

Added lines #L208 - L209 were not covered by tests
}
}
}

Check warning on line 212 in Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs#L212

Added line #L212 was not covered by tests

private NodeId GetTrustListId(NodeId certificateGroupId)
{

Expand Down Expand Up @@ -760,7 +783,7 @@
string privateKeyPassword,
ref NodeId requestId)
{
HasApplicationAdminAccess(context);
HasApplicationSelfAdminPrivilege(context, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -879,7 +902,7 @@
byte[] certificateRequest,
ref NodeId requestId)
{
HasApplicationAdminAccess(context);
HasApplicationSelfAdminPrivilege(context, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -960,7 +983,7 @@
signedCertificate = null;
issuerCertificates = null;
privateKey = null;
HasApplicationAdminAccess(context);
HasApplicationSelfAdminPrivilege(context, applicationId);

var application = m_database.GetApplication(applicationId);
if (application == null)
Expand Down Expand Up @@ -1126,7 +1149,7 @@
NodeId applicationId,
ref NodeId[] certificateGroupIds)
{
HasApplicationUserAccess(context);
HasApplicationSelfAdminPrivilege(context, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -1154,7 +1177,7 @@
NodeId certificateGroupId,
ref NodeId trustListId)
{
HasApplicationUserAccess(context);
HasApplicationSelfAdminPrivilege(context, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -1187,7 +1210,7 @@
NodeId certificateTypeId,
ref Boolean updateRequired)
{
HasApplicationUserAccess(context);
HasApplicationSelfAdminPrivilege(context, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
using System.Threading;
using Opc.Ua.Server;
using Opc.Ua.Gds.Server.Database;
using Opc.Ua.Security.Certificates;
using Org.BouncyCastle.Asn1.Cmp;
using System.Linq;
using System.Data;

namespace Opc.Ua.Gds.Server
{
Expand Down Expand Up @@ -229,9 +233,61 @@
// role = GdsRole.ApplicationAdmin;

Utils.LogInfo("X509 Token Accepted: {0} as {1}", args.Identity.DisplayName, role.ToString());
args.Identity = new RoleBasedIdentity(new UserIdentity(x509Token), role);

string applicationUri = session.SessionDiagnostics.ClientDescription.ApplicationUri;
args.Identity = new RoleBasedIdentity(new UserIdentity(x509Token), role, applicationUri);

Check warning on line 238 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L237-L238

Added lines #L237 - L238 were not covered by tests
return;
}

//check if applicable for application self admin privilege
if(session.ClientCertificate != null)
{
if (VerifiyApplicationRegistered(session.ClientCertificate))
{
ImpersonateAsApplicationSelfAdmin(session, args);

Check warning on line 247 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L247

Added line #L247 was not covered by tests
}
}
}

/// <summary>
/// Verifies if an Application is registered with the provided certificate at at the GDS
/// </summary>
/// <param name="applicationInstanceCertificate">the certificate to check for</param>
/// <returns></returns>
private bool VerifiyApplicationRegistered(X509Certificate2 applicationInstanceCertificate)
{
bool applicationRegistered = false;
//get access to GDS configuration section to find out ApplicationCertificatesStorePath
GlobalDiscoveryServerConfiguration configuration = Configuration.ParseExtension<GlobalDiscoveryServerConfiguration>();
if (configuration == null)
{
configuration = new GlobalDiscoveryServerConfiguration();

Check warning on line 264 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L264

Added line #L264 was not covered by tests
}
//check if application certificate is in the Store of the GDS
using (ICertificateStore ApplicationsStore = CertificateStoreIdentifier.OpenStore(configuration.ApplicationCertificatesStorePath))
{
var matchingCerts = ApplicationsStore.FindByThumbprint(applicationInstanceCertificate.Thumbprint).Result;

if (matchingCerts.Contains(applicationInstanceCertificate))
applicationRegistered = true;

Check warning on line 272 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L272

Added line #L272 was not covered by tests
}
//check if application certificate is revoked
using (ICertificateStore AuthoritiesStore = CertificateStoreIdentifier.OpenStore(configuration.AuthoritiesStorePath))
{
var crls = AuthoritiesStore.EnumerateCRLs().Result;
foreach (X509CRL crl in crls)
{
var revokedCertificates = crl.RevokedCertificates;
foreach (RevokedCertificate revokedCertificate in revokedCertificates)
{
if(revokedCertificate.SerialNumber == applicationInstanceCertificate.SerialNumber)
{
applicationRegistered = false;

Check warning on line 285 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L285

Added line #L285 was not covered by tests
}
}
}
}
return applicationRegistered;
}

/// <summary>
Expand Down Expand Up @@ -282,6 +338,28 @@
// TODO: check username/password permissions
return userNameToken.DecryptedPassword == "demo";
}

/// <summary>
/// Impersonates the current Session as ApplicationSelfAdmin
/// </summary>
/// <param name="session">the current session</param>
/// <param name="args">the impersonateEventArgs</param>
private void ImpersonateAsApplicationSelfAdmin(Session session, ImpersonateEventArgs args)
{
string applicationUri = session.SessionDiagnostics.ClientDescription.ApplicationUri;
ApplicationRecordDataType[] application = m_database.FindApplications(applicationUri);

Check warning on line 350 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L349-L350

Added lines #L349 - L350 were not covered by tests
if(application == null || application.Length>1 || application.Length<1)
{
Utils.LogInfo("Cannot login based on ApplicationInstanceCertificate, no uniqure result for Application with URI: {0}", applicationUri);
return;

Check warning on line 354 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L353-L354

Added lines #L353 - L354 were not covered by tests
}
NodeId applicationId = application.FirstOrDefault().ApplicationId;
Utils.LogInfo("Application {0} accepted based on ApplicationInstanceCertificate as ApplicationSelfAdmin",
applicationUri);
args.Identity = new RoleBasedIdentity(new UserIdentity(), GdsRole.ApplicationSelfAdmin, applicationId);
return;

Check warning on line 360 in Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/GlobalDiscoverySampleServer.cs#L356-L360

Added lines #L356 - L360 were not covered by tests
}

#endregion

#region Private Fields
Expand Down
23 changes: 22 additions & 1 deletion Libraries/Opc.Ua.Gds.Server.Common/RoleBasedIdentity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

using System;
using System.Xml;

namespace Opc.Ua.Gds.Server
Expand All @@ -44,7 +45,12 @@
/// <summary>
/// The GDS application user.
/// </summary>
ApplicationUser
ApplicationUser,

/// <summary>
/// Can manage the own Certificates and pull trust list
/// </summary>
ApplicationSelfAdmin
}

/// <summary>
Expand All @@ -54,6 +60,7 @@
{
private IUserIdentity m_identity;
private GdsRole m_role;
private NodeId m_applicationId;

/// <summary>
/// Initialize the role based identity.
Expand All @@ -64,6 +71,12 @@
m_role = role;
}

public RoleBasedIdentity(IUserIdentity identity, GdsRole role, NodeId applicationId)
:this(identity, role)

Check warning on line 75 in Libraries/Opc.Ua.Gds.Server.Common/RoleBasedIdentity.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/RoleBasedIdentity.cs#L75

Added line #L75 was not covered by tests
{
m_applicationId = applicationId;
}

Check warning on line 78 in Libraries/Opc.Ua.Gds.Server.Common/RoleBasedIdentity.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/RoleBasedIdentity.cs#L77-L78

Added lines #L77 - L78 were not covered by tests

/// <inheritdoc/>
public NodeIdCollection GrantedRoleIds
{
Expand All @@ -79,6 +92,14 @@
get { return m_role; }
}

/// <summary>
/// The applicationId in case the ApplicationSelfAdminPrivilege is used
/// </summary>
public NodeId ApplicationId
{
get { return m_applicationId; }

Check warning on line 100 in Libraries/Opc.Ua.Gds.Server.Common/RoleBasedIdentity.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/RoleBasedIdentity.cs#L100

Added line #L100 was not covered by tests
}

/// <inheritdoc/>
public string DisplayName
{
Expand Down
31 changes: 30 additions & 1 deletion Tests/Opc.Ua.Gds.Tests/ClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ protected async Task OneTimeSetUp()
m_goodRegistrationOk = false;
m_invalidRegistrationOk = false;
m_goodNewKeyPairRequestOk = false;
m_runApplicationSelfAdminTests = false;
}

/// <summary>
Expand Down Expand Up @@ -735,7 +736,9 @@ out byte[][] issuerCertificates
} while (requestBusy);
}

[Test, Order(511)]


[Test, Order(512)]
public void FinishInvalidNewKeyPairRequests()
{
AssertIgnoreTestWithoutInvalidRegistration();
Expand Down Expand Up @@ -941,6 +944,31 @@ public void GetGoodCertificateGroupsAndTrustLists()
Assert.NotNull(trustList);
}
}

if (m_runApplicationSelfAdminTests)
{
AssertIgnoreTestWithoutGoodRegistration();
AssertIgnoreTestWithoutGoodNewKeyPairRequest();
DisconnectGDS();
//connect to GDS without Admin Privilege
ConnectGDS(false);

foreach (var application in m_goodApplicationTestSet)
{
if (application.Certificate != null)
{
var certificateGroups = m_gdsClient.GDSClient.GetCertificateGroups(application.ApplicationRecord.ApplicationId);
foreach (var certificateGroup in certificateGroups)
{
var trustListId = m_gdsClient.GDSClient.GetTrustList(application.ApplicationRecord.ApplicationId, certificateGroup);
// Opc.Ua.TrustListDataType
var trustList = m_gdsClient.GDSClient.ReadTrustList(trustListId);
Assert.NotNull(trustList);
}
}
}

}
}

[Test, Order(690)]
Expand Down Expand Up @@ -1094,6 +1122,7 @@ private int GoodServersOnNetworkCount()
private bool m_goodRegistrationOk;
private bool m_invalidRegistrationOk;
private bool m_goodNewKeyPairRequestOk;
private bool m_runApplicationSelfAdminTests;
#endregion
}

Expand Down
Loading