Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/nuget/src/Authorization/Azure.Sto…
Browse files Browse the repository at this point in the history
…rage.Queues-12.20.0
  • Loading branch information
Thuen authored Sep 23, 2024
2 parents 0654872 + caa7e53 commit 2570f96
Show file tree
Hide file tree
Showing 89 changed files with 3,952 additions and 369 deletions.
9 changes: 9 additions & 0 deletions src/Authorization/Altinn.Platform.Authorization.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@

<ItemGroup>
<PackageReference Include="Altinn.ApiClients.Maskinporten" Version="9.2.0" />
<PackageReference Include="Altinn.Authorization.ProblemDetails" Version="3.0.1" />
<PackageReference Include="Altinn.Authorization.ProblemDetails.Abstractions" Version="3.0.1" />
<PackageReference Include="Altinn.Common.AccessToken" Version="4.5.1" />
<PackageReference Include="Altinn.Common.AccessTokenClient" Version="3.0.7" />
<PackageReference Include="Altinn.Common.PEP" Version="4.0.0" />
<PackageReference Include="Altinn.Platform.Models" Version="1.6.1" />
<PackageReference Include="Altinn.Swashbuckle" Version="2.1.0" />
<PackageReference Include="Altinn.Urn" Version="2.5.0" />
<PackageReference Include="Altinn.Urn.Swashbuckle" Version="2.5.0" />
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
<PackageReference Include="Azure.Storage.Queues" Version="12.20.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.22.0" />
<PackageReference Include="IdentityModel" Version="7.0.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageReference Include="Altinn.Authorization.ABAC" Version="0.0.8" />
<PackageReference Include="Altinn.Platform.Storage.Interface" Version="3.34.0" />
Expand All @@ -28,6 +35,8 @@
<PackageReference Include="Microsoft.FeatureManagement" Version="3.5.0" />
<PackageReference Include="Npgsql" Version="8.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="Yuniql.AspNetCore" Version="1.2.25" />
<PackageReference Include="Yuniql.PostgreSql" Version="1.3.15" />
Expand Down
27 changes: 27 additions & 0 deletions src/Authorization/Configuration/TokenGeneratorSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Altinn.Platform.Authorization.Configuration;

/// <summary>
/// Represents a set of configuration options needed for using the Altinn Platform Token Generator.
/// </summary>
public class TokenGeneratorSettings
{
/// <summary>
/// Gets or sets the url for the token generator.
/// </summary>
public string Url { get; set; }

/// <summary>
/// Gets or sets user for authorized access to the token generator.
/// </summary>
public string User { get; set; }

/// <summary>
/// Gets or sets password for authorized access to the token generator.
/// </summary>
public string Password { get; set; }

/// <summary>
/// Gets or sets the environment to use for the token generator.
/// </summary>
public string Env { get; set; }
}
15 changes: 10 additions & 5 deletions src/Authorization/Constants/AuthzConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ public static class AuthzConstants
/// </summary>
public const string POLICY_STUDIO_DESIGNER = "StudioDesignerAccess";

/// <summary>
/// Policy tag for authorizing PlatformAccessTokens issued by Platform
/// </summary>
public const string POLICY_PLATFORMISSUER_ACCESSTOKEN = "PlatformIssuedAccessToken";

/// <summary>
/// The issuer of access tokens for the platform cluster
/// </summary>
public const string PLATFORM_ACCESSTOKEN_ISSUER = "platform";

/// <summary>
/// Policy tag for authorizing Altinn.Platform.Authorization API access from AltinnII Authorization
/// </summary>
Expand All @@ -25,11 +35,6 @@ public static class AuthzConstants
/// </summary>
public const string AUTHORIZESCOPEACCESS = "AuthorizeScopeAccess";

/// <summary>
/// (deprecated) Scope that gives access to Authorize API
/// </summary>
public const string PDP_SCOPE = "altinn:authorization:pdp";

/// <summary>
/// Scope that gives access to external Authorize API for service/resource owners
/// </summary>
Expand Down
66 changes: 66 additions & 0 deletions src/Authorization/Controllers/AccessListAuthorizationController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Linq;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Altinn.Authorization.Errors;
using Altinn.Authorization.Models.ResourceRegistry;
using Altinn.Authorization.ProblemDetails;
using Altinn.Platform.Authorization.Constants;
using Altinn.Platform.Authorization.Models;
using Altinn.Platform.Authorization.Services.Interface;
using AltinnCore.Authentication.Constants;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Altinn.Platform.Authorization.Controllers;

/// <summary>
/// Contains all actions related to the roles model
/// </summary>
[Route("authorization/api/v1/accesslist")]
[ApiController]
public class AccessListAuthorizationController : ControllerBase
{
private readonly IAccessListAuthorization _accessListAuthorization;
private readonly IResourceRegistry _resourceRegistry;

/// <summary>
/// Initializes a new instance of the <see cref="AccessListAuthorizationController"/> class
/// </summary>
public AccessListAuthorizationController(IAccessListAuthorization accessListAuthorization, IResourceRegistry resourceRegistry)
{
_accessListAuthorization = accessListAuthorization;
_resourceRegistry = resourceRegistry;
}

/// <summary>
/// Internal API for local cluster requests only.
/// Authorization of a given subject for resource access through access lists for any service owner organization's access lists and resources.
/// </summary>
/// <param name="accessListAuthorizationRequest">Access list authorization request model</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/></param>
/// <returns>AccessListAuthorizationResponse</returns>
[HttpPost]
[Route("accessmanagement/authorization")]
[ApiExplorerSettings(IgnoreApi = true)]
[Authorize(Policy = AuthzConstants.POLICY_PLATFORMISSUER_ACCESSTOKEN)]
[Consumes(MediaTypeNames.Application.Json)]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(typeof(AccessListAuthorizationResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult> AuthorizeInternal(AccessListAuthorizationRequest accessListAuthorizationRequest, CancellationToken cancellationToken = default)
{
Result<AccessListAuthorizationResponse> result = await _accessListAuthorization.Authorize(accessListAuthorizationRequest, cancellationToken);

if (result.IsProblem)
{
return result.Problem.ToActionResult();
}

return Ok(result.Value);
}
}
107 changes: 90 additions & 17 deletions src/Authorization/Controllers/DecisionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@
using Altinn.Authorization.ABAC.Utils;
using Altinn.Authorization.ABAC.Xacml;
using Altinn.Authorization.ABAC.Xacml.JsonProfile;
using Altinn.Authorization.Enums;
using Altinn.Authorization.Models;
using Altinn.Authorization.Models.Register;
using Altinn.Authorization.Models.ResourceRegistry;
using Altinn.Authorization.ProblemDetails;
using Altinn.Platform.Authorization.Constants;
using Altinn.Platform.Authorization.Helpers;
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 Altinn.Platform.Register.Models;
using Altinn.Platform.Storage.Interface.Models;
using AutoMapper;
using Azure.Core;
Expand Down Expand Up @@ -47,6 +54,9 @@ public class DecisionController : ControllerBase
private readonly IEventLog _eventLog;
private readonly IFeatureManager _featureManager;
private readonly IAccessManagementWrapper _accessManagement;
private readonly IResourceRegistry _resourceRegistry;
private readonly IRegisterService _registerService;
private readonly IAccessListAuthorization _accessListAuthorization;
private readonly IMapper _mapper;

private readonly SortedDictionary<string, AuthInfo> _appInstanceInfo = new();
Expand All @@ -55,6 +65,9 @@ public class DecisionController : ControllerBase
/// Initializes a new instance of the <see cref="DecisionController"/> class.
/// </summary>
/// <param name="accessManagement">Service for making request the to Access Management API (PIP)</param>
/// <param name="resourceRegistry">Service for making requests to the Resource Registry API</param>
/// <param name="registerService">Service for making requests to the Register API</param>
/// <param name="accessListAuthorization">Service for authorization of subjects based on Resource Registry access lists</param>
/// <param name="contextHandler">The Context handler</param>
/// <param name="delegationContextHandler">The delegation context handler</param>
/// <param name="policyRetrievalPoint">The policy Retrieval point</param>
Expand All @@ -64,7 +77,20 @@ public class DecisionController : ControllerBase
/// <param name="eventLog">the authorization event logger</param>
/// <param name="featureManager">the feature manager</param>
/// <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)
public DecisionController(
IAccessManagementWrapper accessManagement,
IResourceRegistry resourceRegistry,
IRegisterService registerService,
IAccessListAuthorization accessListAuthorization,
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 @@ -75,6 +101,9 @@ public DecisionController(IAccessManagementWrapper accessManagement, IContextHan
_eventLog = eventLog;
_featureManager = featureManager;
_accessManagement = accessManagement;
_resourceRegistry = resourceRegistry;
_registerService = registerService;
_accessListAuthorization = accessListAuthorization;
_mapper = mapper;
}

Expand Down Expand Up @@ -267,41 +296,45 @@ private async Task<XacmlContextResponse> Authorize(XacmlContextRequest decisionR
{
decisionRequest = await this._contextHandler.Enrich(decisionRequest, isExernalRequest, _appInstanceInfo);

////_logger.LogInformation($"// DecisionController // Authorize // Roles // Enriched request: {JsonConvert.SerializeObject(decisionRequest)}.");
XacmlPolicy policy = await _prp.GetPolicyAsync(decisionRequest);

XacmlContextResponse rolesContextResponse = _pdp.Authorize(decisionRequest, policy);
////_logger.LogInformation($"// DecisionController // Authorize // Roles // XACML ContextResponse: {JsonConvert.SerializeObject(rolesContextResponse)}.");

XacmlContextResult roleResult = rolesContextResponse.Results.First();

XacmlContextResponse delegationContextResponse = null;
if (roleResult.Decision.Equals(XacmlContextDecision.NotApplicable))
{
try
{
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, cancellationToken);
}

return delegationContextResponse;
}
delegationContextResponse = await AuthorizeUsingDelegations(decisionRequest, policy, cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "// DecisionController // Authorize // Delegation // Unexpected Exception");
}
}

XacmlContextResponse finalResponse = delegationContextResponse ?? rolesContextResponse;
XacmlContextResult finalResult = finalResponse.Results.First();
if (finalResult.Decision.Equals(XacmlContextDecision.Permit) && !await IsAccessListAuthorized(decisionRequest, cancellationToken))
{
return new XacmlContextResponse(new XacmlContextResult(XacmlContextDecision.Deny)
{
Status = new XacmlContextStatus(XacmlContextStatusCode.Success)
{
StatusMessage = "Access list authorization of resource party required. Access list authorization is controlled by the service owner of the resource/service of the authorization request.",
}
});
}

// If the delegation context response is NOT permit, the final response should be the roles context response
finalResponse = finalResult.Decision.Equals(XacmlContextDecision.Permit) ? finalResponse : rolesContextResponse;
if (logEvent)
{
await _eventLog.CreateAuthorizationEvent(_featureManager, decisionRequest, HttpContext, rolesContextResponse, cancellationToken);
await _eventLog.CreateAuthorizationEvent(_featureManager, decisionRequest, HttpContext, finalResponse, cancellationToken);
}

return rolesContextResponse;
return finalResponse;
}

private async Task<XacmlContextResponse> ProcessDelegationResult(XacmlContextRequest decisionRequest, XacmlPolicy resourcePolicy, IEnumerable<DelegationChangeExternal> delegations, CancellationToken cancellationToken = default)
Expand All @@ -324,6 +357,46 @@ private async Task<XacmlContextResponse> ProcessDelegationResult(XacmlContextReq
});
}

private async Task<bool> IsAccessListAuthorized(XacmlContextRequest decisionRequest, CancellationToken cancellationToken = default)
{
PolicyResourceType policyResourceType = PolicyHelper.GetPolicyResourceType(decisionRequest, out string resourceId);
if (!policyResourceType.Equals(PolicyResourceType.ResourceRegistry))
{
return true;
}

ServiceResource resource = await _resourceRegistry.GetResourceAsync(resourceId, cancellationToken);
if (resource != null && resource.AccessListMode == ResourceAccessListMode.Enabled)
{
var resourceAttributes = _delegationContextHandler.GetResourceAttributes(decisionRequest);

Party party = await _registerService.GetParty(int.Parse(resourceAttributes.ResourcePartyValue));
if (party?.PartyTypeName != Register.Enums.PartyType.Organisation)
{
// Currently only Organization support in AccessLists
return false;
}

resourceAttributes.OrganizationNumber = party.OrgNumber;
AccessListAuthorizationRequest accessListAuthorizationRequest = new AccessListAuthorizationRequest
{
Subject = PartyUrn.OrganizationIdentifier.Create(OrganizationNumber.CreateUnchecked(resourceAttributes.OrganizationNumber)),
Resource = ResourceIdUrn.ResourceId.Create(Altinn.Authorization.Models.ResourceRegistry.ResourceIdentifier.CreateUnchecked(resourceId)),
Action = ActionUrn.ActionId.Create(ActionIdentifier.CreateUnchecked(_delegationContextHandler.GetActionString(decisionRequest)))
};
Result<AccessListAuthorizationResponse> result = await _accessListAuthorization.Authorize(accessListAuthorizationRequest, cancellationToken);

if (result.IsProblem)
{
return false;
}

return result.Value.Result == AccessListAuthorizationResult.Authorized;
}

return true;
}

private static string CreateCacheKey(params string[] cacheKeys) =>
string.Join("-", cacheKeys.Where(c => c != null && (c != string.Empty || !c.EndsWith(':'))));

Expand Down
21 changes: 21 additions & 0 deletions src/Authorization/Errors/Problems.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#nullable enable

using System.Net;
using Altinn.Authorization.ProblemDetails;

namespace Altinn.Authorization.Errors;

/// <summary>
/// Problem descriptors for Authorization
/// </summary>
public static class Problems
{
private static readonly ProblemDescriptorFactory _factory
= ProblemDescriptorFactory.New("AUTHZ");

/// <summary>
/// Gets a <see cref="ProblemDescriptor"/> for not implemented feature.
/// </summary>
public static ProblemDescriptor NotImplemented { get; }
= _factory.Create(0, HttpStatusCode.NotImplemented, "Not implemented.");
}
32 changes: 32 additions & 0 deletions src/Authorization/Errors/ValidationErrors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#nullable enable

using Altinn.Authorization.ProblemDetails;

namespace Altinn.Authorization.Errors;

/// <summary>
/// Validation errors for Authorization
/// </summary>
public static class ValidationErrors
{
private static readonly ValidationErrorDescriptorFactory _factory
= ValidationErrorDescriptorFactory.New("AUTHZ");

/// <summary>
/// Gets a validation error descriptor for when a provided resource registry identifier is not found as a valid resource.
/// </summary>
public static ValidationErrorDescriptor ResourceRegistry_ResourceIdentifier_NotFound { get; }
= _factory.Create(0, "Unknown resource registry identifier.");

/// <summary>
/// Gets a validation error descriptor for when the authencticated organization number is not authorized as the competent authority owner of a resource registry resource.
/// </summary>
public static ValidationErrorDescriptor ResourceRegistry_CompetentAuthority_NotMatchingAuthenticatedOrganization { get; }
= _factory.Create(1, "Authorized organization is not the competent authority owner of the requested resource.");

/// <summary>
/// Gets a validation error descriptor for when the authencticated organization code is not authorized as the competent authority owner of a resource registry resource.
/// </summary>
public static ValidationErrorDescriptor ResourceRegistry_CompetentAuthority_NotMatchingAuthenticatedOrgCode { get; }
= _factory.Create(2, "Authorized organization code is not the competent authority owner of the requested resource.");
}
Loading

0 comments on commit 2570f96

Please sign in to comment.