From 3f7dfa2b8a1f03f68249ba3d4f7903a3fb68ed36 Mon Sep 17 00:00:00 2001 From: Roar Mjelde <36594318+Ceredron@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:34:47 +0200 Subject: [PATCH] Follow-up to previous PR. When testing locally, I accidentally did not make the correct changes to ensure Dialogtokens were authenticated to server (instead bypassing it as we do locally). I cringe. We should consider enabling authentication when running locally when we have appsettings.local. setup alright. (#353) --- .../AltinnAuthorizationService.cs | 6 +- .../Mappers/DialogTokenXacmlMapper.cs | 76 ++++++--- .../Mappers/DialogportenXacmlMapper.cs | 146 ------------------ 3 files changed, 57 insertions(+), 171 deletions(-) delete mode 100644 src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogportenXacmlMapper.cs diff --git a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs index b093b246..85bf5612 100644 --- a/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs +++ b/src/Altinn.Correspondence.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs @@ -121,7 +121,11 @@ private bool ValidateAuthorizationResponse(XacmlJsonResponse response, ClaimsPri { return IdportenXacmlMapper.ValidateIdportenAuthorizationResponse(response, user); } - foreach (var decision in response.Response) + if (personIdClaim.Issuer == _dialogportenSettings.Issuer) + { + return DialogTokenXacmlMapper.ValidateDialogportenResult(response, user); + } + foreach (var decision in response.Response) { var result = DecisionHelper.ValidateDecisionResult(decision, user); if (result == false) diff --git a/src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogTokenXacmlMapper.cs b/src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogTokenXacmlMapper.cs index dc0875de..5447cb0f 100644 --- a/src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogTokenXacmlMapper.cs +++ b/src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogTokenXacmlMapper.cs @@ -1,4 +1,5 @@ -using Altinn.Authorization.ABAC.Xacml.JsonProfile; +using Altinn.Authorization.ABAC.Xacml; +using Altinn.Authorization.ABAC.Xacml.JsonProfile; using Altinn.Common.PEP.Constants; using Altinn.Common.PEP.Helpers; using Microsoft.IdentityModel.Tokens; @@ -40,13 +41,15 @@ private static XacmlJsonCategory CreateActionCategory(ClaimsPrincipal user, bool { throw new SecurityTokenException("Dialogporten token does not contain the required action claim"); } + var actions = actionClaim.Value.Split(';'); XacmlJsonCategory actionAttributes = new() { - Attribute = new List - { - DecisionHelper.CreateXacmlJsonAttribute(MatchAttributeIdentifiers.ActionId, actionClaim.Value, DefaultType, actionClaim.Issuer, includeResult) - } + Attribute = new List() }; + foreach (var action in actions) + { + actionAttributes.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(MatchAttributeIdentifiers.ActionId, action, DefaultType, actionClaim.Issuer, includeResult)); + } return actionAttributes; } @@ -57,7 +60,7 @@ private static XacmlJsonCategory CreateResourceCategory(string resourceId, Claim var orgClaim = user.Claims.FirstOrDefault(claim => IsOrgClaim(claim.Type)); if (orgClaim is not null) { - resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute("urn:altinn:organization:identifier-no", orgClaim.Value, DefaultType, DefaultIssuer)); + resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute("urn:altinn:organization:identifier-no", orgClaim.Value.Replace("urn:altinn:organization:identifier-no:", ""), DefaultType, DefaultIssuer)); } return resourceCategory; } @@ -69,23 +72,7 @@ private static XacmlJsonCategory CreateSubjectCategory(ClaimsPrincipal user) foreach (Claim claim in user.Claims) { - if (IsOrgClaim(claim.Type)) - { - list.Add(CreateXacmlJsonAttribute("urn:altinn:organizationnumber", claim.Value, "string", claim.Issuer)); - list.Add(CreateXacmlJsonAttribute("urn:altinn:organization:identifier-no", claim.Value, "string", claim.Issuer)); - } - else if (IsActionClaim(claim.Type)) - { - if (claim.Value == "read") - { - list.Add(CreateXacmlJsonAttribute("urn:scope", "altinn:correspondence.read", "string", claim.Issuer)); - } - else if (claim.Value == "write") - { - list.Add(CreateXacmlJsonAttribute("urn:scope", "altinn:correspondence.write", "string", claim.Issuer)); - } - } - else if (IsJtiClaim(claim.Type)) + if (IsJtiClaim(claim.Type)) { list.Add(CreateXacmlJsonAttribute("urn:altinn:sessionid", claim.Value, "string", claim.Issuer)); } @@ -95,7 +82,7 @@ private static XacmlJsonCategory CreateSubjectCategory(ClaimsPrincipal user) } else if (IsSsnClaim(claim.Type)) { - list.Add(CreateXacmlJsonAttribute("urn:altinn:person:identifier-no", claim.Value, "string", claim.Issuer)); + list.Add(CreateXacmlJsonAttribute("urn:altinn:person:identifier-no", claim.Value.Replace("urn:altinn:person:identifier-no:", ""), "string", claim.Issuer)); } } xacmlJsonCategory.Attribute = list; @@ -126,6 +113,47 @@ private static bool IsJtiClaim(string value) return value.Equals("jti"); } + public static bool ValidateDialogportenResult(XacmlJsonResponse response, ClaimsPrincipal user) + { + foreach (var result in response.Response) + { + if (!result.Decision.Equals(XacmlContextDecision.Permit.ToString())) + { + return false; + } + if (result.Obligations != null) + { + List obligations = result.Obligations; + XacmlJsonAttributeAssignment obligation = GetObligation("urn:altinn:minimum-authenticationlevel", obligations); + if (obligation != null) + { + string value = obligation.Value; + string value2 = user.Claims.FirstOrDefault((Claim c) => c.Type.Equals("l")).Value; + if (Convert.ToInt32(value2) < Convert.ToInt32(value)) + { + return false; + } + } + } + return true; + } + + return true; + } + + private static XacmlJsonAttributeAssignment? GetObligation(string category, List obligations) + { + foreach (XacmlJsonObligationOrAdvice obligation in obligations) + { + var xacmlJsonAttributeAssignment = obligation.AttributeAssignment.FirstOrDefault((XacmlJsonAttributeAssignment a) => a.Category.Equals(category)); + if (xacmlJsonAttributeAssignment != null) + { + return xacmlJsonAttributeAssignment; + } + } + return null; + } + private static XacmlJsonAttribute CreateXacmlJsonAttribute(string attributeId, string value, string dataType, string issuer, bool includeResult = false) { XacmlJsonAttribute xacmlJsonAttribute = new XacmlJsonAttribute(); diff --git a/src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogportenXacmlMapper.cs b/src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogportenXacmlMapper.cs deleted file mode 100644 index bd0a26df..00000000 --- a/src/Altinn.Correspondence.Integrations/Dialogporten/Mappers/DialogportenXacmlMapper.cs +++ /dev/null @@ -1,146 +0,0 @@ -using Altinn.Authorization.ABAC.Xacml.JsonProfile; -using Altinn.Common.PEP.Constants; -using Altinn.Common.PEP.Helpers; -using static Altinn.Authorization.ABAC.Constants.XacmlConstants; -using System.Security.Claims; -using System.Text.RegularExpressions; -using Microsoft.IdentityModel.Tokens; - -namespace Altinn.Correspondence.Integrations.Dialogporten.Mappers -{ - public static class DialogportenXacmlMapper - { /// - /// Default issuer for attributes - /// - internal const string DefaultIssuer = "Dialogporten"; - - /// - /// Default type for attributes - /// - internal const string DefaultType = "string"; - - /// - /// Subject id for multi requests. Inde should be appended. - /// - internal const string SubjectId = "s"; - - /// - /// Action id for multi requests. Inde should be appended. - /// - internal const string ActionId = "a"; - - /// - /// Resource id for multi requests. Inde should be appended. - /// - internal const string ResourceId = "r"; - - /// Action type represented as a string - /// A value indicating whether the value should be included in the result - /// A XacmlJsonCategory - internal static XacmlJsonCategory CreateActionCategory(ClaimsPrincipal user, bool includeResult = false) - { - var actionClaim = user.Claims.FirstOrDefault(claim => IsActionClaim(claim.Type)); - if (actionClaim is null) - { - throw new SecurityTokenException("Dialogporten token does not contain the required action claim"); - } - XacmlJsonCategory actionAttributes = new() - { - Attribute = new List - { - DecisionHelper.CreateXacmlJsonAttribute(MatchAttributeIdentifiers.ActionId, actionClaim.Value, DefaultType, actionClaim.Issuer, includeResult) - } - }; - return actionAttributes; - } - - /// If id is required this should be included by the caller. - /// Attribute eventId is tagged with `includeInResponse` - internal static XacmlJsonCategory CreateResourceCategory(string resourceId, ClaimsPrincipal user) - { - XacmlJsonCategory resourceCategory = new() { Attribute = new List() }; - resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute(AltinnXacmlUrns.ResourceId, resourceId, DefaultType, DefaultIssuer)); - var claim = user.Claims.FirstOrDefault(claim => IsOrgClaim(claim.Type)); - if (claim is not null) - { - resourceCategory.Attribute.Add(DecisionHelper.CreateXacmlJsonAttribute("urn:altinn:organization:identifier-no", claim.Value, DefaultType, DefaultIssuer)); - } - return resourceCategory; - } - - internal static XacmlJsonCategory CreateSubjectCategory(ClaimsPrincipal user) - { - XacmlJsonCategory xacmlJsonCategory = new XacmlJsonCategory(); - List list = new List(); - - foreach (Claim claim in user.Claims) - { - if (IsOrgClaim(claim.Type)) - { - list.Add(CreateXacmlJsonAttribute("urn:altinn:organizationnumber", claim.Value, "string", claim.Issuer)); - list.Add(CreateXacmlJsonAttribute("urn:altinn:organization:identifier-no", claim.Value, "string", claim.Issuer)); - } - else if (IsActionClaim(claim.Type)) - { - if (claim.Value == "read") - { - list.Add(CreateXacmlJsonAttribute("urn:scope", "altinn:correspondence.read", "string", claim.Issuer)); - } - else if (claim.Value == "write") - { - list.Add(CreateXacmlJsonAttribute("urn:scope", "altinn:correspondence.write", "string", claim.Issuer)); - } - } - else if (IsJtiClaim(claim.Type)) - { - list.Add(CreateXacmlJsonAttribute("urn:altinn:sessionid", claim.Value, "string", claim.Issuer)); - } - else if (IsValidUrn(claim.Type)) - { - list.Add(CreateXacmlJsonAttribute(claim.Type, claim.Value, "string", claim.Issuer)); - } - else if (IsSsnClaim(claim.Type)) - { - list.Add(CreateXacmlJsonAttribute("urn:altinn:person:identifier-no", claim.Value, "string", claim.Issuer)); - } - } - xacmlJsonCategory.Attribute = list; - return xacmlJsonCategory; - } - private static bool IsValidUrn(string value) - { - Regex regex = new Regex("^urn*"); - return regex.Match(value).Success; - } - - private static bool IsOrgClaim(string value) - { - return value.Equals("p"); - } - private static bool IsSsnClaim(string value) - { - return value.Equals("c"); - } - - private static bool IsActionClaim(string value) - { - return value.Equals("a"); - } - - private static bool IsJtiClaim(string value) - { - return value.Equals("jti"); - } - - private static XacmlJsonAttribute CreateXacmlJsonAttribute(string attributeId, string value, string dataType, string issuer, bool includeResult = false) - { - XacmlJsonAttribute xacmlJsonAttribute = new XacmlJsonAttribute(); - xacmlJsonAttribute.AttributeId = attributeId; - xacmlJsonAttribute.Value = value; - xacmlJsonAttribute.DataType = dataType; - xacmlJsonAttribute.Issuer = issuer; - xacmlJsonAttribute.IncludeInResult = includeResult; - return xacmlJsonAttribute; - } - } -}