Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/externalpdp #639

Merged
merged 31 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ac40a09
First models
TheTechArch Dec 15, 2023
8187313
Fixed mapping
TheTechArch Dec 15, 2023
bc0551b
Updated map for respons
TheTechArch Dec 19, 2023
7bfbd99
Test update
TheTechArch Dec 20, 2023
f36f01e
Updates
TheTechArch Dec 20, 2023
3412fb5
Merge
TheTechArch Jan 2, 2024
71a1803
Test case 1
TheTechArch Jan 3, 2024
554e071
Added more tests
TheTechArch Jan 3, 2024
381dca2
Updates testdata
TheTechArch Jan 3, 2024
9e5fe43
UJpdates attribute ID
TheTechArch Jan 4, 2024
d7e284f
Testdata updates
TheTechArch Jan 5, 2024
c1ae926
Updated to latest decision on attribute names
TheTechArch Jan 17, 2024
2d82bee
Added Authentication and scope check
TheTechArch Jan 19, 2024
9290eb3
First version of merge
TheTechArch Feb 2, 2024
5797bdd
Merge fix
TheTechArch Feb 2, 2024
562cd7b
More merge fix
TheTechArch Feb 2, 2024
72ea190
Added scenario for org in policy
TheTechArch Feb 5, 2024
5490c41
Added scenario with old param
TheTechArch Feb 5, 2024
81d494b
Removed pep project
TheTechArch Feb 5, 2024
138f16a
Fixed coding issues
TheTechArch Feb 6, 2024
564d92f
Fixed testdata
TheTechArch Feb 6, 2024
cd48c05
Code updates
TheTechArch Feb 6, 2024
8ee3475
Fixed response
TheTechArch Feb 6, 2024
7abd38c
Fixed path and cancellation token
TheTechArch Feb 6, 2024
850f9af
Added Cancellationtoken
TheTechArch Feb 6, 2024
028e82b
Fixed catch of cancellationtoken
TheTechArch Feb 6, 2024
941af9b
Code smell with missing token
TheTechArch Feb 6, 2024
022ef0d
Code smell
TheTechArch Feb 7, 2024
f48ec71
Allowed list
TheTechArch Feb 8, 2024
9445a51
Multipart test for external
TheTechArch Feb 8, 2024
8829ca9
Merge
TheTechArch Feb 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
/// 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 @@
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 @@
/// <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)

Check warning on line 68 in src/Authorization/Controllers/DecisionController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Constructor has 10 parameters, which is greater than the 7 authorized. (https://rules.sonarsource.com/csharp/RSPEC-107)
{
_pdp = new PolicyDecisionPoint();
_prp = policyRetrievalPoint;
Expand All @@ -72,13 +76,15 @@
_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 @@
}
}

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)
TheTechArch marked this conversation as resolved.
Show resolved Hide resolved
{
_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)

Check warning on line 152 in src/Authorization/Controllers/DecisionController.cs

View workflow job for this annotation

GitHub Actions / Analyze

All 'Authorize' method overloads should be adjacent. (https://rules.sonarsource.com/csharp/RSPEC-4136)

Check warning on line 152 in src/Authorization/Controllers/DecisionController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Refactor this method to reduce its Cognitive Complexity from 41 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
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 @@
}
}

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 @@
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 @@
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 @@
{
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 @@

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 @@
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 @@
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 @@
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 @@
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 @@

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
Loading