From 95b4c89dd975d3ac487511f8309b937210dd2f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Kjetil=20=C3=98ye?= Date: Fri, 11 Oct 2024 08:46:21 +0200 Subject: [PATCH] Added DelegationCheck support for AppInstanceDelegation #840 - New DelegationCheck API endpoint added to AppInstanceDelegationController - New service implementation for DelegationCheck in AppInstanceDelegationService - Rewrite of existing internal delegation check logic in delegation service - Added simple integration test - Added Bruno automated test requests --- .../Constants/AltinnXacmlConstants.cs | 5 + .../Enums/RightsQueryType.cs | 22 ++++ .../Helpers/RightsHelper.cs | 24 ++-- .../Models/AppsInstanceDelegationRequest.cs | 4 +- .../Models/AttributeMatch.cs | 8 ++ .../Models/ResourceDelegationCheckRequest.cs | 16 +++ .../Models/ResourceDelegationCheckResponse.cs | 19 +++ .../ResourceRightDelegationCheckResult.cs | 44 +++++++ .../Models/Rights/ActionIdentifier.cs | 11 +- .../Models/Rights/RightsQuery.cs | 14 ++- .../Services/AppsInstanceDelegationService.cs | 108 ++++++++++++++---- .../IAppsInstanceDelegationService.cs | 8 ++ .../Interfaces/IPolicyInformationPoint.cs | 4 +- .../Services/MaskinportenSchemaService.cs | 36 +++--- .../Services/PolicyInformationPoint.cs | 50 +++++--- .../Services/SingleRightsService.cs | 9 +- .../AppsInstanceDelegationController.cs | 56 ++++++--- .../Controllers/RightsInternalController.cs | 5 +- .../Mappers/AccessManagementMapper.cs | 9 +- .../ResourceDelegationCheckRequestDto.cs | 17 +++ .../ResourceDelegationCheckResponseDto.cs | 22 ++++ .../ResourceRightDelegationCheckResultDto.cs | 44 +++++++ .../Models/Rights/RightsQueryExternal.cs | 31 +++++ .../Utilities/HashUtil.cs | 26 +++++ .../AppsInstanceDelegationControllerTest.cs | 23 ++++ .../TestDataAppsInstanceDelegation.cs | 56 ++++++--- .../request.json | 0 .../response.json | 0 .../request.json | 0 .../response.json | 0 .../request.json | 0 .../response.json | 0 .../request.json | 0 .../response.json | 0 .../request.json | 0 .../response.json | 0 .../request.json | 0 .../response.json | 0 .../request.json | 0 .../response.json | 0 .../response.json | 46 ++++++++ .../digdirs_company_car/resource.json | 16 ++- .../jks_audi_etron_gt/resource.json | 12 +- .../resource.json | 8 +- .../am-devtest-instancedelegation/policy.xml | 10 +- .../Utils/AssertionUtil.cs | 23 ++++ .../AppWithDelegableRights_Ok.bru | 54 +++++++++ .../AppWithoutDelegableRights_Forbidden.bru | 50 ++++++++ .../MissingToken_NotAuthorized.bru | 37 ++++++ .../PlatformAccessToken_Forbidden.bru | 50 ++++++++ .../DelegateInstance.bru | 3 +- .../DelegationCheck.bru | 3 +- .../GetInstanceDelegations.bru | 3 +- .../RevokeInstanceDelegations.bru | 3 +- .../environments/AT22.bru | 1 + .../environments/AT23.bru | 1 + .../environments/AT24.bru | 1 + .../environments/PROD.bru | 3 +- .../environments/TT02.bru | 1 + .../environments/YT01.bru | 1 + 60 files changed, 848 insertions(+), 149 deletions(-) create mode 100644 src/Altinn.AccessManagement.Core/Enums/RightsQueryType.cs create mode 100644 src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckRequest.cs create mode 100644 src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckResponse.cs create mode 100644 src/Altinn.AccessManagement.Core/Models/ResourceRightDelegationCheckResult.cs create mode 100644 src/Altinn.AccessManagement/Models/ResourceDelegationCheckRequestDto.cs create mode 100644 src/Altinn.AccessManagement/Models/ResourceDelegationCheckResponseDto.cs create mode 100644 src/Altinn.AccessManagement/Models/ResourceRightDelegationCheckResultDto.cs create mode 100644 src/Altinn.AccessManagement/Utilities/HashUtil.cs rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/request.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/response.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/request.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/response.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/request.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/response.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/request.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/response.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/request.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/response.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/request.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/response.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/request.json (100%) rename test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/{ => Delegation}/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json (100%) create mode 100644 test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/DelegationCheck/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json create mode 100644 test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithDelegableRights_Ok.bru create mode 100644 test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithoutDelegableRights_Forbidden.bru create mode 100644 test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/MissingToken_NotAuthorized.bru create mode 100644 test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/PlatformAccessToken_Forbidden.bru diff --git a/src/Altinn.AccessManagement.Core/Constants/AltinnXacmlConstants.cs b/src/Altinn.AccessManagement.Core/Constants/AltinnXacmlConstants.cs index 508f3f249..e2a323795 100644 --- a/src/Altinn.AccessManagement.Core/Constants/AltinnXacmlConstants.cs +++ b/src/Altinn.AccessManagement.Core/Constants/AltinnXacmlConstants.cs @@ -86,6 +86,11 @@ public static class MatchAttributeIdentifiers /// public const string ResourceRegistryAttribute = "urn:altinn:resource"; + /// + /// Resource delegation urn prefix used in Xacml policy rule subjects to identify rights the resourceId value of the attribute, is allowed to perform delegation of. + /// + public const string ResourceDelegationAttribute = "urn:altinn:resource:delegation"; + /// /// Organization name /// diff --git a/src/Altinn.AccessManagement.Core/Enums/RightsQueryType.cs b/src/Altinn.AccessManagement.Core/Enums/RightsQueryType.cs new file mode 100644 index 000000000..a7b60abd0 --- /dev/null +++ b/src/Altinn.AccessManagement.Core/Enums/RightsQueryType.cs @@ -0,0 +1,22 @@ +namespace Altinn.AccessManagement.Core.Enums; + +/// +/// Enum for different types of rights queries in Altinn Authorization +/// +public enum RightsQueryType +{ + /// + /// Default + /// + NotSet = 0, + + /// + /// Rights query where the recipient is a user + /// + User = 1, + + /// + /// Rights query where the recipient is an Altinn app + /// + AltinnApp = 2 +} diff --git a/src/Altinn.AccessManagement.Core/Helpers/RightsHelper.cs b/src/Altinn.AccessManagement.Core/Helpers/RightsHelper.cs index fa297b570..b693714dc 100644 --- a/src/Altinn.AccessManagement.Core/Helpers/RightsHelper.cs +++ b/src/Altinn.AccessManagement.Core/Helpers/RightsHelper.cs @@ -1,8 +1,7 @@ -using System.Collections.Generic; -using Altinn.AccessManagement.Core.Constants; +using Altinn.AccessManagement.Core.Constants; using Altinn.AccessManagement.Core.Enums; -using Altinn.AccessManagement.Core.Helpers.Extensions; using Altinn.AccessManagement.Core.Models; +using Altinn.AccessManagement.Core.Models.ResourceRegistry; using Authorization.Platform.Authorization.Models; namespace Altinn.AccessManagement.Core.Helpers @@ -57,23 +56,14 @@ public static List GetDelegationSubjectAttributeMatches(Delegati /// /// Builds a RightsQuery request model for lookup of a users rights for a given service resource on behalf of the given reportee party /// - public static RightsQuery GetRightsQuery(int userId, int fromPartyId, string resourceRegistryId = null, string org = null, string app = null) + public static RightsQuery GetRightsQuery(int userId, int fromPartyId, ServiceResource resource) { - if (!string.IsNullOrEmpty(org) && !string.IsNullOrEmpty(app)) - { - return new RightsQuery - { - To = new List { new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute, Value = userId.ToString() } }, - From = new List { new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute, Value = fromPartyId.ToString() } }, - Resource = new List { new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute, Value = org }, new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute, Value = app } } - }; - } - - return new RightsQuery + return new RightsQuery() { + Type = RightsQueryType.User, To = new List { new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute, Value = userId.ToString() } }, From = new List { new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute, Value = fromPartyId.ToString() } }, - Resource = new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistryAttribute, Value = resourceRegistryId }.SingleToList() + Resource = resource }; } @@ -84,7 +74,7 @@ public static RightsQuery GetRightsQuery(int userId, int fromPartyId, string res /// the decision public static bool CheckIfRuleIsAnEndUserRule(Right right) { - List roleAccessSources = right.RightSources.Where(rs => rs.RightSourceType != Enums.RightSourceType.DelegationPolicy).ToList(); + List roleAccessSources = right.RightSources.Where(rs => rs.RightSourceType != RightSourceType.DelegationPolicy).ToList(); if (roleAccessSources.Any()) { List roles = GetAttributeMatches(roleAccessSources.SelectMany(roleAccessSource => roleAccessSource.PolicySubjects)).FindAll(policySubject => policySubject.Id.Equals(AltinnXacmlConstants.MatchAttributeIdentifiers.RoleAttribute, StringComparison.OrdinalIgnoreCase)); diff --git a/src/Altinn.AccessManagement.Core/Models/AppsInstanceDelegationRequest.cs b/src/Altinn.AccessManagement.Core/Models/AppsInstanceDelegationRequest.cs index 4fa191049..371a82b05 100644 --- a/src/Altinn.AccessManagement.Core/Models/AppsInstanceDelegationRequest.cs +++ b/src/Altinn.AccessManagement.Core/Models/AppsInstanceDelegationRequest.cs @@ -1,9 +1,7 @@ using System.ComponentModel.DataAnnotations; -using System.Text.Json; using Altinn.AccessManagement.Core.Enums; using Altinn.AccessManagement.Core.Models.Register; using Altinn.AccessManagement.Core.Models.ResourceRegistry; -using Altinn.Swashbuckle.Examples; namespace Altinn.AccessManagement.Core.Models; @@ -43,7 +41,7 @@ public class AppsInstanceDelegationRequest /// /// The app performing the delegation /// - public IEnumerable PerformedBy { get; set; } + public ResourceIdUrn PerformedBy { get; set; } /// /// Gets or sets the rights to delegate diff --git a/src/Altinn.AccessManagement.Core/Models/AttributeMatch.cs b/src/Altinn.AccessManagement.Core/Models/AttributeMatch.cs index 63c850489..4eb61c8ab 100644 --- a/src/Altinn.AccessManagement.Core/Models/AttributeMatch.cs +++ b/src/Altinn.AccessManagement.Core/Models/AttributeMatch.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using Altinn.Urn; namespace Altinn.AccessManagement.Core.Models { @@ -58,5 +59,12 @@ public AttributeMatch(string id, object value) /// public override string ToString() => $"{Id.ToLowerInvariant()}:{Value}"; + + /// + /// Creates a KeyValueUrn from the attribute match + /// + /// KeyValueUrn + public KeyValueUrn ToKeyValueUrn() => + KeyValueUrn.CreateUnchecked($"{Id.ToLowerInvariant()}:{Value}", Id.Length + 1); } } diff --git a/src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckRequest.cs b/src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckRequest.cs new file mode 100644 index 000000000..00b53b167 --- /dev/null +++ b/src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckRequest.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using Altinn.AccessManagement.Core.Models.ResourceRegistry; + +namespace Altinn.AccessManagement.Core.Models; + +/// +/// Request model for a list of all delegable rights for a specific resource. +/// +public class ResourceDelegationCheckRequest +{ + /// + /// Gets or sets the urn for identifying the resource of the rights to be checked + /// + [Required] + public ResourceIdUrn ResourceId { get; set; } +} diff --git a/src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckResponse.cs b/src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckResponse.cs new file mode 100644 index 000000000..8ea1cebbf --- /dev/null +++ b/src/Altinn.AccessManagement.Core/Models/ResourceDelegationCheckResponse.cs @@ -0,0 +1,19 @@ +using Altinn.AccessManagement.Core.Models.Register; + +namespace Altinn.AccessManagement.Core.Models; + +/// +/// Response model for the result of a delegation status check, for which rights a user is able to delegate between two parties. +/// +public class ResourceDelegationCheckResponse +{ + /// + /// Gets or sets the urn identifying the party the rights can be delegated from + /// + public required PartyUrn From { get; set; } + + /// + /// Gets or sets a list of right delegation status models + /// + public List ResourceRightDelegationCheckResults { get; set; } +} diff --git a/src/Altinn.AccessManagement.Core/Models/ResourceRightDelegationCheckResult.cs b/src/Altinn.AccessManagement.Core/Models/ResourceRightDelegationCheckResult.cs new file mode 100644 index 000000000..8b8b2397f --- /dev/null +++ b/src/Altinn.AccessManagement.Core/Models/ResourceRightDelegationCheckResult.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Altinn.AccessManagement.Core.Enums; +using Altinn.AccessManagement.Core.Models.Rights; +using Altinn.Urn; + +namespace Altinn.AccessManagement.Core.Models; + +/// +/// Response model describing the delegation status for a given single right, whether the authenticated user is able to delegate the right or not on behalf of the from part. +/// +public class ResourceRightDelegationCheckResult +{ + /// + /// Gets or sets the right key + /// + [Required] + public string RightKey { get; set; } + + /// + /// Gets or sets the list of resource matches which uniquely identifies the resource this right applies to. + /// + [Required] + public List Resource { get; set; } + + /// + /// Gets or sets the set of Attribute Id and Attribute Value for a specific action, to identify the action this right applies to + /// + [Required] + public ActionUrn Action { get; set; } + + /// + /// Gets or sets a value indicating whether the right is delegable or not + /// + [Required] + [JsonConverter(typeof(JsonStringEnumConverter))] + public DelegableStatus Status { get; set; } + + /// + /// Gets or sets a list of details describing why or why not the right is valid in the current user and reportee party context + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public List Details { get; set; } +} diff --git a/src/Altinn.AccessManagement.Core/Models/Rights/ActionIdentifier.cs b/src/Altinn.AccessManagement.Core/Models/Rights/ActionIdentifier.cs index 882697402..d1d81720d 100644 --- a/src/Altinn.AccessManagement.Core/Models/Rights/ActionIdentifier.cs +++ b/src/Altinn.AccessManagement.Core/Models/Rights/ActionIdentifier.cs @@ -1,8 +1,5 @@ #nullable enable -using System; -using System.Buffers; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; @@ -27,6 +24,14 @@ private ActionIdentifier(string value) _value = value; } + /// + /// Creates a new from the specified value without validation. + /// + /// The action identifier. + /// A . + public static ActionIdentifier CreateUnchecked(string value) + => new(value); + /// public static IEnumerable? GetExamples(ExampleDataOptions options) { diff --git a/src/Altinn.AccessManagement.Core/Models/Rights/RightsQuery.cs b/src/Altinn.AccessManagement.Core/Models/Rights/RightsQuery.cs index 9f453e000..8fcfcf16c 100644 --- a/src/Altinn.AccessManagement.Core/Models/Rights/RightsQuery.cs +++ b/src/Altinn.AccessManagement.Core/Models/Rights/RightsQuery.cs @@ -1,4 +1,7 @@ -namespace Altinn.AccessManagement.Core.Models +using Altinn.AccessManagement.Core.Enums; +using Altinn.AccessManagement.Core.Models.ResourceRegistry; + +namespace Altinn.AccessManagement.Core.Models { /// /// Queries for a list of all rights between two parties for a specific resource. @@ -7,6 +10,11 @@ /// public class RightsQuery { + /// + /// Gets or sets the type of rights query to perform + /// + public RightsQueryType Type { get; set; } + /// /// Gets or sets the set of Attribute Id and Attribute Value for the entity having offered rights /// @@ -18,8 +26,8 @@ public class RightsQuery public List To { get; set; } /// - /// Gets or sets the set of Attribute Id and Attribute Value for identifying the resource the rights + /// Gets or sets the service resource model of the rights /// - public List Resource { get; set; } + public ServiceResource Resource { get; set; } } } diff --git a/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs b/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs index f7669375a..7bdf6e9bb 100644 --- a/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs +++ b/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs @@ -1,12 +1,14 @@ using System.ComponentModel.DataAnnotations; using Altinn.AccessManagement.Core.Clients.Interfaces; using Altinn.AccessManagement.Core.Constants; +using Altinn.AccessManagement.Core.Enums; using Altinn.AccessManagement.Core.Errors; using Altinn.AccessManagement.Core.Helpers; using Altinn.AccessManagement.Core.Helpers.Extensions; using Altinn.AccessManagement.Core.Models; using Altinn.AccessManagement.Core.Models.Register; using Altinn.AccessManagement.Core.Models.ResourceRegistry; +using Altinn.AccessManagement.Core.Models.Rights; using Altinn.AccessManagement.Core.Services.Interfaces; using Altinn.AccessManagement.Enums; using Altinn.Authorization.ProblemDetails; @@ -172,13 +174,71 @@ private static void AddValidationErrorsForResourceInstance(ref ValidationErrorBu } /// - public async Task> Delegate(AppsInstanceDelegationRequest appsInstanceDelegationRequest, CancellationToken cancellationToken = default) + public async Task> DelegationCheck(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default) + { + ResourceDelegationCheckResponse result = new ResourceDelegationCheckResponse() { From = null, ResourceRightDelegationCheckResults = new List() }; + + ValidationErrorBuilder errors = default; + + ServiceResource resource = (await _resourceRegistryClient.GetResourceList(cancellationToken)).Find(r => r.Identifier == request.ResourceId); + List delegableRights = null; + + if (resource == null) + { + errors.Add(ValidationErrors.InvalidResource, "appInstanceDelegationRequest.Resource"); + } + else + { + RightsQuery rightsQueryForApp = new RightsQuery + { + Type = RightsQueryType.AltinnApp, + To = new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceDelegationAttribute, Value = request.PerformedBy.ValueSpan.ToString() }.SingleToList(), + From = resource.AuthorizationReference, + Resource = resource + }; + + try + { + delegableRights = await _pip.GetDelegableRightsByApp(rightsQueryForApp, cancellationToken); + } + catch (ValidationException) + { + errors.Add(ValidationErrors.MissingPolicy, "appInstanceDelegationRequest.Resource"); + } + + if (delegableRights == null || delegableRights.Count == 0) + { + errors.Add(ValidationErrors.MissingDelegableRights, "appInstanceDelegationRequest.Resource"); + } + } + + if (errors.TryBuild(out var errorResult)) + { + return errorResult; + } + + foreach (Right right in delegableRights) + { + result.ResourceRightDelegationCheckResults.Add(new() + { + RightKey = right.RightKey, + Resource = right.Resource.Select(r => r.ToKeyValueUrn()).ToList(), + Action = ActionUrn.ActionId.Create(ActionIdentifier.CreateUnchecked(right.Action.Value)), + Status = (right.CanDelegate.HasValue && right.CanDelegate.Value) ? DelegableStatus.Delegable : DelegableStatus.NotDelegable + }); + } + + return await Task.FromResult(result); + } + + /// + public async Task> Delegate(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default) { ValidationErrorBuilder errors = default; // Fetch from and to from partyuuid - (UuidType Type, Guid? Uuid) from = await TranslatePartyUuidToPersonOrganizationUuid(appsInstanceDelegationRequest.From); - (UuidType Type, Guid? Uuid) to = await TranslatePartyUuidToPersonOrganizationUuid(appsInstanceDelegationRequest.To); + (UuidType Type, Guid? Uuid) from = await TranslatePartyUuidToPersonOrganizationUuid(request.From); + (UuidType Type, Guid? Uuid) to = await TranslatePartyUuidToPersonOrganizationUuid(request.To); if (from.Type == UuidType.NotSpecified) { @@ -191,10 +251,10 @@ public async Task> Delegate(AppsInstanceD } // Validate Resource and instance 1. All rights include resource 2. All rights resource is identical to central value - AddValidationErrorsForResourceInstance(ref errors, appsInstanceDelegationRequest.Rights, appsInstanceDelegationRequest.ResourceId); + AddValidationErrorsForResourceInstance(ref errors, request.Rights, request.ResourceId); // Fetch rights valid for delegation - ServiceResource resource = (await _resourceRegistryClient.GetResourceList(cancellationToken)).Find(r => r.Identifier == appsInstanceDelegationRequest.ResourceId); + ServiceResource resource = (await _resourceRegistryClient.GetResourceList(cancellationToken)).Find(r => r.Identifier == request.ResourceId); List delegableRights = null; if (resource == null) @@ -203,11 +263,18 @@ public async Task> Delegate(AppsInstanceD } else { - RightQueryForApp resourceQuery = new RightQueryForApp { OwnerApp = appsInstanceDelegationRequest.PerformedBy, Resource = resource.AuthorizationReference }; + ////RightQueryForApp resourceQuery = new RightQueryForApp { OwnerApp = appsInstanceDelegationRequest.PerformedBy, Resource = resource.AuthorizationReference }; + RightsQuery rightsQueryForApp = new RightsQuery + { + Type = RightsQueryType.AltinnApp, + To = new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceDelegationAttribute, Value = request.PerformedBy.ValueSpan.ToString() }.SingleToList(), + From = resource.AuthorizationReference, + Resource = resource + }; try { - delegableRights = await _pip.GetDelegableRightsByApp(resourceQuery, cancellationToken); + delegableRights = await _pip.GetDelegableRightsByApp(rightsQueryForApp, cancellationToken); } catch (ValidationException) { @@ -226,23 +293,22 @@ public async Task> Delegate(AppsInstanceD } // Perform delegation - DelegationHelper.TryGetPerformerFromAttributeMatches(appsInstanceDelegationRequest.PerformedBy, out string performedById, out UuidType performedByType); InstanceRight rulesToDelegate = new InstanceRight { FromUuid = (Guid)from.Uuid, FromType = from.Type, ToUuid = (Guid)to.Uuid, ToType = to.Type, - PerformedBy = performedById, - PerformedByType = performedByType, - ResourceId = appsInstanceDelegationRequest.ResourceId, - InstanceId = appsInstanceDelegationRequest.InstanceId, - InstanceDelegationMode = appsInstanceDelegationRequest.InstanceDelegationMode + PerformedBy = request.PerformedBy.ValueSpan.ToString(), + PerformedByType = UuidType.Resource, + ResourceId = request.ResourceId, + InstanceId = request.InstanceId, + InstanceDelegationMode = request.InstanceDelegationMode }; List rightsAppCantDelegate = new List(); - UrnJsonTypeValue instanceId = KeyValueUrn.CreateUnchecked($"{AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute}:{appsInstanceDelegationRequest.InstanceId}", AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute.Length + 1); - - foreach (RightInternal rightToDelegate in appsInstanceDelegationRequest.Rights) + UrnJsonTypeValue instanceId = KeyValueUrn.CreateUnchecked($"{AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute}:{request.InstanceId}", AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute.Length + 1); + + foreach (RightInternal rightToDelegate in request.Rights) { if (CheckIfInstanceIsDelegable(delegableRights, rightToDelegate)) { @@ -261,11 +327,11 @@ public async Task> Delegate(AppsInstanceD AppsInstanceDelegationResponse result = new() { - From = appsInstanceDelegationRequest.From, - To = appsInstanceDelegationRequest.To, - ResourceId = appsInstanceDelegationRequest.ResourceId, - InstanceId = appsInstanceDelegationRequest.InstanceId, - InstanceDelegationMode = appsInstanceDelegationRequest.InstanceDelegationMode + From = request.From, + To = request.To, + ResourceId = request.ResourceId, + InstanceId = request.InstanceId, + InstanceDelegationMode = request.InstanceDelegationMode }; List rights = await DelegateRights(rulesToDelegate, rightsAppCantDelegate, cancellationToken); diff --git a/src/Altinn.AccessManagement.Core/Services/Interfaces/IAppsInstanceDelegationService.cs b/src/Altinn.AccessManagement.Core/Services/Interfaces/IAppsInstanceDelegationService.cs index 10fb1501a..151eff125 100644 --- a/src/Altinn.AccessManagement.Core/Services/Interfaces/IAppsInstanceDelegationService.cs +++ b/src/Altinn.AccessManagement.Core/Services/Interfaces/IAppsInstanceDelegationService.cs @@ -8,6 +8,14 @@ namespace Altinn.AccessManagement.Core.Services.Interfaces; /// public interface IAppsInstanceDelegationService { + /// + /// Gets all rights available for delegation by an app for a given app instance + /// + /// App instance delegation check request model + /// The + /// Boolean whether the app instance delegation was successful + public Task> DelegationCheck(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default); + /// /// Delegate access to an app instance /// diff --git a/src/Altinn.AccessManagement.Core/Services/Interfaces/IPolicyInformationPoint.cs b/src/Altinn.AccessManagement.Core/Services/Interfaces/IPolicyInformationPoint.cs index ce839e35c..125208693 100644 --- a/src/Altinn.AccessManagement.Core/Services/Interfaces/IPolicyInformationPoint.cs +++ b/src/Altinn.AccessManagement.Core/Services/Interfaces/IPolicyInformationPoint.cs @@ -34,10 +34,10 @@ public interface IPolicyInformationPoint /// /// Gets the all rights an app have right to delegate /// - /// the resource to query for + /// The query model /// The /// A list of rights - Task> GetDelegableRightsByApp(RightQueryForApp resourceQuery, CancellationToken cancellationToken = default); + Task> GetDelegableRightsByApp(RightsQuery rightsQuery, CancellationToken cancellationToken = default); /// /// Finds all delegation changes for a given user, reportee and app/resource context diff --git a/src/Altinn.AccessManagement.Core/Services/MaskinportenSchemaService.cs b/src/Altinn.AccessManagement.Core/Services/MaskinportenSchemaService.cs index 8b7932753..14bba463c 100644 --- a/src/Altinn.AccessManagement.Core/Services/MaskinportenSchemaService.cs +++ b/src/Altinn.AccessManagement.Core/Services/MaskinportenSchemaService.cs @@ -52,10 +52,8 @@ public async Task DelegationCheck(int authenticatedUser return result; } - DelegationHelper.TryGetResourceFromAttributeMatch(request.Resource, out _, out string resourceRegistryId, out _, out _, out _, out _); - // Get all delegable rights - RightsQuery rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resourceRegistryId); + RightsQuery rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resource); List allDelegableRights = await _pip.GetRights(rightsQuery, getDelegableRights: true, returnAllPolicyRights: true, cancellationToken: cancellationToken); if (allDelegableRights == null || allDelegableRights.Count == 0) { @@ -108,24 +106,24 @@ public async Task DelegationCheck(int authenticatedUser /// public async Task DelegateMaskinportenSchema(int authenticatedUserId, int authenticatedUserAuthlevel, DelegationLookup delegation, CancellationToken cancellationToken = default) { - (DelegationActionResult result, string resourceRegistryId, Party fromParty, Party toParty) = await ValidateMaskinportenDelegationModel(DelegationActionType.Delegation, delegation); + (DelegationActionResult result, ServiceResource resource, Party fromParty, Party toParty) = await ValidateMaskinportenDelegationModel(DelegationActionType.Delegation, delegation); if (!result.IsValid) { return result; } // Verify authenticated users delegable rights - RightsQuery rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resourceRegistryId); + RightsQuery rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resource); List usersDelegableRights = await _pip.GetRights(rightsQuery, getDelegableRights: true, cancellationToken: cancellationToken); if (usersDelegableRights == null || usersDelegableRights.Count == 0) { - result.Errors.Add("right[0].Resource", $"Authenticated user does not have any delegable rights for the resource: {resourceRegistryId}"); + result.Errors.Add("right[0].Resource", $"Authenticated user does not have any delegable rights for the resource: {resource.Identifier}"); return result; } if (usersDelegableRights.Any(r => r.RightSources.Any(rs => rs.MinimumAuthenticationLevel > authenticatedUserAuthlevel))) { - result.Errors.Add("right[0].Resource", $"Authenticated user does not meet the required security level requirement for resource: {resourceRegistryId}"); + result.Errors.Add("right[0].Resource", $"Authenticated user does not meet the required security level requirement for resource: {resource.Identifier}"); return result; } @@ -245,19 +243,19 @@ public async Task> GetMaskinportenDelegations(string supplierOr /// public async Task RevokeMaskinportenSchemaDelegation(int authenticatedUserId, DelegationLookup delegation, CancellationToken cancellationToken = default) { - (DelegationActionResult result, string resourceRegistryId, Party fromParty, Party toParty) = await ValidateMaskinportenDelegationModel(DelegationActionType.Revoke, delegation); + (DelegationActionResult result, ServiceResource resource, Party fromParty, Party toParty) = await ValidateMaskinportenDelegationModel(DelegationActionType.Revoke, delegation); if (!result.IsValid) { return result; } - List policiesToDelete = DelegationHelper.GetRequestToDeleteResourceRegistryService(authenticatedUserId, resourceRegistryId, fromParty.PartyId, toParty.PartyId); + List policiesToDelete = DelegationHelper.GetRequestToDeleteResourceRegistryService(authenticatedUserId, resource.Identifier, fromParty.PartyId, toParty.PartyId); await _pap.TryDeleteDelegationPolicies(policiesToDelete, cancellationToken); return result; } - private async Task<(DelegationActionResult Result, string ResourceRegistryId, Party FromParty, Party ToParty)> ValidateMaskinportenDelegationModel(DelegationActionType delegationAction, DelegationLookup delegation) + private async Task<(DelegationActionResult Result, ServiceResource ServiceResource, Party FromParty, Party ToParty)> ValidateMaskinportenDelegationModel(DelegationActionType delegationAction, DelegationLookup delegation) { DelegationActionResult result = new DelegationActionResult { To = delegation.To, Rights = new List() }; @@ -265,7 +263,7 @@ public async Task RevokeMaskinportenSchemaDelegation(int if (delegation.Rights?.Count != 1) { result.Errors.Add("Rights", "This operation only support requests specifying a single right identifying a Maskinporten schema resource registered in the Altinn Resource Registry"); - return (result, string.Empty, null, null); + return (result, null, null, null); } Right right = delegation.Rights.First(); @@ -274,7 +272,7 @@ public async Task RevokeMaskinportenSchemaDelegation(int if (resourceMatchType != ResourceAttributeMatchType.ResourceRegistry) { result.Errors.Add("right[0].Resource", $"This operation only support requests for resources from the Altinn Resource Registry using the {AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistryAttribute} attribute id"); - return (result, resourceRegistryId, null, null); + return (result, null, null, null); } // Verify resource registry id is a valid MaskinportenSchema @@ -282,19 +280,19 @@ public async Task RevokeMaskinportenSchemaDelegation(int if (resource == null || (delegationAction == DelegationActionType.Delegation && !resource.Delegable)) { result.Errors.Add("right[0].Resource", $"The resource: {resourceRegistryId}, does not exist or is not available for delegation"); - return (result, resourceRegistryId, null, null); + return (result, null, null, null); } if (resource.ResourceType != ResourceType.MaskinportenSchema) { result.Errors.Add("right[0].Resource", $"This operation only support requests for Maskinporten schema resources. Invalid resource: {resource}"); - return (result, resourceRegistryId, null, null); + return (result, null, null, null); } if (!resource.Delegable) { result.Errors.Add("right[0].Resource", $"The resource: {resource}, is not available for delegation"); - return (result, resourceRegistryId, null, null); + return (result, null, null, null); } // Verify and get From reportee party of the delegation @@ -312,7 +310,7 @@ public async Task RevokeMaskinportenSchemaDelegation(int if (fromParty == null || fromParty.PartyTypeName != PartyType.Organisation) { result.Errors.Add("From", $"Maskinporten schema delegation can only be delegated from a valid organization (identified by either {AltinnXacmlConstants.MatchAttributeIdentifiers.OrganizationNumberAttribute} or {AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute} attribute id). Invalid value: {delegation.From.FirstOrDefault()?.Value}"); - return (result, resourceRegistryId, null, null); + return (result, resource, null, null); } // Verify and get To recipient party of the delegation @@ -330,16 +328,16 @@ public async Task RevokeMaskinportenSchemaDelegation(int if (toParty == null || toParty.PartyTypeName != PartyType.Organisation) { result.Errors.Add("To", $"Maskinporten schema delegation can only be delegated to a valid organization (identified by either {AltinnXacmlConstants.MatchAttributeIdentifiers.OrganizationNumberAttribute} or {AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute} attribute id). Invalid value: {delegation.To.FirstOrDefault()?.Value}"); - return (result, resourceRegistryId, null, null); + return (result, resource, null, null); } if (fromParty.PartyId.Equals(toParty.PartyId) && delegationAction == DelegationActionType.Delegation) { result.Errors.Add("To", $"Maskinporten schema delegation can not have the same party in the From and To Attributes: {delegation.To.FirstOrDefault()?.Value}"); - return (result, resourceRegistryId, null, null); + return (result, resource, null, null); } - return (result, resourceRegistryId, fromParty, toParty); + return (result, resource, fromParty, toParty); } private async Task> GetOfferedDelegations(int offeredByPartyId, ResourceType resourceType) diff --git a/src/Altinn.AccessManagement.Core/Services/PolicyInformationPoint.cs b/src/Altinn.AccessManagement.Core/Services/PolicyInformationPoint.cs index 5bdcfb228..5956f9f33 100644 --- a/src/Altinn.AccessManagement.Core/Services/PolicyInformationPoint.cs +++ b/src/Altinn.AccessManagement.Core/Services/PolicyInformationPoint.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Threading; using Altinn.AccessManagement.Core.Clients.Interfaces; using Altinn.AccessManagement.Core.Constants; using Altinn.AccessManagement.Core.Enums; @@ -71,10 +72,15 @@ public async Task> GetRights(RightsQuery rightsQuery, bool returnAll Dictionary result = new Dictionary(); XacmlPolicy policy = null; + if (rightsQuery.Type != RightsQueryType.User) + { + return result.Values.ToList(); + } + // TODO: Caching?? // Verify resource - if (!DelegationHelper.TryGetResourceFromAttributeMatch(rightsQuery.Resource, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out string serviceCode, out string serviceEditionCode) + if (!DelegationHelper.TryGetResourceFromAttributeMatch(rightsQuery.Resource.AuthorizationReference, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out string serviceCode, out string serviceEditionCode) || resourceMatchType == ResourceAttributeMatchType.None) { throw new ValidationException($"RightsQuery must specify a valid Resource. Valid resource can either be a single resource from the Altinn resource registry ({AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistryAttribute}) or an Altinn app (identified by both {AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute} and {AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute})"); @@ -149,13 +155,29 @@ public async Task> GetRights(RightsQuery rightsQuery, bool returnAll } /// - public async Task> GetDelegableRightsByApp(RightQueryForApp resourceQuery, CancellationToken cancellationToken = default) + public async Task> GetDelegableRightsByApp(RightsQuery rightsQuery, CancellationToken cancellationToken = default) + { + Dictionary result = new Dictionary(); + if (rightsQuery.Type != RightsQueryType.AltinnApp) + { + return result.Values.ToList(); + } + + XacmlPolicy policy = await GetPolicy(rightsQuery.Resource.AuthorizationReference, cancellationToken); + + int minimumAuthenticationLevel = PolicyHelper.GetMinimumAuthenticationLevelFromXacmlPolicy(policy); + RightSourceType policyType = rightsQuery.Resource.ResourceType == ResourceType.AltinnApp ? RightSourceType.AppPolicy : RightSourceType.ResourceRegistryPolicy; + EnrichRightsDictionaryWithRightsFromPolicy(result, policy, policyType, rightsQuery.To, minimumAuthenticationLevel: minimumAuthenticationLevel, returnAllPolicyRights: false, getDelegableRights: true); + + return result.Values.Where(r => r.CanDelegate.HasValue && r.CanDelegate.Value).ToList(); + } + + private async Task GetPolicy(List resource, CancellationToken cancellationToken) { - List result; XacmlPolicy policy = null; // Verify resource - if (!DelegationHelper.TryGetResourceFromAttributeMatch(resourceQuery.Resource, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out string serviceCode, out string serviceEditionCode) + if (!DelegationHelper.TryGetResourceFromAttributeMatch(resource, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out string _, out string _) || resourceMatchType == ResourceAttributeMatchType.None) { throw new ValidationException($"RightsQuery must specify a valid Resource. Valid resource can either be a single resource from the Altinn resource registry ({AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistryAttribute}) or an Altinn app (identified by both {AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute} and {AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute})"); @@ -175,10 +197,7 @@ public async Task> GetDelegableRightsByApp(RightQueryForApp resource throw new ValidationException($"No valid policy found for the specified resource"); } - // Fetch all rights delegable - result = GetRightsFromPolicy(policy, resourceQuery.OwnerApp); - - return result; + return policy; } private static List GetRightsFromPolicy(XacmlPolicy policy, IEnumerable delegater) @@ -421,7 +440,7 @@ private static void AddAttributeMatchToRule(XacmlMatch xacmlMatch, Rule rule) } } - private static void EnrichRightsDictionaryWithRightsFromPolicy(Dictionary rights, XacmlPolicy policy, RightSourceType policySourceType, List userSubjects, int minimumAuthenticationLevel = 0, int delegationOfferedByPartyId = 0, bool returnAllPolicyRights = false, bool getDelegableRights = false) + private static void EnrichRightsDictionaryWithRightsFromPolicy(Dictionary rights, XacmlPolicy policy, RightSourceType policySourceType, List subjectMatches, int minimumAuthenticationLevel = 0, int delegationOfferedByPartyId = 0, bool returnAllPolicyRights = false, bool getDelegableRights = false) { PolicyDecisionPoint pdp = new PolicyDecisionPoint(); @@ -434,7 +453,7 @@ private static void EnrichRightsDictionaryWithRightsFromPolicy(Dictionary ruleRights = PolicyHelper.GetRightsFromXacmlRules(rule.SingleToList()); foreach (Right ruleRight in ruleRights) { - ICollection contextAttributes = PolicyHelper.GetContextAttributes(userSubjects, ruleRight.Resource, ruleRight.Action.SingleToList()); + ICollection contextAttributes = PolicyHelper.GetContextAttributes(subjectMatches, ruleRight.Resource, ruleRight.Action.SingleToList()); XacmlContextRequest authRequest = new XacmlContextRequest(false, false, contextAttributes); XacmlContextResponse response = pdp.Authorize(authRequest, singleRulePolicy); @@ -446,24 +465,25 @@ private static void EnrichRightsDictionaryWithRightsFromPolicy(Dictionary RightsDelegationCheck(int authenticat return await _altinn2RightsClient.PostDelegationCheck(authenticatedUserId, fromParty.PartyId, serviceCode, serviceEditionCode); } - rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resourceRegistryId, org, app); + rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resource); List allDelegableRights = await _pip.GetRights(rightsQuery, getDelegableRights: true, returnAllPolicyRights: true); if (allDelegableRights == null || allDelegableRights.Count == 0) @@ -113,8 +113,7 @@ public async Task DelegateRights(int authenticatedUserId } // Altinn 2 service delegation is handled by SBL Bridge - DelegationHelper.TryGetResourceFromAttributeMatch(resource.AuthorizationReference, out ResourceAttributeMatchType resourceMatchType, out string resourceRegistryId, out string org, out string app, out string _, out string _); - if (resourceMatchType == ResourceAttributeMatchType.Altinn2Service) + if (resource.ResourceType == ResourceType.Altinn2Service) { SblRightDelegationRequest sblRightDelegationRequest = new SblRightDelegationRequest { To = to.FirstOrDefault(), Rights = delegation.Rights }; DelegationActionResult sblResult = await _altinn2RightsClient.PostDelegation(authenticatedUserId, fromParty.PartyId, sblRightDelegationRequest); @@ -124,11 +123,11 @@ public async Task DelegateRights(int authenticatedUserId } // Verify authenticated users delegable rights - RightsQuery rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resourceRegistryId, org, app); + RightsQuery rightsQuery = RightsHelper.GetRightsQuery(authenticatedUserId, fromParty.PartyId, resource); List usersDelegableRights = await _pip.GetRights(rightsQuery, getDelegableRights: true, cancellationToken: cancellationToken); if (usersDelegableRights == null || usersDelegableRights.Count == 0) { - result.Errors.Add("right[0].Resource", $"Authenticated user does not have any delegable rights for the resource: {resourceRegistryId}"); + result.Errors.Add("right[0].Resource", $"Authenticated user does not have any delegable rights for the resource: {resource.Identifier}"); return result; } diff --git a/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs b/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs index 46ba26491..1ea7cd807 100644 --- a/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs +++ b/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs @@ -1,7 +1,10 @@ -using System.IdentityModel.Tokens.Jwt; +#nullable enable + +using System.IdentityModel.Tokens.Jwt; using System.Net.Mime; using Altinn.AccessManagement.Core.Constants; using Altinn.AccessManagement.Core.Models; +using Altinn.AccessManagement.Core.Models.ResourceRegistry; using Altinn.AccessManagement.Core.Services.Interfaces; using Altinn.AccessManagement.Models; using Altinn.Authorization.ProblemDetails; @@ -34,6 +37,37 @@ public AppsInstanceDelegationController( _appInstanceDelegationService = appInstanceDelegationService; } + /// + /// Finds all rights the authenticated app can delegate for a given app instance + /// + /// The resource id + /// The instance id + /// platform token needed to define fetch wich app is calling this method + /// Result + [HttpGet] + [Route("v1/apps/instancedelegation/{resourceId}/{instanceId}/delegationcheck")] + [Authorize(Policy = AuthzConstants.PLATFORM_ACCESS_AUTHORIZATION)] + [Consumes(MediaTypeNames.Application.Json)] + [Produces(MediaTypeNames.Application.Json)] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] + public async Task DelegationCheck([FromRoute] string resourceId, [FromRoute] string instanceId, [FromHeader(Name = "PlatformAccessToken")] string token) + { + AppsInstanceDelegationRequest request = new() { ResourceId = resourceId, InstanceId = instanceId, PerformedBy = GetOrgAppFromToken(token) }; + + Result serviceResult = await _appInstanceDelegationService.DelegationCheck(request); + + if (serviceResult.IsProblem) + { + return serviceResult.Problem.ToActionResult(); + } + + return Ok(_mapper.Map>(serviceResult.Value.ResourceRightDelegationCheckResults)); + } + /// /// Delegates access to an app instance /// @@ -55,18 +89,15 @@ public AppsInstanceDelegationController( public async Task Delegation([FromBody] AppsInstanceDelegationRequestDto appInstanceDelegationRequestDto, [FromRoute] string resourceId, [FromRoute] string instanceId, [FromHeader(Name = "PlatformAccessToken")] string token) { AppsInstanceDelegationRequest request = _mapper.Map(appInstanceDelegationRequestDto); - request.ResourceId = resourceId; request.InstanceId = instanceId; - - List performedBy = GetOrgAppFromToken(token); - request.PerformedBy = performedBy; + request.PerformedBy = GetOrgAppFromToken(token); Result serviceResult = await _appInstanceDelegationService.Delegate(request); if (serviceResult.IsProblem) { - return serviceResult.Problem?.ToActionResult(); + return serviceResult.Problem.ToActionResult(); } // Check result @@ -78,8 +109,8 @@ public async Task Delegation([FromBody] AppsInstanceDelegationRequ return Ok(_mapper.Map(serviceResult.Value)); } - return StatusCode(StatusCodes.Status206PartialContent, _mapper.Map(serviceResult.Value)); - } + return StatusCode(StatusCodes.Status206PartialContent, _mapper.Map(serviceResult.Value)); + } /* /// @@ -141,22 +172,19 @@ public async Task Revoke([FromBody] AppsInstanceDelegationRequestD /// /// delegating app from the platform token /// - private static List GetOrgAppFromToken(string token) + private static ResourceIdUrn.ResourceId? GetOrgAppFromToken(string token) { - List performedBy = new List(); - if (!string.IsNullOrEmpty(token)) { var handler = new JwtSecurityTokenHandler(); var jwtSecurityToken = handler.ReadJwtToken(token); var appidentifier = jwtSecurityToken.Claims.FirstOrDefault(c => c.Type == AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute); - performedBy.Add(new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute, Value = jwtSecurityToken.Issuer }); if (appidentifier != null) { - performedBy.Add(new AttributeMatch { Id = appidentifier.Type, Value = appidentifier.Value }); + return ResourceIdUrn.ResourceId.Create(ResourceIdentifier.CreateUnchecked($"app_{jwtSecurityToken.Issuer}_{appidentifier.Value}")); } } - return performedBy; + return null; } } diff --git a/src/Altinn.AccessManagement/Controllers/RightsInternalController.cs b/src/Altinn.AccessManagement/Controllers/RightsInternalController.cs index a94d19684..e8e8fbfc1 100644 --- a/src/Altinn.AccessManagement/Controllers/RightsInternalController.cs +++ b/src/Altinn.AccessManagement/Controllers/RightsInternalController.cs @@ -64,7 +64,7 @@ public async Task>> RightsQuery([FromBody] Righ { try { - RightsQuery rightsQueryInternal = _mapper.Map(rightsQuery); + RightsQuery rightsQueryInternal = rightsQuery.ToRightsQueryInternal(_mapper); List rightsInternal = await _pip.GetRights(rightsQueryInternal, returnAllPolicyRights, cancellationToken: cancellationToken); return _mapper.Map>(rightsInternal); } @@ -98,7 +98,8 @@ public async Task>> DelegableRightsQuery([FromB { try { - RightsQuery rightsQueryInternal = _mapper.Map(rightsQuery); + RightsQuery rightsQueryInternal = rightsQuery.ToRightsQueryInternal(_mapper); + List rightsInternal = await _pip.GetRights(rightsQueryInternal, returnAllPolicyRights, getDelegableRights: true, cancellationToken); return _mapper.Map>(rightsInternal); } diff --git a/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs b/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs index 5c4f08b24..6667102bc 100644 --- a/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs +++ b/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs @@ -1,14 +1,11 @@ -using Altinn.AccessManagement.Core.Enums; +using Altinn.AccessManagement.Core.Constants; +using Altinn.AccessManagement.Core.Enums; using Altinn.AccessManagement.Core.Models; -using Altinn.AccessManagement.Core.Models.Register; using Altinn.AccessManagement.Core.Models.ResourceRegistry; using Altinn.AccessManagement.Enums; using Altinn.AccessManagement.Models; using Altinn.Authorization.ABAC.Constants; -using Altinn.Platform.Register.Enums; using Altinn.Platform.Register.Models; -using Altinn.Urn; -using Altinn.Urn.Json; namespace Altinn.AccessManagement.Mappers { @@ -49,7 +46,6 @@ public AccessManagementMapper() CreateMap(); // Rights - CreateMap(); CreateMap(); CreateMap(); CreateMap(); @@ -90,6 +86,7 @@ public AccessManagementMapper() CreateMap(); CreateMap(); CreateMap(); + CreateMap(); } } } diff --git a/src/Altinn.AccessManagement/Models/ResourceDelegationCheckRequestDto.cs b/src/Altinn.AccessManagement/Models/ResourceDelegationCheckRequestDto.cs new file mode 100644 index 000000000..bfcf6360e --- /dev/null +++ b/src/Altinn.AccessManagement/Models/ResourceDelegationCheckRequestDto.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Altinn.AccessManagement.Core.Models.ResourceRegistry; +using Altinn.Urn.Json; + +namespace Altinn.AccessManagement.Models; + +/// +/// Request model for a list of all delegable rights for a specific resource. +/// +public class ResourceDelegationCheckRequestDto +{ + /// + /// Gets or sets the urn for identifying the resource of the rights to be checked + /// + [Required] + public UrnJsonTypeValue ResourceId { get; set; } +} diff --git a/src/Altinn.AccessManagement/Models/ResourceDelegationCheckResponseDto.cs b/src/Altinn.AccessManagement/Models/ResourceDelegationCheckResponseDto.cs new file mode 100644 index 000000000..d425510d3 --- /dev/null +++ b/src/Altinn.AccessManagement/Models/ResourceDelegationCheckResponseDto.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using Altinn.AccessManagement.Core.Models.Register; +using Altinn.Urn.Json; + +namespace Altinn.AccessManagement.Models; + +/// +/// Response model for the result of a delegation status check, for which rights a user is able to delegate between two parties. +/// +public class ResourceDelegationCheckResponseDto +{ + /// + /// Gets or sets the urn identifying the party the rights can be delegated from + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public required UrnJsonTypeValue From { get; set; } + + /// + /// Gets or sets a list of right delegation status models + /// + public IEnumerable ResourceRightDelegationCheckResults { get; set; } +} diff --git a/src/Altinn.AccessManagement/Models/ResourceRightDelegationCheckResultDto.cs b/src/Altinn.AccessManagement/Models/ResourceRightDelegationCheckResultDto.cs new file mode 100644 index 000000000..6fcf0f767 --- /dev/null +++ b/src/Altinn.AccessManagement/Models/ResourceRightDelegationCheckResultDto.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Altinn.AccessManagement.Core.Models.Rights; +using Altinn.AccessManagement.Enums; +using Altinn.Urn.Json; + +namespace Altinn.AccessManagement.Models; + +/// +/// Response model describing the delegation status for a given single right, whether the authenticated user is able to delegate the right or not on behalf of the from part. +/// +public class ResourceRightDelegationCheckResultDto +{ + /// + /// Gets or sets the right key + /// + [Required] + public string RightKey { get; set; } + + /// + /// Gets or sets the list of resource matches which uniquely identifies the resource this right applies to. + /// + [Required] + public IEnumerable Resource { get; set; } + + /// + /// Gets or sets the set of Attribute Id and Attribute Value for a specific action, to identify the action this right applies to + /// + [Required] + public UrnJsonTypeValue Action { get; set; } + + /// + /// Gets or sets a value indicating whether the right is delegable or not + /// + [Required] + [JsonConverter(typeof(JsonStringEnumConverter))] + public DelegableStatusExternal Status { get; set; } + + /// + /// Gets or sets a list of details describing why or why not the right is valid in the current user and reportee party context + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IEnumerable Details { get; set; } +} diff --git a/src/Altinn.AccessManagement/Models/Rights/RightsQueryExternal.cs b/src/Altinn.AccessManagement/Models/Rights/RightsQueryExternal.cs index c2988a940..ac801c451 100644 --- a/src/Altinn.AccessManagement/Models/Rights/RightsQueryExternal.cs +++ b/src/Altinn.AccessManagement/Models/Rights/RightsQueryExternal.cs @@ -1,4 +1,9 @@ using System.ComponentModel.DataAnnotations; +using Altinn.AccessManagement.Core.Constants; +using Altinn.AccessManagement.Core.Enums; +using Altinn.AccessManagement.Core.Models; +using Altinn.AccessManagement.Core.Models.ResourceRegistry; +using AutoMapper; namespace Altinn.AccessManagement.Models { @@ -26,5 +31,31 @@ public class RightsQueryExternal /// [Required] public List Resource { get; set; } + + /// + /// Method to convert the external rights query to internal rights query + /// + /// mapper to use for subtypes + /// RightsQuery + public RightsQuery ToRightsQueryInternal(IMapper mapper) + { + return new RightsQuery + { + Type = RightsQueryType.User, + From = mapper.Map>(From), + To = mapper.Map>(To), + Resource = new ServiceResource + { + Identifier = GetResourceIdentifier(Resource), + AuthorizationReference = mapper.Map>(Resource) + } + }; + } + + private static string GetResourceIdentifier(List resource) + { + return resource.FirstOrDefault(r => r.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistryAttribute)?.Value ?? + $"app_{resource.FirstOrDefault(r => r.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute)?.Value}_{resource.FirstOrDefault(r => r.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute)?.Value}"; + } } } diff --git a/src/Altinn.AccessManagement/Utilities/HashUtil.cs b/src/Altinn.AccessManagement/Utilities/HashUtil.cs new file mode 100644 index 000000000..a9d2be380 --- /dev/null +++ b/src/Altinn.AccessManagement/Utilities/HashUtil.cs @@ -0,0 +1,26 @@ +using Altinn.AccessManagement.Core.Constants; +using Altinn.AccessManagement.Core.Models; + +namespace Altinn.AccessManagement.Utilities; + +/// +/// Hash utility class +/// +public static class HashUtil +{ + /// + /// Get a hash code for a list of objects that is order independent + /// + /// The list of objects + /// Hash code for the list + public static int GetOrderIndependentHashCode(IEnumerable source) + { + int hash = 0; + foreach (T element in source) + { + hash = unchecked(hash + EqualityComparer.Default.GetHashCode(element)); + } + + return hash; + } +} diff --git a/test/Altinn.AccessManagement.Tests/Controllers/ResourceOwnerAPI/AppsInstanceDelegationControllerTest.cs b/test/Altinn.AccessManagement.Tests/Controllers/ResourceOwnerAPI/AppsInstanceDelegationControllerTest.cs index d60bc8a7d..bc09aec03 100644 --- a/test/Altinn.AccessManagement.Tests/Controllers/ResourceOwnerAPI/AppsInstanceDelegationControllerTest.cs +++ b/test/Altinn.AccessManagement.Tests/Controllers/ResourceOwnerAPI/AppsInstanceDelegationControllerTest.cs @@ -10,6 +10,7 @@ using Altinn.AccessManagement.Models; using Altinn.AccessManagement.Tests.Data; using Altinn.AccessManagement.Tests.Mocks; +using Altinn.AccessManagement.Tests.Utils; using Altinn.Authorization.ProblemDetails; using Altinn.Common.AccessToken.Services; using Altinn.Common.PEP.Interfaces; @@ -35,6 +36,28 @@ public AppsInstanceDelegationControllerTest(CustomWebApplicationFactory + /// Test case: GET apps/instancedelegation/{resourceId}/{instanceId}/delegationcheck + /// with a valid PlatformAccessToken for an app having xacml rules specifying rights available for delegation + /// Expected: - Should return 200 OK + /// Reason: See testdata cases for details + /// + [Theory] + [MemberData(nameof(TestDataAppsInstanceDelegation.DelegationCheck_Ok), MemberType = typeof(TestDataAppsInstanceDelegation))] + public async Task DelegationCheck_ValidToken_OK(string platformToken, string resourceId, string instanceId, List expected) + { + var client = GetTestClient(platformToken); + + // Act + HttpResponseMessage response = await client.GetAsync($"accessmanagement/api/v1/apps/instancedelegation/{resourceId}/{instanceId}/delegationcheck"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + List actual = JsonSerializer.Deserialize>(await response.Content.ReadAsStringAsync(), options); + AssertionUtil.AssertCollections(actual, expected, AssertionUtil.AssertResourceRightDelegationCheckResultDto); + } + /// /// Test case: POST apps/instancedelegation/{resourceId}/{instanceId} /// with a valid delegation diff --git a/test/Altinn.AccessManagement.Tests/Data/AuthorizedParties/TestDataAppsInstanceDelegation.cs b/test/Altinn.AccessManagement.Tests/Data/AuthorizedParties/TestDataAppsInstanceDelegation.cs index 52b6af108..4c9d9f7db 100644 --- a/test/Altinn.AccessManagement.Tests/Data/AuthorizedParties/TestDataAppsInstanceDelegation.cs +++ b/test/Altinn.AccessManagement.Tests/Data/AuthorizedParties/TestDataAppsInstanceDelegation.cs @@ -30,6 +30,26 @@ public static class TestDataAppsInstanceDelegation private static readonly string InstanceIdNormalNewPolicyOrgNumber = "00000000-0000-0000-0000-000000000007"; + /// + /// Test case: GET v1/apps/instancedelegation/{resourceId}/{instanceId}/delegationcheck + /// with: + /// - a valid app authenticated as the delegater, authenticated with PlatformAccessToken + /// - valid resource for instance delegation + /// - xacml policy file for resource to be delegated configured with rights available for delegation by the authenticated app + /// Expected: - Should return 200 OK + /// - Should include the delegated rights + /// Reason: Apps defined in the policy file should be able to delegate the defined rights + /// + public static TheoryData> DelegationCheck_Ok() => new() + { + { + PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), + AppId, + InstanceIdParallelNewPolicy, + GetExpectedResponse>("DelegationCheck", AppId, InstanceIdParallelNewPolicy) + } + }; + /// /// Test case: POST v1/apps/instancedelegation/{resourceId}/{instanceId} /// with: @@ -45,10 +65,10 @@ public static class TestDataAppsInstanceDelegation { { PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), - GetRequest(AppId, InstanceIdParallelNewPolicy), + GetRequest("Delegation", AppId, InstanceIdParallelNewPolicy), AppId, InstanceIdParallelNewPolicy, - GetExpectedResponse(AppId, InstanceIdParallelNewPolicy) + GetExpectedResponse("Delegation", AppId, InstanceIdParallelNewPolicy) } }; @@ -67,10 +87,10 @@ public static class TestDataAppsInstanceDelegation { { PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), - GetRequest(AppId, InstanceIdParallelExistingPolicy), + GetRequest("Delegation", AppId, InstanceIdParallelExistingPolicy), AppId, InstanceIdParallelExistingPolicy, - GetExpectedResponse(AppId, InstanceIdParallelExistingPolicy) + GetExpectedResponse("Delegation", AppId, InstanceIdParallelExistingPolicy) } }; @@ -89,10 +109,10 @@ public static class TestDataAppsInstanceDelegation { { PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), - GetRequest(AppId, InstanceIdNewPolicyNoResponceOnWrite), + GetRequest("Delegation", AppId, InstanceIdNewPolicyNoResponceOnWrite), AppId, InstanceIdNewPolicyNoResponceOnWrite, - GetExpectedResponse(AppId, InstanceIdNewPolicyNoResponceOnWrite) + GetExpectedResponse("Delegation", AppId, InstanceIdNewPolicyNoResponceOnWrite) } }; @@ -100,10 +120,10 @@ public static class TestDataAppsInstanceDelegation { { PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), - GetRequest(AppId, InstanceIdInvalidParty), + GetRequest("Delegation", AppId, InstanceIdInvalidParty), AppId, InstanceIdInvalidParty, - GetExpectedResponse(AppId, InstanceIdInvalidParty) + GetExpectedResponse("Delegation", AppId, InstanceIdInvalidParty) } }; @@ -122,10 +142,10 @@ public static class TestDataAppsInstanceDelegation { { PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), - GetRequest(AppId, InstanceIdNormalNewPolicy), + GetRequest("Delegation", AppId, InstanceIdNormalNewPolicy), AppId, InstanceIdNormalNewPolicy, - GetExpectedResponse(AppId, InstanceIdNormalNewPolicy) + GetExpectedResponse("Delegation", AppId, InstanceIdNormalNewPolicy) } }; @@ -144,10 +164,10 @@ public static class TestDataAppsInstanceDelegation { { PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), - GetRequest(AppId, InstanceIdNormalNewPolicyOrgNumber), + GetRequest("Delegation", AppId, InstanceIdNormalNewPolicyOrgNumber), AppId, InstanceIdNormalNewPolicyOrgNumber, - GetExpectedResponse(AppId, InstanceIdNormalNewPolicyOrgNumber) + GetExpectedResponse("Delegation", AppId, InstanceIdNormalNewPolicyOrgNumber) } }; @@ -166,10 +186,10 @@ public static class TestDataAppsInstanceDelegation { { PrincipalUtil.GetAccessToken("ttd", "am-devtest-instancedelegation"), - GetRequest(AppId, InstanceIdNormalExistingPolicy), + GetRequest("Delegation", AppId, InstanceIdNormalExistingPolicy), AppId, InstanceIdNormalExistingPolicy, - GetExpectedResponse(AppId, InstanceIdNormalExistingPolicy) + GetExpectedResponse("Delegation", AppId, InstanceIdNormalExistingPolicy) } }; @@ -300,15 +320,15 @@ public static void AssertResourceEqual(UrnJsonTypeValue expected, UrnJsonTypeVal Assert.Equal(expected.Value, actual.Value); } - private static T GetExpectedResponse(string appId, string instanceId) + private static T GetExpectedResponse(string operation, string appId, string instanceId) { - string content = File.ReadAllText($"Data/Json/AppsInstanceDelegation/{appId}/{instanceId}/response.json"); + string content = File.ReadAllText($"Data/Json/AppsInstanceDelegation/{operation}/{appId}/{instanceId}/response.json"); return (T)JsonSerializer.Deserialize(content, typeof(T), JsonOptions); } - private static T GetRequest(string appId, string instanceId) + private static T GetRequest(string operation, string appId, string instanceId) { - string content = File.ReadAllText($"Data/Json/AppsInstanceDelegation/{appId}/{instanceId}/request.json"); + string content = File.ReadAllText($"Data/Json/AppsInstanceDelegation/{operation}/{appId}/{instanceId}/request.json"); return (T)JsonSerializer.Deserialize(content, typeof(T), JsonOptions); } } \ No newline at end of file diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/request.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/request.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/request.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/request.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/response.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/response.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000001/response.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/request.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/request.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/request.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/request.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/response.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/response.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000002/response.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/request.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/request.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/request.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/request.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/response.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/response.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000003/response.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/request.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/request.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/request.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/request.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/response.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/response.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000005/response.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/request.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/request.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/request.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/request.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/response.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/response.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000006/response.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/request.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/request.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/request.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/request.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/response.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/response.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/00000000-0000-0000-0000-000000000007/response.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/request.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/request.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/request.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/request.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json similarity index 100% rename from test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json rename to test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/Delegation/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json diff --git a/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/DelegationCheck/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/DelegationCheck/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json new file mode 100644 index 000000000..b172d1828 --- /dev/null +++ b/test/Altinn.AccessManagement.Tests/Data/Json/AppsInstanceDelegation/DelegationCheck/app_ttd_am-devtest-instancedelegation/0191579e-72bc-7977-af5d-f9e92af4393b/response.json @@ -0,0 +1,46 @@ +[ + { + "rightKey": "am-devtest-instancedelegation,ttd,task_1:read", + "resource": [ + { + "type": "urn:altinn:org", + "value": "ttd" + }, + { + "type": "urn:altinn:app", + "value": "am-devtest-instancedelegation" + }, + { + "type": "urn:altinn:task", + "value": "task_1" + } + ], + "action": { + "type": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "value": "read" + }, + "status": "Delegable" + }, + { + "rightKey": "am-devtest-instancedelegation,ttd,task_1:sign", + "resource": [ + { + "type": "urn:altinn:org", + "value": "ttd" + }, + { + "type": "urn:altinn:app", + "value": "am-devtest-instancedelegation" + }, + { + "type": "urn:altinn:task", + "value": "task_1" + } + ], + "action": { + "type": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "value": "sign" + }, + "status": "Delegable" + } +] \ No newline at end of file diff --git a/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/digdirs_company_car/resource.json b/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/digdirs_company_car/resource.json index 17485f02f..fe541433b 100644 --- a/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/digdirs_company_car/resource.json +++ b/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/digdirs_company_car/resource.json @@ -7,13 +7,13 @@ }, "description": { "en": "Only the manager (DAGL) has access to drive the company car", - "nb-no": "Bare daglig leder (DAGL) har tilgang til å kjøre firmabilen", - "nn-no": "Berre dagleg leiar (DAGL) har høve til å kjøyre firmabilen" + "nb-no": "Bare daglig leder (DAGL) har tilgang til � kj�re firmabilen", + "nn-no": "Berre dagleg leiar (DAGL) har h�ve til � kj�yre firmabilen" }, "rightDescription": { "en": "Gives an employee permission to borrow the company car", - "nb-no": "Gir en ansatt lov til å låne firmabilen", - "nn-no": "Gjev ein tilsett løyve til å ta ei rånerunde ned i vik" + "nb-no": "Gir en ansatt lov til � l�ne firmabilen", + "nn-no": "Gjev ein tilsett l�yve til � ta ei r�nerunde ned i vik" }, "homepage": "https://www.jksfirmabiler.no/", "status": "Active", @@ -43,5 +43,11 @@ "sector": [ "offentlig" ], - "resourceType": "MaskinportenSchema" + "resourceType": "MaskinportenSchema", + "authorizationReference": [ + { + "id": "urn:altinn:resource", + "value": "digdirs_company_car" + } + ] } \ No newline at end of file diff --git a/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/jks_audi_etron_gt/resource.json b/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/jks_audi_etron_gt/resource.json index d5b9ff921..e512c27b6 100644 --- a/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/jks_audi_etron_gt/resource.json +++ b/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/jks_audi_etron_gt/resource.json @@ -12,8 +12,8 @@ }, "rightDescription": { "en": "Permission to borrow the car", - "nb-no": "Gir mottaker lov til å låne bilen", - "nn-no": "Løyve til å ta ei rånerunde ned i vik" + "nb-no": "Gir mottaker lov til � l�ne bilen", + "nn-no": "L�yve til � ta ei r�nerunde ned i vik" }, "homepage": "https://www.jksaudi.no/", "status": "Active", @@ -43,5 +43,11 @@ "sector": [ "privat" ], - "resourceType": "MaskinportenSchema" + "resourceType": "MaskinportenSchema", + "authorizationReference": [ + { + "id": "urn:altinn:resource", + "value": "jks_audi_etron_gt" + } + ] } \ No newline at end of file diff --git a/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/non_delegable_maskinportenschema/resource.json b/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/non_delegable_maskinportenschema/resource.json index 7fe130e62..2a645158d 100644 --- a/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/non_delegable_maskinportenschema/resource.json +++ b/test/Altinn.AccessManagement.Tests/Data/ResourceRegistryResources/non_delegable_maskinportenschema/resource.json @@ -40,5 +40,11 @@ "nb-no": "DIGITALISERINGSDIREKTORATET", "nn-no": "DIGITALISERINGSDIREKTORATET" } - } + }, + "authorizationReference": [ + { + "id": "urn:altinn:resource", + "value": "non_delegable_maskinportenschema" + } + ] } \ No newline at end of file diff --git a/test/Altinn.AccessManagement.Tests/Data/Xacml/3.0/AltinnApps/ttd/am-devtest-instancedelegation/policy.xml b/test/Altinn.AccessManagement.Tests/Data/Xacml/3.0/AltinnApps/ttd/am-devtest-instancedelegation/policy.xml index 05aa870bd..7a775f8ce 100644 --- a/test/Altinn.AccessManagement.Tests/Data/Xacml/3.0/AltinnApps/ttd/am-devtest-instancedelegation/policy.xml +++ b/test/Altinn.AccessManagement.Tests/Data/Xacml/3.0/AltinnApps/ttd/am-devtest-instancedelegation/policy.xml @@ -70,18 +70,14 @@ - + A rule defining all instance delegation rights the App itself is allowed to perform for instances of the app ttd/am-devtest-instancedelegation. In this example the app can delegate the Read and Sign actions for Task_1 - - ttd - - - am-devtest-instancedelegation - + app_ttd_am-devtest-instancedelegation + diff --git a/test/Altinn.AccessManagement.Tests/Utils/AssertionUtil.cs b/test/Altinn.AccessManagement.Tests/Utils/AssertionUtil.cs index 206003168..3a077cf4f 100644 --- a/test/Altinn.AccessManagement.Tests/Utils/AssertionUtil.cs +++ b/test/Altinn.AccessManagement.Tests/Utils/AssertionUtil.cs @@ -6,6 +6,8 @@ using Altinn.AccessManagement.Models; using Altinn.Authorization.ABAC.Xacml; using Altinn.Authorization.ABAC.Xacml.JsonProfile; +using Altinn.Urn; +using Altinn.Urn.Json; using Microsoft.AspNetCore.Mvc; using Xunit; @@ -549,6 +551,27 @@ public static void AssertAuthorizedPartyEqual(AuthorizedParty expected, Authoriz AssertCollections(expected.Subunits, actual.Subunits, AssertAuthorizedPartyEqual); } + /// + /// Assert that two have the same property in the same positions. + /// + /// An instance with the expected values. + /// The instance to verify. + public static void AssertResourceRightDelegationCheckResultDto(ResourceRightDelegationCheckResultDto expected, ResourceRightDelegationCheckResultDto actual) + { + Assert.NotNull(actual); + Assert.NotNull(expected); + + Assert.Equal(expected.RightKey, actual.RightKey); + Assert.Equal(expected.Status, actual.Status); + Assert.Equal(expected.Action.Value, actual.Action.Value); + AssertCollections(expected.Resource.ToList(), actual.Resource.ToList(), AssertUrnJsonTypeValue); + } + + private static void AssertUrnJsonTypeValue(UrnJsonTypeValue expected, UrnJsonTypeValue actual) + { + Assert.Equal(expected.Value, actual.Value); + } + private static void AssertPolicySubjects(List expected, List actual) { AssertCollections(expected, actual, AssertPolicyAttributeMatchExternalEqual); diff --git a/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithDelegableRights_Ok.bru b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithDelegableRights_Ok.bru new file mode 100644 index 000000000..dacc4305f --- /dev/null +++ b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithDelegableRights_Ok.bru @@ -0,0 +1,54 @@ +meta { + name: AppWithDelegableRights_Ok + type: http + seq: 1 +} + +get { + url: {{baseUrl}}/accessmanagement/api/v1/apps/instancedelegation/:resourceId/:instanceId/delegationcheck + body: json + auth: none +} + +params:path { + resourceId: app_{{org}}_{{app}} + instanceId: {{instanceId}} +} + +headers { + Accept: application/json + PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} +} + +script:pre-request { + const testdata = require(`./Testdata/maskinportenschema/${bru.getEnvVar("tokenEnv")}testdata.json`); + const sharedtestdata = require(`./Testdata/sharedtestdata.json`); + + const org = sharedtestdata.serviceOwners.ttd.org; + bru.setVar("org", org); + const app = "authz-bruno-instancedelegation"; + bru.setVar("app", app); + + bru.setVar("instanceId", "b39a2326-9fff-4414-a209-61e6f9835564"); + + var getTokenParameters = { + auth_org: org, + auth_app: app, + auth_tokenType: sharedtestdata.authTokenType.platformAccess + } + + const token = await testTokenGenerator.getToken(getTokenParameters); + bru.setVar("platformAccessToken", token); +} + +tests { + test("AppsInstanceDelegation DelegationCheck GET AppWithDelegableRights_Ok", function() { + var body = res.getBody(); + expect(res.status).to.equal(200); + expect(body[0]).to.have.property('rightKey', 'authz-bruno-instancedelegation,ttd,task_1:read') + expect(body[0]).to.have.property('status', 'Delegable') + expect(body[1]).to.have.property('rightKey', 'authz-bruno-instancedelegation,ttd,task_1:sign') + expect(body[1]).to.have.property('status', 'Delegable') + }); +} diff --git a/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithoutDelegableRights_Forbidden.bru b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithoutDelegableRights_Forbidden.bru new file mode 100644 index 000000000..810f8efd9 --- /dev/null +++ b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/AppWithoutDelegableRights_Forbidden.bru @@ -0,0 +1,50 @@ +meta { + name: AppWithoutDelegableRights_Forbidden + type: http + seq: 2 +} + +get { + url: {{baseUrl}}/accessmanagement/api/v1/apps/instancedelegation/:resourceId/:instanceId/delegationcheck + body: json + auth: none +} + +params:path { + resourceId: app_ttd_authz-bruno-instancedelegation + instanceId: {{instanceId}} +} + +headers { + Accept: application/json + PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} +} + +script:pre-request { + const testdata = require(`./Testdata/maskinportenschema/${bru.getEnvVar("tokenEnv")}testdata.json`); + const sharedtestdata = require(`./Testdata/sharedtestdata.json`); + + const org = sharedtestdata.serviceOwners.ttd.org; + bru.setVar("org", org); + const app = "apps-test"; + bru.setVar("app", app); + + bru.setVar("instanceId", "b39a2326-9fff-4414-a209-61e6f9835564"); + + var getTokenParameters = { + auth_org: org, + auth_app: app, + auth_tokenType: sharedtestdata.authTokenType.platformAccess + } + + const token = await testTokenGenerator.getToken(getTokenParameters); + bru.setVar("platformAccessToken", token); +} + +tests { + test("AppsInstanceDelegation DelegationCheck GET AppWithoutDelegableRights_Forbidden", function() { + var body = res.getBody(); + expect(res.status).to.equal(403); + }); +} diff --git a/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/MissingToken_NotAuthorized.bru b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/MissingToken_NotAuthorized.bru new file mode 100644 index 000000000..ad7f47d26 --- /dev/null +++ b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/MissingToken_NotAuthorized.bru @@ -0,0 +1,37 @@ +meta { + name: MissingToken_NotAuthorized + type: http + seq: 4 +} + +get { + url: {{baseUrl}}/accessmanagement/api/v1/apps/instancedelegation/:resourceId/:instanceId/delegationcheck + body: json + auth: none +} + +params:path { + resourceId: app_ttd_authz-bruno-instancedelegation + instanceId: {{instanceId}} +} + +headers { + Accept: application/json + PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} +} + +script:pre-request { + const testdata = require(`./Testdata/maskinportenschema/${bru.getEnvVar("tokenEnv")}testdata.json`); + const sharedtestdata = require(`./Testdata/sharedtestdata.json`); + + bru.setVar("instanceId", "b39a2326-9fff-4414-a209-61e6f9835564"); + bru.setVar("platformAccessToken", ""); +} + +tests { + test("AppsInstanceDelegation DelegationCheck GET MissingToken_NotAuthorized", function() { + var body = res.getBody(); + expect(res.status).to.equal(401); + }); +} diff --git a/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/PlatformAccessToken_Forbidden.bru b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/PlatformAccessToken_Forbidden.bru new file mode 100644 index 000000000..db55fd319 --- /dev/null +++ b/test/Bruno/Altinn.AccessManagement/Automatic Test Collection/AppsInstanceDelegation/DelegationCheck/PlatformAccessToken_Forbidden.bru @@ -0,0 +1,50 @@ +meta { + name: PlatformAccessToken_Forbidden + type: http + seq: 3 +} + +get { + url: {{baseUrl}}/accessmanagement/api/v1/apps/instancedelegation/:resourceId/:instanceId/delegationcheck + body: json + auth: none +} + +params:path { + resourceId: app_ttd_authz-bruno-instancedelegation + instanceId: {{instanceId}} +} + +headers { + Accept: application/json + PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} +} + +script:pre-request { + const testdata = require(`./Testdata/maskinportenschema/${bru.getEnvVar("tokenEnv")}testdata.json`); + const sharedtestdata = require(`./Testdata/sharedtestdata.json`); + + const org = "platform"; + bru.setVar("org", org); + const app = "some-random-app"; + bru.setVar("app", app); + + bru.setVar("instanceId", "b39a2326-9fff-4414-a209-61e6f9835564"); + + var getTokenParameters = { + auth_org: org, + auth_app: app, + auth_tokenType: sharedtestdata.authTokenType.platformAccess + } + + const token = await testTokenGenerator.getToken(getTokenParameters); + bru.setVar("platformAccessToken", token); +} + +tests { + test("AppsInstanceDelegation DelegationCheck GET PlatformAccessToken_Forbidden", function() { + var body = res.getBody(); + expect(res.status).to.equal(403); + }); +} diff --git a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegateInstance.bru b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegateInstance.bru index f39d137d1..535ce7077 100644 --- a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegateInstance.bru +++ b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegateInstance.bru @@ -18,6 +18,7 @@ params:path { headers { Accept: application/json PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} } body:json { @@ -80,7 +81,7 @@ script:pre-request { } tests { - test("Post AppsInstanceDelegation Delegation", function() { + test("AppsInstanceDelegation Delegation POST {InsertRequestName}", function() { var body = res.getBody(); expect(res.status).to.equal(201); }); diff --git a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegationCheck.bru b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegationCheck.bru index 38bba1943..11f47969f 100644 --- a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegationCheck.bru +++ b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/DelegationCheck.bru @@ -18,6 +18,7 @@ params:path { headers { Accept: application/json PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} } script:pre-request { @@ -44,7 +45,7 @@ script:pre-request { } tests { - test("Post AppsInstanceDelegation DelegationCheck", function() { + test("AppsInstanceDelegation DelegationCheck GET {InsertRequestName}", function() { var body = res.getBody(); expect(res.status).to.equal(200); }); diff --git a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/GetInstanceDelegations.bru b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/GetInstanceDelegations.bru index b5ec607a9..a43b1518e 100644 --- a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/GetInstanceDelegations.bru +++ b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/GetInstanceDelegations.bru @@ -18,6 +18,7 @@ params:path { headers { Accept: application/json PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} } script:pre-request { @@ -44,7 +45,7 @@ script:pre-request { } tests { - test("Get AppsInstanceDelegations", function() { + test("AppsInstanceDelegation Delegation GET {InsertRequestName}", function() { var body = res.getBody(); expect(res.status).to.equal(200); }); diff --git a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/RevokeInstanceDelegations.bru b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/RevokeInstanceDelegations.bru index 3e7a1733e..3cfb0f0f1 100644 --- a/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/RevokeInstanceDelegations.bru +++ b/test/Bruno/Altinn.AccessManagement/Manual Test Collection/AppsInstanceDelegation/RevokeInstanceDelegations.bru @@ -18,6 +18,7 @@ params:path { headers { Accept: application/json PlatformAccessToken: {{platformAccessToken}} + Ocp-Apim-Subscription-Key: {{apimSubscriptionKey}} } script:pre-request { @@ -44,7 +45,7 @@ script:pre-request { } tests { - test("Post AppsInstanceDelegations Revoke", function() { + test("AppsInstanceDelegation Revoke POST {InsertRequestName}", function() { var body = res.getBody(); expect(res.status).to.equal(204); }); diff --git a/test/Bruno/Altinn.AccessManagement/environments/AT22.bru b/test/Bruno/Altinn.AccessManagement/environments/AT22.bru index a3e533e34..043bcfa43 100644 --- a/test/Bruno/Altinn.AccessManagement/environments/AT22.bru +++ b/test/Bruno/Altinn.AccessManagement/environments/AT22.bru @@ -3,4 +3,5 @@ vars { tokenEnv: at22 tokenBasicAuthUser: {{process.env.TOKEN_BASIC_AUTH_USER}} tokenBasicAuthPw: {{process.env.TOKEN_BASIC_AUTH_PW}} + apimSubscriptionKey: {{process.env.AT22_APIM_SUBSCRIPTION_KEY}} } diff --git a/test/Bruno/Altinn.AccessManagement/environments/AT23.bru b/test/Bruno/Altinn.AccessManagement/environments/AT23.bru index a042e6632..c8d10c49e 100644 --- a/test/Bruno/Altinn.AccessManagement/environments/AT23.bru +++ b/test/Bruno/Altinn.AccessManagement/environments/AT23.bru @@ -3,4 +3,5 @@ vars { tokenEnv: at23 tokenBasicAuthUser: {{process.env.TOKEN_BASIC_AUTH_USER}} tokenBasicAuthPw: {{process.env.TOKEN_BASIC_AUTH_PW}} + apimSubscriptionKey: {{process.env.AT23_APIM_SUBSCRIPTION_KEY}} } diff --git a/test/Bruno/Altinn.AccessManagement/environments/AT24.bru b/test/Bruno/Altinn.AccessManagement/environments/AT24.bru index 6b150f581..05dc4919e 100644 --- a/test/Bruno/Altinn.AccessManagement/environments/AT24.bru +++ b/test/Bruno/Altinn.AccessManagement/environments/AT24.bru @@ -3,4 +3,5 @@ vars { tokenEnv: at24 tokenBasicAuthUser: {{process.env.TOKEN_BASIC_AUTH_USER}} tokenBasicAuthPw: {{process.env.TOKEN_BASIC_AUTH_PW}} + apimSubscriptionKey: {{process.env.AT24_APIM_SUBSCRIPTION_KEY}} } diff --git a/test/Bruno/Altinn.AccessManagement/environments/PROD.bru b/test/Bruno/Altinn.AccessManagement/environments/PROD.bru index 984230dc7..6d620ca75 100644 --- a/test/Bruno/Altinn.AccessManagement/environments/PROD.bru +++ b/test/Bruno/Altinn.AccessManagement/environments/PROD.bru @@ -2,5 +2,6 @@ vars { baseUrl: https://platform.altinn.no } vars:secret [ - ProdToken + ProdToken, + apimSubscriptionKey ] diff --git a/test/Bruno/Altinn.AccessManagement/environments/TT02.bru b/test/Bruno/Altinn.AccessManagement/environments/TT02.bru index 7e410fc1a..412001cea 100644 --- a/test/Bruno/Altinn.AccessManagement/environments/TT02.bru +++ b/test/Bruno/Altinn.AccessManagement/environments/TT02.bru @@ -3,4 +3,5 @@ vars { tokenEnv: tt02 tokenBasicAuthUser: {{process.env.TOKEN_BASIC_AUTH_USER}} tokenBasicAuthPw: {{process.env.TOKEN_BASIC_AUTH_PW}} + apimSubscriptionKey: {{process.env.TT02_APIM_SUBSCRIPTION_KEY}} } diff --git a/test/Bruno/Altinn.AccessManagement/environments/YT01.bru b/test/Bruno/Altinn.AccessManagement/environments/YT01.bru index 6dc6fb845..9eeb2bb27 100644 --- a/test/Bruno/Altinn.AccessManagement/environments/YT01.bru +++ b/test/Bruno/Altinn.AccessManagement/environments/YT01.bru @@ -3,4 +3,5 @@ vars { tokenEnv: at22 tokenBasicAuthUser: {{process.env.TOKEN_BASIC_AUTH_USER}} tokenBasicAuthPw: {{process.env.TOKEN_BASIC_AUTH_PW}} + apimSubscriptionKey: {{process.env.YT01_APIM_SUBSCRIPTION_KEY}} }