diff --git a/.github/containerscan/allowedlist.yaml b/.github/containerscan/allowedlist.yaml index 67ca24dc..c0ccb9e7 100644 --- a/.github/containerscan/allowedlist.yaml +++ b/.github/containerscan/allowedlist.yaml @@ -1,3 +1,4 @@ general: vulnerabilities: - - CVE-2021-24112 \ No newline at end of file + - CVE-2021-24112 + - CVE-2019-0820 \ No newline at end of file diff --git a/src/Authorization/Altinn.Platform.Authorization.csproj b/src/Authorization/Altinn.Platform.Authorization.csproj index 9cbd4ab7..3f1bdaba 100644 --- a/src/Authorization/Altinn.Platform.Authorization.csproj +++ b/src/Authorization/Altinn.Platform.Authorization.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -11,6 +11,7 @@ + diff --git a/src/Authorization/Clients/EventsQueueClient.cs b/src/Authorization/Clients/EventsQueueClient.cs index a4790b43..666e837d 100644 --- a/src/Authorization/Clients/EventsQueueClient.cs +++ b/src/Authorization/Clients/EventsQueueClient.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Text; +using System.Threading; using System.Threading.Tasks; using Altinn.Platform.Authorization.Clients.Interfaces; using Altinn.Platform.Authorization.Configuration; @@ -38,12 +39,17 @@ public EventsQueueClient( } /// - public async Task EnqueueAuthorizationEvent(string content) + public async Task EnqueueAuthorizationEvent(string content, CancellationToken cancellationToken = default) { try { QueueClient client = await GetAuthorizationEventQueueClient(); - await client.SendMessageAsync(Convert.ToBase64String(Encoding.UTF8.GetBytes(content))); + await client.SendMessageAsync(Convert.ToBase64String(Encoding.UTF8.GetBytes(content)), cancellationToken); + } + catch (OperationCanceledException ex) + { + _logger.LogError(ex, ex.Message); + throw; } catch (Exception ex) { diff --git a/src/Authorization/Clients/Interfaces/IEventsQueueClient.cs b/src/Authorization/Clients/Interfaces/IEventsQueueClient.cs index 0a5af6bd..3e1ab3ff 100644 --- a/src/Authorization/Clients/Interfaces/IEventsQueueClient.cs +++ b/src/Authorization/Clients/Interfaces/IEventsQueueClient.cs @@ -1,3 +1,4 @@ +using System.Threading; using System.Threading.Tasks; using Altinn.Platform.Authorization.Models; @@ -12,7 +13,8 @@ public interface IEventsQueueClient /// Enqueues the provided content to the Event Log queue /// /// The content to push to the queue in string format + /// The cancellation token /// Returns a queue receipt - public Task EnqueueAuthorizationEvent(string content); + public Task EnqueueAuthorizationEvent(string content, CancellationToken cancellationToken = default); } } diff --git a/src/Authorization/Constants/AuthzConstants.cs b/src/Authorization/Constants/AuthzConstants.cs index f5350cac..7df5fd71 100644 --- a/src/Authorization/Constants/AuthzConstants.cs +++ b/src/Authorization/Constants/AuthzConstants.cs @@ -19,5 +19,15 @@ public static class AuthzConstants /// Policy tag for authorizing Altinn.Platform.Authorization API access from the DelegationEvent Azure function /// public const string DELEGATIONEVENT_FUNCTION_AUTHORIZATION = "DelegationEventFunctionAccess"; + + /// + /// Policy scope access + /// + public const string PDPSCOPEACCESS = "PDPScopeAccess"; + + /// + /// Scope that gives access to + /// + public const string PDP_SCOPE = "altinn:authorization:pdp"; } } diff --git a/src/Authorization/Constants/XacmlRequestAttribute.cs b/src/Authorization/Constants/XacmlRequestAttribute.cs index 747a3a2a..74f3c306 100644 --- a/src/Authorization/Constants/XacmlRequestAttribute.cs +++ b/src/Authorization/Constants/XacmlRequestAttribute.cs @@ -43,7 +43,12 @@ public static class XacmlRequestAttribute /// /// xacml string that represents organization number /// - public const string OrganizationNumberAttribute = "urn:altinn:organizationnumber"; + public const string OrganizationNumberAttribute = "urn:altinn:organization:identifier-no"; + + /// + /// Legacu xacml string that represents organization number. Can be removed when all peps are updated + /// + public const string LegacyOrganizationNumberAttribute = "urn:altinn:organizationnumber"; /// /// xacml string that represents user @@ -64,5 +69,10 @@ public static class XacmlRequestAttribute /// xacml string that represents resource /// public const string ResourceRegistryAttribute = "urn:altinn:resource"; + + /// + /// xacml string that represents ssn for + /// + public const string SsnAttribute = "urn:altinn:person:identifier-no"; } } diff --git a/src/Authorization/Controllers/DecisionController.cs b/src/Authorization/Controllers/DecisionController.cs index 85f3f621..2ffe0362 100644 --- a/src/Authorization/Controllers/DecisionController.cs +++ b/src/Authorization/Controllers/DecisionController.cs @@ -4,11 +4,10 @@ using System.Linq; using System.Net.Mime; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Xml; - using Altinn.Authorization.ABAC; -using Altinn.Authorization.ABAC.Interface; using Altinn.Authorization.ABAC.Utils; using Altinn.Authorization.ABAC.Xacml; using Altinn.Authorization.ABAC.Xacml.JsonProfile; @@ -16,10 +15,13 @@ using Altinn.Platform.Authorization.ModelBinding; using Altinn.Platform.Authorization.Models; using Altinn.Platform.Authorization.Models.AccessManagement; +using Altinn.Platform.Authorization.Models.External; using Altinn.Platform.Authorization.Repositories.Interface; using Altinn.Platform.Authorization.Services.Interface; using Altinn.Platform.Authorization.Services.Interfaces; +using AutoMapper; using Azure.Core; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -36,7 +38,6 @@ namespace Altinn.Platform.Authorization.Controllers /// This is the controller responsible for Policy Enformcent Point endpoint. /// It returns a Xacml Context Response based on a Context Request /// - [Route("authorization/api/v1/[controller]")] [ApiController] public class DecisionController : ControllerBase { @@ -49,6 +50,7 @@ public class DecisionController : ControllerBase private readonly IEventLog _eventLog; private readonly IFeatureManager _featureManager; private readonly IAccessManagementWrapper _accessManagement; + private readonly IMapper _mapper; /// /// Initializes a new instance of the class. @@ -57,11 +59,13 @@ public class DecisionController : ControllerBase /// The Context handler /// The delegation context handler /// The policy Retrieval point + /// The delegation repository /// the logger /// memory cache /// the authorization event logger /// the feature manager - public DecisionController(IAccessManagementWrapper accessManagement, IContextHandler contextHandler, IDelegationContextHandler delegationContextHandler, IPolicyRetrievalPoint policyRetrievalPoint, ILogger logger, IMemoryCache memoryCache, IEventLog eventLog, IFeatureManager featureManager) + /// The model mapper + public DecisionController(IAccessManagementWrapper accessManagement, IContextHandler contextHandler, IDelegationContextHandler delegationContextHandler, IPolicyRetrievalPoint policyRetrievalPoint, IDelegationMetadataRepository delegationRepository, ILogger logger, IMemoryCache memoryCache, IEventLog eventLog, IFeatureManager featureManager, IMapper mapper) { _pdp = new PolicyDecisionPoint(); _prp = policyRetrievalPoint; @@ -72,6 +76,7 @@ public DecisionController(IAccessManagementWrapper accessManagement, IContextHan _eventLog = eventLog; _featureManager = featureManager; _accessManagement = accessManagement; + _mapper = mapper; } /// @@ -79,6 +84,7 @@ public DecisionController(IAccessManagementWrapper accessManagement, IContextHan /// /// A Generic model [HttpPost] + [Route("authorization/api/v1/[controller]")] public async Task Post([FromBody] XacmlRequestApiModel model) { try @@ -115,14 +121,42 @@ public async Task Post([FromBody] XacmlRequestApiModel model) } } - private async Task Authorize(XacmlJsonRequest decisionRequest) + /// + /// External endpoint for autorization + /// + [Authorize(Policy = AuthzConstants.PDPSCOPEACCESS)] + [Route("authorization/api/v1/authorize")] + public async Task> AuthorizeExternal([FromBody] XacmlJsonRequestRootExternal authorizationRequest, CancellationToken cancellationToken = default) + { + try + { + XacmlJsonRequestRoot jsonRequest = _mapper.Map(authorizationRequest); + XacmlJsonResponse xacmlResponse = await Authorize(jsonRequest.Request, true, cancellationToken); + return _mapper.Map(xacmlResponse); + } + catch (Exception ex) + { + _logger.LogError(ex, "// DecisionController // External Decision // Unexpected Exception"); + + XacmlContextResult result = new XacmlContextResult(XacmlContextDecision.Indeterminate) + { + Status = new XacmlContextStatus(XacmlContextStatusCode.SyntaxError) + }; + + XacmlContextResponse xacmlContextResponse = new XacmlContextResponse(result); + XacmlJsonResponse jsonResult = XacmlJsonXmlConverter.ConvertResponse(xacmlContextResponse); + return _mapper.Map(jsonResult); + } + } + + private async Task Authorize(XacmlJsonRequest decisionRequest, bool isExternalRequest = false, CancellationToken cancellationToken = default) { bool logEvent = true; if (decisionRequest.MultiRequests == null || decisionRequest.MultiRequests.RequestReference == null || decisionRequest.MultiRequests.RequestReference.Count < 2) { XacmlContextRequest request = XacmlJsonXmlConverter.ConvertRequest(decisionRequest); - XacmlContextResponse xmlResponse = await Authorize(request, logEvent); + XacmlContextResponse xmlResponse = await Authorize(request, isExternalRequest, logEvent, cancellationToken); return XacmlJsonXmlConverter.ConvertResponse(xmlResponse); } else @@ -172,7 +206,7 @@ private async Task Authorize(XacmlJsonRequest decisionRequest } } - XacmlContextResponse partResponse = await Authorize(XacmlJsonXmlConverter.ConvertRequest(jsonMultiRequestPart), logEvent); + XacmlContextResponse partResponse = await Authorize(XacmlJsonXmlConverter.ConvertRequest(jsonMultiRequestPart), isExternalRequest, logEvent, cancellationToken); XacmlJsonResponse xacmlJsonResponsePart = XacmlJsonXmlConverter.ConvertResponse(partResponse); if (multiResponse.Response == null) @@ -195,7 +229,8 @@ private async Task AuthorizeXmlRequest(XacmlRequestApiModel model) request = XacmlParser.ReadContextRequest(reader); } - XacmlContextResponse xacmlContextResponse = await Authorize(request, true); + XacmlContextResponse xacmlContextResponse = await Authorize(request, false, true); + return CreateResponse(xacmlContextResponse); } @@ -221,9 +256,9 @@ private ActionResult CreateResponse(XacmlContextResponse xacmlContextResponse) return Content(xml); } - private async Task Authorize(XacmlContextRequest decisionRequest, bool logEvent = true) + private async Task Authorize(XacmlContextRequest decisionRequest, bool isExernalRequest, bool logEvent = true, CancellationToken cancellationToken = default) { - decisionRequest = await _contextHandler.Enrich(decisionRequest); + decisionRequest = await this._contextHandler.Enrich(decisionRequest, isExernalRequest); ////_logger.LogInformation($"// DecisionController // Authorize // Roles // Enriched request: {JsonConvert.SerializeObject(decisionRequest)}."); XacmlPolicy policy = await _prp.GetPolicyAsync(decisionRequest); @@ -236,13 +271,13 @@ private async Task Authorize(XacmlContextRequest decisionR { try { - XacmlContextResponse delegationContextResponse = await AuthorizeUsingDelegations(decisionRequest, policy); + XacmlContextResponse delegationContextResponse = await AuthorizeUsingDelegations(decisionRequest, policy, cancellationToken); XacmlContextResult delegationResult = delegationContextResponse.Results.First(); if (delegationResult.Decision.Equals(XacmlContextDecision.Permit)) { if (logEvent) { - await _eventLog.CreateAuthorizationEvent(_featureManager, decisionRequest, HttpContext, delegationContextResponse); + await _eventLog.CreateAuthorizationEvent(_featureManager, decisionRequest, HttpContext, delegationContextResponse, cancellationToken); } return delegationContextResponse; @@ -256,21 +291,21 @@ private async Task Authorize(XacmlContextRequest decisionR if (logEvent) { - await _eventLog.CreateAuthorizationEvent(_featureManager, decisionRequest, HttpContext, rolesContextResponse); + await _eventLog.CreateAuthorizationEvent(_featureManager, decisionRequest, HttpContext, rolesContextResponse, cancellationToken); } return rolesContextResponse; } - private async Task ProcessDelegationResult(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy, IEnumerable delegations) + private async Task ProcessDelegationResult(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy, IEnumerable delegations, CancellationToken cancellationToken = default) { if (!delegations.IsNullOrEmpty()) { - List keyrolePartyIds = await _delegationContextHandler.GetKeyRolePartyIds(_delegationContextHandler.GetSubjectUserId(decisionRequest)); + List keyrolePartyIds = await _delegationContextHandler.GetKeyRolePartyIds(_delegationContextHandler.GetSubjectUserId(decisionRequest), cancellationToken); _delegationContextHandler.Enrich(decisionRequest, keyrolePartyIds); } - var delegationContextResponse = await MakeContextDecisionUsingDelegations(decisionRequest, delegations, resourcePolicy); + var delegationContextResponse = await MakeContextDecisionUsingDelegations(decisionRequest, delegations, resourcePolicy, cancellationToken); if (delegationContextResponse.Results.Any(r => r.Decision == XacmlContextDecision.Permit)) { return delegationContextResponse; @@ -303,7 +338,7 @@ private Action WithDefaultGetAllDelegationChangesInput(Xa input.Subject = new(AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute, _delegationContextHandler.GetSubjectUserId(decisionRequest).ToString()); }; - private async Task AuthorizeUsingDelegations(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy) + private async Task AuthorizeUsingDelegations(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy, CancellationToken cancellationToken = default) { var resourceAttributes = _delegationContextHandler.GetResourceAttributes(decisionRequest); if (IsInvalidRequest(resourceAttributes, decisionRequest)) @@ -324,7 +359,7 @@ private async Task AuthorizeUsingDelegations(XacmlContextR new(AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute, resourceAttributes.AppValue), }); - return await ProcessDelegationResult(decisionRequest, resourcePolicy, delegations); + return await ProcessDelegationResult(decisionRequest, resourcePolicy, delegations, cancellationToken); } if (IsTypeResource(resourceAttributes)) @@ -334,7 +369,7 @@ private async Task AuthorizeUsingDelegations(XacmlContextR new(AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistry, resourceAttributes.ResourceRegistryId) }); - return await ProcessDelegationResult(decisionRequest, resourcePolicy, delegations); + return await ProcessDelegationResult(decisionRequest, resourcePolicy, delegations, cancellationToken); } return new XacmlContextResponse(new XacmlContextResult(XacmlContextDecision.NotApplicable) @@ -370,7 +405,7 @@ private async Task> GetAllCachedDelegationChanges( return result; } - private async Task MakeContextDecisionUsingDelegations(XacmlContextRequest decisionRequest, IEnumerable delegations, XacmlPolicy appPolicy) + private async Task MakeContextDecisionUsingDelegations(XacmlContextRequest decisionRequest, IEnumerable delegations, XacmlPolicy appPolicy, CancellationToken cancellationToken = default) { XacmlContextResponse delegationContextResponse = new XacmlContextResponse(new XacmlContextResult(XacmlContextDecision.NotApplicable) { @@ -379,7 +414,7 @@ private async Task MakeContextDecisionUsingDelegations(Xac foreach (DelegationChange delegation in delegations.Where(d => d.DelegationChangeType != DelegationChangeType.RevokeLast)) { - XacmlPolicy delegationPolicy = await _prp.GetPolicyVersionAsync(delegation.BlobStoragePolicyPath, delegation.BlobStorageVersionId); + XacmlPolicy delegationPolicy = await _prp.GetPolicyVersionAsync(delegation.BlobStoragePolicyPath, delegation.BlobStorageVersionId, cancellationToken); foreach (XacmlObligationExpression obligationExpression in appPolicy.ObligationExpressions) { delegationPolicy.ObligationExpressions.Add(obligationExpression); diff --git a/src/Authorization/Models/External/RequestMapper.cs b/src/Authorization/Models/External/RequestMapper.cs new file mode 100644 index 00000000..ffeb1efd --- /dev/null +++ b/src/Authorization/Models/External/RequestMapper.cs @@ -0,0 +1,44 @@ +using Altinn.Authorization.ABAC.Xacml.JsonProfile; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// A class that hold access managment mapping + /// + public class RequestMapper : AutoMapper.Profile + { + /// + /// Initializes a new instance of the class. + /// + public RequestMapper() + { + AllowNullCollections = true; + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonAttributeAssignmentExternal.cs b/src/Authorization/Models/External/XacmlJsonAttributeAssignmentExternal.cs new file mode 100644 index 00000000..81fd3c21 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonAttributeAssignmentExternal.cs @@ -0,0 +1,33 @@ +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// Json reprentation of Attribute assignment returned. + /// + public class XacmlJsonAttributeAssignmentExternal + { + /// + /// Gets or sets a string containing a XACML attribute URI. Mandatory. + /// + public string AttributeId { get; set; } + + /// + /// Gets or sets the value. Mandatory. + /// + public string Value { get; set; } + + /// + /// Gets or sets a string containing a XACML category URI or the shorthand notation defined in section 4.2.2.1. + /// + public string Category { get; set; } + + /// + /// Gets or sets the datattype of the attribute. + /// + public string DataType { get; set; } + + /// + /// Gets or sets the issuer of the attribute. Optional. + /// + public string Issuer { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonAttributeExternal.cs b/src/Authorization/Models/External/XacmlJsonAttributeExternal.cs new file mode 100644 index 00000000..63086dff --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonAttributeExternal.cs @@ -0,0 +1,33 @@ +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// Defines the Attribute Json object. + /// + public class XacmlJsonAttributeExternal + { + /// + /// Gets or sets the AttributeId. Required. + /// + public string AttributeId { get; set; } + + /// + /// Gets or sets the value for the Attribute. Required. + /// + public string Value { get; set; } + + /// + /// Gets or sets the issuer of the attribute. Optional. + /// + public string Issuer { get; set; } + + /// + /// Gets or sets the datatype of the attribute. Optional in some cases. + /// + public string DataType { get; set; } + + /// + /// Gets or sets a value indicating whether the attribute should be returned in the result. + /// + public bool IncludeInResult { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonCategoryExternal.cs b/src/Authorization/Models/External/XacmlJsonCategoryExternal.cs new file mode 100644 index 00000000..c0911e91 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonCategoryExternal.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// The Category object corresponds to the XML element. Just like the element is specific to a given XACML + /// attribute category, the Category object in JSON is specific to a given XACML attribute category. + /// http://docs.oasis-open.org/xacml/xacml-json-http/v1.1/csprd01/xacml-json-http-v1.1-csprd01.html. + /// + public class XacmlJsonCategoryExternal + { + /// + /// Gets or sets CategoryId. + /// Mandatory for a Category object in the "Category" member array; otherwise, optional. See section 4.2.2.2. + /// + public string CategoryId { get; set; } + + /// + /// Gets or sets the Id of the category, mappes to attributeId in xml version of ContextRequest. + /// + public string Id { get; set; } + + /// + /// Gets or sets optional XML content. + /// + public string Content { get; set; } + + /// + /// Gets or sets a list over all attributes for a given attribute Id mappes to the. + /// + public List Attribute { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonIdReferenceExternal.cs b/src/Authorization/Models/External/XacmlJsonIdReferenceExternal.cs new file mode 100644 index 00000000..f907cfe2 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonIdReferenceExternal.cs @@ -0,0 +1,19 @@ +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// A JSON object for policy refernces. + /// + public class XacmlJsonIdReferenceExternal + { + /// + /// Gets or sets a string containing a XACML policy or policy set URI. + /// Represents the value stored inside the XACML XML or element. + /// + public string Id { get; set; } + + /// + /// Gets or sets the version. + /// + public string Version { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonMissingAttributeDetailExternal.cs b/src/Authorization/Models/External/XacmlJsonMissingAttributeDetailExternal.cs new file mode 100644 index 00000000..8f4b91b8 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonMissingAttributeDetailExternal.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// A JSON object for information about missing attributes in the Context Request. + /// + public class XacmlJsonMissingAttributeDetailExternal + { + /// + /// Gets or sets a string containing a XACML attribute URI. + /// + public string AttributeId { get; set; } + + /// + /// Gets or sets the value. + /// + public List Value { get; set; } + + /// + /// Gets or sets the issuer. + /// + public string Issuer { get; set; } + + /// + /// Gets or sets the datatype. + /// + public string DataType { get; set; } + + /// + /// Gets or sets the category of the missing attribute. + /// + public string Category { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonMultiRequestsExternal.cs b/src/Authorization/Models/External/XacmlJsonMultiRequestsExternal.cs new file mode 100644 index 00000000..74c83bbb --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonMultiRequestsExternal.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// A JSON object that defines references to multiple requests. + /// + public class XacmlJsonMultiRequestsExternal + { + /// + /// Gets or sets the request reference. + /// + public List RequestReference { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonObligationOrAdviceExternal.cs b/src/Authorization/Models/External/XacmlJsonObligationOrAdviceExternal.cs new file mode 100644 index 00000000..f1a54658 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonObligationOrAdviceExternal.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// Defines a Json object for ObligationOrAdvice. + /// + public class XacmlJsonObligationOrAdviceExternal + { + /// + /// Gets or sets a string containing a XACML obligation or advice URI. + /// + public string Id { get; set; } + + /// + /// Gets or sets an array of AttributeAssignment objects. + /// + public List AttributeAssignment { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonPolicyIdentifierListExternal.cs b/src/Authorization/Models/External/XacmlJsonPolicyIdentifierListExternal.cs new file mode 100644 index 00000000..bf4e1639 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonPolicyIdentifierListExternal.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// A JSON object that refernces a policy or policy set. + /// + public class XacmlJsonPolicyIdentifierListExternal + { + /// + /// Gets or sets list over policy id references. + /// + public List PolicyIdReference { get; set; } + + /// + /// Gets or sets list policy sets references. + /// + public List PolicySetIdReference { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonRequestExternal.cs b/src/Authorization/Models/External/XacmlJsonRequestExternal.cs new file mode 100644 index 00000000..ab0f27cc --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonRequestExternal.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// 4.2 Representation of the XACML request in JSON + /// An XACML request is represented as an object with a single member named "Request". The value of the "Request" member is a Request object. + /// https://docs.oasis-open.org/xacml/xacml-json-http/v1.1/os/xacml-json-http-v1.1-os.html#_Toc5116207 + /// + public class XacmlJsonRequestExternal + { + /// + /// Gets or sets a value indicating whether the PolicyIdList should be returned. Optional. Default false. + /// + public bool ReturnPolicyIdList { get; set; } + + /// + /// Gets or sets a value indicating whether it is a combined decision. + /// + public bool CombinedDecision { get; set; } + + /// + /// Gets or sets the xpath version. + /// + public string XPathVersion { get; set; } + + /// + /// Gets or sets the Category object corresponds to the XML element. Just like the element is + /// specific to a given XACML attribute category, the Category object in JSON is specific to a given XACML attribute category. + /// + public List Category { get; set; } + + /// + /// Gets or sets the resource attributes. + /// + public List Resource { get; set; } + + /// + /// Gets or sets the action attributes. + /// + public List Action { get; set; } + + /// + /// Gets or sets the subject attributes. + /// + public List AccessSubject { get; set; } + + /// + /// Gets or sets the recipent subjet. + /// + public List RecipientSubject { get; set; } + + /// + /// Gets or sets the intermediary subjects attributes. + /// + public List IntermediarySubject { get; set; } + + /// + /// Gets or sets attributes about requsting machine. + /// + public List RequestingMachine { get; set; } + + /// + /// Gets or sets references to multiple requests. + /// + public XacmlJsonMultiRequestsExternal MultiRequests { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonRequestReferenceExternal.cs b/src/Authorization/Models/External/XacmlJsonRequestReferenceExternal.cs new file mode 100644 index 00000000..dd0c834e --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonRequestReferenceExternal.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// JSON object for request references. + /// + public class XacmlJsonRequestReferenceExternal + { + /// + /// Gets or sets the reference Id. + /// + public List ReferenceId { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonRequestRootExternal.cs b/src/Authorization/Models/External/XacmlJsonRequestRootExternal.cs new file mode 100644 index 00000000..c5b8b95f --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonRequestRootExternal.cs @@ -0,0 +1,13 @@ +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// The JSON object root needed to be able to parse the request. + /// + public class XacmlJsonRequestRootExternal + { + /// + /// Gets or sets the request. + /// + public XacmlJsonRequestExternal Request { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonRequestsExternal.cs b/src/Authorization/Models/External/XacmlJsonRequestsExternal.cs new file mode 100644 index 00000000..c01b58a1 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonRequestsExternal.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// Defines a list of json request + /// + public class XacmlJsonRequestsExternal + { + /// + /// A list of requests + /// + public List Requests { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonResponseExternal.cs b/src/Authorization/Models/External/XacmlJsonResponseExternal.cs new file mode 100644 index 00000000..902ce83b --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonResponseExternal.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// The JSON Response. + /// https://docs.oasis-open.org/xacml/xacml-json-http/v1.1/os/xacml-json-http-v1.1-os.html#_Toc5116225 + /// + public class XacmlJsonResponseExternal + { + /// + /// Gets or sets a list over JSON XACML results. + /// + public List Response { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonResultExternal.cs b/src/Authorization/Models/External/XacmlJsonResultExternal.cs new file mode 100644 index 00000000..29c4ffac --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonResultExternal.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// The JSON object Result. + /// https://docs.oasis-open.org/xacml/xacml-json-http/v1.1/os/xacml-json-http-v1.1-os.html#_Toc5116225 + /// + public class XacmlJsonResultExternal + { + /// + /// Gets or sets the XACML Decision. + /// + public string Decision { get; set; } + + /// + /// Gets or sets the status. + /// + public XacmlJsonStatusExternal Status { get; set; } + + /// + /// Gets or sets any obligations of the result. + /// + public List Obligations { get; set; } + + /// + /// Gets or sets xACML Advice. + /// + public List AssociateAdvice { get; set; } + + /// + /// Gets or sets category. + /// + public List Category { get; set; } + + /// + /// Gets or sets policy Identifyer list related to the result. + /// + public XacmlJsonPolicyIdentifierListExternal PolicyIdentifierList { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonStatusCodeExternal.cs b/src/Authorization/Models/External/XacmlJsonStatusCodeExternal.cs new file mode 100644 index 00000000..4afbe50d --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonStatusCodeExternal.cs @@ -0,0 +1,18 @@ +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// A XACML Json object for status Code. + /// + public class XacmlJsonStatusCodeExternal + { + /// + /// Gets or sets the value. + /// + public string Value { get; set; } + + /// + /// Gets or sets a nested status code. + /// + public XacmlJsonStatusCodeExternal StatusCode { get; set; } + } +} diff --git a/src/Authorization/Models/External/XacmlJsonStatusExternal.cs b/src/Authorization/Models/External/XacmlJsonStatusExternal.cs new file mode 100644 index 00000000..08fe25a7 --- /dev/null +++ b/src/Authorization/Models/External/XacmlJsonStatusExternal.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Altinn.Platform.Authorization.Models.External +{ + /// + /// XACML Json object for status. + /// + public class XacmlJsonStatusExternal + { + /// + /// Gets or sets the status message. + /// + public string StatusMessage { get; set; } + + /// + /// Gets or sets list over status details. + /// + public List StatusDetails { get; set; } + + /// + /// Gets or sets the defined status code. + /// + public XacmlJsonStatusCodeExternal StatusCode { get; set; } + } +} diff --git a/src/Authorization/Models/XacmlResourceAttributes.cs b/src/Authorization/Models/XacmlResourceAttributes.cs index 7cbb7d4f..38036f1a 100644 --- a/src/Authorization/Models/XacmlResourceAttributes.cs +++ b/src/Authorization/Models/XacmlResourceAttributes.cs @@ -44,5 +44,10 @@ public class XacmlResourceAttributes /// Gets or sets the OrganizationNumber for the org owning the resource /// public string OrganizationNumber { get; set; } + + /// + /// Gets or sets the ssn for the person owning the resource + /// + public string Ssn { get; set; } } } diff --git a/src/Authorization/Program.cs b/src/Authorization/Program.cs index 6db8f01e..db621754 100644 --- a/src/Authorization/Program.cs +++ b/src/Authorization/Program.cs @@ -6,7 +6,6 @@ using Altinn.ApiClients.Maskinporten.Extensions; using Altinn.ApiClients.Maskinporten.Interfaces; using Altinn.ApiClients.Maskinporten.Services; -using Altinn.Authorization.ABAC.Interface; using Altinn.Common.AccessTokenClient.Services; using Altinn.Common.PEP.Authorization; using Altinn.Platform.Authorization.Clients; @@ -198,7 +197,7 @@ async Task ConnectToKeyVaultAndSetApplicationInsights(ConfigurationManager confi void ConfigureServices(IServiceCollection services, IConfiguration config) { logger.LogInformation("Startup // ConfigureServices"); - + services.AddAutoMapper(typeof(Program)); services.AddControllers().AddXmlSerializerFormatters(); services.AddHealthChecks().AddCheck("authorization_health_check"); services.AddSingleton(config); @@ -269,9 +268,11 @@ void ConfigureServices(IServiceCollection services, IConfiguration config) options.AddPolicy(AuthzConstants.POLICY_STUDIO_DESIGNER, policy => policy.Requirements.Add(new ClaimAccessRequirement("urn:altinn:app", "studio.designer"))); options.AddPolicy(AuthzConstants.ALTINNII_AUTHORIZATION, policy => policy.Requirements.Add(new ClaimAccessRequirement("urn:altinn:app", "sbl.authorization"))); options.AddPolicy(AuthzConstants.DELEGATIONEVENT_FUNCTION_AUTHORIZATION, policy => policy.Requirements.Add(new ClaimAccessRequirement("urn:altinn:app", "platform.authorization"))); + options.AddPolicy(AuthzConstants.PDPSCOPEACCESS, policy => policy.Requirements.Add(new ScopeAccessRequirement(AuthzConstants.PDP_SCOPE))); }); services.AddTransient(); + services.AddTransient(); services.Configure(options => { diff --git a/src/Authorization/Repositories/Interface/IPolicyRepository.cs b/src/Authorization/Repositories/Interface/IPolicyRepository.cs index 801c118a..ec6c1a3f 100644 --- a/src/Authorization/Repositories/Interface/IPolicyRepository.cs +++ b/src/Authorization/Repositories/Interface/IPolicyRepository.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using Azure; using Azure.Storage.Blobs.Models; @@ -15,24 +16,27 @@ public interface IPolicyRepository /// Gets file stream for the policy file from blob storage, if it exists at the specified path. /// /// The file path. + /// The cancelattionToken /// File stream of the policy file - Task GetPolicyAsync(string filepath); + Task GetPolicyAsync(string filepath, CancellationToken cancellationToken = default); /// /// Gets file stream for the specified version of a policy file from blob storage, if it exists at the specified path. /// /// The file path. /// The blob storage version + /// The cancelletionToken /// File stream of the policy file - Task GetPolicyVersionAsync(string filepath, string version); + Task GetPolicyVersionAsync(string filepath, string version, CancellationToken cancellationToken = default); /// /// Writes a file stream to blobstorage to the specified path. /// /// The file path. /// File stream of the policy file to be written + /// The cancelletionToken /// Azure response BlobContentInfo - Task> WritePolicyAsync(string filepath, Stream fileStream); + Task> WritePolicyAsync(string filepath, Stream fileStream, CancellationToken cancellationToken = default); /// /// Writes a file stream to blobstorage to the specified path, including the conditional check that the provided blob lease id is valid. @@ -40,23 +44,26 @@ public interface IPolicyRepository /// The file path. /// File stream of the policy file to be written /// The blob lease id, required to be able to write after a lock + /// The cancelletionToken /// Azure response BlobContentInfo - Task> WritePolicyConditionallyAsync(string filepath, Stream fileStream, string blobLeaseId); + Task> WritePolicyConditionallyAsync(string filepath, Stream fileStream, string blobLeaseId, CancellationToken cancellationToken = default); /// /// Deletes a specific version of a blob storage file if it exits on the specified path. /// /// The file path. /// The blob storage version + /// The cancelletionToken /// - Task DeletePolicyVersionAsync(string filepath, string version); + Task DeletePolicyVersionAsync(string filepath, string version, CancellationToken cancellationToken = default); /// /// Tries to acquire a blob lease on the base blob for the provided filepath. /// /// The file path of the base blob to aquire a blob lease on + /// The cancelletionToken /// The LeaseId if a release was possible, otherwise null - Task TryAcquireBlobLease(string filepath); + Task TryAcquireBlobLease(string filepath, CancellationToken cancellationToken = default); /// /// Releases a blob lease on the base blob for the provided filepath using the provided leaseId. @@ -69,7 +76,8 @@ public interface IPolicyRepository /// Checks whether there exists a blob at the specified path /// /// The file path to check if a blob exists + /// The cancelletionToken /// Bool whether the blob exists or not - Task PolicyExistsAsync(string filepath); + Task PolicyExistsAsync(string filepath, CancellationToken cancellationToken = default); } } diff --git a/src/Authorization/Repositories/PolicyRepository.cs b/src/Authorization/Repositories/PolicyRepository.cs index 736c289e..dc18bde9 100644 --- a/src/Authorization/Repositories/PolicyRepository.cs +++ b/src/Authorization/Repositories/PolicyRepository.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net; +using System.Threading; using System.Threading.Tasks; using Altinn.Platform.Authorization.Configuration; using Altinn.Platform.Authorization.Repositories.Interface; @@ -48,31 +49,31 @@ public PolicyRepository( } /// - public async Task GetPolicyAsync(string filepath) + public async Task GetPolicyAsync(string filepath, CancellationToken cancellationToken = default) { BlobClient blobClient = CreateBlobClient(filepath); - return await GetBlobStreamInternal(blobClient); + return await GetBlobStreamInternal(blobClient, cancellationToken); } /// - public async Task GetPolicyVersionAsync(string filepath, string version) + public async Task GetPolicyVersionAsync(string filepath, string version, CancellationToken cancellationToken = default) { BlobClient blobClient = CreateBlobClient(filepath).WithVersion(version); - return await GetBlobStreamInternal(blobClient); + return await GetBlobStreamInternal(blobClient, cancellationToken); } /// - public async Task> WritePolicyAsync(string filepath, Stream fileStream) + public async Task> WritePolicyAsync(string filepath, Stream fileStream, CancellationToken cancellationToken = default) { BlobClient blobClient = CreateBlobClient(filepath); - return await WriteBlobStreamInternal(blobClient, fileStream); + return await WriteBlobStreamInternal(blobClient, fileStream, cancellationToken: cancellationToken); } /// - public async Task> WritePolicyConditionallyAsync(string filepath, Stream fileStream, string blobLeaseId) + public async Task> WritePolicyConditionallyAsync(string filepath, Stream fileStream, string blobLeaseId, CancellationToken cancellationToken = default) { BlobClient blobClient = CreateBlobClient(filepath); @@ -84,18 +85,18 @@ public async Task> WritePolicyConditionallyAsync(strin } }; - return await WriteBlobStreamInternal(blobClient, fileStream, blobUploadOptions); + return await WriteBlobStreamInternal(blobClient, fileStream, blobUploadOptions, cancellationToken); } /// - public async Task TryAcquireBlobLease(string filepath) + public async Task TryAcquireBlobLease(string filepath, CancellationToken cancellationToken = default) { BlobClient blobClient = CreateBlobClient(filepath); BlobLeaseClient blobLeaseClient = blobClient.GetBlobLeaseClient(); try { - BlobLease blobLease = await blobLeaseClient.AcquireAsync(TimeSpan.FromSeconds(_storageConfig.BlobLeaseTimeout)); + BlobLease blobLease = await blobLeaseClient.AcquireAsync(TimeSpan.FromSeconds(_storageConfig.BlobLeaseTimeout), cancellationToken: cancellationToken); return blobLease.LeaseId; } catch (RequestFailedException ex) @@ -119,12 +120,12 @@ public async void ReleaseBlobLease(string filepath, string leaseId) } /// - public async Task PolicyExistsAsync(string filepath) + public async Task PolicyExistsAsync(string filepath, CancellationToken cancellationToken = default) { try { BlobClient blobClient = CreateBlobClient(filepath); - return await blobClient.ExistsAsync(); + return await blobClient.ExistsAsync(cancellationToken); } catch (RequestFailedException ex) { @@ -135,13 +136,13 @@ public async Task PolicyExistsAsync(string filepath) } /// - public async Task DeletePolicyVersionAsync(string filepath, string version) + public async Task DeletePolicyVersionAsync(string filepath, string version, CancellationToken cancellationToken = default) { try { BlobClient blockBlob = CreateBlobClient(filepath); - return await blockBlob.WithVersion(version).DeleteAsync(); + return await blockBlob.WithVersion(version).DeleteAsync(cancellationToken: cancellationToken); } catch (RequestFailedException ex) { @@ -171,15 +172,15 @@ private BlobClient CreateBlobClient(string blobName) return _metadataContainerClient.GetBlobClient(blobName); } - private async Task GetBlobStreamInternal(BlobClient blobClient) + private async Task GetBlobStreamInternal(BlobClient blobClient, CancellationToken cancellationToken) { try { Stream memoryStream = new MemoryStream(); - if (await blobClient.ExistsAsync()) + if (await blobClient.ExistsAsync(cancellationToken)) { - await blobClient.DownloadToAsync(memoryStream); + await blobClient.DownloadToAsync(memoryStream, cancellationToken); memoryStream.Position = 0; return memoryStream; @@ -194,16 +195,16 @@ private async Task GetBlobStreamInternal(BlobClient blobClient) } } - private async Task> WriteBlobStreamInternal(BlobClient blobClient, Stream fileStream, BlobUploadOptions blobUploadOptions = null) + private async Task> WriteBlobStreamInternal(BlobClient blobClient, Stream fileStream, BlobUploadOptions blobUploadOptions = null, CancellationToken cancellationToken = default) { try { if (blobUploadOptions != null) { - return await blobClient.UploadAsync(fileStream, blobUploadOptions); + return await blobClient.UploadAsync(fileStream, blobUploadOptions, cancellationToken); } - return await blobClient.UploadAsync(fileStream, true); + return await blobClient.UploadAsync(fileStream, true, cancellationToken); } catch (RequestFailedException ex) { diff --git a/src/Authorization/Services/Implementation/ContextHandler.cs b/src/Authorization/Services/Implementation/ContextHandler.cs index 95e897a1..96355832 100644 --- a/src/Authorization/Services/Implementation/ContextHandler.cs +++ b/src/Authorization/Services/Implementation/ContextHandler.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; -using System.Net.Http.Headers; +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Constants; -using Altinn.Authorization.ABAC.Interface; using Altinn.Authorization.ABAC.Xacml; using Altinn.Platform.Authorization.Configuration; using Altinn.Platform.Authorization.Constants; -using Altinn.Platform.Authorization.Helpers; using Altinn.Platform.Authorization.Models; using Altinn.Platform.Authorization.Models.Oed; using Altinn.Platform.Authorization.Repositories.Interface; @@ -74,23 +71,25 @@ public ContextHandler( /// /// Ads needed information to the Context Request. /// - /// The original Xacml Context Request + /// The original Xacml Context Request + /// Defines if this is an external request /// The enriched XacmlContextRequest - public async Task Enrich(XacmlContextRequest decisionRequest) + public async Task Enrich(XacmlContextRequest request, bool isExternalRequest) { - await EnrichResourceAttributes(decisionRequest); - return await Task.FromResult(decisionRequest); + await EnrichResourceAttributes(request, isExternalRequest); + return await Task.FromResult(request); } /// /// Enriches the resource attribute collection with additional attributes retrieved based on the instance on the request /// /// The original Xacml Context Request - protected async Task EnrichResourceAttributes(XacmlContextRequest request) + /// Defines if request comes + protected async Task EnrichResourceAttributes(XacmlContextRequest request, bool isExternalRequest) { XacmlContextAttributes resourceContextAttributes = request.GetResourceAttributes(); XacmlResourceAttributes resourceAttributes = GetResourceAttributeValues(resourceContextAttributes); - await EnrichResourceParty(resourceAttributes); + await EnrichResourceParty(resourceAttributes, isExternalRequest); bool resourceAttributeComplete = IsResourceComplete(resourceAttributes); @@ -130,7 +129,7 @@ protected async Task EnrichResourceAttributes(XacmlContextRequest request) } } - await EnrichSubjectAttributes(request, resourceAttributes.ResourcePartyValue); + await EnrichSubjectAttributes(request, resourceAttributes.ResourcePartyValue, isExternalRequest); } private static bool IsResourceComplete(XacmlResourceAttributes resourceAttributes) @@ -178,7 +177,7 @@ private static bool IsResourceComplete(XacmlResourceAttributes resourceAttribute /// Method that adds information about the resource party /// /// - protected async Task EnrichResourceParty(XacmlResourceAttributes resourceAttributes) + protected async Task EnrichResourceParty(XacmlResourceAttributes resourceAttributes, bool isExternalRequest) { if (string.IsNullOrEmpty(resourceAttributes.ResourcePartyValue) && !string.IsNullOrEmpty(resourceAttributes.OrganizationNumber)) { @@ -188,6 +187,19 @@ protected async Task EnrichResourceParty(XacmlResourceAttributes resourceAttribu resourceAttributes.ResourcePartyValue = partyId.ToString(); } } + else if (string.IsNullOrEmpty(resourceAttributes.ResourcePartyValue) && !string.IsNullOrEmpty(resourceAttributes.Ssn)) + { + if (!isExternalRequest) + { + throw new ArgumentException("Not allowed to use ssn for internal API"); + } + + int partyId = await _registerService.PartyLookup(null, resourceAttributes.Ssn); + if (partyId != 0) + { + resourceAttributes.ResourcePartyValue = partyId.ToString(); + } + } } /// @@ -240,6 +252,20 @@ protected XacmlResourceAttributes GetResourceAttributeValues(XacmlContextAttribu { resourceAttributes.OrganizationNumber = attribute.AttributeValues.First().Value; } + + if (attribute.AttributeId.OriginalString.Equals(XacmlRequestAttribute.LegacyOrganizationNumberAttribute)) + { + // For supporting legacy use of this attribute. (old PEPS) + if (string.IsNullOrEmpty(resourceAttributes.OrganizationNumber)) + { + resourceAttributes.OrganizationNumber = attribute.AttributeValues.First().Value; + } + } + + if (attribute.AttributeId.OriginalString.Equals(XacmlRequestAttribute.SsnAttribute)) + { + resourceAttributes.Ssn = attribute.AttributeValues.First().Value; + } } return resourceAttributes; @@ -284,7 +310,8 @@ protected XacmlAttribute GetAttribute(string attributeId, string attributeValue) /// /// The original Xacml Context Request /// The resource reportee party id - protected async Task EnrichSubjectAttributes(XacmlContextRequest request, string resourceParty) + /// Used to enforce stricter requirements + protected async Task EnrichSubjectAttributes(XacmlContextRequest request, string resourceParty, bool isExternalRequest) { // If there is no resource party then it is impossible to enrich roles if (string.IsNullOrEmpty(resourceParty)) @@ -296,6 +323,7 @@ protected async Task EnrichSubjectAttributes(XacmlContextRequest request, string int subjectUserId = 0; int resourcePartyId = Convert.ToInt32(resourceParty); + string subjectSsn = string.Empty; foreach (XacmlAttribute xacmlAttribute in subjectContextAttributes.Attributes) { @@ -303,6 +331,35 @@ protected async Task EnrichSubjectAttributes(XacmlContextRequest request, string { subjectUserId = Convert.ToInt32(xacmlAttribute.AttributeValues.First().Value); } + + if (xacmlAttribute.AttributeId.OriginalString.Equals(XacmlRequestAttribute.SsnAttribute)) + { + if (!isExternalRequest) + { + throw new ArgumentException("Not allowed to use ssn for internal API"); + } + + subjectSsn = xacmlAttribute.AttributeValues.First().Value; + } + } + + if (!string.IsNullOrEmpty(subjectSsn) && subjectUserId != 0) + { + throw new ArgumentException("Not possible to set userid and ssn for subject at the same time"); + } + + if (!string.IsNullOrEmpty(subjectSsn)) + { + UserProfile subjectProfile = await _profileWrapper.GetUserProfileBySSN(subjectSsn); + + if (subjectProfile != null) + { + subjectUserId = subjectProfile.UserId; + } + else + { + throw new ArgumentException("Invalid SSN"); + } } if (subjectUserId == 0) @@ -319,7 +376,11 @@ protected async Task EnrichSubjectAttributes(XacmlContextRequest request, string IDictionary> subjectAttributes = xacmlPolicy.GetAttributeDictionaryByCategory(XacmlConstants.MatchAttributeCategory.Subject); if (subjectAttributes.ContainsKey(AltinnXacmlConstants.MatchAttributeIdentifiers.OedRoleAttribute)) { - string subjectSsn = await GetSsnForUser(subjectUserId); + if (string.IsNullOrEmpty(subjectSsn)) + { + subjectSsn = await GetSsnForUser(subjectUserId); + } + string resourceSsn = await GetSSnForParty(resourcePartyId); if (!string.IsNullOrWhiteSpace(subjectSsn) && !string.IsNullOrWhiteSpace(resourceSsn)) @@ -419,15 +480,16 @@ protected async Task> GetRoles(int subjectUserId, int resourcePartyId /// Gets the list of mainunits for a subunit /// /// The subunit partyId to check and retrieve mainunits for + /// The cancellationToken /// List of mainunits - protected async Task> GetMainUnits(int subUnitPartyId) + protected async Task> GetMainUnits(int subUnitPartyId, CancellationToken cancellationToken = default) { string cacheKey = $"GetMainUnitsFor:{subUnitPartyId}"; if (!_memoryCache.TryGetValue(cacheKey, out List mainUnits)) { // Key not in cache, so get data. - mainUnits = await _partiesWrapper.GetMainUnits(new MainUnitQuery { PartyIds = new List { subUnitPartyId } }); + mainUnits = await _partiesWrapper.GetMainUnits(new MainUnitQuery { PartyIds = new List { subUnitPartyId } }, cancellationToken); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetPriority(CacheItemPriority.High) @@ -443,15 +505,16 @@ protected async Task> GetMainUnits(int subUnitPartyId) /// Gets the list of keyrole unit partyIds for a user /// /// The userid to retrieve keyrole unit for + /// The cancellationToken /// List of partyIds for units where user has keyrole - protected async Task> GetKeyRolePartyIds(int subjectUserId) + protected async Task> GetKeyRolePartyIds(int subjectUserId, CancellationToken cancellationToken = default) { string cacheKey = $"GetKeyRolePartyIdsFor:{subjectUserId}"; if (!_memoryCache.TryGetValue(cacheKey, out List keyrolePartyIds)) { // Key not in cache, so get data. - keyrolePartyIds = await _partiesWrapper.GetKeyRoleParties(subjectUserId); + keyrolePartyIds = await _partiesWrapper.GetKeyRoleParties(subjectUserId, cancellationToken); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetPriority(CacheItemPriority.High) diff --git a/src/Authorization/Services/Implementation/DelegationContextHandler.cs b/src/Authorization/Services/Implementation/DelegationContextHandler.cs index d516bd98..46db30ad 100644 --- a/src/Authorization/Services/Implementation/DelegationContextHandler.cs +++ b/src/Authorization/Services/Implementation/DelegationContextHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Interface; using Altinn.Authorization.ABAC.Xacml; @@ -81,20 +82,22 @@ public XacmlResourceAttributes GetResourceAttributes(XacmlContextRequest request /// Gets the list of mainunits for a subunit /// /// The subunit partyIds to check and retrieve mainunits for + /// The cancellationToken /// List of mainunits - public async new Task> GetMainUnits(int subUnitPartyId) + public async new Task> GetMainUnits(int subUnitPartyId, CancellationToken cancellationToken = default) { - return await base.GetMainUnits(subUnitPartyId); + return await base.GetMainUnits(subUnitPartyId, cancellationToken); } /// /// Gets the list of keyrole unit partyIds for a user /// /// The userid to retrieve keyrole unit for + /// The cancellationToken /// List of partyIds for units where user has keyrole - public async new Task> GetKeyRolePartyIds(int subjectUserId) + public async new Task> GetKeyRolePartyIds(int subjectUserId, CancellationToken cancellationToken = default) { - return await base.GetKeyRolePartyIds(subjectUserId); + return await base.GetKeyRolePartyIds(subjectUserId, cancellationToken); } } } diff --git a/src/Authorization/Services/Implementation/EventLogService.cs b/src/Authorization/Services/Implementation/EventLogService.cs index ffb87375..3630efbd 100644 --- a/src/Authorization/Services/Implementation/EventLogService.cs +++ b/src/Authorization/Services/Implementation/EventLogService.cs @@ -1,5 +1,6 @@ using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Xacml; using Altinn.Platform.Authorization.Clients.Interfaces; @@ -36,16 +37,17 @@ public EventLogService(IEventsQueueClient queueClient, ISystemClock systemClock) /// Queues an authorization event to the logqueue /// /// authorization event - public void CreateAuthorizationEvent(AuthorizationEvent authorizationEvent) + /// The cancellationToken + public void CreateAuthorizationEvent(AuthorizationEvent authorizationEvent, CancellationToken cancellationToken = default) { if (authorizationEvent != null) { - _queueClient.EnqueueAuthorizationEvent(JsonSerializer.Serialize(authorizationEvent)); + _queueClient.EnqueueAuthorizationEvent(JsonSerializer.Serialize(authorizationEvent), cancellationToken); } } /// - public async Task CreateAuthorizationEvent(IFeatureManager featureManager, XacmlContextRequest contextRequest, HttpContext context, XacmlContextResponse contextResponse) + public async Task CreateAuthorizationEvent(IFeatureManager featureManager, XacmlContextRequest contextRequest, HttpContext context, XacmlContextResponse contextResponse, CancellationToken cancellationToken = default) { var options = new JsonSerializerOptions(); options.Converters.Add(new JsonStringEnumConverter()); @@ -55,7 +57,9 @@ public async Task CreateAuthorizationEvent(IFeatureManager featureManager, Xacml if (authorizationEvent != null) { - _queueClient.EnqueueAuthorizationEvent(JsonSerializer.Serialize(authorizationEvent, options)); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + _queueClient.EnqueueAuthorizationEvent(JsonSerializer.Serialize(authorizationEvent, options), cancellationToken); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } } } diff --git a/src/Authorization/Services/Implementation/PartiesWrapper.cs b/src/Authorization/Services/Implementation/PartiesWrapper.cs index 9092b82b..b96ec45c 100644 --- a/src/Authorization/Services/Implementation/PartiesWrapper.cs +++ b/src/Authorization/Services/Implementation/PartiesWrapper.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Text; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Altinn.Platform.Authorization.Clients; using Altinn.Platform.Authorization.Models; @@ -38,15 +39,15 @@ public PartiesWrapper(PartyClient partyClient, ILogger logger) } /// - public async Task> GetParties(int userId) + public async Task> GetParties(int userId, CancellationToken cancellationToken = default) { List partiesList = null; try { string endpointUrl = $"authorization/api/parties?userid={userId}"; - HttpResponseMessage response = await _partyClient.Client.GetAsync(endpointUrl); - string partiesDataList = await response.Content.ReadAsStringAsync(); + HttpResponseMessage response = await _partyClient.Client.GetAsync(endpointUrl, cancellationToken); + string partiesDataList = await response.Content.ReadAsStringAsync(cancellationToken); if (response.IsSuccessStatusCode) { return JsonConvert.DeserializeObject>(partiesDataList); @@ -64,14 +65,14 @@ public async Task> GetParties(int userId) } /// - public async Task GetParty(int partyId) + public async Task GetParty(int partyId, CancellationToken cancellationToken = default) { try { string endpointUrl = $"register/api/parties/{partyId}"; - HttpResponseMessage response = await _partyClient.Client.GetAsync(endpointUrl); - string responseContent = await response.Content.ReadAsStringAsync(); + HttpResponseMessage response = await _partyClient.Client.GetAsync(endpointUrl, cancellationToken); + string responseContent = await response.Content.ReadAsStringAsync(cancellationToken); if (response.StatusCode == HttpStatusCode.OK) { @@ -89,15 +90,15 @@ public async Task GetParty(int partyId) } /// - public async Task> GetKeyRoleParties(int userId) + public async Task> GetKeyRoleParties(int userId, CancellationToken cancellationToken = default) { List keyroleParties = null; try { string endpointUrl = $"authorization/api/partieswithkeyroleaccess?userid={userId}"; - HttpResponseMessage response = await _partyClient.Client.GetAsync(endpointUrl); - string responseBody = await response.Content.ReadAsStringAsync(); + HttpResponseMessage response = await _partyClient.Client.GetAsync(endpointUrl, cancellationToken); + string responseBody = await response.Content.ReadAsStringAsync(cancellationToken); if (response.IsSuccessStatusCode) { @@ -116,7 +117,7 @@ public async Task> GetKeyRoleParties(int userId) } /// - public async Task> GetMainUnits(MainUnitQuery subunitPartyIds) + public async Task> GetMainUnits(MainUnitQuery subunitPartyIds, CancellationToken cancellationToken = default) { List mainUnits = null; @@ -129,8 +130,8 @@ public async Task> GetMainUnits(MainUnitQuery subunitPartyIds) Content = new StringContent(JsonConvert.SerializeObject(subunitPartyIds), Encoding.UTF8, "application/json") }; - HttpResponseMessage response = await _partyClient.Client.SendAsync(request); - var responseBody = await response.Content.ReadAsStringAsync(); + HttpResponseMessage response = await _partyClient.Client.SendAsync(request, cancellationToken); + var responseBody = await response.Content.ReadAsStringAsync(cancellationToken); if (response.IsSuccessStatusCode) { @@ -149,11 +150,11 @@ public async Task> GetMainUnits(MainUnitQuery subunitPartyIds) } /// - public async Task ValidateSelectedParty(int userId, int partyId) + public async Task ValidateSelectedParty(int userId, int partyId, CancellationToken cancellationToken = default) { bool result = false; - List partyList = await GetParties(userId); + List partyList = await GetParties(userId, cancellationToken); if (partyList.Count > 0) { diff --git a/src/Authorization/Services/Implementation/PolicyRetrievalPoint.cs b/src/Authorization/Services/Implementation/PolicyRetrievalPoint.cs index c30af5c2..5b4a9b35 100644 --- a/src/Authorization/Services/Implementation/PolicyRetrievalPoint.cs +++ b/src/Authorization/Services/Implementation/PolicyRetrievalPoint.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Xacml; using Altinn.Platform.Authorization.Configuration; @@ -61,19 +62,19 @@ public async Task GetPolicyAsync(string org, string app) } /// - public async Task GetPolicyVersionAsync(string policyPath, string version) + public async Task GetPolicyVersionAsync(string policyPath, string version, CancellationToken cancellationToken = default) { - return await GetPolicyInternalAsync(policyPath, version); + return await GetPolicyInternalAsync(policyPath, version, cancellationToken); } - private async Task GetPolicyInternalAsync(string policyPath, string version = "") + private async Task GetPolicyInternalAsync(string policyPath, string version = "", CancellationToken cancellationToken = default) { string cacheKey = policyPath + version; if (!_memoryCache.TryGetValue(cacheKey, out XacmlPolicy policy)) { Stream policyBlob = string.IsNullOrEmpty(version) ? - await _repository.GetPolicyAsync(policyPath) : - await _repository.GetPolicyVersionAsync(policyPath, version); + await _repository.GetPolicyAsync(policyPath, cancellationToken) : + await _repository.GetPolicyVersionAsync(policyPath, version, cancellationToken); using (policyBlob) { policy = (policyBlob.Length > 0) ? PolicyHelper.ParsePolicy(policyBlob) : null; diff --git a/src/Authorization/Services/Implementation/ProfileWrapper.cs b/src/Authorization/Services/Implementation/ProfileWrapper.cs index 96b1cb89..2693c39f 100644 --- a/src/Authorization/Services/Implementation/ProfileWrapper.cs +++ b/src/Authorization/Services/Implementation/ProfileWrapper.cs @@ -57,5 +57,30 @@ public async Task GetUserProfile(int userId) throw; } } + + /// + public async Task GetUserProfileBySSN(string ssn) + { + try + { + string endpointUrl = $"internal/user"; + + var response = await _profileClient.Client.PostAsJsonAsync(endpointUrl, new { Ssn = ssn }); + string responseContent = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode == HttpStatusCode.OK) + { + return JsonSerializer.Deserialize(responseContent, _serializerOptions); + } + + _logger.LogError("ProfileAPI // ProfileWrapper // GetUserProfile // Failed // Unexpected HttpStatusCode: {statusCode}\n {responseContent}", response.StatusCode, responseContent); + return null; + } + catch (Exception ex) + { + _logger.LogError(ex, "ProfileAPI // ProfileWrapper // GetUserProfile // Failed // Unexpected Exception"); + throw; + } + } } } diff --git a/src/Authorization/Services/Interface/IContextHandler.cs b/src/Authorization/Services/Interface/IContextHandler.cs new file mode 100644 index 00000000..39204932 --- /dev/null +++ b/src/Authorization/Services/Interface/IContextHandler.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Altinn.Authorization.ABAC.Xacml; + +namespace Altinn.Platform.Authorization.Services.Interface +{ + /// + /// Defines Interface for Context Handler. + /// + public interface IContextHandler + { + /// + /// Enrich the DecisionRequest with needed attributes so PDP can evaluate decision request for a policy/policyset. + /// + /// The XacmlContextRequest. + /// Defines if call is comming from external source + /// Enriched context. + Task Enrich(XacmlContextRequest decisionRequest, bool isExternalRequest); + } +} diff --git a/src/Authorization/Services/Interface/IDelegationContextHandler.cs b/src/Authorization/Services/Interface/IDelegationContextHandler.cs index 22ee334d..c7d44a1a 100644 --- a/src/Authorization/Services/Interface/IDelegationContextHandler.cs +++ b/src/Authorization/Services/Interface/IDelegationContextHandler.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Xacml; using Altinn.Platform.Authorization.Models; -namespace Altinn.Authorization.ABAC.Interface +namespace Altinn.Platform.Authorization.Services.Interface { /// /// Defines Interface for the Delegation Context Handler. @@ -35,14 +36,16 @@ public interface IDelegationContextHandler : IContextHandler /// Gets the list of mainunits for a subunit /// /// The subunit partyId to check and retrieve mainunits for + /// The cancellationToken /// List of mainunits - public Task> GetMainUnits(int subUnitPartyId); + public Task> GetMainUnits(int subUnitPartyId, CancellationToken cancellationToken = default); /// /// Gets the list of keyrole unit partyIds for a user /// /// The userid to retrieve keyrole unit for + /// The cancellationToken /// List of partyIds for units where user has keyrole - public Task> GetKeyRolePartyIds(int subjectUserId); + public Task> GetKeyRolePartyIds(int subjectUserId, CancellationToken cancellationToken = default); } } diff --git a/src/Authorization/Services/Interface/IEventLog.cs b/src/Authorization/Services/Interface/IEventLog.cs index 6dc78891..1b0d556c 100644 --- a/src/Authorization/Services/Interface/IEventLog.cs +++ b/src/Authorization/Services/Interface/IEventLog.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using Altinn.Authorization.ABAC.Xacml; using Altinn.Platform.Authorization.Models.EventLog; using Microsoft.AspNetCore.Http; @@ -15,7 +16,8 @@ public interface IEventLog /// Creates an authorization event in storage queue /// /// authorization event - public void CreateAuthorizationEvent(AuthorizationEvent authorizationEvent); + /// The cancellationToken + public void CreateAuthorizationEvent(AuthorizationEvent authorizationEvent, CancellationToken cancellationToken = default); /// /// Creates an authorization event in storage queue @@ -24,6 +26,7 @@ public interface IEventLog /// the enriched context request /// the http context /// the decision after the request process - public Task CreateAuthorizationEvent(IFeatureManager featureManager, XacmlContextRequest contextRequest, HttpContext context, XacmlContextResponse contextResponse); + /// The cancellationToken + public Task CreateAuthorizationEvent(IFeatureManager featureManager, XacmlContextRequest contextRequest, HttpContext context, XacmlContextResponse contextResponse, CancellationToken cancellationToken = default); } } diff --git a/src/Authorization/Services/Interface/IParties.cs b/src/Authorization/Services/Interface/IParties.cs index 860ae0f0..569c410f 100644 --- a/src/Authorization/Services/Interface/IParties.cs +++ b/src/Authorization/Services/Interface/IParties.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Altinn.Platform.Authorization.Models; using Altinn.Platform.Register.Models; @@ -14,36 +15,41 @@ public interface IParties /// Method that fetches parties list based on user id /// /// The user id + /// The cancellationToken /// list of parties that the logged in user can represent - Task> GetParties(int userId); + Task> GetParties(int userId, CancellationToken cancellationToken = default); /// /// Method that fetches a given party /// /// The party id + /// The cancellationToken /// The party - Task GetParty(int partyId); + Task GetParty(int partyId, CancellationToken cancellationToken = default); /// /// Method that fetches a list of PartyIds the given user id has key role access to (where the user inherit delegations to their organization) /// /// The user id + /// The cancellationToken /// list of PartyIds where the logged in user have key role access - Task> GetKeyRoleParties(int userId); + Task> GetKeyRoleParties(int userId, CancellationToken cancellationToken = default); /// /// Method that fetches a list of main units for the input list of sub unit partyIds. If any of the input partyIds are not a sub unit the response model will have null values for main unit properties. /// /// The list of PartyIds to check and retrieve any main units for + /// The cancellationToken /// list of main units - Task> GetMainUnits(MainUnitQuery subunitPartyIds); + Task> GetMainUnits(MainUnitQuery subunitPartyIds, CancellationToken cancellationToken = default); /// /// Verifies that the selected party is contained in the user's party list /// /// The user id" /// The party id" + /// The cancellationToken /// Boolean indicating whether or not the user can represent the selected party. - Task ValidateSelectedParty(int userId, int partyId); + Task ValidateSelectedParty(int userId, int partyId, CancellationToken cancellationToken = default); } } diff --git a/src/Authorization/Services/Interface/IPolicyRetrievalPoint.cs b/src/Authorization/Services/Interface/IPolicyRetrievalPoint.cs index 7e9cd521..f33493ba 100644 --- a/src/Authorization/Services/Interface/IPolicyRetrievalPoint.cs +++ b/src/Authorization/Services/Interface/IPolicyRetrievalPoint.cs @@ -1,3 +1,4 @@ +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Xacml; @@ -28,7 +29,8 @@ public interface IPolicyRetrievalPoint /// /// The blobstorage path to the policy file /// The specific blob storage version to get + /// The cancellationToken /// XacmlPolicy and ETag tuple - Task GetPolicyVersionAsync(string policyPath, string version); + Task GetPolicyVersionAsync(string policyPath, string version, CancellationToken cancellationToken = default); } } diff --git a/src/Authorization/Services/Interface/IProfile.cs b/src/Authorization/Services/Interface/IProfile.cs index e1f4d956..56c5e654 100644 --- a/src/Authorization/Services/Interface/IProfile.cs +++ b/src/Authorization/Services/Interface/IProfile.cs @@ -14,5 +14,10 @@ public interface IProfile /// The user id /// The user profile Task GetUserProfile(int userId); + + /// + /// Method that fetches the user profile for a given ssn + /// + Task GetUserProfileBySSN(string ssn); } } diff --git a/test/IntegrationTests/Altinn.Platform.Authorization.IntegrationTests.csproj b/test/IntegrationTests/Altinn.Platform.Authorization.IntegrationTests.csproj index 5b19511c..37d84485 100644 --- a/test/IntegrationTests/Altinn.Platform.Authorization.IntegrationTests.csproj +++ b/test/IntegrationTests/Altinn.Platform.Authorization.IntegrationTests.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -119,6 +119,20 @@ + + + + + + + + + + + + + + @@ -130,9 +144,53 @@ + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + Always diff --git a/test/IntegrationTests/AltinnApps_DecisionTests.cs b/test/IntegrationTests/AltinnApps_DecisionTests.cs index c1c9e87d..ec5a6d79 100644 --- a/test/IntegrationTests/AltinnApps_DecisionTests.cs +++ b/test/IntegrationTests/AltinnApps_DecisionTests.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Interface; using Altinn.Authorization.ABAC.Xacml; @@ -47,7 +48,7 @@ public async Task PDP_Decision_AltinnApps0001() .Setup(m => m.IsEnabledAsync("AuditLog")) .Returns(Task.FromResult(true)); Mock eventQueue = new Mock(); - eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny())); + eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny(), It.IsAny())); AuthorizationEvent expectedAuthorizationEvent = TestSetupUtil.GetAuthorizationEvent(testCase); HttpClient client = GetTestClient(eventQueue.Object, featureManageMock.Object, systemClock.Object); @@ -74,7 +75,7 @@ public async Task PDP_Decision_AltinnApps0001_Auditlog_Off() .Returns(Task.FromResult(false)); Mock eventQueue = new Mock(); - eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny())); + eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny(), It.IsAny())); AuthorizationEvent expectedAuthorizationEvent = TestSetupUtil.GetAuthorizationEvent(testCase); HttpClient client = GetTestClient(eventQueue.Object, featureManageMock.Object); @@ -99,7 +100,7 @@ public async Task PDP_Decision_AltinnApps0007() .Setup(m => m.IsEnabledAsync("AuditLog")) .Returns(Task.FromResult(true)); Mock eventQueue = new Mock(); - eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny())); + eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny(), It.IsAny())); AuthorizationEvent expectedAuthorizationEvent = TestSetupUtil.GetAuthorizationEvent(testCase); HttpClient client = GetTestClient(eventQueue.Object, featureManageMock.Object, systemClock.Object); @@ -154,7 +155,7 @@ public async Task PDP_Decision_AltinnApps0004() .Setup(m => m.IsEnabledAsync("AuditLog")) .Returns(Task.FromResult(true)); Mock eventQueue = new Mock(); - eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny())); + eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny(), It.IsAny())); AuthorizationEvent expectedAuthorizationEvent = TestSetupUtil.GetAuthorizationEvent(testCase); HttpClient client = GetTestClient(eventQueue.Object, featureManageMock.Object, systemClock.Object); @@ -240,7 +241,7 @@ public async Task PDP_Decision_AltinnApps0010() .Setup(m => m.IsEnabledAsync("AuditLog")) .Returns(Task.FromResult(true)); Mock eventQueue = new Mock(); - eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny())); + eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny(), It.IsAny())); AuthorizationEvent expectedAuthorizationEvent = null; HttpClient client = GetTestClient(); diff --git a/test/IntegrationTests/ContextHandlerTest.cs b/test/IntegrationTests/ContextHandlerTest.cs index 01d08c2f..7195f313 100644 --- a/test/IntegrationTests/ContextHandlerTest.cs +++ b/test/IntegrationTests/ContextHandlerTest.cs @@ -59,7 +59,7 @@ public async Task ContextHandler_TC01() XacmlContextRequest expectedEnrichedRequest = TestSetupUtil.GetEnrichedRequest(testCase); // Act - XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request); + XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request, false); // Assert Assert.NotNull(enrichedRequest); @@ -88,7 +88,7 @@ public async Task ContextHandler_TC02() XacmlContextRequest expectedEnrichedRequest = TestSetupUtil.GetEnrichedRequest(testCase); // Act - XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request); + XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request, false); // Assert Assert.NotNull(enrichedRequest); @@ -117,7 +117,7 @@ public async Task ContextHandler_TC03() XacmlContextRequest expectedEnrichedRequest = TestSetupUtil.GetEnrichedRequest(testCase); // Act - XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request); + XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request, false); // Assert Assert.NotNull(enrichedRequest); @@ -146,7 +146,7 @@ public async Task ContextHandler_TC04() XacmlContextRequest expectedEnrichedRequest = TestSetupUtil.GetEnrichedRequest(testCase); // Act - XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request); + XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request, false); // Assert Assert.NotNull(enrichedRequest); @@ -175,7 +175,7 @@ public async Task ContextHandler_TC05() XacmlContextRequest expectedEnrichedRequest = TestSetupUtil.GetEnrichedRequest(testCase); // Act - XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request); + XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request, false); // Assert Assert.NotNull(enrichedRequest); @@ -204,7 +204,7 @@ public async Task ContextHandler_TC06() XacmlContextRequest expectedEnrichedRequest = TestSetupUtil.GetEnrichedRequest(testCase); // Act - XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request); + XacmlContextRequest enrichedRequest = await _contextHandler.Enrich(request, false); // Assert Assert.NotNull(enrichedRequest); diff --git a/test/IntegrationTests/Data/Register/1.json b/test/IntegrationTests/Data/Register/1.json index d4b2c631..81871bd2 100644 --- a/test/IntegrationTests/Data/Register/1.json +++ b/test/IntegrationTests/Data/Register/1.json @@ -91,7 +91,7 @@ "childParties": null }, { - "partyId": "54321", + "partyId": "501337", "partyTypeName": 1, "orgNumber": "null", "ssn": "01039012345", diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0003Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0003Request.json index 8a103cf7..96d2d48a 100644 --- a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0003Request.json +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0003Request.json @@ -30,7 +30,7 @@ "Value": "apidelegation" }, { - "AttributeId": "urn:altinn:organizationnumber", + "AttributeId": "urn:altinn:organization:identifier-no", "Value": "950474084" } ] diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0005Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0005Request.json new file mode 100644 index 00000000..5306d1ed --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0005Request.json @@ -0,0 +1,40 @@ +{ + "Request": { + "ReturnPolicyIdList": true, + "AccessSubject": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:person:identifier-no", + "Value": "01039012345" + } + ] + } + ], + "Action": [ + { + "Attribute": [ + { + "AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "Value": "read", + "DataType": "http://www.w3.org/2001/XMLSchema#string" + } + ] + } + ], + "Resource": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:resource", + "Value": "ttd-externalpdp-resource1" + }, + { + "AttributeId": "urn:altinn:organization:identifier-no", + "Value": "950474084" + } + ] + } + ] + } +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0005Response.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0005Response.json new file mode 100644 index 00000000..2a9de976 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0005Response.json @@ -0,0 +1,27 @@ +{ + "Response": [ + { + "Decision": "Permit", + "Status": { + "StatusCode": { + "Value": "urn:oasis:names:tc:xacml:1.0:status:ok" + } + }, + "Obligations": [ + { + "id": "urn:altinn:obligation:authenticationLevel1", + "attributeAssignment": [ + + { + "attributeId": "urn:altinn:obligation-assignment:1", + "value": "2", + "category": "urn:altinn:minimum-authenticationlevel", + "dataType": "http://www.w3.org/2001/XMLSchema#integer", + "issuer": null + } + ] + } + ] + } + ] +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0006Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0006Request.json new file mode 100644 index 00000000..7f7ba2bb --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0006Request.json @@ -0,0 +1,40 @@ +{ + "Request": { + "ReturnPolicyIdList": true, + "AccessSubject": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:person:identifier-no", + "Value": "01039012345" + } + ] + } + ], + "Action": [ + { + "Attribute": [ + { + "AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "Value": "read", + "DataType": "http://www.w3.org/2001/XMLSchema#string" + } + ] + } + ], + "Resource": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:resource", + "Value": "ttd-externalpdp-resource1" + }, + { + "AttributeId": "urn:altinn:person:identifier-no", + "Value": "01039012345" + } + ] + } + ] + } +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0006Response.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0006Response.json new file mode 100644 index 00000000..2a9de976 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0006Response.json @@ -0,0 +1,27 @@ +{ + "Response": [ + { + "Decision": "Permit", + "Status": { + "StatusCode": { + "Value": "urn:oasis:names:tc:xacml:1.0:status:ok" + } + }, + "Obligations": [ + { + "id": "urn:altinn:obligation:authenticationLevel1", + "attributeAssignment": [ + + { + "attributeId": "urn:altinn:obligation-assignment:1", + "value": "2", + "category": "urn:altinn:minimum-authenticationlevel", + "dataType": "http://www.w3.org/2001/XMLSchema#integer", + "issuer": null + } + ] + } + ] + } + ] +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0007Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0007Request.json new file mode 100644 index 00000000..5e78c96c --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0007Request.json @@ -0,0 +1,40 @@ +{ + "Request": { + "ReturnPolicyIdList": true, + "AccessSubject": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:person:identifier-no", + "Value": "01039012345" + } + ] + } + ], + "Action": [ + { + "Attribute": [ + { + "AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "Value": "read", + "DataType": "http://www.w3.org/2001/XMLSchema#string" + } + ] + } + ], + "Resource": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:resource", + "Value": "ttd-externalpdp-resource1" + }, + { + "AttributeId": "urn:altinn:person:identifier-no", + "Value": "01038712345" + } + ] + } + ] + } +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0007Response.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0007Response.json new file mode 100644 index 00000000..14cba3aa --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0007Response.json @@ -0,0 +1,12 @@ +{ + "Response": [ + { + "Decision": "NotApplicable", + "Status": { + "StatusCode": { + "Value": "urn:oasis:names:tc:xacml:1.0:status:ok" + } + } + } + ] +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0008Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0008Request.json new file mode 100644 index 00000000..cc325b0a --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0008Request.json @@ -0,0 +1,40 @@ +{ + "Request": { + "ReturnPolicyIdList": true, + "AccessSubject": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:person:identifier-no", + "Value": "01039012345" + } + ] + } + ], + "Action": [ + { + "Attribute": [ + { + "AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "Value": "nukerocket", + "DataType": "http://www.w3.org/2001/XMLSchema#string" + } + ] + } + ], + "Resource": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:resource", + "Value": "ttd-externalpdp-resource1" + }, + { + "AttributeId": "urn:altinn:person:identifier-no", + "Value": "01039012345" + } + ] + } + ] + } +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0008Response.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0008Response.json new file mode 100644 index 00000000..14cba3aa --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0008Response.json @@ -0,0 +1,12 @@ +{ + "Response": [ + { + "Decision": "NotApplicable", + "Status": { + "StatusCode": { + "Value": "urn:oasis:names:tc:xacml:1.0:status:ok" + } + } + } + ] +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0009Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0009Request.json new file mode 100644 index 00000000..e333f233 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0009Request.json @@ -0,0 +1,40 @@ +{ + "Request": { + "ReturnPolicyIdList": true, + "AccessSubject": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:organization:identifier-no", + "Value": "991825827" + } + ] + } + ], + "Action": [ + { + "Attribute": [ + { + "AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "Value": "read", + "DataType": "http://www.w3.org/2001/XMLSchema#string" + } + ] + } + ], + "Resource": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:resource", + "Value": "ttd-externalpdp-resource2" + }, + { + "AttributeId": "urn:altinn:organization:identifier-no", + "Value": "950474084" + } + ] + } + ] + } +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0009Response.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0009Response.json new file mode 100644 index 00000000..2a9de976 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0009Response.json @@ -0,0 +1,27 @@ +{ + "Response": [ + { + "Decision": "Permit", + "Status": { + "StatusCode": { + "Value": "urn:oasis:names:tc:xacml:1.0:status:ok" + } + }, + "Obligations": [ + { + "id": "urn:altinn:obligation:authenticationLevel1", + "attributeAssignment": [ + + { + "attributeId": "urn:altinn:obligation-assignment:1", + "value": "2", + "category": "urn:altinn:minimum-authenticationlevel", + "dataType": "http://www.w3.org/2001/XMLSchema#integer", + "issuer": null + } + ] + } + ] + } + ] +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0010Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0010Request.json new file mode 100644 index 00000000..490e5812 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0010Request.json @@ -0,0 +1,40 @@ +{ + "Request": { + "ReturnPolicyIdList": true, + "AccessSubject": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:organization:identifier-no", + "Value": "889640782" + } + ] + } + ], + "Action": [ + { + "Attribute": [ + { + "AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "Value": "read", + "DataType": "http://www.w3.org/2001/XMLSchema#string" + } + ] + } + ], + "Resource": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:resource", + "Value": "ttd-externalpdp-resource2" + }, + { + "AttributeId": "urn:altinn:organization:identifier-no", + "Value": "950474084" + } + ] + } + ] + } +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0010Response.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0010Response.json new file mode 100644 index 00000000..14cba3aa --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0010Response.json @@ -0,0 +1,12 @@ +{ + "Response": [ + { + "Decision": "NotApplicable", + "Status": { + "StatusCode": { + "Value": "urn:oasis:names:tc:xacml:1.0:status:ok" + } + } + } + ] +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0011Request.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0011Request.json new file mode 100644 index 00000000..4034f2dc --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0011Request.json @@ -0,0 +1,40 @@ +{ + "Request": { + "ReturnPolicyIdList": true, + "AccessSubject": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:organizationnumber", + "Value": "991825827" + } + ] + } + ], + "Action": [ + { + "Attribute": [ + { + "AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id", + "Value": "read", + "DataType": "http://www.w3.org/2001/XMLSchema#string" + } + ] + } + ], + "Resource": [ + { + "Attribute": [ + { + "AttributeId": "urn:altinn:resource", + "Value": "ttd-externalpdp-resource2" + }, + { + "AttributeId": "urn:altinn:organization:identifier-no", + "Value": "950474084" + } + ] + } + ] + } +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0011Response.json b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0011Response.json new file mode 100644 index 00000000..2a9de976 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistry0011Response.json @@ -0,0 +1,27 @@ +{ + "Response": [ + { + "Decision": "Permit", + "Status": { + "StatusCode": { + "Value": "urn:oasis:names:tc:xacml:1.0:status:ok" + } + }, + "Obligations": [ + { + "id": "urn:altinn:obligation:authenticationLevel1", + "attributeAssignment": [ + + { + "attributeId": "urn:altinn:obligation-assignment:1", + "value": "2", + "category": "urn:altinn:minimum-authenticationlevel", + "dataType": "http://www.w3.org/2001/XMLSchema#integer", + "issuer": null + } + ] + } + ] + } + ] +} diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/ttd-externalpdp-resource1/policy.xml b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/ttd-externalpdp-resource1/policy.xml new file mode 100644 index 00000000..24afc4c3 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/ttd-externalpdp-resource1/policy.xml @@ -0,0 +1,64 @@ + + + + + A rule giving user with role REGNA or DAGL and the ttd the right to pulish and subscribe to events registered to the altinn.test.events resource + + + + + REGNA + + + + + + DAGL + + + + + + MEDL + + + + + + ttd + + + + + + + + ttd-externalpdp-resource1 + + + + + + + + read + + + + + + write + + + + + + + + + + 2 + + + + \ No newline at end of file diff --git a/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/ttd-externalpdp-resource2/policy.xml b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/ttd-externalpdp-resource2/policy.xml new file mode 100644 index 00000000..f999eea6 --- /dev/null +++ b/test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/ttd-externalpdp-resource2/policy.xml @@ -0,0 +1,58 @@ + + + + + A rule giving user with role REGNA or DAGL and the ttd the right to pulish and subscribe to events registered to the altinn.test.events resource + + + + + 991825827 + + + + + + 991825827 + + + + + + ttd + + + + + + + + ttd-externalpdp-resource2 + + + + + + + + read + + + + + + write + + + + + + + + + + 2 + + + + \ No newline at end of file diff --git a/test/IntegrationTests/DelegationsControllerTest.cs b/test/IntegrationTests/DelegationsControllerTest.cs index 65abc5a6..da8609ec 100644 --- a/test/IntegrationTests/DelegationsControllerTest.cs +++ b/test/IntegrationTests/DelegationsControllerTest.cs @@ -6,7 +6,6 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; -using Altinn.Authorization.ABAC.Interface; using Altinn.Platform.Authorization.Constants; using Altinn.Platform.Authorization.Controllers; using Altinn.Platform.Authorization.IntegrationTests.Data; diff --git a/test/IntegrationTests/ExternalDecisionTest.cs b/test/IntegrationTests/ExternalDecisionTest.cs new file mode 100644 index 00000000..07248968 --- /dev/null +++ b/test/IntegrationTests/ExternalDecisionTest.cs @@ -0,0 +1,224 @@ +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Altinn.Authorization.ABAC.Interface; +using Altinn.Authorization.ABAC.Xacml; +using Altinn.Authorization.ABAC.Xacml.JsonProfile; +using Altinn.Common.Authentication.Configuration; +using Altinn.Platform.Authorization.Controllers; +using Altinn.Platform.Authorization.IntegrationTests.MockServices; +using Altinn.Platform.Authorization.IntegrationTests.Util; +using Altinn.Platform.Authorization.IntegrationTests.Webfactory; +using Altinn.Platform.Authorization.Repositories.Interface; +using Altinn.Platform.Authorization.Services.Interface; +using Altinn.Platform.Authorization.Services.Interfaces; +using Altinn.Platform.Events.Tests.Mocks; +using Altinn.ResourceRegistry.Tests.Mocks; +using AltinnCore.Authentication.JwtCookie; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Altinn.Platform.Authorization.IntegrationTests +{ + public class ExternalDecisionTest :IClassFixture> + { + private readonly CustomWebApplicationFactory _factory; + + public ExternalDecisionTest(CustomWebApplicationFactory fixture) + { + _factory = fixture; + } + + [Fact] + public async Task PDPExternal_Decision_AltinnApps0008() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnApps0008"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + [Fact] + public async Task PDPExternal_Decision_AltinnApps0010() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnApps0010"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + [Fact] + public async Task PDPExternal_Decision_AltinnResourceRegistry0005() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnResourceRegistry0005"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + [Fact] + public async Task PDPExternal_Decision_AltinnResourceRegistry0006() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + + string testCase = "AltinnResourceRegistry0006"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + [Fact] + public async Task PDPExternal_Decision_AltinnResourceRegistry0007() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnResourceRegistry0007"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + [Fact] + public async Task PDPExternal_Decision_AltinnResourceRegistry0008() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnResourceRegistry0008"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + /// + /// Scenario where org is listed in policy. Should work. Policy org is digir. Authz subject org is digdir + /// + [Fact] + public async Task PDPExternal_Decision_AltinnResourceRegistry0009() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnResourceRegistry0009"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + /// + /// Scenario where org is listed in policy. Should not work. Policy org is digir. Authz subject org is nav + /// + [Fact] + public async Task PDPExternal_Decision_AltinnResourceRegistry0010() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnResourceRegistry0010"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + /// + /// Scenario where org is listed in policy. Should work. Policy org is digir. Authz subject org is digdir. Uses old attribute id + /// + [Fact] + public async Task PDPExternal_Decision_AltinnResourceRegistry0011() + { + string token = PrincipalUtil.GetOrgToken("skd", "974761076", "altinn:authorization:pdp"); + string testCase = "AltinnResourceRegistry0011"; + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); + HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequestExternal(testCase); + XacmlJsonResponse expected = TestSetupUtil.ReadExpectedJsonProfileResponse(testCase); + + // Act + XacmlJsonResponse contextResponse = await TestSetupUtil.GetXacmlJsonProfileContextResponseAsync(client, httpRequestMessage); + + // Assert + AssertionUtil.AssertEqual(expected, contextResponse); + } + + private HttpClient GetTestClient() + { + HttpClient client = _factory.WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton, OidcProviderPostConfigureSettingsStub>(); + services.AddSingleton(); + }); + }).CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); + + return client; + } + } +} diff --git a/test/IntegrationTests/MockServices/ContextHandlerMock.cs b/test/IntegrationTests/MockServices/ContextHandlerMock.cs index b9ec64fb..c390403d 100644 --- a/test/IntegrationTests/MockServices/ContextHandlerMock.cs +++ b/test/IntegrationTests/MockServices/ContextHandlerMock.cs @@ -6,7 +6,6 @@ using System.Xml; using Altinn.Authorization.ABAC.Constants; -using Altinn.Authorization.ABAC.Interface; using Altinn.Authorization.ABAC.Utils; using Altinn.Authorization.ABAC.Xacml; @@ -49,12 +48,12 @@ public ContextHandlerMock(IHttpContextAccessor httpContextAccessor, IRoles roles _rolesWrapper = rolesWrapper; } - public async Task Enrich(XacmlContextRequest decisionRequest) + public async Task Enrich(XacmlContextRequest request, bool isExternalRequest) { string testID = GetTestId(_httpContextAccessor.HttpContext); if (!string.IsNullOrEmpty(testID) && testID.ToLower().Contains("altinnapps")) { - await EnrichResourceAttributes(decisionRequest); + await EnrichResourceAttributes(request); } else { @@ -67,7 +66,7 @@ public async Task Enrich(XacmlContextRequest decisionReques } } - return decisionRequest; + return request; } private async Task EnrichResourceAttributes(XacmlContextRequest request) diff --git a/test/IntegrationTests/MockServices/OidcProviderPostConfigureSettingsStub.cs b/test/IntegrationTests/MockServices/OidcProviderPostConfigureSettingsStub.cs new file mode 100644 index 00000000..8b5867a4 --- /dev/null +++ b/test/IntegrationTests/MockServices/OidcProviderPostConfigureSettingsStub.cs @@ -0,0 +1,21 @@ +using Altinn.Common.Authentication.Configuration; +using Altinn.Common.Authentication.Models; +using Microsoft.Extensions.Options; + +namespace Altinn.ResourceRegistry.Tests.Mocks +{ + public class OidcProviderPostConfigureSettingsStub : IPostConfigureOptions + { + public void PostConfigure(string name, OidcProviderSettings options) + { + OidcProvider provider = new OidcProvider(); + provider.Issuer = "www.altinn.no"; + provider.WellKnownConfigEndpoint = "https://testEndpoint.no"; + + options = new OidcProviderSettings() + { + { "altinn", provider } + }; + } + } +} diff --git a/test/IntegrationTests/MockServices/OidcProviderSettingsStub.cs b/test/IntegrationTests/MockServices/OidcProviderSettingsStub.cs new file mode 100644 index 00000000..2e4a8d8b --- /dev/null +++ b/test/IntegrationTests/MockServices/OidcProviderSettingsStub.cs @@ -0,0 +1,31 @@ +using System.Threading; +using System.Threading.Tasks; +using Altinn.Common.Authentication.Configuration; +using Altinn.Common.Authentication.Models; +using Microsoft.IdentityModel.Protocols; + +namespace Altinn.ResourceRegistry.Tests.Mocks +{ + public class OidcProviderSettingsStub : IConfigurationManager + { + /// + public Task GetConfigurationAsync(CancellationToken cancel) + { + OidcProvider provider = new OidcProvider(); + provider.Issuer = "www.altinn.no"; + provider.WellKnownConfigEndpoint = "https://testEndpoint.no"; + + OidcProviderSettings settings = new OidcProviderSettings() + { + { "altinn", provider } + }; + + return Task.FromResult(settings); + } + + public void RequestRefresh() + { + throw new System.NotImplementedException(); + } + } +} diff --git a/test/IntegrationTests/MockServices/PartiesMock.cs b/test/IntegrationTests/MockServices/PartiesMock.cs index 2202d406..0687a38e 100644 --- a/test/IntegrationTests/MockServices/PartiesMock.cs +++ b/test/IntegrationTests/MockServices/PartiesMock.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Altinn.Platform.Authorization.Models; using Altinn.Platform.Authorization.Services.Interface; @@ -12,7 +13,7 @@ namespace Altinn.Platform.Authorization.IntegrationTests.MockServices { public class PartiesMock : IParties { - public Task> GetKeyRoleParties(int userId) + public Task> GetKeyRoleParties(int userId, CancellationToken cancellationToken = default) { List result = new List(); switch (userId) @@ -27,7 +28,7 @@ public Task> GetKeyRoleParties(int userId) return Task.FromResult(result); } - public Task> GetMainUnits(MainUnitQuery subunitPartyIds) + public Task> GetMainUnits(MainUnitQuery subunitPartyIds, CancellationToken cancellationToken = default) { List result = new List(); foreach (int subunitPartyId in subunitPartyIds.PartyIds) @@ -45,12 +46,12 @@ public Task> GetMainUnits(MainUnitQuery subunitPartyIds) return Task.FromResult(result); } - public Task> GetParties(int userId) + public Task> GetParties(int userId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public Task GetParty(int partyId) + public Task GetParty(int partyId, CancellationToken cancellationToken = default) { Party party = null; if (partyId == 50740574) @@ -61,7 +62,7 @@ public Task GetParty(int partyId) return Task.FromResult(party); } - public Task ValidateSelectedParty(int userId, int partyId) + public Task ValidateSelectedParty(int userId, int partyId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/test/IntegrationTests/MockServices/PolicyRepositoryMock.cs b/test/IntegrationTests/MockServices/PolicyRepositoryMock.cs index d2352354..579ca25c 100644 --- a/test/IntegrationTests/MockServices/PolicyRepositoryMock.cs +++ b/test/IntegrationTests/MockServices/PolicyRepositoryMock.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Net; +using System.Threading; using System.Threading.Tasks; using Altinn.Platform.Authorization.Repositories.Interface; @@ -20,27 +21,27 @@ public PolicyRepositoryMock(ILogger logger) _logger = logger; } - public Task GetPolicyAsync(string filepath) + public Task GetPolicyAsync(string filepath, CancellationToken cancellationToken = default) { return Task.FromResult(GetTestDataStream(filepath)); } - public Task GetPolicyVersionAsync(string filepath, string version) + public Task GetPolicyVersionAsync(string filepath, string version, CancellationToken cancellationToken = default) { return Task.FromResult(GetTestDataStream(filepath)); } - public Task> WritePolicyAsync(string filepath, Stream fileStream) + public Task> WritePolicyAsync(string filepath, Stream fileStream, CancellationToken cancellationToken = default) { return WriteStreamToTestDataFolder(filepath, fileStream); } - public Task DeletePolicyVersionAsync(string filepath, string version) + public Task DeletePolicyVersionAsync(string filepath, string version, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public async Task> WritePolicyConditionallyAsync(string filepath, Stream fileStream, string blobLeaseId) + public async Task> WritePolicyConditionallyAsync(string filepath, Stream fileStream, string blobLeaseId, CancellationToken cancellationToken = default) { if (blobLeaseId == "CorrectLeaseId" && !filepath.Contains("error/blobstorageleaselockwritefail")) { @@ -50,7 +51,7 @@ public async Task> WritePolicyConditionallyAsync(strin throw new RequestFailedException((int)HttpStatusCode.PreconditionFailed, "The condition specified using HTTP conditional header(s) is not met."); } - public Task TryAcquireBlobLease(string filepath) + public Task TryAcquireBlobLease(string filepath, CancellationToken cancellationToken = default) { if (filepath.Contains("error/blobstoragegetleaselockfail")) { @@ -64,7 +65,7 @@ public void ReleaseBlobLease(string filepath, string leaseId) { } - public Task PolicyExistsAsync(string filepath) + public Task PolicyExistsAsync(string filepath, CancellationToken cancellationToken = default) { string fullpath = Path.Combine(GetDataInputBlobPath(), filepath); diff --git a/test/IntegrationTests/MockServices/PolicyRetrievalPointMock.cs b/test/IntegrationTests/MockServices/PolicyRetrievalPointMock.cs index 59ba2929..386bafc9 100644 --- a/test/IntegrationTests/MockServices/PolicyRetrievalPointMock.cs +++ b/test/IntegrationTests/MockServices/PolicyRetrievalPointMock.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -23,7 +24,7 @@ public class PolicyRetrievalPointMock : IPolicyRetrievalPoint private readonly string _appAttributeId = "urn:altinn:app"; - private readonly string _resourceregistryAttributeId = "urn:altinn:resourceregistry"; + private readonly string _resourceregistryAttributeId = "urn:altinn:resource"; public PolicyRetrievalPointMock(IHttpContextAccessor httpContextAccessor, ILogger logger) { @@ -68,7 +69,7 @@ public async Task GetPolicyAsync(string org, string app) return null; } - public async Task GetPolicyVersionAsync(string policyPath, string version) + public async Task GetPolicyVersionAsync(string policyPath, string version, CancellationToken cancellationToken = default) { string path = GetAltinnAppsDelegationPolicyPath(policyPath); if (File.Exists(path)) diff --git a/test/IntegrationTests/MockServices/ProfileMock.cs b/test/IntegrationTests/MockServices/ProfileMock.cs index 6ea015f9..3e7cfb31 100644 --- a/test/IntegrationTests/MockServices/ProfileMock.cs +++ b/test/IntegrationTests/MockServices/ProfileMock.cs @@ -22,5 +22,24 @@ public Task GetUserProfile(int userId) return Task.FromResult(userProfile); } + + public Task GetUserProfileBySSN(string ssn) + { + UserProfile userProfile = null; + if (ssn == "13923949741") + { + userProfile = new UserProfile { Party = new Party { SSN = "13923949741" } }; + } + else if (ssn == "13371337133") + { + userProfile = new UserProfile { Party = new Party { SSN = "13371337133" } }; + } + else if (ssn == "01039012345") + { + userProfile = new UserProfile { Party = new Party { SSN = "01039012345", PartyId = 1337 }, UserId = 1337 }; + } + + return Task.FromResult(userProfile); + } } } diff --git a/test/IntegrationTests/PartiesControllerTest.cs b/test/IntegrationTests/PartiesControllerTest.cs index fde15a61..93691325 100644 --- a/test/IntegrationTests/PartiesControllerTest.cs +++ b/test/IntegrationTests/PartiesControllerTest.cs @@ -2,7 +2,6 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; -using Altinn.Authorization.ABAC.Interface; using Altinn.Platform.Authorization.Controllers; using Altinn.Platform.Authorization.IntegrationTests.MockServices; using Altinn.Platform.Authorization.IntegrationTests.Util; diff --git a/test/IntegrationTests/PolicyControllerTest.cs b/test/IntegrationTests/PolicyControllerTest.cs index 913894a5..1c8bcaf6 100644 --- a/test/IntegrationTests/PolicyControllerTest.cs +++ b/test/IntegrationTests/PolicyControllerTest.cs @@ -6,7 +6,6 @@ using System.Net.Http.Headers; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Constants; -using Altinn.Authorization.ABAC.Interface; using Altinn.Platform.Authorization.Constants; using Altinn.Platform.Authorization.Controllers; using Altinn.Platform.Authorization.IntegrationTests.Data; diff --git a/test/IntegrationTests/ResourceRegistry_DecisionTests.cs b/test/IntegrationTests/ResourceRegistry_DecisionTests.cs index 08fd86a9..4023aa87 100644 --- a/test/IntegrationTests/ResourceRegistry_DecisionTests.cs +++ b/test/IntegrationTests/ResourceRegistry_DecisionTests.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using Altinn.Authorization.ABAC.Xacml; using Altinn.Authorization.ABAC.Xacml.JsonProfile; @@ -103,7 +104,7 @@ public async Task PDP_Decision_ResourceRegistry0001() { string testCase = "AltinnResourceRegistry0001"; Mock eventQueue = new Mock(); - eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny())); + eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny(), It.IsAny())); AuthorizationEvent expectedAuthorizationEvent = TestSetupUtil.GetAuthorizationEvent(testCase); HttpClient client = GetTestClient(eventQueue.Object, featureManageMock.Object, systemClock.Object); HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateXacmlRequest(testCase); @@ -152,7 +153,7 @@ public async Task PDP_Decision_ResourceRegistry0004() { string testCase = "AltinnResourceRegistry0004"; Mock eventQueue = new Mock(); - eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny())); + eventQueue.Setup(q => q.EnqueueAuthorizationEvent(It.IsAny(), It.IsAny())); AuthorizationEvent expectedAuthorizationEvent = TestSetupUtil.GetAuthorizationEvent(testCase); HttpClient client = GetTestClient(eventQueue.Object, featureManageMock.Object, systemClock.Object); HttpRequestMessage httpRequestMessage = TestSetupUtil.CreateJsonProfileXacmlRequest(testCase); diff --git a/test/IntegrationTests/Util/AssertionUtil.cs b/test/IntegrationTests/Util/AssertionUtil.cs index 14f67ade..993d6fcb 100644 --- a/test/IntegrationTests/Util/AssertionUtil.cs +++ b/test/IntegrationTests/Util/AssertionUtil.cs @@ -5,6 +5,7 @@ using System.Net; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading; using Altinn.Authorization.ABAC.Xacml; using Altinn.Authorization.ABAC.Xacml.JsonProfile; using Altinn.Platform.Authorization.Clients.Interfaces; @@ -281,7 +282,7 @@ public static void AssertAuthorizationEvent(Mock eventQueue, string serializedAuthorizationEvent = JsonSerializer.Serialize(expectedAuthorizationEvent, options); eventQueue.Verify( e => e.EnqueueAuthorizationEvent( - It.Is(q => q == serializedAuthorizationEvent)), + It.Is(q => q == serializedAuthorizationEvent), It.IsAny()), numberOfTimes); } diff --git a/test/IntegrationTests/Util/PrincipalUtil.cs b/test/IntegrationTests/Util/PrincipalUtil.cs index 0e101a88..c76f4dd1 100644 --- a/test/IntegrationTests/Util/PrincipalUtil.cs +++ b/test/IntegrationTests/Util/PrincipalUtil.cs @@ -47,6 +47,49 @@ public static string GetOrgToken(string org, int orgNumber = 111111111, string s return token; } + public static string GetOrgToken(string org, string orgNumber = "991825827", string? scope = null, string[]? prefixes = null) + { + ClaimsPrincipal principal = GetClaimsPrincipal(org, orgNumber, scope, prefixes); + + string token = JwtTokenMock.GenerateToken(principal, new TimeSpan(1, 1, 1)); + + return token; + } + + public static ClaimsPrincipal GetClaimsPrincipal(string org, string orgNumber, string? scope = null, string[]? prefixes = null) + { + string issuer = "www.altinn.no"; + + List claims = new List(); + if (!string.IsNullOrEmpty(org)) + { + claims.Add(new Claim(AltinnCoreClaimTypesOrg, org, ClaimValueTypes.String, issuer)); + } + + if (scope != null) + { + claims.Add(new Claim("scope", scope, ClaimValueTypes.String, "maskinporten")); + } + + if (prefixes is { Length: > 0 }) + { + foreach (string prefix in prefixes) + { + claims.Add(new Claim("consumer_prefix", prefix, ClaimValueTypes.String, "maskinporten")); + } + } + + claims.Add(new Claim(AltinnCoreClaimTypes.OrgNumber, orgNumber.ToString(), ClaimValueTypes.Integer32, issuer)); + claims.Add(new Claim(AltinnCoreClaimTypes.AuthenticateMethod, "Mock", ClaimValueTypes.String, issuer)); + claims.Add(new Claim(AltinnCoreClaimTypes.AuthenticationLevel, "3", ClaimValueTypes.Integer32, issuer)); + claims.Add(new Claim("consumer", GetOrgNoObject(orgNumber))); + + ClaimsIdentity identity = new ClaimsIdentity("mock-org"); + identity.AddClaims(claims); + + return new ClaimsPrincipal(identity); + } + public static string GetAccessToken(string appId) { List claims = new List(); @@ -63,5 +106,10 @@ public static string GetAccessToken(string appId) return token; } + + private static string GetOrgNoObject(string orgNo) + { + return $"{{ \"authority\":\"iso6523-actorid-upis\", \"ID\":\"0192:{orgNo}\"}}"; + } } } diff --git a/test/IntegrationTests/Util/TestSetupUtil.cs b/test/IntegrationTests/Util/TestSetupUtil.cs index a56cefa4..71cebb37 100644 --- a/test/IntegrationTests/Util/TestSetupUtil.cs +++ b/test/IntegrationTests/Util/TestSetupUtil.cs @@ -46,6 +46,33 @@ public static HttpRequestMessage CreateXacmlRequest(string testcase) return message; } + public static HttpRequestMessage CreateXacmlRequestExternal(string testcase) + { + string requestText; + + if (testcase.Contains("AltinnApps")) + { + requestText = File.ReadAllText(Path.Combine(GetAltinnAppsPath(), testcase + "Request.json")); + } + else if (testcase.Contains("ResourceRegistry")) + { + requestText = File.ReadAllText(Path.Combine(GetResourceRegistryPath(), testcase + "Request.json")); + } + else + { + requestText = File.ReadAllText(Path.Combine(GetConformancePath(), testcase + "Request.json")); + } + + HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, "authorization/api/v1/authorize") + { + Content = new StringContent(requestText, Encoding.UTF8, "application/json") + }; + message.Headers.Add("testcase", testcase); + message.Headers.Add("Accept", "application/json"); + + return message; + } + public static HttpRequestMessage CreateJsonProfileXacmlRequest(string testcase) { string requestText; diff --git a/test/IntegrationTests/Xacml30ConformanceTests.cs b/test/IntegrationTests/Xacml30ConformanceTests.cs index b3393c4d..11028f86 100644 --- a/test/IntegrationTests/Xacml30ConformanceTests.cs +++ b/test/IntegrationTests/Xacml30ConformanceTests.cs @@ -1,6 +1,5 @@ using System.Net.Http; using System.Threading.Tasks; -using Altinn.Authorization.ABAC.Interface; using Altinn.Authorization.ABAC.Xacml; using Altinn.Authorization.ABAC.Xacml.JsonProfile; using Altinn.Platform.Authorization.Controllers; diff --git a/test/IntegrationTests/appsettings.json b/test/IntegrationTests/appsettings.json index 7c8ccb87..b696c687 100644 --- a/test/IntegrationTests/appsettings.json +++ b/test/IntegrationTests/appsettings.json @@ -41,5 +41,11 @@ }, "FeatureManagement": { "AuditLog": false + }, + "OidcProviders": { + "altinn": { + "Issuer": "http://localhost:5101/authentication/api/v1/openid/", + "WellKnownConfigEndpoint": "http://localhost:5101/authentication/api/v1/openid/.well-known/openid-configuration" + } } }