diff --git a/src/Certify.Core/Management/Access/AccessControl.cs b/src/Certify.Core/Management/Access/AccessControl.cs index 4f7a37e6a..09500136f 100644 --- a/src/Certify.Core/Management/Access/AccessControl.cs +++ b/src/Certify.Core/Management/Access/AccessControl.cs @@ -223,7 +223,7 @@ public async Task IsAuthorised(string contextUserId, string principleId, s { // if any of the service principles assigned roles are restricted by the type of resource type, // check for identifier matches (e.g. role assignment restricted on domains ) - if (spSpecificAssignedRoles.Any(a => a.IncludedResources.Any(r => r.ResourceType == resourceType))) + if (spSpecificAssignedRoles.Any(a => a.IncludedResources?.Any(r => r.ResourceType == resourceType) == true)) { var allIncludedResources = spSpecificAssignedRoles.SelectMany(a => a.IncludedResources).Distinct(); diff --git a/src/Certify.Models/Hub/AccessControlConfig.cs b/src/Certify.Models/Hub/AccessControlConfig.cs index 6e3dcaa99..d50645de0 100644 --- a/src/Certify.Models/Hub/AccessControlConfig.cs +++ b/src/Certify.Models/Hub/AccessControlConfig.cs @@ -19,6 +19,7 @@ public class StandardRoles { public static Role Administrator { get; } = new Role("sysadmin", "Administrator", "Certify Server Administrator", policies: new List { + StandardPolicies.ManagementHubAdmin, StandardPolicies.ManagedItemAdmin, StandardPolicies.CertificateAuthorityAdmin, StandardPolicies.AcmeAccountAdmin, @@ -29,8 +30,9 @@ public class StandardRoles public static Role CertificateManager { get; } = new Role("cert_manager", "Certificate Manager", "Can manage and administer all certificates", policies: new List { - StandardPolicies.ManagedItemAdmin, - StandardPolicies.StoredCredentialAdmin + StandardPolicies.ManagementHubReader, + StandardPolicies.ManagedItemAdmin, + StandardPolicies.StoredCredentialAdmin }); public static Role CertificateConsumer { get; } = new Role("cert_consumer", "Certificate Consumer", "User of a given certificate", policies: new List { StandardPolicies.CertificateConsumer }); @@ -74,6 +76,7 @@ public class ResourceTypes public static string CertificateAuthority { get; } = "ca"; public static string AcmeAccount { get; } = "acmeaccount"; public static string ManagedChallenge { get; } = "managedchallenge"; + public static string ManagedInstance { get; } = "managedinstance"; } public static class StandardResourceActions @@ -120,6 +123,8 @@ public static class StandardResourceActions public const string ManagedChallengeDelete = "managedchallenge_update"; public const string ManagedChallengeRequest = "managedchallenge_request"; + public const string ManagementHubInstancesList = "managementhub_instances_list"; + } public class StandardPolicies @@ -133,6 +138,8 @@ public class StandardPolicies public const string StoredCredentialConsumer = "storedcredential_consumer"; public const string ManagedChallengeConsumer = "managedchallenge_consumer"; public const string ManagedChallengeAdmin = "managedchallenge_admin"; + public const string ManagementHubAdmin = "managementhub_admin"; + public const string ManagementHubReader = "managementhub_reader"; } public static class Policies @@ -145,7 +152,7 @@ public static List GetStandardRoles() StandardRoles.CertificateManager, StandardRoles.CertificateConsumer, StandardRoles.StoredCredentialConsumer, - StandardRoles.ManagedChallengeConsumer + StandardRoles.ManagedChallengeConsumer, }; } @@ -199,6 +206,8 @@ public static List GetStandardResourceActions() new(StandardResourceActions.ManagedChallengeUpdate, "Update managed challenge", ResourceTypes.ManagedChallenge), new(StandardResourceActions.ManagedChallengeDelete, "Delete managed challenge", ResourceTypes.ManagedChallenge), new(StandardResourceActions.ManagedChallengeRequest, "Request to perform a managed challenge response", ResourceTypes.ManagedChallenge), + + new(StandardResourceActions.ManagementHubInstancesList, "List managed instances", ResourceTypes.ManagedInstance), }; } @@ -206,10 +215,10 @@ public static List GetStandardPolicies() { return new List { new() { - Id=StandardPolicies.ManagedItemAdmin, - Title="Managed Item Administration", - SecurityPermissionType= SecurityPermissionType.ALLOW, - ResourceActions= new List{ + Id = StandardPolicies.ManagedItemAdmin, + Title = "Managed Item Administration", + SecurityPermissionType = SecurityPermissionType.ALLOW, + ResourceActions = new List { StandardResourceActions.ManagedItemList, StandardResourceActions.ManagedItemAdd, StandardResourceActions.ManagedItemUpdate, @@ -224,87 +233,107 @@ public static List GetStandardPolicies() } }, new() { - Id=StandardPolicies.AccessAdmin, - Title="Access Control Administration", - SecurityPermissionType= SecurityPermissionType.ALLOW, - ResourceActions= new List{ - StandardResourceActions.SecurityPrincipleList, - StandardResourceActions.SecurityPrincipleAdd, - StandardResourceActions.SecurityPrincipleUpdate, - StandardResourceActions.SecurityPrincipleDelete, - StandardResourceActions.SecurityPrinciplePasswordUpdate + Id = StandardPolicies.AccessAdmin, + Title = "Access Control Administration", + SecurityPermissionType = SecurityPermissionType.ALLOW, + ResourceActions = new List { + StandardResourceActions.SecurityPrincipleList, + StandardResourceActions.SecurityPrincipleAdd, + StandardResourceActions.SecurityPrincipleUpdate, + StandardResourceActions.SecurityPrincipleDelete, + StandardResourceActions.SecurityPrinciplePasswordUpdate } }, new() { - Id=StandardPolicies.CertificateConsumer, - Title="Consume Certificates", - SecurityPermissionType= SecurityPermissionType.ALLOW, - ResourceActions= new List{ + Id = StandardPolicies.CertificateConsumer, + Title = "Consume Certificates", + SecurityPermissionType = SecurityPermissionType.ALLOW, + ResourceActions = new List { StandardResourceActions.CertificateDownload, StandardResourceActions.CertificateKeyDownload } }, - new() { - Id=StandardPolicies.CertificateAuthorityAdmin, - Title="Certificate Authority Administration", - SecurityPermissionType= SecurityPermissionType.ALLOW, - ResourceActions= new List{ - StandardResourceActions.CertificateAuthorityAdd, - StandardResourceActions.CertificateAuthorityUpdate, - StandardResourceActions.CertificateAuthorityDelete, - StandardResourceActions.CertificateAuthorityList - } - }, new() { - Id=StandardPolicies.AcmeAccountAdmin, - Title="ACME Account Administration", - SecurityPermissionType= SecurityPermissionType.ALLOW, - ResourceActions= new List{ + Id = StandardPolicies.CertificateAuthorityAdmin, + Title = "Certificate Authority Administration", + SecurityPermissionType = SecurityPermissionType.ALLOW, + ResourceActions = new List { + StandardResourceActions.CertificateAuthorityAdd, + StandardResourceActions.CertificateAuthorityUpdate, + StandardResourceActions.CertificateAuthorityDelete, + StandardResourceActions.CertificateAuthorityList + } + }, + new() { + Id = StandardPolicies.AcmeAccountAdmin, + Title = "ACME Account Administration", + SecurityPermissionType = SecurityPermissionType.ALLOW, + ResourceActions = new List { StandardResourceActions.AcmeAccountList, StandardResourceActions.AcmeAccountAdd, StandardResourceActions.AcmeAccountUpdate, StandardResourceActions.AcmeAccountDelete - } - }, + } + }, new() { - Id=StandardPolicies.StoredCredentialAdmin, - Title="Stored Credential Administration", - SecurityPermissionType= SecurityPermissionType.ALLOW, - ResourceActions= new List{ - StandardResourceActions.StoredCredentialList, - StandardResourceActions.StoredCredentialAdd, - StandardResourceActions.StoredCredentialUpdate, - StandardResourceActions.StoredCredentialDelete + Id = StandardPolicies.StoredCredentialAdmin, + Title = "Stored Credential Administration", + SecurityPermissionType = SecurityPermissionType.ALLOW, + ResourceActions = new List { + StandardResourceActions.StoredCredentialList, + StandardResourceActions.StoredCredentialAdd, + StandardResourceActions.StoredCredentialUpdate, + StandardResourceActions.StoredCredentialDelete } }, new() { - Id=StandardPolicies.StoredCredentialConsumer, - Title="Stored Credential Consumer", - Description="Provides access to fetch a decrypted stored credential.", - SecurityPermissionType= SecurityPermissionType.ALLOW, - IsResourceSpecific=true, - ResourceActions= new List{ - StandardResourceActions.StoredCredentialDownload + Id = StandardPolicies.StoredCredentialConsumer, + Title = "Stored Credential Consumer", + Description = "Provides access to fetch a decrypted stored credential.", + SecurityPermissionType = SecurityPermissionType.ALLOW, + IsResourceSpecific = true, + ResourceActions = new List { + StandardResourceActions.StoredCredentialDownload } }, - new() { - Id=StandardPolicies.ManagedChallengeAdmin, - Title="Managed Challenge Administration", - SecurityPermissionType= SecurityPermissionType.ALLOW, - ResourceActions= new List{ + new() { + Id = StandardPolicies.ManagedChallengeAdmin, + Title = "Managed Challenge Administration", + SecurityPermissionType = SecurityPermissionType.ALLOW, + ResourceActions = new List { StandardResourceActions.ManagedChallengeList, StandardResourceActions.ManagedChallengeUpdate, StandardResourceActions.ManagedChallengeDelete } }, - new() { - Id=StandardPolicies.ManagedChallengeConsumer, - Title="Managed Challenge Consumer", - Description="Allows consumer to request that a managed challenge be performed.", - SecurityPermissionType= SecurityPermissionType.ALLOW, - IsResourceSpecific=true, - ResourceActions= new List{ - StandardResourceActions.ManagedChallengeRequest + new() { + Id = StandardPolicies.ManagedChallengeConsumer, + Title = "Managed Challenge Consumer", + Description = "Allows consumer to request that a managed challenge be performed.", + SecurityPermissionType = SecurityPermissionType.ALLOW, + IsResourceSpecific = true, + ResourceActions = new List { + StandardResourceActions.ManagedChallengeRequest + } + }, + new() { + Id = StandardPolicies.ManagementHubAdmin, + Title = "Management Hub Admin", + Description = "Administer management hub.", + SecurityPermissionType = SecurityPermissionType.ALLOW, + IsResourceSpecific = true, + ResourceActions = new List { + StandardResourceActions.ManagementHubInstancesList + } + }, + new() { + Id = StandardPolicies.ManagementHubAdmin, + Title = "Management Hub Reader", + Description = "View management hub.", + SecurityPermissionType = SecurityPermissionType.ALLOW, + IsResourceSpecific = true, + ResourceActions = new List { + StandardResourceActions.ManagementHubInstancesList } } }; diff --git a/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs b/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs index 2b402d629..fb6b84397 100644 --- a/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs +++ b/src/Certify.Server/Certify.Server.Api.Public.Client/Certify.API.Public.cs @@ -78,6 +78,109 @@ public string BaseUrl partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + /// + /// Get list of Assigned Roles for a given security principle [Generated by Certify.SourceGenerators] + /// + /// OK + /// A server side error occurred. + public virtual System.Threading.Tasks.Task CheckSecurityPrincipleHasAccessAsync(string id, string resourceType, string resourceAction, string identifier) + { + return CheckSecurityPrincipleHasAccessAsync(id, resourceType, resourceAction, identifier, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Get list of Assigned Roles for a given security principle [Generated by Certify.SourceGenerators] + /// + /// OK + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task CheckSecurityPrincipleHasAccessAsync(string id, string resourceType, string resourceAction, string identifier, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + if (resourceType == null) + throw new System.ArgumentNullException("resourceType"); + + if (resourceAction == null) + throw new System.ArgumentNullException("resourceAction"); + + if (identifier == null) + throw new System.ArgumentNullException("identifier"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "internal/v1/access/securityprinciple/{id}/allowedaction/{resourceType}/{resourceAction}/{identifier}" + urlBuilder_.Append("internal/v1/access/securityprinciple/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Append("/allowedaction/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(resourceType, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Append('/'); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(resourceAction, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Append('/'); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(identifier, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + /// /// Get list of Assigned Roles for a given security principle [Generated by Certify.SourceGenerators] /// diff --git a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ApiControllerBase.cs b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ApiControllerBase.cs index e6f366e25..4dddf737e 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ApiControllerBase.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/ApiControllerBase.cs @@ -12,6 +12,12 @@ namespace Certify.Server.Api.Public.Controllers public partial class ApiControllerBase : ControllerBase { + internal async Task IsAuthorized(ICertifyInternalApiClient client, string resourceType, string resourceAction, string? identifier = null) + { + var isAuthorized = await client.CheckSecurityPrincipleHasAccess(CurrentAuthContext?.UserId, resourceType, resourceAction, identifier, CurrentAuthContext); + return isAuthorized; + } + /// /// Get the corresponding auth context to pass to the backend service /// diff --git a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs index a3cdefc3c..b34e47121 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Controllers/internal/HubController.cs @@ -101,6 +101,11 @@ public async Task GetHubManagedItems(string? instanceId, string? [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List))] public async Task GetHubManagedInstances() { + if (!await IsAuthorized(_client, ResourceTypes.ManagedInstance, StandardResourceActions.ManagementHubInstancesList)) + { + return Unauthorized(); + } + var managedInstances = _mgmtStateProvider.GetConnectedInstances(); return new OkObjectResult(managedInstances); } diff --git a/src/Certify.Server/Certify.Server.Api.Public/Middleware/AuthenticationExtension.cs b/src/Certify.Server/Certify.Server.Api.Public/Middleware/AuthenticationExtension.cs index 5f4a1b781..990ee518f 100644 --- a/src/Certify.Server/Certify.Server.Api.Public/Middleware/AuthenticationExtension.cs +++ b/src/Certify.Server/Certify.Server.Api.Public/Middleware/AuthenticationExtension.cs @@ -45,7 +45,6 @@ public static IServiceCollection AddTokenAuthentication(this IServiceCollection }; }); - return services; } } diff --git a/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/AccessController.cs b/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/AccessController.cs index d0b445726..dced47c8d 100644 --- a/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/AccessController.cs +++ b/src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/AccessController.cs @@ -1,5 +1,5 @@ -using Certify.Models.Hub; -using Certify.Management; +using Certify.Management; +using Certify.Models.Hub; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; @@ -102,6 +102,14 @@ public async Task> GetRoles() return roles; } + [HttpGet, Route("securityprinciple/{id}/allowedaction/{resourceType}/{resourceAction}/{identifier?}")] + public async Task CheckSecurityPrincipleHasAccess(string id, string resourceType, string resourceAction, string? identifier) + { + var accessControl = await _certifyManager.GetCurrentAccessControl(); + + return await accessControl.IsAuthorised(GetContextUserId(), id, null, resourceType, actionId: resourceAction, identifier); + } + [HttpGet, Route("securityprinciple/{id}/assignedroles")] public async Task> GetSecurityPrincipleAssignedRoles(string id) { diff --git a/src/Certify.SourceGenerators/ApiMethods.cs b/src/Certify.SourceGenerators/ApiMethods.cs index 6fac3c8cc..fcafd0d65 100644 --- a/src/Certify.SourceGenerators/ApiMethods.cs +++ b/src/Certify.SourceGenerators/ApiMethods.cs @@ -33,6 +33,16 @@ public static List GetApiDefinitions() return new List { + new GeneratedAPI { + OperationName = "CheckSecurityPrincipleHasAccess", + OperationMethod = HttpGet, + Comment = "Get list of Assigned Roles for a given security principle", + PublicAPIController = "Access", + PublicAPIRoute = "securityprinciple/{id}/allowedaction/{resourceType}/{resourceAction}/{identifier}", + ServiceAPIRoute = "access/securityprinciple/{id}/allowedaction/{resourceType}/{resourceAction}/{identifier}", + ReturnType = "bool", + Params =new Dictionary{{"id","string"}, { "resourceType", "string" },{ "resourceAction", "string" }, { "identifier", "string" } } + }, new GeneratedAPI { OperationName = "GetSecurityPrincipleAssignedRoles",