Skip to content

Commit

Permalink
GDS: implement ApplicationSelfAdmin privilege in GlobalDiscoverySampl…
Browse files Browse the repository at this point in the history
…eServer (#2338)

-Implement the support for ApplicationSelfAdmin privilege
-implement the workflow in a test to pull the trust list after sucessfully receiving a gds signed certificate. 
-this authorization is described here in the OPC Spec: https://reference.opcfoundation.org/GDS/v105/docs/7.6
  • Loading branch information
romanett authored Feb 27, 2024
1 parent 1b893b5 commit 22096f4
Show file tree
Hide file tree
Showing 20 changed files with 904 additions and 210 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* ========================================================================
/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Application
public Application()
{
Certificate = new Dictionary<string, byte[]>();
TrustListId = new Dictionary<string, Guid>();
TrustListId = new Dictionary<string, string>();
}
public uint ID { get; set; }
public Guid ApplicationId { get; set; }
Expand All @@ -59,7 +59,7 @@ public Application()
public string ProductUri { get; set; }
public string ServerCapabilities { get; set; }
public Dictionary<string, byte[]> Certificate { get; }
public Dictionary<string, Guid> TrustListId { get; }
public Dictionary<string, string> TrustListId { get; }
}

[Serializable]
Expand Down Expand Up @@ -665,7 +665,7 @@ byte[] certificate

public override bool GetApplicationCertificate(
NodeId applicationId,
string certificateType,
string certificateTypeId,
out byte[] certificate)
{
certificate = null;
Expand All @@ -685,7 +685,7 @@ public override bool GetApplicationCertificate(
throw new ArgumentException("A record with the specified application id does not exist.", nameof(applicationId));
}

if (!application.Certificate.TryGetValue(certificateType, out certificate))
if (!application.Certificate.TryGetValue(certificateTypeId, out certificate))
{
return false;
}
Expand All @@ -695,7 +695,7 @@ public override bool GetApplicationCertificate(

public override bool SetApplicationTrustLists(
NodeId applicationId,
string certificateType,
string certificateTypeId,
string trustListId
)
{
Expand All @@ -711,11 +711,7 @@ string trustListId

if (trustListId != null)
{
var result2 = (from x in CertificateStores where x.Path == trustListId select x).SingleOrDefault();
if (result2 != null)
{
result.TrustListId[certificateType] = result2.TrustListId;
}
result.TrustListId[certificateTypeId] = trustListId;
}
SaveChanges();
}
Expand All @@ -725,7 +721,7 @@ string trustListId

public override bool GetApplicationTrustLists(
NodeId applicationId,
string certificateType,
string certificateTypeId,
out string trustListId
)
{
Expand All @@ -740,20 +736,8 @@ out string trustListId
{
return false;
}

Guid trustListGuid;
if (result.TrustListId.TryGetValue(certificateType, out trustListGuid))
{
var result2 = (from x in CertificateStores where x.TrustListId == trustListGuid select x).SingleOrDefault();
if (result2 != null)
{
trustListId = result2.Path;
return true;
}
}
return result.TrustListId.TryGetValue(certificateTypeId, out trustListId);
}

return false;
}
#endregion

Expand Down
91 changes: 39 additions & 52 deletions Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,33 +162,7 @@ public override NodeId New(ISystemContext context, NodeState node)
}
#endregion

#region Private Methods
private void HasApplicationAdminAccess(ISystemContext context)
{
if (context != null)
{
RoleBasedIdentity identity = context.UserIdentity as RoleBasedIdentity;

if ((identity == null) || (!identity.Roles.Contains(GdsRole.ApplicationAdmin)))
{
throw new ServiceResultException(StatusCodes.BadUserAccessDenied, "Application Administrator access required.");
}
}
}

private void HasApplicationUserAccess(ISystemContext context)
{
if (context != null)
{
RoleBasedIdentity identity = context.UserIdentity as RoleBasedIdentity;

if (identity == null)
{
throw new ServiceResultException(StatusCodes.BadUserAccessDenied, "Application User access required.");
}
}
}

#region Private methods
private NodeId GetTrustListId(NodeId certificateGroupId)
{

Expand Down Expand Up @@ -257,13 +231,14 @@ private ICertificateGroup GetGroupForCertificate(byte[] certificate)
{
if (certificate != null && certificate.Length > 0)
{
var x509 = new X509Certificate2(certificate);

foreach (var certificateGroup in m_certificateGroups.Values)
using (var x509 = new X509Certificate2(certificate))
{
if (X509Utils.CompareDistinguishedName(certificateGroup.Certificate.Subject, x509.Issuer))
foreach (var certificateGroup in m_certificateGroups.Values)
{
return certificateGroup;
if (X509Utils.CompareDistinguishedName(certificateGroup.Certificate.Subject, x509.Issuer))
{
return certificateGroup;
}
}
}
}
Expand All @@ -279,14 +254,16 @@ private async Task RevokeCertificateAsync(byte[] certificate)

if (certificateGroup != null)
{
try
using (var x509 = new X509Certificate2(certificate))
{
var x509 = new X509Certificate2(certificate);
await certificateGroup.RevokeCertificateAsync(x509).ConfigureAwait(false);
}
catch (Exception e)
{
Utils.LogError(e, "Unexpected error revoking certificate. {0} for Authority={1}", new X509Certificate2(certificate).Subject, certificateGroup.Id);
try
{
await certificateGroup.RevokeCertificateAsync(x509).ConfigureAwait(false);
}
catch (Exception e)
{
Utils.LogError(e, "Unexpected error revoking certificate. {0} for Authority={1}", x509.Subject, certificateGroup.Id);
}
}
}
}
Expand Down Expand Up @@ -516,7 +493,7 @@ private ServiceResult OnRegisterApplication(
ApplicationRecordDataType application,
ref NodeId applicationId)
{
HasApplicationAdminAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.DiscoveryAdmin);

Utils.LogInfo("OnRegisterApplication: {0}", application.ApplicationUri);

Expand All @@ -531,7 +508,7 @@ private ServiceResult OnUpdateApplication(
NodeId objectId,
ApplicationRecordDataType application)
{
HasApplicationAdminAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.DiscoveryAdmin);

Utils.LogInfo("OnUpdateApplication: {0}", application.ApplicationUri);

Expand All @@ -553,7 +530,7 @@ private ServiceResult OnUnregisterApplication(
NodeId objectId,
NodeId applicationId)
{
HasApplicationAdminAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.DiscoveryAdmin);

Utils.LogInfo("OnUnregisterApplication: {0}", applicationId.ToString());

Expand Down Expand Up @@ -588,7 +565,7 @@ private ServiceResult OnFindApplications(
string applicationUri,
ref ApplicationRecordDataType[] applications)
{
HasApplicationUserAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.AuthenticatedUser);
Utils.LogInfo("OnFindApplications: {0}", applicationUri);
applications = m_database.FindApplications(applicationUri);
return ServiceResult.Good;
Expand All @@ -601,7 +578,7 @@ private ServiceResult OnGetApplication(
NodeId applicationId,
ref ApplicationRecordDataType application)
{
HasApplicationUserAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.AuthenticatedUserOrSelfAdmin, applicationId); ;
Utils.LogInfo("OnGetApplication: {0}", applicationId);
application = m_database.GetApplication(applicationId);
return ServiceResult.Good;
Expand Down Expand Up @@ -761,7 +738,7 @@ private ServiceResult OnStartNewKeyPairRequest(
string privateKeyPassword,
ref NodeId requestId)
{
HasApplicationAdminAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId); ;

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -880,7 +857,7 @@ private ServiceResult OnStartSigningRequest(
byte[] certificateRequest,
ref NodeId requestId)
{
HasApplicationAdminAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId); ;

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -961,7 +938,7 @@ private ServiceResult OnFinishRequest(
signedCertificate = null;
issuerCertificates = null;
privateKey = null;
HasApplicationAdminAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId); ;

var application = m_database.GetApplication(applicationId);
if (application == null)
Expand Down Expand Up @@ -1115,6 +1092,8 @@ out privateKeyPassword

m_database.SetApplicationCertificate(applicationId, m_certTypeMap[certificateGroup.CertificateType], signedCertificate);

m_database.SetApplicationTrustLists(applicationId, m_certTypeMap[certificateGroup.CertificateType], certificateGroup.Configuration.TrustedListPath);

m_request.AcceptRequest(requestId, signedCertificate);

return ServiceResult.Good;
Expand All @@ -1127,7 +1106,7 @@ public ServiceResult OnGetCertificateGroups(
NodeId applicationId,
ref NodeId[] certificateGroupIds)
{
HasApplicationUserAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -1155,7 +1134,7 @@ public ServiceResult OnGetTrustList(
NodeId certificateGroupId,
ref NodeId trustListId)
{
HasApplicationUserAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -1188,7 +1167,7 @@ public ServiceResult OnGetCertificateStatus(
NodeId certificateTypeId,
ref Boolean updateRequired)
{
HasApplicationUserAccess(context);
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.AuthenticatedUserOrSelfAdmin, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -1354,11 +1333,19 @@ protected void SetCertificateGroupNodes(ICertificateGroup certificateGroup)
certificateGroup.DefaultTrustList,
certificateGroup.Configuration.TrustedListPath,
certificateGroup.Configuration.IssuerListPath,
new TrustList.SecureAccess(HasApplicationUserAccess),
new TrustList.SecureAccess(HasApplicationAdminAccess));
new TrustList.SecureAccess(HasTrustListAccess),
new TrustList.SecureAccess(HasTrustListAccess));
}
}

#region AuthorizationHelpers
private void HasTrustListAccess(ISystemContext context, string trustedStorePath)
{
AuthorizationHelper.HasTrustListAccess(context, trustedStorePath, m_certTypeMap, m_database);
}
#endregion


private ServiceResult VerifyApprovedState(CertificateRequestState state)
{
switch (state)
Expand Down
Loading

0 comments on commit 22096f4

Please sign in to comment.