Skip to content

Commit

Permalink
Feature/externalpdp (#639)
Browse files Browse the repository at this point in the history
* First models

* Fixed mapping

* Updated map for respons

* Test update

* Updates

* Test case 1

* Added more tests

* Updates testdata

* UJpdates attribute ID

* Testdata updates

* Updated to latest decision on attribute names

* Added Authentication and scope check

* Merge fix

* More merge fix

* Added scenario for org in policy

* Added scenario with old param

* Removed pep project

* Fixed coding issues

* Fixed testdata

* Code updates

* Fixed response

* Fixed path and cancellation token

* Added Cancellationtoken

* Fixed catch of cancellationtoken

* Code smell with missing token

* Code smell

* Allowed list

* Multipart test for external
  • Loading branch information
TheTechArch authored Feb 13, 2024
1 parent 5a9458d commit 3f0d4c0
Show file tree
Hide file tree
Showing 78 changed files with 1,813 additions and 152 deletions.
3 changes: 2 additions & 1 deletion .github/containerscan/allowedlist.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
general:
vulnerabilities:
- CVE-2021-24112
- CVE-2021-24112
- CVE-2019-0820
3 changes: 2 additions & 1 deletion src/Authorization/Altinn.Platform.Authorization.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
Expand All @@ -11,6 +11,7 @@
<PackageReference Include="Altinn.Common.AccessTokenClient" Version="2.0.1" />
<PackageReference Include="Altinn.Common.PEP" Version="1.3.0" />
<PackageReference Include="Altinn.Platform.Models" Version="1.4.0" />
<PackageReference Include="AutoMapper" Version="13.0.0" />
<PackageReference Include="Azure.Identity" Version="1.10.4" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.5.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" />
Expand Down
10 changes: 8 additions & 2 deletions src/Authorization/Clients/EventsQueueClient.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -38,12 +39,17 @@ public EventsQueueClient(
}

/// <inheritdoc/>
public async Task<QueuePostReceipt> EnqueueAuthorizationEvent(string content)
public async Task<QueuePostReceipt> 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)
{
Expand Down
4 changes: 3 additions & 1 deletion src/Authorization/Clients/Interfaces/IEventsQueueClient.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks;
using Altinn.Platform.Authorization.Models;

Expand All @@ -12,7 +13,8 @@ public interface IEventsQueueClient
/// Enqueues the provided content to the Event Log queue
/// </summary>
/// <param name="content">The content to push to the queue in string format</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>Returns a queue receipt</returns>
public Task<QueuePostReceipt> EnqueueAuthorizationEvent(string content);
public Task<QueuePostReceipt> EnqueueAuthorizationEvent(string content, CancellationToken cancellationToken = default);
}
}
10 changes: 10 additions & 0 deletions src/Authorization/Constants/AuthzConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,15 @@ public static class AuthzConstants
/// Policy tag for authorizing Altinn.Platform.Authorization API access from the DelegationEvent Azure function
/// </summary>
public const string DELEGATIONEVENT_FUNCTION_AUTHORIZATION = "DelegationEventFunctionAccess";

/// <summary>
/// Policy scope access
/// </summary>
public const string PDPSCOPEACCESS = "PDPScopeAccess";

/// <summary>
/// Scope that gives access to
/// </summary>
public const string PDP_SCOPE = "altinn:authorization:pdp";
}
}
12 changes: 11 additions & 1 deletion src/Authorization/Constants/XacmlRequestAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ public static class XacmlRequestAttribute
/// <summary>
/// xacml string that represents organization number
/// </summary>
public const string OrganizationNumberAttribute = "urn:altinn:organizationnumber";
public const string OrganizationNumberAttribute = "urn:altinn:organization:identifier-no";

/// <summary>
/// Legacu xacml string that represents organization number. Can be removed when all peps are updated
/// </summary>
public const string LegacyOrganizationNumberAttribute = "urn:altinn:organizationnumber";

/// <summary>
/// xacml string that represents user
Expand All @@ -64,5 +69,10 @@ public static class XacmlRequestAttribute
/// xacml string that represents resource
/// </summary>
public const string ResourceRegistryAttribute = "urn:altinn:resource";

/// <summary>
/// xacml string that represents ssn for
/// </summary>
public const string SsnAttribute = "urn:altinn:person:identifier-no";
}
}
77 changes: 56 additions & 21 deletions src/Authorization/Controllers/DecisionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
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;
using Altinn.Platform.Authorization.Constants;
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;
Expand All @@ -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
/// </summary>
[Route("authorization/api/v1/[controller]")]
[ApiController]
public class DecisionController : ControllerBase
{
Expand All @@ -49,6 +50,7 @@ public class DecisionController : ControllerBase
private readonly IEventLog _eventLog;
private readonly IFeatureManager _featureManager;
private readonly IAccessManagementWrapper _accessManagement;
private readonly IMapper _mapper;

/// <summary>
/// Initializes a new instance of the <see cref="DecisionController"/> class.
Expand All @@ -57,11 +59,13 @@ public class DecisionController : ControllerBase
/// <param name="contextHandler">The Context handler</param>
/// <param name="delegationContextHandler">The delegation context handler</param>
/// <param name="policyRetrievalPoint">The policy Retrieval point</param>
/// <param name="delegationRepository">The delegation repository</param>
/// <param name="logger">the logger</param>
/// <param name="memoryCache">memory cache</param>
/// <param name="eventLog">the authorization event logger</param>
/// <param name="featureManager">the feature manager</param>
public DecisionController(IAccessManagementWrapper accessManagement, IContextHandler contextHandler, IDelegationContextHandler delegationContextHandler, IPolicyRetrievalPoint policyRetrievalPoint, ILogger<DecisionController> logger, IMemoryCache memoryCache, IEventLog eventLog, IFeatureManager featureManager)
/// <param name="mapper">The model mapper</param>
public DecisionController(IAccessManagementWrapper accessManagement, IContextHandler contextHandler, IDelegationContextHandler delegationContextHandler, IPolicyRetrievalPoint policyRetrievalPoint, IDelegationMetadataRepository delegationRepository, ILogger<DecisionController> logger, IMemoryCache memoryCache, IEventLog eventLog, IFeatureManager featureManager, IMapper mapper)
{
_pdp = new PolicyDecisionPoint();
_prp = policyRetrievalPoint;
Expand All @@ -72,13 +76,15 @@ public DecisionController(IAccessManagementWrapper accessManagement, IContextHan
_eventLog = eventLog;
_featureManager = featureManager;
_accessManagement = accessManagement;
_mapper = mapper;
}

/// <summary>
/// Decision Point endpoint to authorize Xacml Context Requests
/// </summary>
/// <param name="model">A Generic model</param>
[HttpPost]
[Route("authorization/api/v1/[controller]")]
public async Task<ActionResult> Post([FromBody] XacmlRequestApiModel model)
{
try
Expand Down Expand Up @@ -115,14 +121,42 @@ public async Task<ActionResult> Post([FromBody] XacmlRequestApiModel model)
}
}

private async Task<XacmlJsonResponse> Authorize(XacmlJsonRequest decisionRequest)
/// <summary>
/// External endpoint for autorization
/// </summary>
[Authorize(Policy = AuthzConstants.PDPSCOPEACCESS)]
[Route("authorization/api/v1/authorize")]
public async Task<ActionResult<XacmlJsonResponseExternal>> AuthorizeExternal([FromBody] XacmlJsonRequestRootExternal authorizationRequest, CancellationToken cancellationToken = default)
{
try
{
XacmlJsonRequestRoot jsonRequest = _mapper.Map<XacmlJsonRequestRoot>(authorizationRequest);
XacmlJsonResponse xacmlResponse = await Authorize(jsonRequest.Request, true, cancellationToken);
return _mapper.Map<XacmlJsonResponseExternal>(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<XacmlJsonResponseExternal>(jsonResult);
}
}

private async Task<XacmlJsonResponse> 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
Expand Down Expand Up @@ -172,7 +206,7 @@ private async Task<XacmlJsonResponse> 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)
Expand All @@ -195,7 +229,8 @@ private async Task<ActionResult> AuthorizeXmlRequest(XacmlRequestApiModel model)
request = XacmlParser.ReadContextRequest(reader);
}

XacmlContextResponse xacmlContextResponse = await Authorize(request, true);
XacmlContextResponse xacmlContextResponse = await Authorize(request, false, true);

return CreateResponse(xacmlContextResponse);
}

Expand All @@ -221,9 +256,9 @@ private ActionResult CreateResponse(XacmlContextResponse xacmlContextResponse)
return Content(xml);
}

private async Task<XacmlContextResponse> Authorize(XacmlContextRequest decisionRequest, bool logEvent = true)
private async Task<XacmlContextResponse> 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);
Expand All @@ -236,13 +271,13 @@ private async Task<XacmlContextResponse> 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;
Expand All @@ -256,21 +291,21 @@ private async Task<XacmlContextResponse> 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<XacmlContextResponse> ProcessDelegationResult(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy, IEnumerable<DelegationChange> delegations)
private async Task<XacmlContextResponse> ProcessDelegationResult(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy, IEnumerable<DelegationChange> delegations, CancellationToken cancellationToken = default)
{
if (!delegations.IsNullOrEmpty())
{
List<int> keyrolePartyIds = await _delegationContextHandler.GetKeyRolePartyIds(_delegationContextHandler.GetSubjectUserId(decisionRequest));
List<int> 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;
Expand Down Expand Up @@ -303,7 +338,7 @@ private Action<DelegationChangeInput> WithDefaultGetAllDelegationChangesInput(Xa
input.Subject = new(AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute, _delegationContextHandler.GetSubjectUserId(decisionRequest).ToString());
};

private async Task<XacmlContextResponse> AuthorizeUsingDelegations(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy)
private async Task<XacmlContextResponse> AuthorizeUsingDelegations(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy, CancellationToken cancellationToken = default)
{
var resourceAttributes = _delegationContextHandler.GetResourceAttributes(decisionRequest);
if (IsInvalidRequest(resourceAttributes, decisionRequest))
Expand All @@ -324,7 +359,7 @@ private async Task<XacmlContextResponse> AuthorizeUsingDelegations(XacmlContextR
new(AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute, resourceAttributes.AppValue),
});

return await ProcessDelegationResult(decisionRequest, resourcePolicy, delegations);
return await ProcessDelegationResult(decisionRequest, resourcePolicy, delegations, cancellationToken);
}

if (IsTypeResource(resourceAttributes))
Expand All @@ -334,7 +369,7 @@ private async Task<XacmlContextResponse> 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)
Expand Down Expand Up @@ -370,7 +405,7 @@ private async Task<IEnumerable<DelegationChange>> GetAllCachedDelegationChanges(
return result;
}

private async Task<XacmlContextResponse> MakeContextDecisionUsingDelegations(XacmlContextRequest decisionRequest, IEnumerable<DelegationChange> delegations, XacmlPolicy appPolicy)
private async Task<XacmlContextResponse> MakeContextDecisionUsingDelegations(XacmlContextRequest decisionRequest, IEnumerable<DelegationChange> delegations, XacmlPolicy appPolicy, CancellationToken cancellationToken = default)
{
XacmlContextResponse delegationContextResponse = new XacmlContextResponse(new XacmlContextResult(XacmlContextDecision.NotApplicable)
{
Expand All @@ -379,7 +414,7 @@ private async Task<XacmlContextResponse> 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);
Expand Down
Loading

0 comments on commit 3f0d4c0

Please sign in to comment.