From 7342998202ddf36f01ca77202e392cc391173f20 Mon Sep 17 00:00:00 2001 From: Hammerbeck Date: Fri, 8 Nov 2024 08:34:33 +0100 Subject: [PATCH 1/3] correct auth for legacy overview --- .../LegacyGetCorrespondenceHistoryHandler.cs | 2 +- .../LegacyGetCorrespondenceOverviewHandler.cs | 5 +-- .../IAltinnAuthorizationService.cs | 2 +- .../AltinnAuthorizationDevService.cs | 2 +- .../AltinnAuthorizationService.cs | 28 +++++++++---- .../Authorization/AltinnTokenXacmlMapper..cs | 42 +++++++++++++++++-- 6 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/Altinn.Correspondence.Application/GetCorrespondenceHistory/LegacyGetCorrespondenceHistoryHandler.cs b/src/Altinn.Correspondence.Application/GetCorrespondenceHistory/LegacyGetCorrespondenceHistoryHandler.cs index 84636693..4b2c713c 100644 --- a/src/Altinn.Correspondence.Application/GetCorrespondenceHistory/LegacyGetCorrespondenceHistoryHandler.cs +++ b/src/Altinn.Correspondence.Application/GetCorrespondenceHistory/LegacyGetCorrespondenceHistoryHandler.cs @@ -43,7 +43,7 @@ public async Task> Process( { return Errors.CouldNotFindOrgNo; } - var minimumAuthLevel = await _altinnAuthorizationService.CheckUserAccessAndGetMinimumAuthLevel(correspondence.ResourceId, new List { ResourceAccessLevel.Read }, cancellationToken); + var minimumAuthLevel = await _altinnAuthorizationService.CheckUserAccessAndGetMinimumAuthLevel(recipientParty.SSN, correspondence.ResourceId, new List { ResourceAccessLevel.Read }, correspondence.Recipient, cancellationToken); if (minimumAuthLevel is not int authenticationLevel) { authenticationLevel = 2; diff --git a/src/Altinn.Correspondence.Application/GetCorrespondenceOverview/LegacyGetCorrespondenceOverviewHandler.cs b/src/Altinn.Correspondence.Application/GetCorrespondenceOverview/LegacyGetCorrespondenceOverviewHandler.cs index 245d3dd9..22799602 100644 --- a/src/Altinn.Correspondence.Application/GetCorrespondenceOverview/LegacyGetCorrespondenceOverviewHandler.cs +++ b/src/Altinn.Correspondence.Application/GetCorrespondenceOverview/LegacyGetCorrespondenceOverviewHandler.cs @@ -46,12 +46,11 @@ public async Task> Process { return Errors.CorrespondenceNotFound; } - /*var minimumAuthLevel = await _altinnAuthorizationService.CheckUserAccessAndGetMinimumAuthLevel(correspondence.ResourceId, new List { ResourceAccessLevel.Read }, cancellationToken); + var minimumAuthLevel = await _altinnAuthorizationService.CheckUserAccessAndGetMinimumAuthLevel(userParty.SSN, correspondence.ResourceId, new List { ResourceAccessLevel.Read }, correspondence.Recipient, cancellationToken); if (minimumAuthLevel == null) { return Errors.LegacyNoAccessToCorrespondence; - } */ - var minimumAuthLevel = 2; + } var recipients = new List(); if (correspondence.Recipient != userParty.SSN && correspondence.Recipient != ("0192:" + userParty.OrgNumber)) { diff --git a/src/Altinn.Correspondence.Core/Repositories/IAltinnAuthorizationService.cs b/src/Altinn.Correspondence.Core/Repositories/IAltinnAuthorizationService.cs index cfb60651..3617079d 100644 --- a/src/Altinn.Correspondence.Core/Repositories/IAltinnAuthorizationService.cs +++ b/src/Altinn.Correspondence.Core/Repositories/IAltinnAuthorizationService.cs @@ -5,6 +5,6 @@ namespace Altinn.Correspondence.Core.Repositories; public interface IAltinnAuthorizationService { Task CheckUserAccess(string resourceId, List rights, CancellationToken cancellationToken = default, string? recipientOrgNo = null); - Task CheckUserAccessAndGetMinimumAuthLevel(string resourceId, List rights, CancellationToken cancellationToken = default, string? recipientOrgNo = null); + Task CheckUserAccessAndGetMinimumAuthLevel(string ssn, string resourceId, List rights, string recipientOrgNo, CancellationToken cancellationToken = default); Task CheckMigrationAccess(string resourceId, List rights, CancellationToken cancellationToken = default); } diff --git a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationDevService.cs b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationDevService.cs index 8d614cc4..08c73157 100644 --- a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationDevService.cs +++ b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationDevService.cs @@ -15,7 +15,7 @@ public Task CheckUserAccess(string resourceId, List r return Task.FromResult(true); } - public Task CheckUserAccessAndGetMinimumAuthLevel(string resourceId, List rights, CancellationToken cancellationToken = default, string? recipientOrgNo = null) + public Task CheckUserAccessAndGetMinimumAuthLevel(string ssn, string resourceId, List rights, string recipientOrgNo, CancellationToken cancellationToken = default) { return Task.FromResult((int?)3); } diff --git a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs index 2a6cb1ca..7c1fd7e9 100644 --- a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs +++ b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs @@ -47,7 +47,9 @@ public async Task CheckUserAccess(string resourceId, List CheckUserAccess(string resourceId, List CheckUserAccessAndGetMinimumAuthLevel(string resourceId, List rights, CancellationToken cancellationToken = default, string? recipientOrgNo = null) + public async Task CheckUserAccessAndGetMinimumAuthLevel(string ssn, string resourceId, List rights, string recipientOrgNo, CancellationToken cancellationToken = default) { var user = _httpContextAccessor.HttpContext?.User; var validation = await ValidateCheckUserAccess(user, resourceId, cancellationToken); - if (validation != null) return (bool)validation ? 3 : null; - var responseContent = await AuthorizeRequest(user, rights, resourceId, recipientOrgNo, cancellationToken); + if (validation != null) return null; + var actionIds = rights.Select(GetActionId).ToList(); + var orgnr = recipientOrgNo.Split(":")[1]; + XacmlJsonRequestRoot jsonRequest = CreateDecisionRequestForLegacy(user, ssn, actionIds, resourceId, orgnr); + var responseContent = await AuthorizeRequest(jsonRequest, cancellationToken); if (responseContent is null) return null; var validationResult = ValidateAuthorizationResponse(responseContent, user); @@ -107,10 +112,8 @@ public async Task CheckMigrationAccess(string resourceId, List AuthorizeRequest(ClaimsPrincipal user, List rights, string resourceId, string? recipientOrgNo, CancellationToken cancellationToken) + private async Task AuthorizeRequest(XacmlJsonRequestRoot jsonRequest, CancellationToken cancellationToken) { - var actionIds = rights.Select(GetActionId).ToList(); - XacmlJsonRequestRoot jsonRequest = CreateDecisionRequest(user, actionIds, resourceId, recipientOrgNo); var response = await _httpClient.PostAsJsonAsync("authorization/api/v1/authorize", jsonRequest, cancellationToken); if (!response.IsSuccessStatusCode) { @@ -143,6 +146,17 @@ private XacmlJsonRequestRoot CreateDecisionRequest(ClaimsPrincipal user, List actionTypes, string resourceId, string recipientOrgNo) + { + var personIdClaim = GetPersonIdClaim(); + if (personIdClaim is null || personIdClaim.Issuer == $"{_altinnOptions.PlatformGatewayUrl.TrimEnd('/')}/authentication/api/v1/openid/") + { + return AltinnTokenXacmlMapper.CreateAltinnDecisionRequestForLegacy(user, ssn, actionTypes, resourceId, recipientOrgNo); + } + throw new SecurityTokenInvalidIssuerException(); + } + + private bool ValidateAuthorizationResponse(XacmlJsonResponse response, ClaimsPrincipal user) { if (response.Response.IsNullOrEmpty()) diff --git a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs index 3b726eed..d166c27a 100644 --- a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs +++ b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs @@ -34,6 +34,27 @@ public static XacmlJsonRequestRoot CreateAltinnDecisionRequest(ClaimsPrincipal u XacmlJsonRequestRoot jsonRequest = new() { Request = request }; return jsonRequest; } + public static XacmlJsonRequestRoot CreateAltinnDecisionRequestForLegacy(ClaimsPrincipal user, string ssn, List actionTypes, string resourceId, string recipient) + { + XacmlJsonRequest request = new() + { + AccessSubject = new List(), + Action = new List(), + Resource = new List() + }; + + var subjectCategory = CreateSubjectCategoryForLegacy(user, ssn); + request.AccessSubject.Add(subjectCategory); + foreach (var actionType in actionTypes) + { + request.Action.Add(CreateActionCategory(actionType)); + } + var resourceCategory = CreateResourceCategory(resourceId, user, true, recipient); + request.Resource.Add(resourceCategory); + + XacmlJsonRequestRoot jsonRequest = new() { Request = request }; + return jsonRequest; + } private static XacmlJsonCategory CreateActionCategory(string actionType, bool includeResult = false) { @@ -47,15 +68,15 @@ private static XacmlJsonCategory CreateActionCategory(string actionType, bool in return actionAttributes; } - private static XacmlJsonCategory CreateResourceCategory(string resourceId, ClaimsPrincipal user) + private static XacmlJsonCategory CreateResourceCategory(string resourceId, ClaimsPrincipal user, bool legacy = false, string? orgNr = null) { XacmlJsonCategory resourceCategory = new() { Attribute = new List() }; resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(AltinnXacmlUrns.ResourceId, resourceId, DefaultType, DefaultIssuer)); var claim = user.Claims.FirstOrDefault(claim => IsClientOrgNo(claim.Type)); - if (claim is not null) + if (claim is not null || legacy) { - resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(OrgNumberAttributeId, claim.Value, DefaultType, DefaultIssuer)); + resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(OrgNumberAttributeId, orgNr ?? claim.Value, DefaultType, DefaultIssuer)); } return resourceCategory; @@ -93,6 +114,19 @@ private static XacmlJsonCategory CreateSubjectCategory(ClaimsPrincipal user) xacmlJsonCategory.Attribute = list; return xacmlJsonCategory; } + private static XacmlJsonCategory CreateSubjectCategoryForLegacy(ClaimsPrincipal user, string ssn) + { + XacmlJsonCategory xacmlJsonCategory = new XacmlJsonCategory(); + List list = new List(); + var claim = user.Claims.FirstOrDefault(claim => IsScopeClaim(claim.Type)); + if (claim is not null) + { + list.Add(CreateXacmlJsonAttribute("urn:altinn:person:identifier-no", ssn, "string", claim.Issuer)); + list.Add(CreateXacmlJsonAttribute("urn:scope", claim.Value, "string", claim.Issuer)); + } + xacmlJsonCategory.Attribute = list; + return xacmlJsonCategory; + } private static bool IsValidUrn(string value) { Regex regex = new Regex("^urn*"); @@ -122,7 +156,7 @@ private static bool IsJtiClaim(string value) private static bool IsValidPid(string value) { return value.Equals("pid"); - } + } private static XacmlJsonAttribute CreateXacmlJsonAttribute(string attributeId, string value, string dataType, string issuer, bool includeResult = false) { From 2071925f2737e18782cab1461c8474b4eb3309d1 Mon Sep 17 00:00:00 2001 From: Hammerbeck Date: Fri, 8 Nov 2024 08:58:28 +0100 Subject: [PATCH 2/3] correct authlevel in dev --- .../Altinn/Authorization/AltinnAuthorizationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs index 7c1fd7e9..9b9ff29b 100644 --- a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs +++ b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs @@ -61,7 +61,7 @@ public async Task CheckUserAccess(string resourceId, List Date: Fri, 8 Nov 2024 09:18:31 +0100 Subject: [PATCH 3/3] better error handling --- .../Altinn/Authorization/AltinnTokenXacmlMapper..cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs index d166c27a..cbd1e3cd 100644 --- a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs +++ b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnTokenXacmlMapper..cs @@ -74,9 +74,13 @@ private static XacmlJsonCategory CreateResourceCategory(string resourceId, Claim resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(AltinnXacmlUrns.ResourceId, resourceId, DefaultType, DefaultIssuer)); var claim = user.Claims.FirstOrDefault(claim => IsClientOrgNo(claim.Type)); - if (claim is not null || legacy) + if (legacy && orgNr is not null) { - resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(OrgNumberAttributeId, orgNr ?? claim.Value, DefaultType, DefaultIssuer)); + resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(OrgNumberAttributeId, orgNr, DefaultType, DefaultIssuer)); + } + else if (claim is not null) + { + resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(OrgNumberAttributeId, claim.Value, DefaultType, DefaultIssuer)); } return resourceCategory;