diff --git a/.github/workflows/_deploy-app.yml b/.github/workflows/_deploy-app.yml
index 794ed910..81806c71 100644
--- a/.github/workflows/_deploy-app.yml
+++ b/.github/workflows/_deploy-app.yml
@@ -89,27 +89,27 @@ jobs:
echo "Image: ${IMAGE}"
echo "Tag: ${TAG}"
- deploy-terraform:
- name: Run terraform deployment
-
- if: inputs.terraform
-
- strategy:
- fail-fast: false
- matrix:
- environment: [at21, at22]
-
- needs:
- - build-push
-
- uses: ./.github/workflows/_deploy-app-terraform.yml
- with:
- environment: ${{ matrix.environment }}
- working_dir: ${{ inputs.path }}/deploy
- tf_state: ${{ inputs.terraformStateFile }}
- tf_args: "-var image=${{ needs.build-push.outputs.image }}"
-
- databaseBootstrap: ${{ inputs.databaseBootstrap }}
- databaseName: ${{ inputs.databaseName }}
- databaseRoleprefix: ${{ inputs.databaseRoleprefix }}
- databaseSchema: ${{ inputs.databaseSchema }}
+ # deploy-terraform:
+ # name: Run terraform deployment
+
+ # if: inputs.terraform
+
+ # strategy:
+ # fail-fast: false
+ # matrix:
+ # environment: [at21, at22]
+
+ # needs:
+ # - build-push
+
+ # uses: ./.github/workflows/_deploy-app-terraform.yml
+ # with:
+ # environment: ${{ matrix.environment }}
+ # working_dir: ${{ inputs.path }}/deploy
+ # tf_state: ${{ inputs.terraformStateFile }}
+ # tf_args: "-var image=${{ needs.build-push.outputs.image }}"
+
+ # databaseBootstrap: ${{ inputs.databaseBootstrap }}
+ # databaseName: ${{ inputs.databaseName }}
+ # databaseRoleprefix: ${{ inputs.databaseRoleprefix }}
+ # databaseSchema: ${{ inputs.databaseSchema }}
diff --git a/.justfile b/.justfile
index e75e5016..c13aa508 100644
--- a/.justfile
+++ b/.justfile
@@ -5,22 +5,22 @@
# Install node packages required to run scripts - uses pnpm to install the packages
[private]
@install-script-packages:
- #!pwsh
+ #!/usr/bin/env pwsh
pushd .github/scripts
pnpm install
[private]
@install-script-packages-frozen:
- #!pwsh
+ #!/usr/bin/env pwsh
pushd .github/scripts
pnpm install --frozen-lockfile
# Run the script to update solution files
@update-sln-files: install-script-packages-frozen
- #!pwsh
+ #!/usr/bin/env pwsh
./.github/scripts/node_modules/.bin/tsx ./.github/scripts/update-sln-files.mts
# Print all projects metadata
@get-metadata: install-script-packages-frozen
- #!pwsh
+ #!/usr/bin/env pwsh
./.github/scripts/node_modules/.bin/tsx ./.github/scripts/get-metadata.mts
diff --git a/Altinn.Authorization.sln b/Altinn.Authorization.sln
index 1570f66f..64725550 100644
--- a/Altinn.Authorization.sln
+++ b/Altinn.Authorization.sln
@@ -3,55 +3,87 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{975139F1-669A-47EF-B6FB-9514471F6058}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DE24A865-2B94-42FE-84BF-F21C06B28927}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apps", "apps", "{7A57912A-A922-4866-8BD8-A55CEB91B2FD}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apps", "apps", "{3ABDFDEE-A4D9-4553-B232-E7D5FCDB2143}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.AccessPackages", "Altinn.Authorization.AccessPackages", "{4563D148-CDCF-4126-9888-BD57F293BC37}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.AccessManagement", "Altinn.Authorization.AccessManagement", "{78BCBBBB-4B3F-4621-999E-ADC3D49C3400}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3BF6354A-38B6-44FA-8372-C297B02593AD}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3EE68CF0-E104-414F-978D-AAAFE606E706}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.AccessPackages", "src\apps\Altinn.Authorization.AccessPackages\src\Altinn.Authorization.AccessPackages\Altinn.Authorization.AccessPackages.csproj", "{3BE07AA7-161E-447E-AB3E-1EF3EBC0CFAA}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Core", "src\apps\Altinn.Authorization.AccessManagement\src\Altinn.AccessManagement.Core\Altinn.AccessManagement.Core.csproj", "{307E7D19-617B-4CA7-9D21-05ECD210E742}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.DeployApi", "Altinn.Authorization.DeployApi", "{D257B57A-E3D7-4D91-AFBA-43C0A892ABDC}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Integration", "src\apps\Altinn.Authorization.AccessManagement\src\Altinn.AccessManagement.Integration\Altinn.AccessManagement.Integration.csproj", "{2FFBF1A5-5F7D-4DEB-B119-E395F6F89C9A}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C2A52F6B-FA60-49E5-A74B-F508EE7BE4F9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Persistence", "src\apps\Altinn.Authorization.AccessManagement\src\Altinn.AccessManagement.Persistence\Altinn.AccessManagement.Persistence.csproj", "{835EE660-767B-42F7-AD06-3748F6A0CAFC}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.DeployApi", "src\apps\Altinn.Authorization.DeployApi\src\Altinn.Authorization.DeployApi\Altinn.Authorization.DeployApi.csproj", "{FB9E7D21-3AAA-40A8-A1D2-9140671838EF}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement", "src\apps\Altinn.Authorization.AccessManagement\src\Altinn.Authorization.AccessManagement\Altinn.AccessManagement.csproj", "{DF6C3D34-D5D8-4FF6-BD65-972B58979AD7}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.AccessPackages.Models", "src\apps\Altinn.Authorization.AccessPackages\src\Altinn.Authorization.AccessPackages.Models\Altinn.Authorization.AccessPackages.Models.csproj", "{6AAC3869-D894-45B7-9339-94774CC1F321}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C60F699D-057F-4FD5-B65A-DF94D4D8B6E7}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.Index", "Altinn.Authorization.Index", "{C417CFA9-3AEE-42FF-A70D-C50CA927CA3D}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Tests", "src\apps\Altinn.Authorization.AccessManagement\test\Altinn.AccessManagement.Tests\Altinn.AccessManagement.Tests.csproj", "{E951B93E-1CDB-4553-9AF1-FB04AE3E3FF7}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6D228887-7EEA-4E7F-A6FA-06B34D614141}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.AccessPackages", "Altinn.Authorization.AccessPackages", "{F5BD7A32-3249-462D-9DBB-B0199E30789C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Index", "src\apps\Altinn.Authorization.Index\src\Altinn.Authorization.Index\Altinn.Authorization.Index.csproj", "{9776299B-47AE-4602-8A34-2A53F8EBAF22}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E02F98B0-98F1-4B74-B69D-EE820433360A}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libs", "libs", "{8A0804D3-795C-4D1B-B896-EFBF61F275DC}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.AccessPackages", "src\apps\Altinn.Authorization.AccessPackages\src\Altinn.Authorization.AccessPackages\Altinn.Authorization.AccessPackages.csproj", "{D64F8AE2-486C-4A4B-A683-3E7BA3074E84}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.Configuration", "Altinn.Authorization.Configuration", "{B80707D6-12E4-41EC-B72E-D93A264FCD65}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.AccessPackages.Models", "src\apps\Altinn.Authorization.AccessPackages\src\Altinn.Authorization.AccessPackages.Models\Altinn.Authorization.AccessPackages.Models.csproj", "{86977C4D-7BEB-4420-AD9A-40AC2F41DD12}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C37C79A6-1256-4EAC-BC21-918743D0E7EF}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.DeployApi", "Altinn.Authorization.DeployApi", "{2AEAF5A1-3548-40F9-8EF9-16CCE43B21CB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration.Postgres", "src\libs\Altinn.Authorization.Configuration\src\Altinn.Authorization.Configuration.Postgres\Altinn.Authorization.Configuration.Postgres.csproj", "{2296F778-A71F-40B2-818E-23F077D63722}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{54F6103D-EF1A-4AB4-8E4C-AD89552D130E}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{12944668-5C16-4E7B-878D-16797DE5307C}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.DeployApi", "src\apps\Altinn.Authorization.DeployApi\src\Altinn.Authorization.DeployApi\Altinn.Authorization.DeployApi.csproj", "{9C38BBCB-A82D-49B8-9E33-9BEADDF029E5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration.Tests", "src\libs\Altinn.Authorization.Configuration\tests\Altinn.Authorization.Configuration.Tests\Altinn.Authorization.Configuration.Tests.csproj", "{886728F4-7632-45A6-9C0F-147F71325F12}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.Index", "Altinn.Authorization.Index", "{02ECB7FC-14D1-4310-884E-C0FC79F787FF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration", "src\libs\Altinn.Authorization.Configuration\src\Altinn.Authorization.Configuration\Altinn.Authorization.Configuration.csproj", "{A0B4E5A8-D7AE-4566-BEEB-40670F1AB32B}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E4043CB1-7F13-45F3-B62F-5306971435EB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration.OpenTelemetry", "src\libs\Altinn.Authorization.Configuration\src\Altinn.Authorization.Configuration.OpenTelemetry\Altinn.Authorization.Configuration.OpenTelemetry.csproj", "{C9A0D181-700B-4B54-995E-6AAB62584B15}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Index", "src\apps\Altinn.Authorization.Index\src\Altinn.Authorization.Index\Altinn.Authorization.Index.csproj", "{D6D1724F-873B-48BD-A8AD-F6E0915ABD67}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.Hosting", "Altinn.Authorization.Hosting", "{EB54B6F3-D4CE-4C49-90AC-993D59327008}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libs", "libs", "{5514C81E-13A7-40C9-A695-19ECFCD23DE8}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D4E493DD-D9AB-4D68-BD8E-9BE156CD175A}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.Configuration", "Altinn.Authorization.Configuration", "{FE9CEC6D-C72C-4F7B-AF91-AEBAF175C3C9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Hosting", "src\libs\Altinn.Authorization.Hosting\src\Altinn.Authorization.Hosting\Altinn.Authorization.Hosting.csproj", "{C7F9BA45-8010-4B92-8CC7-5050107F2BFE}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FFE61949-10AB-475C-B45D-E405B72C48AF}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7E2E4A16-8A53-4E6A-BC22-832D541E1B92}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration", "src\libs\Altinn.Authorization.Configuration\src\Altinn.Authorization.Configuration\Altinn.Authorization.Configuration.csproj", "{B35658FD-6786-4E62-8E33-A16E850A0681}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Hosting.Tests", "src\libs\Altinn.Authorization.Hosting\tests\Altinn.Authorization.Hosting.Tests\Altinn.Authorization.Hosting.Tests.csproj", "{53F3961A-B2F9-4BF8-9365-80D93DB621B2}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration.OpenTelemetry", "src\libs\Altinn.Authorization.Configuration\src\Altinn.Authorization.Configuration.OpenTelemetry\Altinn.Authorization.Configuration.OpenTelemetry.csproj", "{1C8DAFC4-5436-4B88-B755-C714B3AF55D1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration.Postgres", "src\libs\Altinn.Authorization.Configuration\src\Altinn.Authorization.Configuration.Postgres\Altinn.Authorization.Configuration.Postgres.csproj", "{D363DDF3-2B0F-4982-B169-A3D13F3DC227}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7274498B-9227-4C4D-8FA1-FEB39C237092}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Configuration.Tests", "src\libs\Altinn.Authorization.Configuration\tests\Altinn.Authorization.Configuration.Tests\Altinn.Authorization.Configuration.Tests.csproj", "{A90481D9-6811-42DC-982B-CD6B43173F22}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.Authorization.Hosting", "Altinn.Authorization.Hosting", "{D847014E-BF21-47CB-84EE-609BBA9DC0FA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2324178C-A39A-4D66-B389-47715534DCD4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Hosting", "src\libs\Altinn.Authorization.Hosting\src\Altinn.Authorization.Hosting\Altinn.Authorization.Hosting.csproj", "{8853EEFF-C485-4ED4-B107-EB8CFBC6B1B1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{07BEEFD7-EF32-4213-B94B-D0911252FBFA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.Authorization.Hosting.Tests", "src\libs\Altinn.Authorization.Hosting\tests\Altinn.Authorization.Hosting.Tests\Altinn.Authorization.Hosting.Tests.csproj", "{F592F818-1762-4BD4-A0B4-829246053E6F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Altinn.AccessManagement", "Altinn.AccessManagement", "{51F9AEC3-3367-427F-867E-E4F4A0169A5C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1B272AAB-1044-4E8C-988B-090D6F838FFC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement", "src\apps\Altinn.AccessManagement\src\Altinn.AccessManagement\Altinn.AccessManagement.csproj", "{70DDC969-E82E-4A85-9651-86A5D3B68D3E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Core", "src\apps\Altinn.AccessManagement\src\Altinn.AccessManagement.Core\Altinn.AccessManagement.Core.csproj", "{7BCEAA14-AF6A-4C03-8DF6-E4461C0A612F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Integration", "src\apps\Altinn.AccessManagement\src\Altinn.AccessManagement.Integration\Altinn.AccessManagement.Integration.csproj", "{7D93124A-8BD9-4BB4-8605-126BA6D6E7D0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Persistence", "src\apps\Altinn.AccessManagement\src\Altinn.AccessManagement.Persistence\Altinn.AccessManagement.Persistence.csproj", "{EFBF6A37-CB36-4323-A6D2-5A914C29C838}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6AF3BDE5-F14C-496C-90A9-29DDC108A8FB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Tests", "src\apps\Altinn.AccessManagement\test\Altinn.AccessManagement.Tests\Altinn.AccessManagement.Tests.csproj", "{0DA470DA-7794-4352-89E0-0F22546DF2E4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -62,71 +94,127 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {3BE07AA7-161E-447E-AB3E-1EF3EBC0CFAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3BE07AA7-161E-447E-AB3E-1EF3EBC0CFAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3BE07AA7-161E-447E-AB3E-1EF3EBC0CFAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3BE07AA7-161E-447E-AB3E-1EF3EBC0CFAA}.Release|Any CPU.Build.0 = Release|Any CPU
- {FB9E7D21-3AAA-40A8-A1D2-9140671838EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FB9E7D21-3AAA-40A8-A1D2-9140671838EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FB9E7D21-3AAA-40A8-A1D2-9140671838EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FB9E7D21-3AAA-40A8-A1D2-9140671838EF}.Release|Any CPU.Build.0 = Release|Any CPU
- {6AAC3869-D894-45B7-9339-94774CC1F321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6AAC3869-D894-45B7-9339-94774CC1F321}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6AAC3869-D894-45B7-9339-94774CC1F321}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6AAC3869-D894-45B7-9339-94774CC1F321}.Release|Any CPU.Build.0 = Release|Any CPU
- {9776299B-47AE-4602-8A34-2A53F8EBAF22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9776299B-47AE-4602-8A34-2A53F8EBAF22}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9776299B-47AE-4602-8A34-2A53F8EBAF22}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9776299B-47AE-4602-8A34-2A53F8EBAF22}.Release|Any CPU.Build.0 = Release|Any CPU
- {2296F778-A71F-40B2-818E-23F077D63722}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2296F778-A71F-40B2-818E-23F077D63722}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2296F778-A71F-40B2-818E-23F077D63722}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2296F778-A71F-40B2-818E-23F077D63722}.Release|Any CPU.Build.0 = Release|Any CPU
- {886728F4-7632-45A6-9C0F-147F71325F12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {886728F4-7632-45A6-9C0F-147F71325F12}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {886728F4-7632-45A6-9C0F-147F71325F12}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {886728F4-7632-45A6-9C0F-147F71325F12}.Release|Any CPU.Build.0 = Release|Any CPU
- {A0B4E5A8-D7AE-4566-BEEB-40670F1AB32B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A0B4E5A8-D7AE-4566-BEEB-40670F1AB32B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A0B4E5A8-D7AE-4566-BEEB-40670F1AB32B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A0B4E5A8-D7AE-4566-BEEB-40670F1AB32B}.Release|Any CPU.Build.0 = Release|Any CPU
- {C9A0D181-700B-4B54-995E-6AAB62584B15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C9A0D181-700B-4B54-995E-6AAB62584B15}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C9A0D181-700B-4B54-995E-6AAB62584B15}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C9A0D181-700B-4B54-995E-6AAB62584B15}.Release|Any CPU.Build.0 = Release|Any CPU
- {C7F9BA45-8010-4B92-8CC7-5050107F2BFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C7F9BA45-8010-4B92-8CC7-5050107F2BFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C7F9BA45-8010-4B92-8CC7-5050107F2BFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C7F9BA45-8010-4B92-8CC7-5050107F2BFE}.Release|Any CPU.Build.0 = Release|Any CPU
- {53F3961A-B2F9-4BF8-9365-80D93DB621B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {53F3961A-B2F9-4BF8-9365-80D93DB621B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {53F3961A-B2F9-4BF8-9365-80D93DB621B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {53F3961A-B2F9-4BF8-9365-80D93DB621B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {307E7D19-617B-4CA7-9D21-05ECD210E742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {307E7D19-617B-4CA7-9D21-05ECD210E742}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {307E7D19-617B-4CA7-9D21-05ECD210E742}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {307E7D19-617B-4CA7-9D21-05ECD210E742}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2FFBF1A5-5F7D-4DEB-B119-E395F6F89C9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2FFBF1A5-5F7D-4DEB-B119-E395F6F89C9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2FFBF1A5-5F7D-4DEB-B119-E395F6F89C9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2FFBF1A5-5F7D-4DEB-B119-E395F6F89C9A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {835EE660-767B-42F7-AD06-3748F6A0CAFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {835EE660-767B-42F7-AD06-3748F6A0CAFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {835EE660-767B-42F7-AD06-3748F6A0CAFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {835EE660-767B-42F7-AD06-3748F6A0CAFC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DF6C3D34-D5D8-4FF6-BD65-972B58979AD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DF6C3D34-D5D8-4FF6-BD65-972B58979AD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DF6C3D34-D5D8-4FF6-BD65-972B58979AD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DF6C3D34-D5D8-4FF6-BD65-972B58979AD7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E951B93E-1CDB-4553-9AF1-FB04AE3E3FF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E951B93E-1CDB-4553-9AF1-FB04AE3E3FF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E951B93E-1CDB-4553-9AF1-FB04AE3E3FF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E951B93E-1CDB-4553-9AF1-FB04AE3E3FF7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D64F8AE2-486C-4A4B-A683-3E7BA3074E84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D64F8AE2-486C-4A4B-A683-3E7BA3074E84}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D64F8AE2-486C-4A4B-A683-3E7BA3074E84}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D64F8AE2-486C-4A4B-A683-3E7BA3074E84}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86977C4D-7BEB-4420-AD9A-40AC2F41DD12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86977C4D-7BEB-4420-AD9A-40AC2F41DD12}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86977C4D-7BEB-4420-AD9A-40AC2F41DD12}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86977C4D-7BEB-4420-AD9A-40AC2F41DD12}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9C38BBCB-A82D-49B8-9E33-9BEADDF029E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9C38BBCB-A82D-49B8-9E33-9BEADDF029E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9C38BBCB-A82D-49B8-9E33-9BEADDF029E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9C38BBCB-A82D-49B8-9E33-9BEADDF029E5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6D1724F-873B-48BD-A8AD-F6E0915ABD67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6D1724F-873B-48BD-A8AD-F6E0915ABD67}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6D1724F-873B-48BD-A8AD-F6E0915ABD67}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6D1724F-873B-48BD-A8AD-F6E0915ABD67}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B35658FD-6786-4E62-8E33-A16E850A0681}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B35658FD-6786-4E62-8E33-A16E850A0681}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B35658FD-6786-4E62-8E33-A16E850A0681}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B35658FD-6786-4E62-8E33-A16E850A0681}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C8DAFC4-5436-4B88-B755-C714B3AF55D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1C8DAFC4-5436-4B88-B755-C714B3AF55D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C8DAFC4-5436-4B88-B755-C714B3AF55D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1C8DAFC4-5436-4B88-B755-C714B3AF55D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D363DDF3-2B0F-4982-B169-A3D13F3DC227}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D363DDF3-2B0F-4982-B169-A3D13F3DC227}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D363DDF3-2B0F-4982-B169-A3D13F3DC227}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D363DDF3-2B0F-4982-B169-A3D13F3DC227}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A90481D9-6811-42DC-982B-CD6B43173F22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A90481D9-6811-42DC-982B-CD6B43173F22}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A90481D9-6811-42DC-982B-CD6B43173F22}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A90481D9-6811-42DC-982B-CD6B43173F22}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8853EEFF-C485-4ED4-B107-EB8CFBC6B1B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8853EEFF-C485-4ED4-B107-EB8CFBC6B1B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8853EEFF-C485-4ED4-B107-EB8CFBC6B1B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8853EEFF-C485-4ED4-B107-EB8CFBC6B1B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F592F818-1762-4BD4-A0B4-829246053E6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F592F818-1762-4BD4-A0B4-829246053E6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F592F818-1762-4BD4-A0B4-829246053E6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F592F818-1762-4BD4-A0B4-829246053E6F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {70DDC969-E82E-4A85-9651-86A5D3B68D3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70DDC969-E82E-4A85-9651-86A5D3B68D3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70DDC969-E82E-4A85-9651-86A5D3B68D3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70DDC969-E82E-4A85-9651-86A5D3B68D3E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7BCEAA14-AF6A-4C03-8DF6-E4461C0A612F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7BCEAA14-AF6A-4C03-8DF6-E4461C0A612F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7BCEAA14-AF6A-4C03-8DF6-E4461C0A612F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7BCEAA14-AF6A-4C03-8DF6-E4461C0A612F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7D93124A-8BD9-4BB4-8605-126BA6D6E7D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D93124A-8BD9-4BB4-8605-126BA6D6E7D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D93124A-8BD9-4BB4-8605-126BA6D6E7D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D93124A-8BD9-4BB4-8605-126BA6D6E7D0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EFBF6A37-CB36-4323-A6D2-5A914C29C838}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EFBF6A37-CB36-4323-A6D2-5A914C29C838}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EFBF6A37-CB36-4323-A6D2-5A914C29C838}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EFBF6A37-CB36-4323-A6D2-5A914C29C838}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0DA470DA-7794-4352-89E0-0F22546DF2E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0DA470DA-7794-4352-89E0-0F22546DF2E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0DA470DA-7794-4352-89E0-0F22546DF2E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0DA470DA-7794-4352-89E0-0F22546DF2E4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {7A57912A-A922-4866-8BD8-A55CEB91B2FD} = {975139F1-669A-47EF-B6FB-9514471F6058}
- {4563D148-CDCF-4126-9888-BD57F293BC37} = {7A57912A-A922-4866-8BD8-A55CEB91B2FD}
- {3BF6354A-38B6-44FA-8372-C297B02593AD} = {4563D148-CDCF-4126-9888-BD57F293BC37}
- {3BE07AA7-161E-447E-AB3E-1EF3EBC0CFAA} = {3BF6354A-38B6-44FA-8372-C297B02593AD}
- {D257B57A-E3D7-4D91-AFBA-43C0A892ABDC} = {7A57912A-A922-4866-8BD8-A55CEB91B2FD}
- {C2A52F6B-FA60-49E5-A74B-F508EE7BE4F9} = {D257B57A-E3D7-4D91-AFBA-43C0A892ABDC}
- {FB9E7D21-3AAA-40A8-A1D2-9140671838EF} = {C2A52F6B-FA60-49E5-A74B-F508EE7BE4F9}
- {6AAC3869-D894-45B7-9339-94774CC1F321} = {3BF6354A-38B6-44FA-8372-C297B02593AD}
- {C417CFA9-3AEE-42FF-A70D-C50CA927CA3D} = {7A57912A-A922-4866-8BD8-A55CEB91B2FD}
- {6D228887-7EEA-4E7F-A6FA-06B34D614141} = {C417CFA9-3AEE-42FF-A70D-C50CA927CA3D}
- {9776299B-47AE-4602-8A34-2A53F8EBAF22} = {6D228887-7EEA-4E7F-A6FA-06B34D614141}
- {8A0804D3-795C-4D1B-B896-EFBF61F275DC} = {975139F1-669A-47EF-B6FB-9514471F6058}
- {B80707D6-12E4-41EC-B72E-D93A264FCD65} = {8A0804D3-795C-4D1B-B896-EFBF61F275DC}
- {C37C79A6-1256-4EAC-BC21-918743D0E7EF} = {B80707D6-12E4-41EC-B72E-D93A264FCD65}
- {2296F778-A71F-40B2-818E-23F077D63722} = {C37C79A6-1256-4EAC-BC21-918743D0E7EF}
- {12944668-5C16-4E7B-878D-16797DE5307C} = {B80707D6-12E4-41EC-B72E-D93A264FCD65}
- {886728F4-7632-45A6-9C0F-147F71325F12} = {12944668-5C16-4E7B-878D-16797DE5307C}
- {A0B4E5A8-D7AE-4566-BEEB-40670F1AB32B} = {C37C79A6-1256-4EAC-BC21-918743D0E7EF}
- {C9A0D181-700B-4B54-995E-6AAB62584B15} = {C37C79A6-1256-4EAC-BC21-918743D0E7EF}
- {EB54B6F3-D4CE-4C49-90AC-993D59327008} = {8A0804D3-795C-4D1B-B896-EFBF61F275DC}
- {D4E493DD-D9AB-4D68-BD8E-9BE156CD175A} = {EB54B6F3-D4CE-4C49-90AC-993D59327008}
- {C7F9BA45-8010-4B92-8CC7-5050107F2BFE} = {D4E493DD-D9AB-4D68-BD8E-9BE156CD175A}
- {7E2E4A16-8A53-4E6A-BC22-832D541E1B92} = {EB54B6F3-D4CE-4C49-90AC-993D59327008}
- {53F3961A-B2F9-4BF8-9365-80D93DB621B2} = {7E2E4A16-8A53-4E6A-BC22-832D541E1B92}
+ {3ABDFDEE-A4D9-4553-B232-E7D5FCDB2143} = {DE24A865-2B94-42FE-84BF-F21C06B28927}
+ {78BCBBBB-4B3F-4621-999E-ADC3D49C3400} = {3ABDFDEE-A4D9-4553-B232-E7D5FCDB2143}
+ {3EE68CF0-E104-414F-978D-AAAFE606E706} = {78BCBBBB-4B3F-4621-999E-ADC3D49C3400}
+ {307E7D19-617B-4CA7-9D21-05ECD210E742} = {3EE68CF0-E104-414F-978D-AAAFE606E706}
+ {2FFBF1A5-5F7D-4DEB-B119-E395F6F89C9A} = {3EE68CF0-E104-414F-978D-AAAFE606E706}
+ {835EE660-767B-42F7-AD06-3748F6A0CAFC} = {3EE68CF0-E104-414F-978D-AAAFE606E706}
+ {DF6C3D34-D5D8-4FF6-BD65-972B58979AD7} = {3EE68CF0-E104-414F-978D-AAAFE606E706}
+ {C60F699D-057F-4FD5-B65A-DF94D4D8B6E7} = {78BCBBBB-4B3F-4621-999E-ADC3D49C3400}
+ {E951B93E-1CDB-4553-9AF1-FB04AE3E3FF7} = {C60F699D-057F-4FD5-B65A-DF94D4D8B6E7}
+ {F5BD7A32-3249-462D-9DBB-B0199E30789C} = {3ABDFDEE-A4D9-4553-B232-E7D5FCDB2143}
+ {E02F98B0-98F1-4B74-B69D-EE820433360A} = {F5BD7A32-3249-462D-9DBB-B0199E30789C}
+ {D64F8AE2-486C-4A4B-A683-3E7BA3074E84} = {E02F98B0-98F1-4B74-B69D-EE820433360A}
+ {86977C4D-7BEB-4420-AD9A-40AC2F41DD12} = {E02F98B0-98F1-4B74-B69D-EE820433360A}
+ {2AEAF5A1-3548-40F9-8EF9-16CCE43B21CB} = {3ABDFDEE-A4D9-4553-B232-E7D5FCDB2143}
+ {54F6103D-EF1A-4AB4-8E4C-AD89552D130E} = {2AEAF5A1-3548-40F9-8EF9-16CCE43B21CB}
+ {9C38BBCB-A82D-49B8-9E33-9BEADDF029E5} = {54F6103D-EF1A-4AB4-8E4C-AD89552D130E}
+ {02ECB7FC-14D1-4310-884E-C0FC79F787FF} = {3ABDFDEE-A4D9-4553-B232-E7D5FCDB2143}
+ {E4043CB1-7F13-45F3-B62F-5306971435EB} = {02ECB7FC-14D1-4310-884E-C0FC79F787FF}
+ {D6D1724F-873B-48BD-A8AD-F6E0915ABD67} = {E4043CB1-7F13-45F3-B62F-5306971435EB}
+ {5514C81E-13A7-40C9-A695-19ECFCD23DE8} = {DE24A865-2B94-42FE-84BF-F21C06B28927}
+ {FE9CEC6D-C72C-4F7B-AF91-AEBAF175C3C9} = {5514C81E-13A7-40C9-A695-19ECFCD23DE8}
+ {FFE61949-10AB-475C-B45D-E405B72C48AF} = {FE9CEC6D-C72C-4F7B-AF91-AEBAF175C3C9}
+ {B35658FD-6786-4E62-8E33-A16E850A0681} = {FFE61949-10AB-475C-B45D-E405B72C48AF}
+ {1C8DAFC4-5436-4B88-B755-C714B3AF55D1} = {FFE61949-10AB-475C-B45D-E405B72C48AF}
+ {D363DDF3-2B0F-4982-B169-A3D13F3DC227} = {FFE61949-10AB-475C-B45D-E405B72C48AF}
+ {7274498B-9227-4C4D-8FA1-FEB39C237092} = {FE9CEC6D-C72C-4F7B-AF91-AEBAF175C3C9}
+ {A90481D9-6811-42DC-982B-CD6B43173F22} = {7274498B-9227-4C4D-8FA1-FEB39C237092}
+ {D847014E-BF21-47CB-84EE-609BBA9DC0FA} = {5514C81E-13A7-40C9-A695-19ECFCD23DE8}
+ {2324178C-A39A-4D66-B389-47715534DCD4} = {D847014E-BF21-47CB-84EE-609BBA9DC0FA}
+ {8853EEFF-C485-4ED4-B107-EB8CFBC6B1B1} = {2324178C-A39A-4D66-B389-47715534DCD4}
+ {07BEEFD7-EF32-4213-B94B-D0911252FBFA} = {D847014E-BF21-47CB-84EE-609BBA9DC0FA}
+ {F592F818-1762-4BD4-A0B4-829246053E6F} = {07BEEFD7-EF32-4213-B94B-D0911252FBFA}
+ {51F9AEC3-3367-427F-867E-E4F4A0169A5C} = {3ABDFDEE-A4D9-4553-B232-E7D5FCDB2143}
+ {1B272AAB-1044-4E8C-988B-090D6F838FFC} = {51F9AEC3-3367-427F-867E-E4F4A0169A5C}
+ {70DDC969-E82E-4A85-9651-86A5D3B68D3E} = {1B272AAB-1044-4E8C-988B-090D6F838FFC}
+ {7BCEAA14-AF6A-4C03-8DF6-E4461C0A612F} = {1B272AAB-1044-4E8C-988B-090D6F838FFC}
+ {7D93124A-8BD9-4BB4-8605-126BA6D6E7D0} = {1B272AAB-1044-4E8C-988B-090D6F838FFC}
+ {EFBF6A37-CB36-4323-A6D2-5A914C29C838} = {1B272AAB-1044-4E8C-988B-090D6F838FFC}
+ {6AF3BDE5-F14C-496C-90A9-29DDC108A8FB} = {51F9AEC3-3367-427F-867E-E4F4A0169A5C}
+ {0DA470DA-7794-4352-89E0-0F22546DF2E4} = {6AF3BDE5-F14C-496C-90A9-29DDC108A8FB}
EndGlobalSection
EndGlobal
diff --git a/Makefile b/Makefile
deleted file mode 100644
index a41a8cbc..00000000
--- a/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-SLN=Altinn.Authorization
-
-dotnet_solution: clean
- dotnet new sln -n $(SLN)
- find src -name "*.csproj" -print0 | xargs -0 dotnet sln add
-
-clean:
- @rm -f $(SLN).sln
- echo "** Clean Solution **"
\ No newline at end of file
diff --git a/global.json b/global.json
index 501e79a8..d5bf446d 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "9.0.100",
"rollForward": "latestFeature"
}
}
\ No newline at end of file
diff --git a/package.json b/package.json
deleted file mode 100644
index 0103007d..00000000
--- a/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "devDependencies": {
- "@types/node": "^22.7.5"
- }
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
deleted file mode 100644
index fcc75539..00000000
--- a/pnpm-lock.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-importers:
-
- .:
- devDependencies:
- '@types/node':
- specifier: ^22.7.5
- version: 22.7.5
-
-packages:
-
- '@types/node@22.7.5':
- resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==}
-
- undici-types@6.19.8:
- resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
-
-snapshots:
-
- '@types/node@22.7.5':
- dependencies:
- undici-types: 6.19.8
-
- undici-types@6.19.8: {}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 163dfa2d..f2c13c12 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,7 +1,8 @@
- net8.0
+ Altinn.Authorization
+ net9.0
enable
12.0
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 6aeb1641..2f13c8c9 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -1,54 +1,96 @@
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/Altinn.AccessManagement.sln b/src/apps/Altinn.AccessManagement/Altinn.AccessManagement.sln
new file mode 100644
index 00000000..a9876f49
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/Altinn.AccessManagement.sln
@@ -0,0 +1,57 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{960E20A2-19CD-4579-9E93-D2E7C02D3629}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement", "src\Altinn.AccessManagement\Altinn.AccessManagement.csproj", "{A3F09A0C-0978-4F23-9F35-A7343DC76D03}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7EBD92A3-BEBF-4608-9535-F419BF22EE83}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Tests", "test\Altinn.AccessManagement.Tests\Altinn.AccessManagement.Tests.csproj", "{DC83D8C7-BE67-451D-AD00-172CE364468C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Integration", "src\Altinn.AccessManagement.Integration\Altinn.AccessManagement.Integration.csproj", "{F6682422-0B31-4E83-8B03-BE21267A95C4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Core", "src\Altinn.AccessManagement.Core\Altinn.AccessManagement.Core.csproj", "{601CE177-6CC3-4D7B-8CC5-5B173598ECAE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.AccessManagement.Persistence", "src\Altinn.AccessManagement.Persistence\Altinn.AccessManagement.Persistence.csproj", "{8482BD82-0121-4B24-B7E9-4688E41584F8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A3F09A0C-0978-4F23-9F35-A7343DC76D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A3F09A0C-0978-4F23-9F35-A7343DC76D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3F09A0C-0978-4F23-9F35-A7343DC76D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A3F09A0C-0978-4F23-9F35-A7343DC76D03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DC83D8C7-BE67-451D-AD00-172CE364468C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DC83D8C7-BE67-451D-AD00-172CE364468C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DC83D8C7-BE67-451D-AD00-172CE364468C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DC83D8C7-BE67-451D-AD00-172CE364468C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F6682422-0B31-4E83-8B03-BE21267A95C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6682422-0B31-4E83-8B03-BE21267A95C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F6682422-0B31-4E83-8B03-BE21267A95C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F6682422-0B31-4E83-8B03-BE21267A95C4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {601CE177-6CC3-4D7B-8CC5-5B173598ECAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {601CE177-6CC3-4D7B-8CC5-5B173598ECAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {601CE177-6CC3-4D7B-8CC5-5B173598ECAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {601CE177-6CC3-4D7B-8CC5-5B173598ECAE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8482BD82-0121-4B24-B7E9-4688E41584F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8482BD82-0121-4B24-B7E9-4688E41584F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8482BD82-0121-4B24-B7E9-4688E41584F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8482BD82-0121-4B24-B7E9-4688E41584F8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {A3F09A0C-0978-4F23-9F35-A7343DC76D03} = {960E20A2-19CD-4579-9E93-D2E7C02D3629}
+ {DC83D8C7-BE67-451D-AD00-172CE364468C} = {7EBD92A3-BEBF-4608-9535-F419BF22EE83}
+ {F6682422-0B31-4E83-8B03-BE21267A95C4} = {960E20A2-19CD-4579-9E93-D2E7C02D3629}
+ {601CE177-6CC3-4D7B-8CC5-5B173598ECAE} = {960E20A2-19CD-4579-9E93-D2E7C02D3629}
+ {8482BD82-0121-4B24-B7E9-4688E41584F8} = {960E20A2-19CD-4579-9E93-D2E7C02D3629}
+ EndGlobalSection
+EndGlobal
diff --git a/src/apps/Altinn.AccessManagement/Dockerfile b/src/apps/Altinn.AccessManagement/Dockerfile
new file mode 100644
index 00000000..5d1c310f
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/Dockerfile
@@ -0,0 +1,23 @@
+FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
+WORKDIR /app
+
+# Copy everything and build
+COPY . .
+RUN dotnet restore
+
+# Copy everything else and build
+RUN dotnet publish -c Release -o out ./src/Altinn.AccessManagement/Altinn.AccessManagement.csproj
+
+# Build runtime image
+FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
+EXPOSE 5110
+WORKDIR /app
+COPY --from=build /app/out .
+
+# setup the user and group
+# the user will have no password, using shell /bin/false and using the group dotnet
+RUN addgroup -g 3000 dotnet && adduser -u 1000 -G dotnet -D -s /bin/false dotnet
+# update permissions of files if neccessary before becoming dotnet user
+USER dotnet
+RUN mkdir /tmp/logtelemetry
+ENTRYPOINT ["dotnet", "Altinn.AccessManagement.dll"]
diff --git a/src/apps/Altinn.AccessManagement/README.md b/src/apps/Altinn.AccessManagement/README.md
new file mode 100644
index 00000000..aeeabb33
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/README.md
@@ -0,0 +1,45 @@
+# altinn-access-management
+
+This component will handle backend functionality related to Access Management
+- Administration of rights for apps, resources
+- Administration of rights for api schemes
+
+## Getting started
+
+The fastest way to get development going is to open the main solution Altinn.AccessManagement.sln and selecting 'Altinn.AccessManagement' as the start up project from Visual Studio. Browser should open automatically to the swagger ui for the API.
+
+Alternatively:
+
+- Start the backend in `/src/Altinn.AccessManagement/Altinn.AccessManagement` with `dotnet run` or `dotnet watch`
+
+## Project organisation
+
+This is a typical backend API solution written in .NET C#.
+
+- The main back end project is in `/src/Altinn.Authorizationadmin` and it has [its own README](backend/src/Altinn.Authorizationadmin/Altinn.Authorizationadmin/README.md)
+
+- There is also a "bridge" between the back end and older APIs. For local development, this is implemented in `/development/src/LocalBridge`
+
+
+## Setting up database
+
+To run Access Management locally you need to have PostgreSQL database installed
+
+- Download [PostgreSQL](https://www.postgresql.org/download/) (Currently using 14 in Azure, but 15 works locally)
+- Install database server (choose your own admin password and save it some place you can find it again)
+- Start PG admin
+
+
+Create database authorizationdb
+
+Create the following users (with priveliges for authorizationdb)
+-platform_authorization_admin (superuser, canlogin)
+-platform_authorization (canlogin)
+password: Password
+
+Create schema delegations in authorizationdb
+
+Set platform_authorization_admin as owner
+
+
+
diff --git a/src/apps/Altinn.AccessManagement/conf.json b/src/apps/Altinn.AccessManagement/conf.json
new file mode 100644
index 00000000..50009f85
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/conf.json
@@ -0,0 +1,8 @@
+{
+ "image": {},
+ "infra": {
+ "terraform": {
+ "stateFile": "Altinn.Authorization.AccessManagement.tfstate"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Altinn.AccessManagement.Core.csproj b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Altinn.AccessManagement.Core.csproj
new file mode 100644
index 00000000..61037b5b
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Altinn.AccessManagement.Core.csproj
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\..\.nuget\packages\altinn.common.accesstokenclient\1.0.6\lib\netstandard2.0\Altinn.Common.AccessTokenClient.dll
+
+
+
+
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/Asserter.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/Asserter.cs
new file mode 100644
index 00000000..00e5a754
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/Asserter.cs
@@ -0,0 +1,137 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Altinn.AccessManagement.Core.Asserters;
+
+///
+/// The function signature of an Assertion that validates data
+///
+public delegate void Assertion(IDictionary errors, IEnumerable attributes);
+
+///
+public class Asserter : IAssert
+{
+ ///
+ public Assertion Any(params Assertion[] actions) => (errors, values) =>
+ {
+ var result = new List>();
+ foreach (var action in actions)
+ {
+ var err = new Dictionary();
+ action(err, values);
+ if (err.Count == 0)
+ {
+ return;
+ }
+
+ result.Add(err);
+ }
+
+ foreach (var entry in result)
+ {
+ foreach (var err in errors)
+ {
+ AddError(errors, err);
+ }
+ }
+ };
+
+ ///
+ public Assertion All(params Assertion[] actions) => (errors, values) =>
+ {
+ foreach (var action in actions)
+ {
+ action(errors, values);
+ }
+ };
+
+ ///
+ public Assertion Single(params Assertion[] actions) => (errors, values) =>
+ {
+ var result = new List>();
+ foreach (var action in actions)
+ {
+ var err = new Dictionary();
+ action(err, values);
+ if (err.Count > 0)
+ {
+ result.Add(err);
+ }
+ }
+
+ if (result.Count + 1 == actions.Length)
+ {
+ return;
+ }
+
+ if (result.Count == 0)
+ {
+ errors.Add(nameof(Single), ["all assertions passed while it should only be one that passed"]);
+ }
+
+ foreach (var err in result)
+ {
+ foreach (var entry in err)
+ {
+ AddError(errors, entry);
+ }
+ }
+ };
+
+ ///
+ public ValidationProblemDetails Evaluate(IEnumerable values, params Assertion[] actions)
+ {
+ var result = new Dictionary();
+ foreach (var action in actions)
+ {
+ action(result, values);
+ }
+
+ if (result.Count > 0)
+ {
+ return new ValidationProblemDetails(result);
+ }
+
+ return null;
+ }
+
+ ///
+ public ValidationProblemDetails Join(params ValidationProblemDetails[] evaluations)
+ {
+ var result = new ValidationProblemDetails()
+ {
+ Status = StatusCodes.Status400BadRequest,
+ Title = "there is an issue with provided input",
+ };
+
+ foreach (var evaluation in evaluations)
+ {
+ if (evaluation != null)
+ {
+ foreach (var error in evaluation.Errors)
+ {
+ AddError(result.Errors, error);
+ }
+ }
+ }
+
+ return result.Errors.Count > 0 ? result : null;
+ }
+
+ ///
+ /// add error to the dictionary
+ ///
+ /// error dictionary
+ /// the key-value pair that should be written to the error dict
+ private static void AddError(IDictionary errors, KeyValuePair entry)
+ {
+ if (errors.TryGetValue(entry.Key, out var value))
+ {
+ errors[entry.Key] = value == null ? entry.Value : value.Concat(entry.Value ?? Enumerable.Empty()).Distinct().ToArray();
+ }
+ else
+ {
+ errors.Add(entry);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/AttributeMatchAsserter.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/AttributeMatchAsserter.cs
new file mode 100644
index 00000000..f9240d70
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/AttributeMatchAsserter.cs
@@ -0,0 +1,191 @@
+using Altinn.AccessManagement.Core.Constants;
+using Altinn.AccessManagement.Core.Models;
+using Altinn.AccessManagement.Core.Resolvers;
+
+namespace Altinn.AccessManagement.Core.Asserters;
+
+///
+/// Asserts values for model .
+///
+public static class AttributeMatchAsserter
+{
+ private static string StringifyAttributeIds(IEnumerable values) => $"[{string.Join(",", values.Select(v => v.Id).OrderDescending())}]";
+
+ private static string StringifyStrings(IEnumerable values) => $"[{string.Join(",", values.OrderDescending())}]";
+
+ ///
+ /// Passes if all the given attribute types are contained in the given list of attributes.
+ ///
+ public static Assertion HasAttributeTypes(this IAssert _, params string[] attributes) => (errors, values) =>
+ {
+ IEnumerable intersection = values.Select(v => v.Id).Intersect(attributes);
+ if (intersection.Count() == attributes.Count() && intersection.Count() == values.Count())
+ {
+ return;
+ }
+
+ errors.Add(nameof(HasAttributeTypes), [$"attributes {StringifyAttributeIds(values)} is not a combination of {StringifyStrings(attributes)}"]);
+ };
+
+ ///
+ /// Checks if all given types has a value of type integer. Attributes that don't exist in the list of attributes are ignored.
+ ///
+ /// list of attributes
+ /// URN of the types that should be integers
+ public static Assertion AttributesAreIntegers(this IAssert assert, params string[] types) => (errors, values) =>
+ {
+ var matchingAttributes = values.Where(attribute => types.Any(type => type.Equals(attribute.Id, StringComparison.InvariantCultureIgnoreCase)));
+ if (matchingAttributes.Where(attribute => !int.TryParse(attribute.Value, out _)) is var assertedNoneIntegers && assertedNoneIntegers.Any())
+ {
+ errors.Add(nameof(AttributesAreIntegers), [$"attributes {StringifyAttributeIds(values)} can't be parsed as integers"]);
+ }
+ };
+
+ ///
+ /// Checks if all given types has a value of type boolean. Attributes that don't exist in the list of attributes are ignored.
+ ///
+ /// list of attributes
+ /// URN of the types that should be boolean
+ public static Assertion AttributesAreBoolean(this IAssert assert, params string[] types) => (errors, values) =>
+ {
+ var matchingAttributes = values.Where(attribute => types.Any(type => type.Equals(attribute.Id, StringComparison.InvariantCultureIgnoreCase)));
+ if (matchingAttributes.Where(attribute => !bool.TryParse(attribute.Value, out _)) is var assertedNoneIntegers && assertedNoneIntegers.Any())
+ {
+ errors.Add(nameof(AttributesAreIntegers), [$"attributes {StringifyAttributeIds(values)} can't be parsed as boolean"]);
+ }
+ };
+
+ ///
+ /// Can pass a custom compare function that compares a single attribute an return a boolean that specifies if it passed or not.
+ ///
+ /// list of attributes
+ /// compare function
+ public static Assertion AttributeCompare(string type, Func cmp) => (errors, values) =>
+ {
+ if (values.FirstOrDefault(value => value.Id.Equals(type, StringComparison.InvariantCultureIgnoreCase)) is var value && value != null)
+ {
+ if (cmp(value))
+ {
+ return;
+ }
+
+ errors.Add(nameof(AttributeCompare), [$"comparable functions for attribute {type} gave inequal result"]);
+ }
+ else
+ {
+ errors.Add(nameof(AttributeCompare), [$"could not find an attribute with type {StringifyAttributeIds(values)}"]);
+ }
+ };
+
+ ///
+ /// Passes if all attributes has a populated value field. Content is irrelevant, but it can't be an empty string or null
+ ///
+ /// list of assertions
+ /// dictionary for writing assertion errors
+ /// list of attributes
+ public static void AllAttributesHasValues(this IAssert assert, IDictionary errors, IEnumerable values)
+ {
+ var attributesWithEmptyValues = values.Where(attribute => string.IsNullOrEmpty(attribute?.Value));
+ if (attributesWithEmptyValues.Any())
+ {
+ errors.Add(nameof(AllAttributesHasValues), StringifyAttributeIds(attributesWithEmptyValues).Select(type => $"attribute {type} contains empty value").ToArray());
+ }
+ }
+
+ ///
+ /// Checks if a resource is delegable. The resource must be in the list of attributes otherwise it fails.
+ ///
+ /// list of assertions
+ /// dictionary for writing assertion errors
+ /// list of attributes
+ public static void IsDelegatableResource(this IAssert assert, IDictionary errors, IEnumerable values)
+ {
+ if (values.FirstOrDefault(value => value.Id.Equals(BaseUrn.Altinn.Resource.Delegable, StringComparison.InvariantCultureIgnoreCase)) is var attribute && attribute != null)
+ {
+ if (bool.TryParse(attribute.Value, out var value) && value)
+ {
+ return;
+ }
+ else
+ {
+ errors.Add(nameof(IsDelegatableResource), [$"resource is not delegable"]);
+ }
+ }
+ else
+ {
+ errors.Add(nameof(IsDelegatableResource), [$"failed to find any attributes with value {BaseUrn.Altinn.Resource.Delegable}"]);
+ }
+ }
+
+ ///
+ /// A default list of assertions that contains the baseline for validating in input delegaton to an entity.
+ ///
+ /// list of assertions
+ /// dictionary for writing assertion errors
+ /// list of attributes
+ public static void DefaultTo(this IAssert assert, IDictionary errors, IEnumerable values) =>
+ assert.All(
+ assert.Single(
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.IdentifierNo),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.Uuid),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.UserId),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.PartyId),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Organization.IdentifierNo),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Organization.Uuid),
+ assert.HasAttributeTypes(BaseUrn.Altinn.EnterpriseUser.Username),
+ assert.HasAttributeTypes(BaseUrn.Altinn.EnterpriseUser.Uuid),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Organization.PartyId),
+ assert.HasAttributeTypes(AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute),
+ assert.HasAttributeTypes(AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute)),
+ assert.AllAttributesHasValues,
+ assert.AttributesAreIntegers(BaseUrn.InternalIds))(errors, values);
+
+ ///
+ /// A default list of assertions that contains the baseline for validating in input delegaton from an entity.
+ ///
+ /// list of assertions
+ /// dictionary for writing assertion errors
+ /// list of attributes
+ public static void DefaultFrom(this IAssert assert, IDictionary errors, IEnumerable values) =>
+ assert.All(
+ assert.Single(
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.IdentifierNo),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.Uuid),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.UserId),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Person.PartyId),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Organization.IdentifierNo),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Organization.Uuid),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Organization.PartyId),
+ assert.HasAttributeTypes(BaseUrn.Altinn.EnterpriseUser.Username),
+ assert.HasAttributeTypes(BaseUrn.Altinn.EnterpriseUser.Uuid),
+ assert.HasAttributeTypes(AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute)),
+ assert.AllAttributesHasValues,
+ assert.AttributesAreIntegers(BaseUrn.InternalIds))(errors, values);
+
+ ///
+ /// A list of assertions for validating input is a single value of either of the internal Altinn 2 identifiers: UserId or PartyId.
+ ///
+ /// list of assertions
+ /// dictionary for writing assertion errors
+ /// list of attributes
+ public static void Altinn2InternalIds(this IAssert assert, IDictionary errors, IEnumerable values) =>
+ assert.All(
+ assert.Single(
+ assert.HasAttributeTypes(AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute),
+ assert.HasAttributeTypes(AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute)),
+ assert.AllAttributesHasValues,
+ assert.AttributesAreIntegers(BaseUrn.Altinn2InternalIds))(errors, values);
+
+ ///
+ /// A default list of assertions that contains the baseline for validating input for a resource.
+ ///
+ /// list of assertions
+ /// dictionary for writing assertion errors
+ /// list of attributes
+ public static void DefaultResource(this IAssert assert, IDictionary errors, IEnumerable values) =>
+ assert.All(
+ assert.Single(
+ assert.HasAttributeTypes(BaseUrn.Altinn.Resource.AppOwner, BaseUrn.Altinn.Resource.AppId),
+ assert.HasAttributeTypes(BaseUrn.Altinn.Resource.ResourceRegistryId)),
+ assert.AllAttributesHasValues)(errors, values);
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/Interfaces/IAssert.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/Interfaces/IAssert.cs
new file mode 100644
index 00000000..30df8984
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Asserters/Interfaces/IAssert.cs
@@ -0,0 +1,59 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Altinn.AccessManagement.Core.Asserters;
+
+///
+/// Contains the basic methods for combining and nesting assertions.
+/// Use the to make assertions for one dataset. If you have multiple datasets,
+/// you can pass the evaluations to the to get a single assertions result for all
+/// the datasets.
+///
+/// the model that should
+public interface IAssert
+{
+ ///
+ /// Joins multiple evaluations to a single result
+ ///
+ /// evaluations
+ ///
+ /// returns null if all the assertions passed. If an assertion generated an error then these errors will be present in
+ /// dictionary where the keys should be named the assertion method and value should contain the message(s).
+ ///
+ public ValidationProblemDetails Join(params ValidationProblemDetails[] evaluations);
+
+ ///
+ /// Executes all the assertions using the given dataset
+ ///
+ /// the values to be asserted
+ /// assertions
+ ///
+ /// returns null if all the assertions passed. If an assertion generated an error then these errors will be present in
+ /// dictionary where the keys should be named the assertion method and value should contain the message(s).
+ ///
+ ValidationProblemDetails Evaluate(IEnumerable values, params Assertion[] actions);
+
+ ///
+ /// Single will omit writing to the error dict if there is just one assertion that passes.
+ /// Can be combined with and
+ /// to create even more complex assertion.
+ ///
+ /// assertions
+ ///
+ Assertion Single(params Assertion[] actions);
+
+ ///
+ /// All errors are directly written to the errors dictionary. This method works as calling the Evaluate method directly
+ /// with a set of asserts. However, this can also be combined inside or
+ /// to create even more complex assertion.
+ ///
+ /// assertions
+ ///
+ Assertion All(params Assertion[] actions);
+
+ ///
+ /// If any given asserts don't add an error to the errors dictionary parameter, then all other errors are ignored
+ ///
+ /// assertions
+ ///
+ Assertion Any(params Assertion[] actions);
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAccessListsAuthorizationClient.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAccessListsAuthorizationClient.cs
new file mode 100644
index 00000000..0ad47b74
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAccessListsAuthorizationClient.cs
@@ -0,0 +1,17 @@
+using Altinn.AccessManagement.Core.Models.AccessList;
+
+namespace Altinn.AccessManagement.Core.Clients.Interfaces;
+
+///
+/// Access list authorization interface.
+///
+public interface IAccessListsAuthorizationClient
+{
+ ///
+ /// Authorization of a given subject for resource access through access lists.
+ ///
+ /// The
+ /// The
+ /// A representing the result of the asynchronous operation.
+ Task AuthorizePartyForAccessList(AccessListAuthorizationRequest request, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAltinn2RightsClient.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAltinn2RightsClient.cs
new file mode 100644
index 00000000..4e5fc5c9
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAltinn2RightsClient.cs
@@ -0,0 +1,39 @@
+using Altinn.AccessManagement.Core.Models;
+
+namespace Altinn.AccessManagement.Core.Clients.Interfaces
+{
+ ///
+ /// Interface for client for getting Altinn rights from AltinnII SBL Bridge
+ ///
+ public interface IAltinn2RightsClient
+ {
+ ///
+ /// Get Altinn rights from AltinnII SBL bridge
+ ///
+ /// the authenticated user id
+ /// the party id of the reportee/from party
+ /// the service code
+ /// the service edition code
+ /// Delegation Check Response
+ Task PostDelegationCheck(int authenticatedUserId, int reporteePartyId, string serviceCode, string serviceEditionCode);
+
+ ///
+ /// Post delegation of Altinn 2 service rights to SBL bridge
+ ///
+ /// the authenticated user id
+ /// the party id of the reportee/from party
+ /// the delegation request model
+ /// Delegation Response
+ Task PostDelegation(int authenticatedUserId, int reporteePartyId, SblRightDelegationRequest delegationRequest);
+
+ ///
+ /// Operation to clear a recipients cached rights from a given reportee/from party, and the recipients authorized parties/reportees
+ ///
+ /// The party id of the from party
+ /// The party id of the to party
+ /// The user id of the to party (if the recipient is a user)
+ /// Cancellation token
+ /// HttpResponse
+ Task ClearReporteeRights(int fromPartyId, int toPartyId, int toUserId = 0, CancellationToken cancellationToken = default);
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAltinnRolesClient.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAltinnRolesClient.cs
new file mode 100644
index 00000000..c8df3f84
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAltinnRolesClient.cs
@@ -0,0 +1,37 @@
+using Altinn.AccessManagement.Core.Models;
+using Authorization.Platform.Authorization.Models;
+
+namespace Altinn.AccessManagement.Core.Clients.Interfaces;
+
+///
+/// Interface for client for getting Altinn roles from AltinnII SBL Bridge
+///
+public interface IAltinnRolesClient
+{
+ ///
+ /// Get the decision point roles for the loggedin user for a selected party
+ ///
+ /// the logged in user id
+ /// the partyid of the person/org the logged in user is representing
+ /// Cancellation token for the request
+ /// list of actors that the logged in user can represent
+ Task> GetDecisionPointRolesForUser(int coveredByUserId, int offeredByPartyId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Get the roles the user has for a given reportee, as basis for evaluating rights for delegation.
+ /// For any user having HADM this means, getting additional roles as DAGL etc.
+ ///
+ /// the user id
+ /// the partyid of the person/org the user is representing
+ /// Cancellation token for the request
+ /// list of actors that the logged in user can represent
+ Task> GetRolesForDelegation(int coveredByUserId, int offeredByPartyId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Get the list of authorized parties from Altinn 2 that a given user have one or more accesses for, including
+ ///
+ /// The user to get the list of AuthorizedParties for
+ /// Cancellation token for the request
+ ///
+ Task> GetAuthorizedPartiesWithRoles(int userId, CancellationToken cancellationToken = default);
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAuthenticationClient.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAuthenticationClient.cs
new file mode 100644
index 00000000..da0aefa7
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IAuthenticationClient.cs
@@ -0,0 +1,35 @@
+using Altinn.AccessManagement.Core.Models.Authentication;
+using DefaultRight = Altinn.AccessManagement.Core.Models.Authentication.DefaultRight;
+
+namespace Altinn.AccessManagement.Core.Clients.Interfaces
+{
+ ///
+ /// Authentication interface.
+ ///
+ public interface IAuthenticationClient
+ {
+ ///
+ /// Refreshes the AltinnStudioRuntime JwtToken.
+ ///
+ /// Cancellation token
+ /// Response message from Altinn Platform with refreshed token.
+ Task RefreshToken(CancellationToken cancellationToken = default);
+
+ ///
+ /// Fetching a System user from Authentication
+ ///
+ /// The party id of the party the systemUSer is registered on
+ /// The uuid identifying the systemUser
+ /// Cancellation token
+ ///
+ Task GetSystemUser(int partyId, string systemUserId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Get defined rights for a given System
+ ///
+ /// The uuid identifier of the system
+ /// Cancellation token
+ ///
+ Task> GetDefaultRightsForRegisteredSystem(string systemId, CancellationToken cancellationToken = default);
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IPartiesClient.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IPartiesClient.cs
new file mode 100644
index 00000000..71d9246a
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IPartiesClient.cs
@@ -0,0 +1,72 @@
+using Altinn.AccessManagement.Core.Models.SblBridge;
+using Altinn.Platform.Register.Models;
+
+namespace Altinn.AccessManagement.Core.Clients.Interfaces;
+
+///
+/// Interface for a client wrapper for integration with SBL bridge delegation request API
+///
+public interface IPartiesClient
+{
+ ///
+ /// Returns partyInfo
+ ///
+ /// The party ID to lookup
+ /// The
+ /// party information
+ Task GetPartyAsync(int partyId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Returns a list of parties
+ ///
+ /// List of party IDs to lookup
+ /// (Optional) Whether subunits should be included as ChildParties, if any of the lookup party IDs are for a main unit
+ /// The
+ /// List of parties
+ Task> GetPartiesAsync(List partyIds, bool includeSubunits = false, CancellationToken cancellationToken = default);
+
+ ///
+ /// Returns a list of parties
+ ///
+ /// List of party uuids to lookup
+ /// (Optional) Whether subunits should be included as ChildParties, if any of the parties are a main unit
+ /// The
+ /// List of parties
+ Task> GetPartiesAsync(List partyUuids, bool includeSubunits = false, CancellationToken cancellationToken = default);
+
+ ///
+ /// Returns a list of parties for user
+ ///
+ /// The user id
+ /// The
+ /// List of parties
+ Task> GetPartiesForUserAsync(int userId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Method that fetches a list of PartyIds the given user id has key role access to (where the user inherit delegations to their organization)
+ ///
+ /// The user id
+ /// The
+ /// list of PartyIds where the logged in user have key role access
+ Task> GetKeyRoleParties(int userId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Method that fetches a list of main units for the input list of sub unit partyIds. If any of the input partyIds are not a sub unit the response model will have null values for main unit properties.
+ ///
+ /// The list of PartyIds to check and retrieve any main units for
+ /// The
+ /// list of main units
+ Task> GetMainUnits(MainUnitQuery subunitPartyIds, CancellationToken cancellationToken = default);
+
+ ///
+ /// Looks up a party based on SSN or OrgNumber.
+ ///
+ ///
+ /// SSN or OrgNumber as a PartyLookup object.
+ ///
+ /// The
+ ///
+ /// The party that represents the given SSN or OrgNumber.
+ ///
+ Task LookupPartyBySSNOrOrgNo(PartyLookup partyLookup, CancellationToken cancellationToken = default);
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IProfileClient.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IProfileClient.cs
new file mode 100644
index 00000000..141feefd
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IProfileClient.cs
@@ -0,0 +1,23 @@
+using Altinn.AccessManagement.Core.Models.Profile;
+using Altinn.Platform.Profile.Models;
+
+namespace Altinn.AccessManagement.Core.Clients.Interfaces
+{
+ ///
+ /// Interface for Profile functionality.
+ ///
+ public interface IProfileClient
+ {
+ ///
+ /// Method for getting the userprofile for a given user identified by one of the available types of user identifiers:
+ /// UserId (from Altinn 2 Authn UserProfile)
+ /// Username (from Altinn 2 Authn UserProfile)
+ /// SSN/Dnr (from Freg)
+ /// Uuid (from Altinn 2 Party/UserProfile implementation will be added later)
+ ///
+ /// Model for specifying the user identifier to use for the UserProfile lookup
+ /// The
+ /// The UserProfile for the given user
+ Task GetUser(UserProfileLookup userProfileLookup, CancellationToken cancellationToken = default);
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IResourceRegistryClient.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IResourceRegistryClient.cs
new file mode 100644
index 00000000..e1637fc5
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Clients/Interfaces/IResourceRegistryClient.cs
@@ -0,0 +1,41 @@
+using Altinn.AccessManagement.Core.Models;
+using Altinn.AccessManagement.Core.Models.ResourceRegistry;
+
+namespace Altinn.AccessManagement.Core.Clients.Interfaces
+{
+ ///
+ /// Interface for client integration with the Resource Registry
+ ///
+ public interface IResourceRegistryClient
+ {
+ ///
+ /// Integration point for retrieving a single resoure by it's resource id
+ ///
+ /// The identifier of the resource in the Resource Registry
+ /// The
+ /// The resource if exists
+ Task GetResource(string resourceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Integration point for retrieving a list of resources
+ ///
+ /// The
+ /// The resource list if exists
+ Task> GetResources(CancellationToken cancellationToken = default);
+
+ ///
+ /// Integration point for retrieving the full list of resources
+ ///
+ /// The
+ /// The resource full list of all resources if exists
+ Task> GetResourceList(CancellationToken cancellationToken = default);
+
+ ///
+ /// Integration point for retrieving all resources having any of the request subjects in one or more resource policy rules
+ ///
+ /// Urn string representation of the subjects to lookup resources for
+ /// The
+ /// Dictionary of all resources per subject, having policy rules with the subject
+ Task>> GetSubjectResources(IEnumerable subjects, CancellationToken cancellationToken = default);
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/CacheConfig.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/CacheConfig.cs
new file mode 100644
index 00000000..1b035d8b
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/CacheConfig.cs
@@ -0,0 +1,48 @@
+namespace Altinn.AccessManagement.Core.Configuration
+{
+ ///
+ /// Cache configuration settings
+ ///
+ public class CacheConfig
+ {
+ ///
+ /// Gets or sets the policy cache timeout (in minutes)
+ ///
+ public int PolicyCacheTimeout { get; set; }
+
+ ///
+ /// Gets or sets the Altinn role cache timeout (in minutes)
+ ///
+ public int AltinnRoleCacheTimeout { get; set; }
+
+ ///
+ /// Gets or sets the cache timeout (in minutes) for lookup of party information
+ ///
+ public int PartyCacheTimeout { get; set; }
+
+ ///
+ /// Gets or sets the cache timeout (in minutes) for lookup of mainunits
+ ///
+ public int MainUnitCacheTimeout { get; set; }
+
+ ///
+ /// Gets or sets the cache timeout (in minutes) for lookup of keyrole partyIds
+ ///
+ public int KeyRolePartyIdsCacheTimeout { get; set; }
+
+ ///
+ /// Gets or sets the cache timeout (in minutes) for lookup of a resource from the resource registry
+ ///
+ public int ResourceRegistryResourceCacheTimeout { get; set; }
+
+ ///
+ /// Gets or sets the cache timeout (in minutes) for lookup of a subject resources from the resourceregistry
+ ///
+ public int ResourceRegistrySubjectResourcesCacheTimeout { get; set; }
+
+ ///
+ /// Gets or sets the cache timeout (in minutes) for lookup of a rights
+ ///
+ public int RightsCacheTimeout { get; set; }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/FeatureFlags.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/FeatureFlags.cs
new file mode 100644
index 00000000..959d9346
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/FeatureFlags.cs
@@ -0,0 +1,18 @@
+namespace Altinn.AccessManagement.Core.Configuration
+{
+ ///
+ /// Feature management flags
+ ///
+ public static class FeatureFlags
+ {
+ ///
+ /// Feature flag for activating the Rights Delegation API
+ ///
+ public const string RightsDelegationApi = "RightsDelegationApi";
+
+ ///
+ /// Feature flag for activating the Rights Delegation API External
+ ///
+ public const string RightsDelegationApiExternal = nameof(RightsDelegationApiExternal);
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/GeneralSettings.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/GeneralSettings.cs
new file mode 100644
index 00000000..be3b44bc
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/GeneralSettings.cs
@@ -0,0 +1,13 @@
+namespace Altinn.AccessManagement.Core.Configuration
+{
+ ///
+ /// General configuration settings
+ ///
+ public class GeneralSettings
+ {
+ ///
+ /// Gets or sets the host name.
+ ///
+ public string Hostname { get; set; }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/UserProfileLookupSettings.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/UserProfileLookupSettings.cs
new file mode 100644
index 00000000..7e3cd073
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Configuration/UserProfileLookupSettings.cs
@@ -0,0 +1,18 @@
+namespace Altinn.AccessManagement.Core.Configuration
+{
+ ///
+ /// UserProfile Lookup Settings
+ ///
+ public class UserProfileLookupSettings
+ {
+ ///
+ /// Gets or sets the cache timeout for number of failed lookup attempts (in seconds)
+ ///
+ public int FailedAttemptsCacheLifetimeSeconds { get; set; }
+
+ ///
+ /// Gets or sets the maximum number of failed attempts before lockout
+ ///
+ public int MaximumFailedAttempts { get; set; }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AltinnCoreClaimTypes.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AltinnCoreClaimTypes.cs
new file mode 100644
index 00000000..13dd2d68
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AltinnCoreClaimTypes.cs
@@ -0,0 +1,63 @@
+namespace Altinn.AccessManagement.Core.Constants
+{
+ ///
+ /// The different claim types.
+ ///
+ public static class AltinnCoreClaimTypes
+ {
+ ///
+ /// AuthenticationLevel.
+ ///
+ public const string AuthenticationLevel = "urn:altinn:authlevel";
+
+ ///
+ /// User id.
+ ///
+ public const string UserId = "urn:altinn:userid";
+
+ ///
+ /// Party id.
+ ///
+ public const string PartyID = "urn:altinn:partyid";
+
+ ///
+ /// The representing party id.
+ ///
+ public const string RepresentingPartyId = "urn:altinn:representingpartyid";
+
+ ///
+ /// Usernam.
+ ///
+ public const string UserName = "urn:altinn:username";
+
+ ///
+ /// Developer.
+ ///
+ public const string Developer = "urn:altinn:developer";
+
+ ///
+ /// Developertoken.
+ ///
+ public const string DeveloperToken = "urn:altinn:developertoken";
+
+ ///
+ /// Developertoken id.
+ ///
+ public const string DeveloperTokenId = "urn:altinn:developertokenid";
+
+ ///
+ /// Method of authentication.
+ ///
+ public const string AuthenticateMethod = "urn:altinn:authenticatemethod";
+
+ ///
+ /// Organization.
+ ///
+ public const string Org = "urn:altinn:org";
+
+ ///
+ /// Organization number.
+ ///
+ public const string OrgNumber = "urn:altinn:orgNumber";
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AltinnXacmlConstants.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AltinnXacmlConstants.cs
new file mode 100644
index 00000000..bad1be53
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AltinnXacmlConstants.cs
@@ -0,0 +1,186 @@
+namespace Altinn.AccessManagement.Core.Constants
+{
+ ///
+ /// Altinn specific XACML constants used for urn identifiers and attributes
+ ///
+ public static class AltinnXacmlConstants
+ {
+ ///
+ /// Altinn specific prefixes
+ ///
+ public static class Prefixes
+ {
+ ///
+ /// The Policy Id prefix.
+ ///
+ public const string PolicyId = "urn:altinn:policyid:";
+
+ ///
+ /// The Obligation Id prefix.
+ ///
+ public const string ObligationId = "urn:altinn:obligationid:";
+
+ ///
+ /// The Obligation Assignment Id prefix.
+ ///
+ public const string ObligationAssignmentid = "urn:altinn:obligation-assignmentid:";
+ }
+
+ ///
+ /// Match attribute identifiers
+ ///
+ public static class MatchAttributeIdentifiers
+ {
+ ///
+ /// Org attribute match indentifier
+ ///
+ public const string OrgAttribute = "urn:altinn:org";
+
+ ///
+ /// App attribute match indentifier
+ ///
+ public const string AppAttribute = "urn:altinn:app";
+
+ ///
+ /// Instance attribute match indentifier
+ ///
+ public const string InstanceAttribute = "urn:altinn:instance-id";
+
+ ///
+ /// ResouceRegistry Instance attribute match indentifier
+ ///
+ public const string ResourceInstanceAttribute = "urn:altinn:resource:instance-id";
+
+ ///
+ /// App resource attribute match indentifier
+ ///
+ public const string AppResourceAttribute = "urn:altinn:appresource";
+
+ ///
+ /// Task attribute match indentifier
+ ///
+ public const string TaskAttribute = "urn:altinn:task";
+
+ ///
+ /// End-event attribute match indentifier
+ ///
+ public const string EndEventAttribute = "urn:altinn:end-event";
+
+ ///
+ /// Party Id attribute match indentifier
+ ///
+ public const string PartyAttribute = "urn:altinn:partyid";
+
+ ///
+ /// Party uuid attribute match indentifier
+ ///
+ public const string PartyUuidAttribute = "urn:altinn:party:uuid";
+
+ ///
+ /// User Id attribute match indentifier
+ /// >
+ public const string UserAttribute = "urn:altinn:userid";
+
+ ///
+ /// Role Code attribute match indentifier
+ ///
+ public const string RoleAttribute = "urn:altinn:rolecode";
+
+ ///
+ /// Resource Registry attribute match indentifier
+ ///
+ public const string ResourceRegistryAttribute = "urn:altinn:resource";
+
+ ///
+ /// Resource delegation urn prefix used in Xacml policy rule subjects to identify rights the resourceId value of the attribute, is allowed to perform delegation of.
+ ///
+ public const string ResourceDelegationAttribute = "urn:altinn:resource:delegation";
+
+ ///
+ /// Organization name
+ ///
+ public const string OrganizationName = "urn:altinn:organization:name";
+
+ ///
+ /// Organization number attribute match indentifier
+ ///
+ public const string OrganizationNumberAttribute = "urn:altinn:organizationnumber";
+
+ ///
+ /// Social security number attribute match indentifier
+ ///
+ public const string SocialSecurityNumberAttribute = "urn:altinn:ssn";
+
+ ///
+ /// Altinn 2 service code attribute match indentifier
+ ///
+ public const string ServiceCodeAttribute = "urn:altinn:servicecode";
+
+ ///
+ /// Altinn 2 service edition code attribute match indentifier
+ ///
+ public const string ServiceEditionCodeAttribute = "urn:altinn:serviceeditioncode";
+
+ ///
+ /// Person uuid
+ ///
+ public const string PersonUuid = "urn:altinn:person:uuid";
+
+ ///
+ /// National identity number for a person
+ ///
+ public const string PersonId = "urn:altinn:person:identifier-no";
+
+ ///
+ /// Last name of a person
+ ///
+ public const string PersonLastName = "urn:altinn:person:lastname";
+
+ ///
+ /// Person username
+ ///
+ public const string PersonUserName = "urn:altinn:person:username";
+
+ ///
+ /// Enterprise user uuid
+ ///
+ public const string EnterpriseUserUuid = "urn:altinn:enterpriseuser:uuid";
+
+ ///
+ /// Enterprise user username
+ ///
+ public const string EnterpriseUserName = "urn:altinn:enterpriseuser:username";
+
+ ///
+ /// Organization uuid
+ ///
+ public const string OrganizationUuid = "urn:altinn:organization:uuid";
+
+ ///
+ /// Organization number
+ ///
+ public const string OrganizationId = "urn:altinn:organization:identifier-no";
+
+ ///
+ /// SystemUser uuid
+ ///
+ public const string SystemUserUuid = "urn:altinn:systemuser:uuid";
+ }
+
+ ///
+ /// Attribute categories.
+ ///
+ public static class MatchAttributeCategory
+ {
+ ///
+ /// The minimum authentication level category.
+ ///
+ public const string MinimumAuthenticationLevel = "urn:altinn:minimum-authenticationlevel";
+
+ ///
+ /// The minimum authentication level for organization category
+ ///
+ public const string MinimumAuthenticationLevelOrg = "urn:altinn:minimum-authenticationlevel-org";
+ }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AuthzConstants.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AuthzConstants.cs
new file mode 100644
index 00000000..a8470e32
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/AuthzConstants.cs
@@ -0,0 +1,98 @@
+namespace Altinn.AccessManagement.Core.Constants
+{
+ ///
+ /// Constants related to authorization.
+ ///
+ public static class AuthzConstants
+ {
+ ///
+ /// Policy tag for authorizing Altinn.Platform.Authorization API access from AltinnII Authorization
+ ///
+ public const string ALTINNII_AUTHORIZATION = "AltinnIIAuthorizationAccess";
+
+ ///
+ /// Policy tag for authorizing internal Altinn.Platform.Authorization API access
+ ///
+ public const string INTERNAL_AUTHORIZATION = "InternalAuthorizationAccess";
+
+ ///
+ /// Policy tag for authorizing Altinn.Platform.Authorization API access
+ ///
+ public const string PLATFORM_ACCESS_AUTHORIZATION = "PlatformAccess";
+
+ ///
+ /// Policy tag for reading an maskinporten delegation
+ ///
+ public const string POLICY_MASKINPORTEN_DELEGATION_READ = "MaskinportenDelegationRead";
+
+ ///
+ /// Policy tag for writing an maskinporten delegation
+ ///
+ public const string POLICY_MASKINPORTEN_DELEGATION_WRITE = "MaskinportenDelegationWrite";
+
+ ///
+ /// Policy tag for reading access management information
+ ///
+ public const string POLICY_ACCESS_MANAGEMENT_READ = "AccessManagementRead";
+
+ ///
+ /// Policy tag for writing access management delegations
+ ///
+ public const string POLICY_ACCESS_MANAGEMENT_WRITE = "AccessManagementWrite";
+
+ ///
+ /// Policy tag for scope authorization on the proxy API from Altinn II for the maskinporten integration API
+ ///
+ public const string POLICY_MASKINPORTEN_DELEGATIONS_PROXY = "MaskinportenDelegationsProxy";
+
+ ///
+ /// Policy tag for scope authorization on the resource owner API for getting the Authorized Party list for a third party
+ ///
+ public const string POLICY_RESOURCEOWNER_AUTHORIZEDPARTIES = "ResourceOwnerAuthorizedParty";
+
+ ///
+ /// Policy tag for scope authorization on the instance delegation API for Apps
+ ///
+ public const string POLICY_APPS_INSTANCEDELEGATION = "AppsInstanceDelegation";
+
+ ///
+ /// Scope giving access to getting authorized parties for a given subject.
+ ///
+ public const string SCOPE_AUTHORIZEDPARTIES_ENDUSERSYSTEM = "altinn:accessmanagement/authorizedparties";
+
+ ///
+ /// Scope giving access to getting authorized parties for any third party, for which the third party have access to one or more of the resource owners services, apps or resources.
+ ///
+ public const string SCOPE_AUTHORIZEDPARTIES_RESOURCEOWNER = "altinn:accessmanagement/authorizedparties.resourceowner";
+
+ ///
+ /// Scope giving access to getting all authorized parties for any third party
+ ///
+ public const string SCOPE_AUTHORIZEDPARTIES_ADMIN = "altinn:accessmanagement/authorizedparties.admin";
+
+ ///
+ /// Scope giving access to delegations for Maskinporten schemes owned by authenticated party
+ ///
+ public const string SCOPE_MASKINPORTEN_DELEGATIONS = "altinn:maskinporten/delegations";
+
+ ///
+ /// Scope giving access to delegations for arbitrary Maskinporten schemes
+ ///
+ public const string SCOPE_MASKINPORTEN_DELEGATIONS_ADMIN = "altinn:maskinporten/delegations.admin";
+
+ ///
+ /// Claim for scopes from maskinporten token
+ ///
+ public const string CLAIM_MASKINPORTEN_SCOPE = "scope";
+
+ ///
+ /// Claim for full consumer from maskinporten token
+ ///
+ public const string CLAIM_MASKINPORTEN_CONSUMER = "consumer";
+
+ ///
+ /// Claim for consumer prefixes from maskinporten token
+ ///
+ public const string CLAIM_MASKINPORTEN_CONSUMER_PREFIX = "consumer_prefix";
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/XacmlRequestAttribute.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/XacmlRequestAttribute.cs
new file mode 100644
index 00000000..19b16ad3
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Constants/XacmlRequestAttribute.cs
@@ -0,0 +1,63 @@
+namespace Altinn.AccessManagement.Core.Constants
+{
+ ///
+ /// Attribute representations in XACML
+ ///
+ public static class XacmlRequestAttribute
+ {
+ ///
+ /// xacml string that represents org
+ ///
+ public const string OrgAttribute = "urn:altinn:org";
+
+ ///
+ /// xacml string that represents app
+ ///
+ public const string AppAttribute = "urn:altinn:app";
+
+ ///
+ /// xacml string that represents instanceid
+ ///
+ public const string InstanceAttribute = "urn:altinn:instance-id";
+
+ ///
+ /// xacm string that represents appresource
+ ///
+ public const string AppResourceAttribute = "urn:altinn:appresource";
+
+ ///
+ /// xacml string that represents task
+ ///
+ public const string TaskAttribute = "urn:altinn:task";
+
+ ///
+ /// xacml string that represents end event
+ ///
+ public const string EndEventAttribute = "urn:altinn:end-event";
+
+ ///
+ /// xacml string that represents party
+ ///
+ public const string PartyAttribute = "urn:altinn:partyid";
+
+ ///
+ /// xacml string that represents organization number
+ ///
+ public const string OrganizationNumberAttribute = "urn:altinn:organizationnumber";
+
+ ///
+ /// xacml string that represents user
+ ///
+ public const string UserAttribute = "urn:altinn:userid";
+
+ ///
+ /// xacml string that represents role
+ ///
+ public const string RoleAttribute = "urn:altinn:rolecode";
+
+ ///
+ /// xacml string that represents resource
+ ///
+ public const string ResourceRegistryAttribute = "urn:altinn:resource";
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/AuthorizedPartyType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/AuthorizedPartyType.cs
new file mode 100644
index 00000000..49f0f536
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/AuthorizedPartyType.cs
@@ -0,0 +1,30 @@
+using System.Text.Json.Serialization;
+
+namespace Altinn.AccessManagement.Core.Enums;
+
+///
+/// Enum for different types of Authorized Party
+///
+[JsonConverter(typeof(JsonStringEnumConverter))]
+public enum AuthorizedPartyType
+{
+ ///
+ /// Unknown or unspecified
+ ///
+ None = 0,
+
+ ///
+ /// Party Type is a Person
+ ///
+ Person = 1,
+
+ ///
+ /// Party Type is an Organization
+ ///
+ Organization = 2,
+
+ ///
+ /// Party Type is a Self Identified user
+ ///
+ SelfIdentified = 3
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegableStatus.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegableStatus.cs
new file mode 100644
index 00000000..8e5c7627
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegableStatus.cs
@@ -0,0 +1,22 @@
+using System.Runtime.Serialization;
+
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum for different right delegation status responses
+ ///
+ public enum DelegableStatus
+ {
+ ///
+ /// User is not able to delegate the right
+ ///
+ [EnumMember(Value = "NotDelegable")]
+ NotDelegable = 0,
+
+ ///
+ /// User is able to delegate the right
+ ///
+ [EnumMember(Value = "Delegable")]
+ Delegable = 1
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationActionType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationActionType.cs
new file mode 100644
index 00000000..0745bc37
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationActionType.cs
@@ -0,0 +1,23 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum for the different delegation action types
+ ///
+ public enum DelegationActionType
+ {
+ ///
+ /// Default value
+ ///
+ Unknown = 0,
+
+ ///
+ /// Delegation action
+ ///
+ Delegation = 1,
+
+ ///
+ /// Revoke action
+ ///
+ Revoke = 2
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationStatus.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationStatus.cs
new file mode 100644
index 00000000..0182cbd4
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationStatus.cs
@@ -0,0 +1,18 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum for different right delegation status responses
+ ///
+ public enum DelegationStatus
+ {
+ ///
+ /// Right was not delegated
+ ///
+ NotDelegated = 0,
+
+ ///
+ /// Right was delegated
+ ///
+ Delegated = 1
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationType.cs
new file mode 100644
index 00000000..3eecc6d5
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DelegationType.cs
@@ -0,0 +1,43 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum for the different the types of delegations exist for a delegated right in Altinn Authorization
+ ///
+ public enum DelegationType
+ {
+ ///
+ /// Default value
+ ///
+ Unknown = 0,
+
+ ///
+ /// Delegated to user
+ ///
+ DirectUserDelegation = 1,
+
+ ///
+ /// Delegated to user, from main unit
+ ///
+ DirectUserDelegationFromMainUnit = 2,
+
+ ///
+ /// Delegated to organization
+ ///
+ DirectOrgDelegation = 3,
+
+ ///
+ /// Delegated to organization, from main unit
+ ///
+ DirectOrgDelegationFromMainUnit = 4,
+
+ ///
+ /// Delegated to key role relation
+ ///
+ KeyRoleDelegation = 5,
+
+ ///
+ /// Delegated to key role relation, from main unit
+ ///
+ KeyRoleDelegationFromMainUnit = 6
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DetailCode.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DetailCode.cs
new file mode 100644
index 00000000..4cb3f291
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/DetailCode.cs
@@ -0,0 +1,66 @@
+using System.Text.Json.Serialization;
+
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Fixed values for DetailCodes
+ ///
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum DetailCode
+ {
+ ///
+ /// Unknown reason
+ ///
+ Unknown = 0,
+
+ ///
+ /// Has access by a delegated role in ER or Altinn
+ ///
+ RoleAccess = 1,
+
+ ///
+ /// Has access by direct delegation
+ ///
+ DelegationAccess = 2,
+
+ ///
+ /// The service requires explicit access in SRR and the reportee has this
+ ///
+ SrrRightAccess = 3,
+
+ ///
+ /// Has not access by a delegation of role in ER or Altinn
+ ///
+ MissingRoleAccess = 4,
+
+ ///
+ /// Has not access by direct delegation
+ ///
+ MissingDelegationAccess = 5,
+
+ ///
+ /// The service requires explicit access in SRR and the reportee is missing this
+ ///
+ MissingSrrRightAccess = 6,
+
+ ///
+ /// The service requires explicit authentication level and the reportee is missing this
+ ///
+ InsufficientAuthenticationLevel = 7,
+
+ ///
+ /// The receiver already has the right
+ ///
+ AlreadyDelegated = 8,
+
+ ///
+ /// The receiver has the right based on Access List delegation
+ ///
+ AccessListValidationPass = 9,
+
+ ///
+ /// The receiver does not have the right based on Access List delegation
+ ///
+ AccessListValidationFail = 10,
+ }
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/InstanceDelegationMode.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/InstanceDelegationMode.cs
new file mode 100644
index 00000000..7a5d0403
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/InstanceDelegationMode.cs
@@ -0,0 +1,27 @@
+using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
+using NpgsqlTypes;
+
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum defining delegation mode (normal or paralellsigning)
+ ///
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum InstanceDelegationMode
+ {
+ ///
+ /// Normal instance delegation
+ ///
+ [EnumMember(Value = "normal")]
+ [PgName("normal")]
+ Normal,
+
+ ///
+ /// Special case of instance delegation extends delegations to organizations to all users in the receiving organization with parallel role/package to also getting access to the instance
+ ///
+ [EnumMember(Value = "parallelSigning")]
+ [PgName("parallelsigning")]
+ ParallelSigning
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/InstanceDelegationSource.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/InstanceDelegationSource.cs
new file mode 100644
index 00000000..786ac8ca
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/InstanceDelegationSource.cs
@@ -0,0 +1,25 @@
+using System.Runtime.Serialization;
+using NpgsqlTypes;
+
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum defining delegation source (app or user)
+ ///
+ public enum InstanceDelegationSource
+ {
+ ///
+ /// Delegation by user
+ ///
+ [EnumMember(Value = "user")]
+ [PgName("user")]
+ User,
+
+ ///
+ /// Delegation by app
+ ///
+ [EnumMember(Value = "app")]
+ [PgName("app")]
+ App
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/PolicyAccountType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/PolicyAccountType.cs
new file mode 100644
index 00000000..aea0d673
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/PolicyAccountType.cs
@@ -0,0 +1,14 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Storage Account
+ ///
+ public enum PolicyAccountType
+ {
+ ResourceRegister,
+
+ Delegations,
+
+ Metadata,
+ }
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceAttributeMatchType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceAttributeMatchType.cs
new file mode 100644
index 00000000..8b9dc16d
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceAttributeMatchType.cs
@@ -0,0 +1,28 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum representation of the different types of resource attribute match types supported
+ ///
+ public enum ResourceAttributeMatchType
+ {
+ ///
+ /// Default value
+ ///
+ None = 0,
+
+ ///
+ /// Resource registered in the Altinn Resource Registry
+ ///
+ ResourceRegistry = 1,
+
+ ///
+ /// Legacy App resource identified by org owner and app name
+ ///
+ AltinnAppId = 2,
+
+ ///
+ /// Legacy Altinn 2 service resource identified by service codes
+ ///
+ Altinn2Service = 3
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ReferenceSource.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ReferenceSource.cs
new file mode 100644
index 00000000..f4ac9282
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ReferenceSource.cs
@@ -0,0 +1,25 @@
+using System.Runtime.Serialization;
+
+namespace Altinn.AccessManagement.Core.Models.ResourceRegistry
+{
+ ///
+ /// Enum for the different reference sources for resources in the resource registry
+ ///
+ public enum ReferenceSource : int
+ {
+ [EnumMember(Value = "Default")]
+ Default = 0,
+
+ [EnumMember(Value = "Altinn1")]
+ Altinn1 = 1,
+
+ [EnumMember(Value = "Altinn2")]
+ Altinn2 = 2,
+
+ [EnumMember(Value = "Altinn3")]
+ Altinn3 = 3,
+
+ [EnumMember(Value = "ExternalPlatform")]
+ ExternalPlatform = 4
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ReferenceType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ReferenceType.cs
new file mode 100644
index 00000000..bf7ab2da
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ReferenceType.cs
@@ -0,0 +1,31 @@
+using System.Runtime.Serialization;
+
+namespace Altinn.AccessManagement.Core.Models.ResourceRegistry
+{
+ ///
+ /// Enum for reference types of resources in the resource registry
+ ///
+ public enum ReferenceType : int
+ {
+ [EnumMember(Value = "Default")]
+ Default = 0,
+
+ [EnumMember(Value = "Uri")]
+ Uri = 1,
+
+ [EnumMember(Value = "DelegationSchemeId")]
+ DelegationSchemeId = 2,
+
+ [EnumMember(Value = "MaskinportenScope")]
+ MaskinportenScope = 3,
+
+ [EnumMember(Value = "ServiceCode")]
+ ServiceCode = 4,
+
+ [EnumMember(Value = "ServiceEditionCode")]
+ ServiceEditionCode = 5,
+
+ [EnumMember(Value = "ApplicationId")]
+ ApplicationId = 6
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourceAccessListMode.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourceAccessListMode.cs
new file mode 100644
index 00000000..8a11f72d
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourceAccessListMode.cs
@@ -0,0 +1,12 @@
+namespace Altinn.AccessManagement.Core.Enums.ResourceRegistry
+{
+ ///
+ /// Enum representation of the different types of ResourceAccessListModes supported by the resource registry
+ ///
+ public enum ResourceAccessListMode
+ {
+ Disabled = 0,
+
+ Enabled = 1,
+ }
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourcePartyType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourcePartyType.cs
new file mode 100644
index 00000000..a954027b
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourcePartyType.cs
@@ -0,0 +1,27 @@
+using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
+
+namespace Altinn.AccessManagement.Core.Enums.ResourceRegistry
+{
+ ///
+ /// Defines the type of party that a resource is targeting
+ ///
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum ResourcePartyType
+ {
+ [EnumMember(Value = "PrivatePerson")]
+ PrivatePerson = 0,
+
+ [EnumMember(Value = "LegalEntityEnterprise")]
+ LegalEntityEnterprise = 1,
+
+ [EnumMember(Value = "Company")]
+ Company = 2,
+
+ [EnumMember(Value = "BankruptcyEstate")]
+ BankruptcyEstate = 3,
+
+ [EnumMember(Value = "SelfRegisteredUser")]
+ SelfRegisteredUser = 4
+ }
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourceType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourceType.cs
new file mode 100644
index 00000000..292c8e17
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/ResourceRegistry/ResourceType.cs
@@ -0,0 +1,37 @@
+using NpgsqlTypes;
+
+namespace Altinn.AccessManagement.Core.Models.ResourceRegistry
+{
+ ///
+ /// Enum representation of the different types of resources supported by the resource registry
+ ///
+ [Flags]
+ public enum ResourceType
+ {
+ [PgName("default")]
+ Default = 0,
+
+ [PgName("systemresource")]
+ Systemresource = 1 << 0,
+
+ [PgName("maskinportenschema")]
+ MaskinportenSchema = 1 << 1,
+
+ [PgName("altinn2service")]
+ Altinn2Service = 1 << 2,
+
+ [PgName("altinnapp")]
+ AltinnApp = 1 << 3,
+
+ [PgName("genericaccessresource")]
+ GenericAccessResource = 1 << 4,
+
+ [PgName("brokerservice")]
+ BrokerService = 1 << 5,
+
+ [PgName("correspondenceservice")]
+ CorrespondenceService = 1 << 6,
+
+ All = ~Default
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RestAuthorizationRequestDirection.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RestAuthorizationRequestDirection.cs
new file mode 100644
index 00000000..0eab777a
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RestAuthorizationRequestDirection.cs
@@ -0,0 +1,28 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum for deciding which authRequests to get
+ ///
+ public enum RestAuthorizationRequestDirection
+ {
+ ///
+ /// Default none
+ ///
+ None = 0,
+
+ ///
+ /// Incoming requests
+ ///
+ Incoming = 1,
+
+ ///
+ /// Outgoing requests
+ ///
+ Outgoing = 2,
+
+ ///
+ /// Both incoming and outgoing
+ ///
+ Both = 3,
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RestAuthorizationRequestStatus.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RestAuthorizationRequestStatus.cs
new file mode 100644
index 00000000..ae20e2a5
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RestAuthorizationRequestStatus.cs
@@ -0,0 +1,38 @@
+namespace Altinn.AccessManagement.Core.Models
+{
+ ///
+ /// Enum for determining the status of an Authorization Request
+ ///
+ public enum RestAuthorizationRequestStatus
+ {
+ ///
+ /// Should not be used as a status for an AuthorizationRequest.
+ ///
+ None = 0,
+
+ ///
+ /// Used when a AuthorizationRequest is unopened.
+ ///
+ Unopened = 1,
+
+ ///
+ /// Used when a AuthorizationRequest is opened.
+ ///
+ Opened = 2,
+
+ ///
+ /// Used when a AuthorizationRequest is accepted.
+ ///
+ Accepted = 3,
+
+ ///
+ /// Used when a AuthorizationRequest is rejected.
+ ///
+ Rejected = 4,
+
+ ///
+ /// Used when a AuthorizationRequest is created.
+ ///
+ Created = 6
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RevokeStatus.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RevokeStatus.cs
new file mode 100644
index 00000000..f6f70708
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RevokeStatus.cs
@@ -0,0 +1,18 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum for different right revoke status responses
+ ///
+ public enum RevokeStatus
+ {
+ ///
+ /// Right was not revoked
+ ///
+ NotRevoked = 0,
+
+ ///
+ /// Right was revoked
+ ///
+ Revoked = 1
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RightSourceType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RightSourceType.cs
new file mode 100644
index 00000000..21de2ff7
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RightSourceType.cs
@@ -0,0 +1,28 @@
+namespace Altinn.AccessManagement.Core.Enums
+{
+ ///
+ /// Enum for different the source types exist for a right in Altinn Authorization
+ ///
+ public enum RightSourceType
+ {
+ ///
+ /// Default value
+ ///
+ Unknown = 0,
+
+ ///
+ /// XACML policy for an Altinn app
+ ///
+ AppPolicy = 1,
+
+ ///
+ /// XACML policy for a resource from the resource registry
+ ///
+ ResourceRegistryPolicy = 2,
+
+ ///
+ /// Altinn delegation policy
+ ///
+ DelegationPolicy = 3
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RightsQueryType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RightsQueryType.cs
new file mode 100644
index 00000000..a7b60abd
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/RightsQueryType.cs
@@ -0,0 +1,22 @@
+namespace Altinn.AccessManagement.Core.Enums;
+
+///
+/// Enum for different types of rights queries in Altinn Authorization
+///
+public enum RightsQueryType
+{
+ ///
+ /// Default
+ ///
+ NotSet = 0,
+
+ ///
+ /// Rights query where the recipient is a user
+ ///
+ User = 1,
+
+ ///
+ /// Rights query where the recipient is an Altinn app
+ ///
+ AltinnApp = 2
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/UuidType.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/UuidType.cs
new file mode 100644
index 00000000..3cf755f2
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Enums/UuidType.cs
@@ -0,0 +1,54 @@
+using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
+using NpgsqlTypes;
+
+namespace Altinn.AccessManagement.Enums
+{
+ ///
+ /// Enum defining the different uuids used for defining parts in a delegation
+ ///
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum UuidType
+ {
+ ///
+ /// Placeholder when type is not specified should only happen when there is no Uuid to match it with
+ ///
+ [EnumMember]
+ NotSpecified,
+
+ ///
+ /// Defining a person this could also be identified with "Fødselsnummer"/"Dnummer"
+ ///
+ [EnumMember(Value = "urn:altinn:person:uuid")]
+ [PgName("urn:altinn:person:uuid")]
+ Person,
+
+ ///
+ /// Identifies a unit could also be identified with a Organization number
+ ///
+ [EnumMember(Value = "urn:altinn:organization:uuid")]
+ [PgName("urn:altinn:organization:uuid")]
+ Organization,
+
+ ///
+ /// Identifies a systemuser this is a identifier for machine integration it could also be identified with a unique name
+ ///
+ [EnumMember(Value = "urn:altinn:systemuser:uuid")]
+ [PgName("urn:altinn:systemuser:uuid")]
+ SystemUser,
+
+ ///
+ /// Identifies a enterpriseuser this is marked as obsolete and is used for existing integration is also identified with an unique username
+ ///
+ [EnumMember(Value = "urn:altinn:enterpriseuser:uuid")]
+ [PgName("urn:altinn:enterpriseuser:uuid")]
+ EnterpriseUser,
+
+ ///
+ /// Identifies a enterpriseuser this is marked as obsolete and is used for existing integration is also identified with an unique username
+ ///
+ [EnumMember(Value = "urn:altinn:resource")]
+ [PgName("urn:altinn:resource")]
+ Resource
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Errors/ValidationErrors.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Errors/ValidationErrors.cs
new file mode 100644
index 00000000..d71f8d40
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Errors/ValidationErrors.cs
@@ -0,0 +1,44 @@
+#nullable enable
+
+using Altinn.Authorization.ProblemDetails;
+
+namespace Altinn.AccessManagement.Core.Errors;
+
+///
+/// Validation errors for the Access Management.
+///
+public static class ValidationErrors
+{
+ private static readonly ValidationErrorDescriptorFactory _factory
+ = ValidationErrorDescriptorFactory.New("AM");
+
+ ///
+ /// Gets a validation error descriptor for when an invalid party URN is provided.
+ ///
+ public static ValidationErrorDescriptor InvalidPartyUrn { get; }
+ = _factory.Create(1, "Invalid party URN.");
+
+ ///
+ /// Gets a validation error descriptor for when an invalid Resource URN is provided.
+ ///
+ public static ValidationErrorDescriptor InvalidResource { get; }
+ = _factory.Create(2, $"Resource must be valid.");
+
+ ///
+ /// Gets a validation error descriptor for when a resource is missing policy file
+ ///
+ public static ValidationErrorDescriptor MissingPolicy { get; }
+ = _factory.Create(3, $"Resource must have a policy.");
+
+ ///
+ /// Gets a validation error descriptor for when a Resource not has any delegable rights for the app
+ ///
+ public static ValidationErrorDescriptor MissingDelegableRights { get; }
+ = _factory.Create(4, $"The Resource must have a policy giving the app rights to delegate at least one right.");
+
+ ///
+ /// Gets a validation error descriptor for when a Resource not has any delegable rights for the app
+ ///
+ public static ValidationErrorDescriptor ToManyDelegationsToRevoke { get; }
+ = _factory.Create(5, $"There must be 10 or less policy files to update.");
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Exceptions/TooManyFailedLookupsException.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Exceptions/TooManyFailedLookupsException.cs
new file mode 100644
index 00000000..d5da786e
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Exceptions/TooManyFailedLookupsException.cs
@@ -0,0 +1,24 @@
+namespace Altinn.AccessManagement.Core;
+
+///
+/// Represents a situation where a user has performed too many failed lookup requests.
+///
+[Serializable]
+public class TooManyFailedLookupsException : Exception
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TooManyFailedLookupsException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message that descibes the error.
+ public TooManyFailedLookupsException(string message)
+ : base(message)
+ {
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Extensions/CoreDependencyInjectionExtensions.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Extensions/CoreDependencyInjectionExtensions.cs
new file mode 100644
index 00000000..5a533166
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Extensions/CoreDependencyInjectionExtensions.cs
@@ -0,0 +1,57 @@
+using Altinn.AccessManagement.Core.Asserters;
+using Altinn.AccessManagement.Core.Clients.Interfaces;
+using Altinn.AccessManagement.Core.Models;
+using Altinn.AccessManagement.Core.Resolvers;
+using Altinn.AccessManagement.Core.Services;
+using Altinn.AccessManagement.Core.Services.Implementation;
+using Altinn.AccessManagement.Core.Services.Interfaces;
+using Altinn.AccessManagement.Resolvers;
+using Altinn.Common.AccessTokenClient.Services;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Altinn.AccessManagement.Core.Extensions;
+
+///
+/// Extension methods for adding access management core services to the dependency injection container.
+///
+public static class CoreDependencyInjectionExtensions
+{
+ ///
+ /// Extension methods for adding access management core services to the dependency injection container.
+ ///
+ /// web application builder
+ public static void AddAccessManagementCore(this WebApplicationBuilder builder)
+ {
+ builder.Services.AddTransient();
+ builder.Services.AddTransient, Asserter>();
+ builder.Services.AddTransient();
+
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+
+ builder.Services.AddSingleton();
+ builder.Services.AddTransient();
+
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ }
+}
\ No newline at end of file
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/AuthenticationHelper.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/AuthenticationHelper.cs
new file mode 100644
index 00000000..b76f3e43
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/AuthenticationHelper.cs
@@ -0,0 +1,59 @@
+using Altinn.AccessManagement.Core.Constants;
+using Microsoft.AspNetCore.Http;
+
+namespace Altinn.AccessManagement.Core.Helpers
+{
+ ///
+ /// helper class for authentication
+ ///
+ public static class AuthenticationHelper
+ {
+ ///
+ /// Gets the users id
+ ///
+ /// the http context
+ /// the logged in users id
+ public static int GetUserId(HttpContext context)
+ {
+ var claim = context.User?.Claims.FirstOrDefault(c => c.Type.Equals(AltinnCoreClaimTypes.UserId));
+ if (claim != null && int.TryParse(claim.Value, out int userId))
+ {
+ return userId;
+ }
+
+ return 0;
+ }
+
+ ///
+ /// Gets the authenticated user's party id
+ ///
+ /// the http context
+ /// the logged in users party id
+ public static int GetPartyId(HttpContext context)
+ {
+ var claim = context.User?.Claims.FirstOrDefault(c => c.Type.Equals(AltinnCoreClaimTypes.PartyID));
+ if (claim != null && int.TryParse(claim.Value, out int partyId))
+ {
+ return partyId;
+ }
+
+ return 0;
+ }
+
+ ///
+ /// Gets the users authentication level
+ ///
+ /// the http context
+ /// the logged in users authentication level
+ public static int GetUserAuthenticationLevel(HttpContext context)
+ {
+ var claim = context.User?.Claims.FirstOrDefault(c => c.Type.Equals(AltinnCoreClaimTypes.AuthenticationLevel));
+ if (claim != null && int.TryParse(claim.Value, out int authenticationLevel))
+ {
+ return authenticationLevel;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/DelegationHelper.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/DelegationHelper.cs
new file mode 100644
index 00000000..2e2927ca
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/DelegationHelper.cs
@@ -0,0 +1,997 @@
+using System.Text;
+using Altinn.AccessManagement.Core.Constants;
+using Altinn.AccessManagement.Core.Enums;
+using Altinn.AccessManagement.Core.Helpers.Extensions;
+using Altinn.AccessManagement.Core.Models;
+using Altinn.AccessManagement.Core.Models.Authentication;
+using Altinn.AccessManagement.Core.Models.ResourceRegistry;
+using Altinn.AccessManagement.Enums;
+using Altinn.Authorization.ABAC.Constants;
+using Altinn.Authorization.ABAC.Xacml;
+using Altinn.Platform.Register.Enums;
+using Altinn.Platform.Register.Models;
+using Altinn.Urn.Json;
+
+namespace Altinn.AccessManagement.Core.Helpers
+{
+ ///
+ /// Delegation helper methods
+ ///
+ public static class DelegationHelper
+ {
+ ///
+ /// Sort rules for delegation by delegation policy file path, i.e. Org/App/OfferedBy/CoveredBy
+ ///
+ /// The list of rules to be sorted
+ /// The list of rules not able to sort by org/app/offeredBy/CoveredBy
+ /// A dictionary with key being the filepath for the delegation policy file, and value being the list of rules to be written to the delegation policy
+ public static Dictionary> SortRulesByDelegationPolicyPath(List rules, out List unsortableRules)
+ {
+ Dictionary> result = new Dictionary>();
+ unsortableRules = new List();
+
+ foreach (Rule rule in rules)
+ {
+ if (!TryGetDelegationPolicyPathFromRule(rule, out string path))
+ {
+ unsortableRules.Add(rule);
+ continue;
+ }
+
+ if (result.ContainsKey(path))
+ {
+ result[path].Add(rule);
+ }
+ else
+ {
+ result.Add(path, new List { rule });
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Tries to get the PartyId attribute value from a list of AttributeMatch models
+ ///
+ /// The true if party id is found as the single attribute in the collection
+ public static bool TryGetPartyIdFromAttributeMatch(List match, out int partyid)
+ {
+ partyid = 0;
+ AttributeMatch partyIdAttribute = match?.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute);
+ if (partyIdAttribute != null)
+ {
+ return int.TryParse(partyIdAttribute.Value, out partyid) && partyid != 0;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Tries to get the Uuid attribute value from a list of AttributeMatch models and specifies which type it finds by setting based on the id in the attribute match
+ ///
+ /// The true if party id is found as the single attribute in the collection
+ public static bool TryGetUuidFromAttributeMatch(List match, out Guid uuid, out UuidType type)
+ {
+ uuid = Guid.Empty;
+ type = UuidType.NotSpecified;
+
+ if (match == null)
+ {
+ return false;
+ }
+
+ AttributeMatch currentAttributeMatch = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.PersonUuid);
+ if (currentAttributeMatch != null)
+ {
+ type = UuidType.Person;
+ return Guid.TryParse(currentAttributeMatch.Value, out uuid) && uuid != Guid.Empty;
+ }
+
+ currentAttributeMatch = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.OrganizationUuid);
+ if (currentAttributeMatch != null)
+ {
+ type = UuidType.Organization;
+ return Guid.TryParse(currentAttributeMatch.Value, out uuid) && uuid != Guid.Empty;
+ }
+
+ currentAttributeMatch = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.SystemUserUuid);
+ if (currentAttributeMatch != null)
+ {
+ type = UuidType.SystemUser;
+ return Guid.TryParse(currentAttributeMatch.Value, out uuid) && uuid != Guid.Empty;
+ }
+
+ currentAttributeMatch = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.EnterpriseUserUuid);
+ if (currentAttributeMatch != null)
+ {
+ type = UuidType.EnterpriseUser;
+ return Guid.TryParse(currentAttributeMatch.Value, out uuid) && uuid != Guid.Empty;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Trys to get the UserId attribute value from a list of AttributeMatch models
+ ///
+ /// The true if user id is found as the single attribute in the collection
+ public static bool TryGetUserIdFromAttributeMatch(List match, out int userid)
+ {
+ userid = 0;
+ AttributeMatch userIdAttribute = match?.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute);
+ if (userIdAttribute != null)
+ {
+ return int.TryParse(userIdAttribute.Value, out userid) && userid != 0;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Trys to get the organization number attribute value from a list of AttributeMatch models
+ ///
+ /// The true if organization number is found as the single attribute in the collection
+ public static bool TryGetOrganizationNumberFromAttributeMatch(List match, out string orgNo)
+ {
+ orgNo = string.Empty;
+ if (match != null && match.Count == 1 && match[0].Id == AltinnXacmlConstants.MatchAttributeIdentifiers.OrganizationNumberAttribute)
+ {
+ orgNo = match[0].Value;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Trys to get the social security number attribute value from a list of AttributeMatch models
+ ///
+ /// The true if social security number is found as the single attribute in the collection
+ public static bool TryGetSocialSecurityNumberAttributeMatch(List match, out string ssn)
+ {
+ ssn = string.Empty;
+ if (match != null && match.Count == 1 && match[0].Id == AltinnXacmlConstants.MatchAttributeIdentifiers.PersonId)
+ {
+ ssn = match[0].Value;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Trys to get both social security number and last name attribute value from a list of AttributeMatch models
+ ///
+ /// The true if both social security number and last name is found as the only attributes in the collection
+ public static bool TryGetSocialSecurityNumberAndLastNameAttributeMatch(List match, out string ssn, out string lastName)
+ {
+ ssn = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.PersonId)?.Value;
+ lastName = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.PersonLastName)?.Value;
+
+ if (match.Count == 2 && !string.IsNullOrWhiteSpace(ssn) && !string.IsNullOrWhiteSpace(lastName))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Trys to get both username and last name attribute value from a list of AttributeMatch models
+ ///
+ /// The true if both username and last name is found as the only attributes in the collection
+ public static bool TryGetUsernameAndLastNameAttributeMatch(List match, out string username, out string lastName)
+ {
+ username = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.PersonUserName)?.Value;
+ lastName = match.Find(m => m.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.PersonLastName)?.Value;
+
+ if (match.Count == 2 && !string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(lastName))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Trys to get enterprise username attribute value from a list of AttributeMatch models
+ ///
+ /// The true if both enterprise username is found as the only attributes in the collection
+ public static bool TryGetEnterpriseUserNameAttributeMatch(List match, out string enterpriseUserName)
+ {
+ enterpriseUserName = string.Empty;
+ if (match != null && match.Count == 1 && match[0].Id == AltinnXacmlConstants.MatchAttributeIdentifiers.EnterpriseUserName)
+ {
+ enterpriseUserName = match[0].Value;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Trys to get an single specific attribute value from a list of AttributeMatch models, if it's the only attribute in the list
+ ///
+ /// The true if person uuid is found as the only attributes in the collection
+ public static bool TryGetSingleAttributeMatchValue(List match, string matchAttributeIdentifier, out string value)
+ {
+ value = string.Empty;
+ if (match != null && match.Count == 1 && match[0].Id == matchAttributeIdentifier)
+ {
+ value = match[0].Value;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Check if i given AttributeMatch list is in a list of defaultRights so it is valid for delegation to SystemUsers
+ ///
+ /// List of default rights to check if resource is included
+ /// The rights to delegate to check for existence in list of defaultRights
+ /// true if all the rights is valid for in the given defaultRights list
+ public static bool CheckResourceIsInListOfDefaultRights(List defaultRights, List resource)
+ {
+ return defaultRights.Exists(defaultRight => CheckAllPartsInDefaultRightsIsInActualDelegatedResource(defaultRight.Resource, resource));
+ }
+
+ ///
+ /// Compares a list of Resource details from allowed default rights with the list of Resource details in the actual delegation so all of the defined resource details
+ /// in the allowed resource must be present in the actual delegation but the actual delegation can have more details narrowing the actual delegation more granulated than
+ /// the allowed resource but not the other way round
+ ///
+ /// List describing the allowed resource
+ /// List describing the actual delegation
+ /// True if the delegation is allowed false if not.
+ public static bool CheckAllPartsInDefaultRightsIsInActualDelegatedResource(List allowedResource, List actualResource)
+ {
+ return allowedResource.TrueForAll(attributeMatch => actualResource.Exists(r => r.Id == attributeMatch.Id && r.Value == attributeMatch.Value));
+ }
+
+ ///
+ /// Gets a int representation of the CoveredByUserId and CoverdByPartyId from an AttributeMatch list.
+ /// This works under the asumptions that any valid search for å valid policy contains a CoveredBy and this must be in the form
+ /// of a PartyId or a UserId. So any valid search containing a PartyId should not contain a USerId and vice versa.
+ /// If the search does not contain any of those this should be considered as an invalid search.
+ ///
+ /// the list to fetch coveredBy from
+ /// The value for coveredByUserId or null if not present
+ /// The value for coveredByPartyId or null if not present
+ /// The uuid of the covered by found in the AttributeMatch list
+ /// The uuid type of the covered by found in the AttributeMatch list
+ /// The CoveredByUserId or CoveredByPartyId in the input AttributeMatch list as a string primarily used to create a policy path for fetching a delegated policy file.
+ public static string GetCoveredByFromMatch(List match, out int? coveredByUserId, out int? coveredByPartyId, out Guid? coveredByUuid, out UuidType coveredByUuidType)
+ {
+ bool validUser = TryGetUserIdFromAttributeMatch(match, out int coveredByUserIdTemp);
+ bool validParty = TryGetPartyIdFromAttributeMatch(match, out int coveredByPartyIdTemp);
+ bool validUuid = TryGetUuidFromAttributeMatch(match, out Guid coveredByUuidIdTemp, out coveredByUuidType);
+
+ coveredByPartyId = validParty ? coveredByPartyIdTemp : null;
+ coveredByUserId = validUser ? coveredByUserIdTemp : null;
+ coveredByUuid = validUuid ? coveredByUuidIdTemp : null;
+
+ if (validUser)
+ {
+ return coveredByUserIdTemp.ToString();
+ }
+ else if (validParty)
+ {
+ return coveredByPartyIdTemp.ToString();
+ }
+ else if (validUuid)
+ {
+ return coveredByUuidIdTemp.ToString();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the resource attribute values as out params from a Resource specified as a List of AttributeMatches
+ ///
+ /// The resource to fetch org and app from
+ /// the resource match type
+ /// the resource id. Either a resource registry id or org/app
+ /// the org part of the resource
+ /// the app part of the resource
+ /// altinn 2 service code
+ /// altinn 2 service edition code
+ /// A bool indicating whether params where found
+ public static bool TryGetResourceFromAttributeMatch(List input, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out string serviceCode, out string serviceEditionCode)
+ {
+ resourceMatchType = ResourceAttributeMatchType.None;
+ resourceId = null;
+ org = null;
+ app = null;
+ serviceCode = null;
+ serviceEditionCode = null;
+
+ AttributeMatch resourceRegistryMatch = input.Find(am => am.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistryAttribute);
+ AttributeMatch orgMatch = input.Find(am => am.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute);
+ AttributeMatch appMatch = input.Find(am => am.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute);
+ AttributeMatch serviceCodeMatch = input.Find(am => am.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.ServiceCodeAttribute);
+ AttributeMatch serviceEditionMatch = input.Find(am => am.Id == AltinnXacmlConstants.MatchAttributeIdentifiers.ServiceEditionCodeAttribute);
+
+ if (resourceRegistryMatch != null && orgMatch == null && appMatch == null)
+ {
+ resourceMatchType = ResourceAttributeMatchType.ResourceRegistry;
+ resourceId = resourceRegistryMatch.Value;
+ return true;
+ }
+
+ if (orgMatch != null && appMatch != null && resourceRegistryMatch == null)
+ {
+ resourceMatchType = ResourceAttributeMatchType.AltinnAppId;
+ org = orgMatch.Value;
+ app = appMatch.Value;
+ resourceId = $"{org}/{app}";
+ return true;
+ }
+
+ if (serviceCodeMatch != null && serviceEditionMatch != null && resourceRegistryMatch == null && orgMatch == null && appMatch == null)
+ {
+ resourceMatchType = ResourceAttributeMatchType.Altinn2Service;
+ serviceCode = serviceCodeMatch.Value;
+ serviceEditionCode = serviceEditionMatch.Value;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets ResourceType, ResourceRegistryId, Org, App, OfferedBy and CoveredBy as out params from a single Rule
+ ///
+ /// A bool indicating whether sufficent params where found
+ public static bool TryGetDelegationParamsFromRule(Rule rule, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out int offeredByPartyId, out Guid? fromUuid, out UuidType fromUuidType, out Guid? toUuid, out UuidType toUuidType, out int? coveredByPartyId, out int? coveredByUserId, out int? delegatedByUserId, out int? delegatedByPartyId, out DateTime delegatedDateTime)
+ {
+ resourceMatchType = ResourceAttributeMatchType.None;
+ resourceId = null;
+ org = null;
+ app = null;
+ offeredByPartyId = 0;
+ fromUuid = null;
+ fromUuidType = UuidType.NotSpecified;
+ coveredByPartyId = null;
+ coveredByUserId = null;
+ toUuid = null;
+ toUuidType = UuidType.NotSpecified;
+
+ delegatedByUserId = null;
+ delegatedByPartyId = null;
+ delegatedDateTime = DateTime.UtcNow;
+
+ try
+ {
+ TryGetResourceFromAttributeMatch(rule.Resource, out resourceMatchType, out resourceId, out org, out app, out string _, out string _);
+ offeredByPartyId = rule.OfferedByPartyId;
+ fromUuid = rule.OfferedByPartyUuid;
+ fromUuidType = rule.OfferedByPartyType;
+ coveredByPartyId = TryGetPartyIdFromAttributeMatch(rule.CoveredBy, out int coveredByParty) ? coveredByParty : null;
+ coveredByUserId = TryGetUserIdFromAttributeMatch(rule.CoveredBy, out int coveredByUser) ? coveredByUser : null;
+ bool validUuid = TryGetUuidFromAttributeMatch(rule.CoveredBy, out Guid toUuidTemp, out toUuidType);
+ if (validUuid)
+ {
+ toUuid = toUuidTemp;
+ }
+
+ delegatedByUserId = rule.DelegatedByUserId;
+ delegatedByPartyId = rule.DelegatedByPartyId;
+ delegatedDateTime = rule.DelegatedDateTime ?? DateTime.UtcNow;
+
+ if (resourceMatchType != ResourceAttributeMatchType.None
+ && offeredByPartyId != 0
+ && (coveredByPartyId.HasValue || coveredByUserId.HasValue || toUuid.HasValue)
+ && (delegatedByUserId.HasValue || delegatedByPartyId.HasValue))
+ {
+ return true;
+ }
+ }
+ catch (Exception)
+ {
+ // Any exceptions here are caused by invalid input which should be handled and logged by the calling entity
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets the delegation policy path for a single Rule
+ ///
+ /// A bool indicating whether necessary params to build the path where found
+ public static bool TryGetDelegationPolicyPathFromRule(Rule rule, out string delegationPolicyPath)
+ {
+ delegationPolicyPath = null;
+ if (TryGetDelegationParamsFromRule(rule, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out int offeredBy, out Guid? fromUuid, out UuidType fromUuidType, out Guid? toUuid, out UuidType toUuidType, out int? coveredByPartyId, out int? coveredByUserId, out _, out _, out _))
+ {
+ try
+ {
+ delegationPolicyPath = PolicyHelper.GetDelegationPolicyPath(resourceMatchType, resourceId, org, app, offeredBy.ToString(), coveredByUserId, coveredByPartyId, toUuid, toUuidType);
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets the delegation policy path for a single Rule
+ ///
+ /// A bool indicating whether necessary params to build the path where found
+ public static bool TryGetDelegationPolicyPathFromInstanceRule(InstanceRight rule, out string instanceDelegationPolicyPath)
+ {
+ instanceDelegationPolicyPath = null;
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append("resourceregistry");
+ sb.Append('/');
+
+ sb.Append(rule.ResourceId);
+ sb.Append('/');
+
+ sb.Append("instances");
+ sb.Append('/');
+
+ try
+ {
+ sb.Append(rule.InstanceId.AsFileName());
+ sb.Append('/');
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ sb.Append(rule.ToType);
+ sb.Append('-');
+ sb.Append(rule.ToUuid);
+ sb.Append('/');
+
+ sb.Append(rule.InstanceDelegationSource);
+ sb.Append('/');
+
+ sb.Append(rule.InstanceDelegationMode);
+ sb.Append('/');
+
+ sb.Append("delegationpolicy.xml");
+ instanceDelegationPolicyPath = sb.ToString();
+
+ return true;
+ }
+
+ ///
+ /// Returns the count of unique Policies in a list of Rules
+ ///
+ /// List of rules to check how many individual policies exist
+ /// count of policies
+ public static int GetPolicyCount(List rules)
+ {
+ List policyPaths = new List();
+ foreach (Rule rule in rules)
+ {
+ bool pathOk = TryGetDelegationPolicyPathFromRule(rule, out string delegationPolicyPath);
+ if (pathOk && !policyPaths.Contains(delegationPolicyPath))
+ {
+ policyPaths.Add(delegationPolicyPath);
+ }
+ }
+
+ return policyPaths.Count;
+ }
+
+ ///
+ /// Returns the count of unique ruleids in a list dele
+ ///
+ /// List of rules and policies to check how many individual ruleids exist
+ /// count of rules
+ public static int GetRulesCountToDeleteFromRequestToDelete(List rulesToDelete)
+ {
+ int result = 0;
+ foreach (RequestToDelete ruleToDelete in rulesToDelete)
+ {
+ result += ruleToDelete.RuleIds.Count;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Checks whether the provided XacmlPolicy contains a rule having an identical Resource signature and contains the Action from the rule,
+ /// to be used for checking for duplicate rules in delegation, or that the rule exists in the Apps Xacml policy.
+ ///
+ /// A bool
+ public static bool PolicyContainsMatchingInstanceRule(XacmlPolicy policy, InstanceRule rule)
+ {
+ string ruleResourceKey = GetAttributeMatchKey(rule.Resource.ToList());
+
+ foreach (XacmlRule policyRule in policy.Rules)
+ {
+ if (!policyRule.Effect.Equals(XacmlEffectType.Permit) || policyRule.Target == null)
+ {
+ continue;
+ }
+
+ bool matchingActionFound = MatchingActionFound(policyRule.Target.AnyOf, rule, out List> policyResourceMatches);
+
+ if (policyResourceMatches.Exists(resourceMatch => GetAttributeMatchKey(resourceMatch) == ruleResourceKey) && matchingActionFound)
+ {
+ rule.RuleId = policyRule.RuleId;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Returns Xacml rule in the provided XacmlPolicy that contains a rule having an identical Resource signature and contains the Action from the InstanceRule null if no match,
+ /// to be used for finding rules to revoke.
+ ///
+ /// A bool
+ public static XacmlRule GetXamlRuleContainsMatchingInstanceRule(XacmlPolicy policy, InstanceRule rule)
+ {
+ string ruleResourceKey = GetAttributeMatchKey(rule.Resource.ToList());
+
+ foreach (XacmlRule policyRule in policy.Rules)
+ {
+ if (!policyRule.Effect.Equals(XacmlEffectType.Permit) || policyRule.Target == null)
+ {
+ continue;
+ }
+
+ bool matchingActionFound = MatchingActionFound(policyRule.Target.AnyOf, rule, out List> policyResourceMatches);
+
+ if (policyResourceMatches.Exists(resourceMatch => GetAttributeMatchKey(resourceMatch) == ruleResourceKey) && matchingActionFound)
+ {
+ rule.RuleId = policyRule.RuleId;
+ return policyRule;
+ }
+ }
+
+ return null;
+ }
+
+ private static bool MatchingActionFound(ICollection input, InstanceRule rule, out List> policyResourceMatches)
+ {
+ policyResourceMatches = [];
+ bool matchingActionFound = false;
+
+ foreach (XacmlAnyOf anyOf in input)
+ {
+ foreach (XacmlAllOf allOf in anyOf.AllOf)
+ {
+ matchingActionFound = GetResourceMatch(allOf.Matches, rule, out List resourceMatch);
+
+ if (resourceMatch.Count > 0)
+ {
+ policyResourceMatches.Add(resourceMatch);
+ }
+ }
+ }
+
+ return matchingActionFound;
+ }
+
+ private static bool GetResourceMatch(ICollection input, InstanceRule rule, out List resourceMatch)
+ {
+ resourceMatch = [];
+ bool matchingActionFound = false;
+ foreach (XacmlMatch xacmlMatch in input)
+ {
+ if (xacmlMatch.AttributeDesignator.Category.Equals(XacmlConstants.MatchAttributeCategory.Resource))
+ {
+ resourceMatch.Add(new AttributeMatch { Id = xacmlMatch.AttributeDesignator.AttributeId.OriginalString, Value = xacmlMatch.AttributeValue.Value });
+ }
+ else if (xacmlMatch.AttributeDesignator.Category.Equals(XacmlConstants.MatchAttributeCategory.Action) &&
+ xacmlMatch.AttributeDesignator.AttributeId.OriginalString == rule.Action.PrefixSpan.ToString() &&
+ xacmlMatch.AttributeValue.Value == rule.Action.ValueSpan.ToString())
+ {
+ matchingActionFound = true;
+ }
+ }
+
+ return matchingActionFound;
+ }
+
+ ///
+ /// Checks whether the provided XacmlPolicy contains a rule having an identical Resource signature and contains the Action from the rule,
+ /// to be used for checking for duplicate rules in delegation, or that the rule exists in the Apps Xacml policy.
+ ///
+ /// A bool
+ public static bool PolicyContainsMatchingRule(XacmlPolicy policy, Rule rule)
+ {
+ string ruleResourceKey = GetAttributeMatchKey(rule.Resource);
+
+ foreach (XacmlRule policyRule in policy.Rules)
+ {
+ if (!policyRule.Effect.Equals(XacmlEffectType.Permit) || policyRule.Target == null)
+ {
+ continue;
+ }
+
+ List> policyResourceMatches = new List>();
+ bool matchingActionFound = false;
+ foreach (XacmlAnyOf anyOf in policyRule.Target.AnyOf)
+ {
+ foreach (XacmlAllOf allOf in anyOf.AllOf)
+ {
+ List resourceMatch = new List();
+ foreach (XacmlMatch xacmlMatch in allOf.Matches)
+ {
+ if (xacmlMatch.AttributeDesignator.Category.Equals(XacmlConstants.MatchAttributeCategory.Resource))
+ {
+ resourceMatch.Add(new AttributeMatch { Id = xacmlMatch.AttributeDesignator.AttributeId.OriginalString, Value = xacmlMatch.AttributeValue.Value });
+ }
+ else if (xacmlMatch.AttributeDesignator.Category.Equals(XacmlConstants.MatchAttributeCategory.Action) &&
+ xacmlMatch.AttributeDesignator.AttributeId.OriginalString == rule.Action.Id &&
+ xacmlMatch.AttributeValue.Value == rule.Action.Value)
+ {
+ matchingActionFound = true;
+ }
+ }
+
+ if (resourceMatch.Any())
+ {
+ policyResourceMatches.Add(resourceMatch);
+ }
+ }
+ }
+
+ if (policyResourceMatches.Any(resourceMatch => GetAttributeMatchKey(resourceMatch) == ruleResourceKey) && matchingActionFound)
+ {
+ rule.RuleId = policyRule.RuleId;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets a string key representing the a list of attributematches
+ ///
+ /// A key string
+ public static string GetAttributeMatchKey(List attributeMatches)
+ {
+ return string.Concat(attributeMatches.OrderBy(r => r.Id).Select(r => r.Id + r.Value));
+ }
+
+ ///
+ /// Gets a string key representing the a list of attributematches
+ ///
+ /// A key string
+ public static string GetAttributeMatchKey(List attributeMatches)
+ {
+ return string.Concat(attributeMatches.OrderBy(r => r.Value.KeySpan.ToString()).Select(r => r.Value.PrefixSpan.ToString() + r.Value.ValueSpan.ToString()));
+ }
+
+ ///
+ /// Extract UuidType and Uuid from a AttributeMatch list
+ ///
+ /// The lsit to fetch data from
+ /// The id of the party in the
+ /// The resulting type
+ /// true if a valid type and id was extracted else false
+ public static bool TryGetPerformerFromAttributeMatches(IEnumerable performer, out string id, out UuidType type)
+ {
+ string org = null, app = null, person = null, organization = null, enterpriseUser = null, systemUser = null;
+ id = null;
+ type = UuidType.NotSpecified;
+ int counter = 0;
+
+ foreach (AttributeMatch match in performer)
+ {
+ counter++;
+ switch (match.Id)
+ {
+ case AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute:
+ org = match.Value;
+ break;
+ case AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute:
+ app = match.Value;
+ break;
+ case AltinnXacmlConstants.MatchAttributeIdentifiers.OrganizationUuid:
+ organization = match.Value;
+ break;
+ case AltinnXacmlConstants.MatchAttributeIdentifiers.PersonUuid:
+ person = match.Value;
+ break;
+ case AltinnXacmlConstants.MatchAttributeIdentifiers.EnterpriseUserUuid:
+ enterpriseUser = match.Value;
+ break;
+ case AltinnXacmlConstants.MatchAttributeIdentifiers.SystemUserUuid:
+ systemUser = match.Value;
+ break;
+ }
+ }
+
+ if (org != null && app != null && person == null && organization == null && enterpriseUser == null && systemUser == null && counter == 2)
+ {
+ id = $"app_{org}_{app}";
+ type = UuidType.Resource;
+ return true;
+ }
+
+ if (org == null && app == null && person != null && organization == null && enterpriseUser == null && systemUser == null && counter == 1)
+ {
+ id = person;
+ type = UuidType.Person;
+ return true;
+ }
+
+ if (org == null && app == null && person == null && organization != null && enterpriseUser == null && systemUser == null && counter == 1)
+ {
+ id = organization;
+ type = UuidType.Organization;
+ return true;
+ }
+
+ if (org == null && app == null && person == null && organization == null && enterpriseUser != null && systemUser == null && counter == 1)
+ {
+ id = enterpriseUser;
+ type = UuidType.EnterpriseUser;
+ return true;
+ }
+
+ if (org == null && app == null && person == null && organization == null && enterpriseUser == null && systemUser != null && counter == 1)
+ {
+ id = systemUser;
+ type = UuidType.SystemUser;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Sets the RuleType on each rule in the given list
+ ///
+ public static void SetRuleType(List rulesList, int offeredByPartyId, List keyRolePartyIds, List coveredBy, int parentPartyId = 0)
+ {
+ foreach (Rule rule in rulesList)
+ {
+ if (TryGetDelegationParamsFromRule(rule, out _, out _, out _, out _, out _, out Guid? fromUuid, out UuidType fromUuidType, out Guid? toUuid, out UuidType toUuidType, out int? coveredByPartyId, out int? coveredByUserId, out _, out _, out _)
+ && rule.Type == RuleType.None)
+ {
+ SetTypeForSingleRule(keyRolePartyIds, offeredByPartyId, coveredBy, parentPartyId, rule, coveredByPartyId, coveredByUserId);
+ }
+ }
+ }
+
+ ///
+ /// Extracts the (assumed) party ID from the given 'who' string.
+ ///
+ ///
+ /// Who, valid values are an organization number, or a party ID (the letter R followed by
+ /// the party ID as used in SBL).
+ ///
+ /// Party ID extracted from 'who', or NULL if 'who' contains no party id.
+ public static int? TryParsePartyId(string who)
+ {
+ int partyId;
+ if (!int.TryParse(who, out partyId))
+ {
+ return 0;
+ }
+
+ return partyId;
+ }
+
+ ///
+ /// Gets the reference value for a given resourcereference type
+ ///
+ /// resource
+ /// reference source
+ /// reference type
+ public static string GetReferenceValue(ServiceResource resource, ReferenceSource referenceSource, ReferenceType referenceType)
+ {
+ ResourceReference reference = resource.ResourceReferences.Find(rf => rf.ReferenceSource == referenceSource && rf.ReferenceType == referenceType);
+ return reference.Reference;
+ }
+
+ ///
+ /// Builds a RequestToDelete request model for revoking all delegated rules for a resource registry service
+ ///
+ public static List GetRequestToDeleteResourceRegistryService(int authenticatedUserId, string resourceRegistryId, int fromPartyId, int toPartyId)
+ {
+ return new List
+ {
+ new RequestToDelete
+ {
+ DeletedByUserId = authenticatedUserId,
+ PolicyMatch = new PolicyMatch
+ {
+ OfferedByPartyId = fromPartyId,
+ CoveredBy = new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute, Value = toPartyId.ToString() }.SingleToList(),
+ Resource = new AttributeMatch { Id = AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceRegistryAttribute, Value = resourceRegistryId }.SingleToList()
+ }
+ }
+ };
+ }
+
+ ///
+ /// Builds a RequestToDelete request model for revoking all delegated rules for the resource if delegated between the from and to parties
+ ///
+ public static List GetRequestToDeleteResource(int authenticatedUserId, IEnumerable resource, int fromPartyId, AttributeMatch to)
+ {
+ return new List
+ {
+ new RequestToDelete
+ {
+ DeletedByUserId = authenticatedUserId,
+ PolicyMatch = new PolicyMatch
+ {
+ OfferedByPartyId = fromPartyId,
+ CoveredBy = to.SingleToList(),
+ Resource = resource.ToList()
+ }
+ }
+ };
+ }
+
+ ///
+ /// Gets the list of Rules as a list of RightDelegationResult
+ ///
+ /// The rules output from a delegation to convert
+ /// List of RightDelegationResult
+ public static List GetRightDelegationResultsFromRules(List rules)
+ {
+ return rules.Select(rule => new RightDelegationResult
+ {
+ Resource = rule.Resource,
+ Action = rule.Action,
+ Status = rule.CreatedSuccessfully ? DelegationStatus.Delegated : DelegationStatus.NotDelegated
+ }).ToList();
+ }
+
+ ///
+ /// Gets the list of Rules as a list of RightDelegationResult
+ ///
+ /// The rules output from a delegation to convert
+ /// List of RightDelegationResult
+ public static IEnumerable GetRightDelegationResultsFromInstanceRules(InstanceRight rules)
+ {
+ return rules.InstanceRules.Select(rule => new InstanceRightDelegationResult
+ {
+ Resource = rule.Resource,
+ Action = rule.Action,
+ Status = rule.CreatedSuccessfully ? DelegationStatus.Delegated : DelegationStatus.NotDelegated
+ });
+ }
+
+ ///
+ /// Gets the list of Rules as a list of RightDelegationResult
+ ///
+ /// The rules output from a delegation to convert
+ /// List of RightDelegationResult
+ public static IEnumerable GetRightRevokeResultsFromInstanceRules(InstanceRight rules)
+ {
+ return rules.InstanceRules.Select(rule => new InstanceRightRevokeResult
+ {
+ Resource = rule.Resource,
+ Action = rule.Action,
+ Status = rule.CreatedSuccessfully ? RevokeStatus.Revoked : RevokeStatus.NotRevoked
+ });
+ }
+
+ ///
+ /// Gets the list of Rights as a list of RightDelegationResult
+ ///
+ /// The rights to convert
+ /// List of RightDelegationResult
+ public static List GetRightDelegationResultsFromFailedRights(List rights)
+ {
+ return rights.Select(right => new RightDelegationResult
+ {
+ Resource = right.Resource,
+ Action = right.Action,
+ Status = DelegationStatus.NotDelegated
+ }).ToList();
+ }
+
+ ///
+ /// Gets the list of Rights as a list of RightDelegationResult
+ ///
+ /// The rights to convert
+ /// List of RightDelegationResult
+ public static IEnumerable GetRightDelegationResultsFromFailedInternalRights(List rights)
+ {
+ return rights.Select(right => new InstanceRightDelegationResult
+ {
+ Resource = right.Resource,
+ Action = right.Action,
+ Status = DelegationStatus.NotDelegated
+ });
+ }
+
+ ///
+ /// Evaluates the party type in order to return the correct uuid type and value for a party
+ ///
+ /// The party to get uuid info from
+ /// UuidType and Uuid
+ public static (UuidType DelegationType, Guid Uuid) GetUuidTypeAndValueFromParty(Party party)
+ {
+ if (party?.Organization != null)
+ {
+ return (UuidType.Organization, party.PartyUuid.Value);
+ }
+ else if (party?.Person != null)
+ {
+ return (UuidType.Person, party.PartyUuid.Value);
+ }
+
+ return (UuidType.NotSpecified, Guid.Empty);
+ }
+
+ ///
+ /// Gets the list of Rights as a list of RightDelegationResult
+ ///
+ /// The rights to convert
+ /// List of RightDelegationResult
+ public static IEnumerable GetRightRevokeResultsFromFailedInternalRights(List rights)
+ {
+ return rights.Select(right => new InstanceRightRevokeResult
+ {
+ Resource = right.Resource,
+ Action = right.Action,
+ Status = RevokeStatus.NotRevoked
+ });
+ }
+
+ ///
+ /// Checks if AccessList feature is enabled and applicable for the given right, resource, and fromParty. The AccessListMode feature is currently enabled only for orgs.
+ ///
+ /// The right to be delegated
+ /// The resource we are making delegations for
+ /// The party we are making delegations on behalf of
+ /// True if Access List authorization mode is enabled and applicable
+ public static bool IsAccessListModeEnabledAndApplicable(Right right, ServiceResource resource, Party fromParty)
+ {
+ if (right.CanDelegate.HasValue && right.CanDelegate.Value
+ && resource.AccessListMode == Enums.ResourceRegistry.ResourceAccessListMode.Enabled
+ && fromParty.PartyTypeName == PartyType.Organisation)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static void SetTypeForSingleRule(List keyRolePartyIds, int offeredByPartyId, List coveredBy, int parentPartyId, Rule rule, int? coveredByPartyId, int? coveredByUserId)
+ {
+ bool isUserId = TryGetUserIdFromAttributeMatch(coveredBy, out int coveredByUserIdFromRequest);
+ bool isPartyId = TryGetPartyIdFromAttributeMatch(coveredBy, out int coveredByPartyIdFromRequest);
+
+ if (((isUserId && coveredByUserIdFromRequest == coveredByUserId) || (isPartyId && coveredByPartyIdFromRequest == coveredByPartyId))
+ && rule.OfferedByPartyId == offeredByPartyId)
+ {
+ rule.Type = RuleType.DirectlyDelegated;
+ }
+ else if (isUserId && keyRolePartyIds.Any(id => id == coveredByPartyId) && rule.OfferedByPartyId == offeredByPartyId)
+ {
+ rule.Type = RuleType.InheritedViaKeyRole;
+ }
+ else if (((isUserId && coveredByUserIdFromRequest == coveredByUserId) || (isPartyId && coveredByPartyIdFromRequest == coveredByPartyId))
+ && parentPartyId != 0 && rule.OfferedByPartyId == parentPartyId)
+ {
+ rule.Type = RuleType.InheritedAsSubunit;
+ }
+ else if (isUserId && keyRolePartyIds.Any(id => id == coveredByPartyId) && parentPartyId != 0 && rule.OfferedByPartyId == parentPartyId)
+ {
+ rule.Type = RuleType.InheritedAsSubunitViaKeyrole;
+ }
+ else
+ {
+ rule.Type = RuleType.None;
+ }
+ }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/EnumExtensions.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/EnumExtensions.cs
new file mode 100644
index 00000000..d7370515
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/EnumExtensions.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Runtime.Serialization;
+
+namespace Altinn.AccessManagement.Core.Helpers.Extensions
+{
+ ///
+ /// Enum Extensions
+ ///
+ public static class EnumExtensions
+ {
+ ///
+ /// Fetch the content of the Value for a defined EnumMemberAttribute or just returns the name if no such declaration exists
+ ///
+ /// The enum to fetch data from
+ /// The content of the declared EnumMemberAttribute.Value for the defined enum or just ToString if no EnumMemberAttribute is defined
+ public static string EnumMemberAttributeValueOrName(this Enum value)
+ {
+ var enumType = value.GetType();
+ var enumMemeberAttribute = enumType
+ .GetTypeInfo()
+ .DeclaredMembers
+ .Single(x => x.Name == value.ToString())
+ .GetCustomAttribute(false);
+
+ if (enumMemeberAttribute == null || enumMemeberAttribute.Value == null)
+ {
+ return value.ToString();
+ }
+
+ return enumMemeberAttribute.Value;
+ }
+
+ ///
+ /// Tries to parse a string as a given Enum on the EnumMemberAttribute rater than the Name of the value in the enum returns false if no matching vale was found true if parsed ok
+ ///
+ /// The string to use for value in the enum
+ /// Out parameter containing The parsed enum or default value
+ /// The Enum type to Parse
+ /// Value indicating the parsing went ok or not
+ public static bool EnumValue(string value, out T enumValue)
+ {
+ string[] names = Enum.GetNames(typeof(T));
+ string name = Array.Find(names, name => EnumMemberAttributeValueOrName((Enum)Enum.Parse(typeof(T), name)).Equals(value));
+
+ if (name != null)
+ {
+ enumValue = (T)Enum.Parse(typeof(T), name);
+ return true;
+ }
+
+ enumValue = default;
+ return false;
+ }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/GenericExtensions.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/GenericExtensions.cs
new file mode 100644
index 00000000..8bd67717
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/GenericExtensions.cs
@@ -0,0 +1,18 @@
+namespace Altinn.AccessManagement.Core.Helpers.Extensions
+{
+ ///
+ /// Generic Extensions
+ ///
+ public static class GenericExtensions
+ {
+ ///
+ /// Creates a new List of objects type, containing just the single object
+ ///
+ /// The object to create a list of
+ /// A list containing the object
+ public static List SingleToList(this T @object)
+ {
+ return new List { @object };
+ }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/HttpClientExtension.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/HttpClientExtension.cs
new file mode 100644
index 00000000..32f10ed0
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/HttpClientExtension.cs
@@ -0,0 +1,97 @@
+#nullable enable
+
+namespace Altinn.AccessManagement.Core.Extensions;
+
+///
+/// This extension is created to make it easy to add a bearer token to a HttpRequests.
+///
+public static class HttpClientExtension
+{
+ ///
+ /// Extension that add authorization header to request
+ ///
+ /// The HttpClient
+ /// the authorization token (jwt)
+ /// The request Uri
+ /// The http content
+ /// The platformAccess tokens
+ /// The
+ /// A HttpResponseMessage
+ public static Task PostAsync(this HttpClient httpClient, string authorizationToken, string requestUri, HttpContent content, string? platformAccessToken = null, CancellationToken cancellationToken = default)
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
+ request.Headers.Add("Authorization", "Bearer " + authorizationToken);
+ request.Content = content;
+ if (!string.IsNullOrEmpty(platformAccessToken))
+ {
+ request.Headers.Add("PlatformAccessToken", platformAccessToken);
+ }
+
+ return httpClient.SendAsync(request, cancellationToken);
+ }
+
+ ///
+ /// Extension that add authorization header to request
+ ///
+ /// The HttpClient
+ /// the authorization token (jwt)
+ /// The request Uri
+ /// The http content
+ /// The platformAccess tokens
+ /// The
+ /// A HttpResponseMessage
+ public static Task PutAsync(this HttpClient httpClient, string authorizationToken, string requestUri, HttpContent content, string? platformAccessToken = null, CancellationToken cancellationToken = default)
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, requestUri);
+ request.Headers.Add("Authorization", "Bearer " + authorizationToken);
+ request.Content = content;
+ if (!string.IsNullOrEmpty(platformAccessToken))
+ {
+ request.Headers.Add("PlatformAccessToken", platformAccessToken);
+ }
+
+ return httpClient.SendAsync(request, cancellationToken);
+ }
+
+ ///
+ /// Extension that add authorization header to request
+ ///
+ /// The HttpClient
+ /// the authorization token (jwt)
+ /// The request Uri
+ /// The platformAccess tokens
+ /// The
+ /// A HttpResponseMessage
+ public static Task GetAsync(this HttpClient httpClient, string authorizationToken, string requestUri, string? platformAccessToken = null, CancellationToken cancellationToken = default)
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri);
+ request.Headers.Add("Authorization", "Bearer " + authorizationToken);
+ if (!string.IsNullOrEmpty(platformAccessToken))
+ {
+ request.Headers.Add("PlatformAccessToken", platformAccessToken);
+ }
+
+ return httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+ }
+
+ ///
+ /// Extension that add authorization header to request
+ ///
+ /// The HttpClient
+ /// the authorization token (jwt)
+ /// The request Uri
+ /// The platformAccess tokens
+ /// The
+ /// A HttpResponseMessage
+ public static Task DeleteAsync(this HttpClient httpClient, string authorizationToken, string requestUri, string? platformAccessToken = null, CancellationToken cancellationToken = default)
+ {
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Delete, requestUri);
+ request.Headers.Add("Authorization", "Bearer " + authorizationToken);
+ if (!string.IsNullOrEmpty(platformAccessToken))
+ {
+ request.Headers.Add("PlatformAccessToken", platformAccessToken);
+ }
+
+ return httpClient.SendAsync(request, cancellationToken);
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/StringExtensions.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/StringExtensions.cs
new file mode 100644
index 00000000..11b8fda8
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/Extensions/StringExtensions.cs
@@ -0,0 +1,109 @@
+using System.Globalization;
+using System.Text;
+
+namespace Altinn.AccessManagement.Core.Helpers.Extensions
+{
+ ///
+ /// Extensions to facilitate sanitization of string values
+ ///
+ public static class StringExtensions
+ {
+ ///
+ /// Sanitize the input as a file name.
+ ///
+ /// The input variable to be sanitized
+ /// Throw exception instead of replacing invalid characters with '-'
+ ///
+ public static string AsFileName(this string input, bool throwExceptionOnInvalidCharacters = true)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return input;
+ }
+
+ char[] illegalFileNameCharacters = Path.GetInvalidFileNameChars();
+ if (throwExceptionOnInvalidCharacters)
+ {
+ if (illegalFileNameCharacters.Any(ic => input.Any(i => ic == i)))
+ {
+ throw new ArgumentOutOfRangeException(nameof(input));
+ }
+
+ return input;
+ }
+
+ return illegalFileNameCharacters.Aggregate(input, (current, c) => current.Replace(c, '-'));
+ }
+
+ ///
+ /// Compare to strings doing a loose compare ignoring case and diacritics
+ ///
+ /// First text
+ /// Second text
+ /// true if the texts are similar.
+ public static bool IsSimilarTo(this string text1, string text2)
+ {
+ const int CompareLength = 4;
+
+ text1 ??= string.Empty;
+ text2 ??= string.Empty;
+
+ text1 = text1.Trim().Length > CompareLength ? text1.Remove(CompareLength).Trim() : text1.Trim();
+ text2 = text2.Trim().Length > CompareLength ? text2.Remove(CompareLength).Trim() : text2.Trim();
+
+ text1 = text1.RemoveDiacritics();
+ text2 = text2.RemoveDiacritics();
+
+ return text1.Equals(text2, StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ ///
+ /// Remove diacritics from a string while normalizing it.
+ ///
+ /// The text to normalize.
+ /// The normalized text.
+ public static string RemoveDiacritics(this string text)
+ {
+ string normalizedText;
+
+ // Å is not a special character in Norwegian hence handling this character differently
+ if (text.ToUpper().Contains('Å'))
+ {
+ StringBuilder firstPassBuilder = new();
+ foreach (char ch in text)
+ {
+ if (ch == 'Å' || ch == 'å')
+ {
+ // NormalizationForm C doesn't convert Å to A
+ firstPassBuilder.Append(ch.ToString().Normalize(NormalizationForm.FormC));
+ }
+ else
+ {
+ firstPassBuilder.Append(ch.ToString().Normalize(NormalizationForm.FormD));
+ }
+ }
+
+ normalizedText = firstPassBuilder.ToString();
+ }
+ else
+ {
+ normalizedText = text.Normalize(NormalizationForm.FormD);
+ }
+
+ StringBuilder secondPassBuilder = new();
+ foreach (char ch in normalizedText)
+ {
+ // Unicode information of the characters, e.g. Uppercase, Lowercase, NonSpacingMark, etc.
+ UnicodeCategory unicode = CharUnicodeInfo.GetUnicodeCategory(ch);
+
+ if (unicode != UnicodeCategory.NonSpacingMark)
+ {
+ // StringBuilder is appended with the characters that are not diacritics
+ secondPassBuilder.Append(ch);
+ }
+ }
+
+ return secondPassBuilder.ToString().Normalize(NormalizationForm.FormC);
+ }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/JwtTokenUtil.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/JwtTokenUtil.cs
new file mode 100644
index 00000000..6a87620e
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/JwtTokenUtil.cs
@@ -0,0 +1,56 @@
+using Microsoft.AspNetCore.Http;
+
+namespace Altinn.AccessManagement.Core.Helpers
+{
+ ///
+ /// This class contains utilities for handling JWT tokens
+ ///
+ public static class JwtTokenUtil
+ {
+ ///
+ /// Retrieves JWT token value from HTTP context.
+ ///
+ /// The HTTP context that contains the token
+ /// The name of the cookie where the token might be stored
+ /// The JWT token string.
+ public static string GetTokenFromContext(HttpContext context, string cookieName)
+ {
+ // Get the cookie from request
+ string token = context.Request.Cookies[cookieName];
+
+ // If no cookie present
+ if (string.IsNullOrEmpty(token))
+ {
+ string authorization = context.Request.Headers["Authorization"];
+
+ // If no authorization header found, nothing to process further
+ if (string.IsNullOrEmpty(authorization))
+ {
+ return string.Empty;
+ }
+
+ if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ {
+ token = authorization.Substring("Bearer ".Length).Trim();
+ }
+ }
+
+ return token;
+ }
+
+ ///
+ /// Updates http client by including authorization token in request header
+ ///
+ /// The HTTP client
+ /// The authorization token
+ public static void AddTokenToRequestHeader(HttpClient client, string token)
+ {
+ if (client.DefaultRequestHeaders.Contains("Authorization"))
+ {
+ client.DefaultRequestHeaders.Remove("Authorization");
+ }
+
+ client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
+ }
+ }
+}
diff --git a/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/PolicyHelper.cs b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/PolicyHelper.cs
new file mode 100644
index 00000000..9879f243
--- /dev/null
+++ b/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement.Core/Helpers/PolicyHelper.cs
@@ -0,0 +1,1065 @@
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Xml;
+using Altinn.AccessManagement.Core.Constants;
+using Altinn.AccessManagement.Core.Enums;
+using Altinn.AccessManagement.Core.Helpers.Extensions;
+using Altinn.AccessManagement.Core.Models;
+using Altinn.AccessManagement.Enums;
+using Altinn.Authorization.ABAC.Constants;
+using Altinn.Authorization.ABAC.Utils;
+using Altinn.Authorization.ABAC.Xacml;
+using Altinn.Urn.Json;
+
+namespace Altinn.AccessManagement.Core.Helpers
+{
+ ///
+ /// Policy helper methods
+ ///
+ public static class PolicyHelper
+ {
+ private const string CoveredByNotDefined = "CoveredBy was not defined";
+
+ ///
+ /// Extracts a list of all roles codes mentioned in a permit rule in a policy.
+ ///
+ /// The policy
+ /// List of role codes
+ public static List GetRolesWithAccess(XacmlPolicy policy)
+ {
+ HashSet roleCodes = new HashSet();
+
+ foreach (XacmlRule rule in policy.Rules)
+ {
+ if (rule.Effect.Equals(XacmlEffectType.Permit) && rule.Target != null)
+ {
+ foreach (XacmlAnyOf anyOf in rule.Target.AnyOf)
+ {
+ foreach (XacmlAllOf allOf in anyOf.AllOf)
+ {
+ foreach (XacmlMatch xacmlMatch in allOf.Matches)
+ {
+ if (xacmlMatch.AttributeDesignator.AttributeId.Equals(AltinnXacmlConstants.MatchAttributeIdentifiers.RoleAttribute))
+ {
+ roleCodes.Add(xacmlMatch.AttributeValue.Value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return roleCodes.ToList();
+ }
+
+ ///
+ /// Finds the correct policy path based on a XacmlContextRequest
+ ///
+ /// Xacml context request to use for finding the org and app for building the path
+ ///
+ public static string GetPolicyPath(XacmlContextRequest request)
+ {
+ string org = string.Empty;
+ string app = string.Empty;
+
+ foreach (XacmlContextAttributes attr in request.Attributes.Where(attr => attr.Category.OriginalString.Equals(XacmlConstants.MatchAttributeCategory.Resource)))
+ {
+ foreach (XacmlAttribute asd in attr.Attributes)
+ {
+ if (asd.AttributeId.OriginalString.Equals(AltinnXacmlConstants.MatchAttributeIdentifiers.OrgAttribute))
+ {
+ XacmlAttributeValue orgAttr = asd.AttributeValues.FirstOrDefault();
+ org = orgAttr != null ? orgAttr.Value : string.Empty;
+ }
+
+ if (asd.AttributeId.OriginalString.Equals(AltinnXacmlConstants.MatchAttributeIdentifiers.AppAttribute))
+ {
+ XacmlAttributeValue appAttr = asd.AttributeValues.FirstOrDefault();
+ app = appAttr != null ? appAttr.Value : string.Empty;
+ }
+ }
+ }
+
+ return GetAltinnAppsPolicyPath(org, app);
+ }
+
+ ///
+ /// Builds the policy path based on org and app names
+ ///
+ /// The organization name/identifier
+ /// The altinn app name
+ ///
+ public static string GetAltinnAppsPolicyPath(string org, string app)
+ {
+ if (string.IsNullOrWhiteSpace(org))
+ {
+ throw new ArgumentException("Org was not defined");
+ }
+
+ if (string.IsNullOrWhiteSpace(app))
+ {
+ throw new ArgumentException("App was not defined");
+ }
+
+ return $"{org.AsFileName()}/{app.AsFileName()}/policy.xml";
+ }
+
+ ///
+ /// Builds the policy path based on resourceRegistryId
+ ///
+ /// The resource registry Id
+ /// Returns the path to the policyfile.
+ public static string GetResourceRegistryPolicyPath(string resourceRegistryId)
+ {
+ if (string.IsNullOrWhiteSpace(resourceRegistryId))
+ {
+ throw new ArgumentException("ResourceRegistryId was not defined");
+ }
+
+ return $"{resourceRegistryId.AsFileName()}/resourcepolicy.xml";
+ }
+
+ ///
+ /// Creates a Rule representation based on a search and a xacmlRule found in a XacmlPolicyFile based on the search
+ ///
+ /// The search used to find the correct rule
+ /// XacmlRule found by the search param to enrich the result with Action and Resource
+ /// The created Rule
+ public static Rule CreateRuleFromPolicyAndRuleMatch(RequestToDelete search, XacmlRule xacmlRule)
+ {
+ Rule rule = new Rule
+ {
+ RuleId = xacmlRule.RuleId,
+ CreatedSuccessfully = true,
+ DelegatedByUserId = search.DeletedByUserId,
+ OfferedByPartyId = search.PolicyMatch.OfferedByPartyId,
+ CoveredBy = search.PolicyMatch.CoveredBy,
+ Resource = GetResourceFromXcamlRule(xacmlRule),
+ Action = GetActionValueFromRule(xacmlRule)
+ };
+
+ return rule;
+ }
+
+ ///
+ /// Builds the delegation policy path based on org and app names, as well as identifiers for the delegating and receiving entities
+ ///
+ /// the resource match type
+ /// The id of the resource. Either a resource registry id or org/app
+ /// The organization name/identifier
+ /// The altinn app name
+ /// The party id of the entity offering the delegated the policy
+ /// The user id of the entity having received the delegated policy or null if party id
+ /// The party id of the entity having received the delegated policy or null if user id
+ /// the uuid of the coveredBy only valid value when the receiver is a system user
+ /// the type of uuid to set as prefix for the coveredBy
+ /// policypath matching input data
+ public static string GetDelegationPolicyPath(ResourceAttributeMatchType resourceMatchType, string resourceId, string org, string app, string offeredBy, int? coveredByUserId, int? coveredByPartyId, Guid? coveredByUuid, UuidType uuidType)
+ {
+ if (string.IsNullOrWhiteSpace(offeredBy))
+ {
+ throw new ArgumentException("OfferedBy was not defined");
+ }
+
+ if (coveredByPartyId == null && coveredByUserId == null && coveredByUuid == null)
+ {
+ throw new ArgumentException(CoveredByNotDefined);
+ }
+
+ if (coveredByPartyId <= 0)
+ {
+ throw new ArgumentException(CoveredByNotDefined);
+ }
+
+ if (coveredByUserId <= 0)
+ {
+ throw new ArgumentException(CoveredByNotDefined);
+ }
+
+ if (coveredByUuid == Guid.Empty)
+ {
+ throw new ArgumentException(CoveredByNotDefined);
+ }
+
+ string coveredByPrefix;
+ string coveredBy;
+ if (coveredByPartyId != null)
+ {
+ coveredByPrefix = "p";
+ coveredBy = coveredByPartyId.ToString();
+ }
+ else if (coveredByUserId != null)
+ {
+ coveredByPrefix = "u";
+ coveredBy = coveredByUserId.ToString();
+ }
+ else
+ {
+ coveredByPrefix = uuidType.ToString();
+ coveredBy = coveredByUuid.ToString();
+ }
+
+ if (resourceMatchType == ResourceAttributeMatchType.None)
+ {
+ throw new ArgumentException("Resource could not be identified. Resource must be for either a single resource from the resource registry or for a single Altinn app identified by org owner and app name");
+ }
+
+ if (resourceMatchType == ResourceAttributeMatchType.ResourceRegistry)
+ {
+ if (string.IsNullOrWhiteSpace(resourceId))
+ {
+ throw new ArgumentException("ResourceRegistryId was not defined");
+ }
+
+ return $"resourceregistry/{resourceId.AsFileName()}/{offeredBy.AsFileName()}/{coveredByPrefix}{coveredBy.AsFileName()}/delegationpolicy.xml";
+ }
+
+ if (resourceMatchType == ResourceAttributeMatchType.AltinnAppId)
+ {
+ if (string.IsNullOrWhiteSpace(org))
+ {
+ throw new ArgumentException("Org was not defined");
+ }
+
+ if (string.IsNullOrWhiteSpace(app))
+ {
+ throw new ArgumentException("App was not defined");
+ }
+
+ return $"{org.AsFileName()}/{app.AsFileName()}/{offeredBy.AsFileName()}/{coveredByPrefix}{coveredBy.AsFileName()}/delegationpolicy.xml";
+ }
+
+ throw new ArgumentException("Unable to build a valid delegation policy path from the provided parameters");
+ }
+
+ ///
+ /// Builds the delegation policy path based on input policyMatch
+ ///
+ /// param to build policypath from
+ /// policypath matching input data
+ public static string GetAltinnAppDelegationPolicyPath(PolicyMatch policyMatch)
+ {
+ DelegationHelper.TryGetResourceFromAttributeMatch(policyMatch.Resource, out ResourceAttributeMatchType resourceMatchType, out string resourceId, out string org, out string app, out string _, out string _);
+ DelegationHelper.GetCoveredByFromMatch(policyMatch.CoveredBy, out int? coveredByUserId, out int? coveredByPartyId, out Guid? coveredByUuid, out UuidType coveredByUuidType);
+
+ return GetDelegationPolicyPath(resourceMatchType, resourceId, org, app, policyMatch.OfferedByPartyId.ToString(), coveredByUserId, coveredByPartyId, coveredByUuid, coveredByUuidType);
+ }
+
+ ///
+ /// Takes the file IO stream and parses the policy file to a XacmlPolicy
+ ///
+ /// The file IO stream
+ /// XacmlPolicy
+ public static XacmlPolicy ParsePolicy(Stream stream)
+ {
+ stream.Position = 0;
+ XacmlPolicy policy;
+ using (XmlReader reader = XmlReader.Create(stream))
+ {
+ policy = XacmlParser.ParseXacmlPolicy(reader);
+ }
+
+ return policy;
+ }
+
+ ///
+ /// Serializes the XacmlPolicy to Xml and returns it as a Memory stream
+ ///
+ /// The XacmlPolicy model to serialize to a memory stream
+ /// MemoryStream of the Xml serialized policy
+ public static MemoryStream GetXmlMemoryStreamFromXacmlPolicy(XacmlPolicy policy)
+ {
+ MemoryStream stream = new MemoryStream();
+ XmlWriter writer = XmlWriter.Create(stream);
+
+ XacmlSerializer.WritePolicy(writer, policy);
+
+ writer.Flush();
+ stream.Position = 0;
+ return stream;
+ }
+
+ ///
+ /// Check the input and returns a vale for CoveredBy and a urn to be used when creating a Attribute match given taht the covered by could be a user, party or SystemUser.
+ ///
+ /// PartyId to evaluate for coveredBy
+ /// UserId to evaluate for coveredBy
+ /// Uuid to evaluate for coveredBy
+ /// The type of covered by to evaluate for type and chose what input to use for covered by value
+ /// coveredBy value and type of value
+ /// When no valid coveredBy is defined
+ public static (string CoveredBy, string CoveredByType) GetCoveredByAndType(int? coveredByPartyId, int? coveredByUserId, Guid? toUuid, UuidType toType)
+ {
+ string coveredBy = null;
+ string coveredByType = null;
+
+ if (coveredByPartyId.HasValue)
+ {
+ coveredBy = coveredByPartyId.Value.ToString();
+ coveredByType = AltinnXacmlConstants.MatchAttributeIdentifiers.PartyAttribute;
+ }
+ else if (coveredByUserId.HasValue)
+ {
+ coveredBy = coveredByUserId.Value.ToString();
+ coveredByType = AltinnXacmlConstants.MatchAttributeIdentifiers.UserAttribute;
+ }
+ else if (toType.Equals(UuidType.SystemUser))
+ {
+ coveredBy = toUuid.ToString();
+ coveredByType = toType.EnumMemberAttributeValueOrName();
+ }
+
+ if (coveredBy == null)
+ {
+ throw new ArgumentException($"No valid coveredBy was provided");
+ }
+
+ return (coveredBy, coveredByType);
+ }
+
+ ///
+ /// Builds a XacmlPolicy representation based on the DelegationPolicy input
+ ///
+ /// The identifier of the resource, either a resource in the resource registry or altinn app
+ /// The party id of the entity offering the delegated the policy
+ /// The party of the entity having received the delegated policy, if the receiving entity is an organization
+ /// The user id of the entity having received the delegated policy, if the receiving entity is a user
+ /// The uuid id of the entity having received the delegated policy
+ /// The uuid type id of the entity having received the delegated policy
+ /// The set of rules to be delegated
+ public static XacmlPolicy BuildDelegationPolicy(string resourceId, int offeredByPartyId, int? coveredByPartyId, int? coveredByUserId, Guid? toUuid, UuidType toType, IList rules)
+ {
+ XacmlPolicy delegationPolicy = new XacmlPolicy(new Uri($"{AltinnXacmlConstants.Prefixes.PolicyId}{1}"), new Uri(XacmlConstants.CombiningAlgorithms.PolicyDenyOverrides), new XacmlTarget(new List()));
+ delegationPolicy.Version = "1.0";
+
+ (string coveredBy, string coveredByType) = GetCoveredByAndType(coveredByPartyId, coveredByUserId, toUuid, toType);
+
+ delegationPolicy.Description = $"Delegation policy containing all delegated rights/actions from {offeredByPartyId} to {coveredBy}, for the resource; {resourceId}";
+
+ foreach (Rule rule in rules)
+ {
+ if (!DelegationHelper.PolicyContainsMatchingRule(delegationPolicy, rule))
+ {
+ delegationPolicy.Rules.Add(BuildDelegationRule(resourceId, offeredByPartyId, coveredBy, coveredByType, rule));
+ }
+ }
+
+ return delegationPolicy;
+ }
+
+ ///
+ /// Extracts policy data from InstanceRight
+ ///
+ /// The rules that is delegated
+ public static PolicyParameters GetPolicyDataFromInstanceRight(InstanceRight rules)
+ {
+ PolicyParameters result = new PolicyParameters
+ {
+ ResourceId = rules.ResourceId,
+ InstanceId = rules.InstanceId,
+ FromType = rules.FromType.EnumMemberAttributeValueOrName(),
+ FromId = rules.FromUuid.ToString().ToLowerInvariant(),
+ ToType = rules.ToType.EnumMemberAttributeValueOrName(),
+ ToId = rules.ToUuid.ToString().ToLowerInvariant(),
+ PerformedById = rules.PerformedBy,
+ PerformedByType = rules.PerformedByType.EnumMemberAttributeValueOrName()
+ };
+
+ return result;
+ }
+
+ ///
+ /// Builds a XacmlPolicy representation based on the DelegationPolicy input
+ ///
+ /// The set of rules to be delegated
+ public static XacmlPolicy BuildInstanceDelegationPolicy(InstanceRight rules)
+ {
+ XacmlPolicy delegationPolicy = new XacmlPolicy(new Uri($"{AltinnXacmlConstants.Prefixes.PolicyId}{1}"), new Uri(XacmlConstants.CombiningAlgorithms.PolicyPermidOverrides), new XacmlTarget(new List()));
+ delegationPolicy.Version = "1.0";
+ PolicyParameters policydata = GetPolicyDataFromInstanceRight(rules);
+
+ delegationPolicy.Description = $"Delegation policy containing all delegated rights/actions from {policydata.FromType}:{policydata.FromId} to {policydata.ToType}:{policydata.ToId}, for the resource: {policydata.ResourceId} and InstanceId: {policydata.InstanceId}";
+
+ foreach (InstanceRule rule in rules.InstanceRules)
+ {
+ if (!DelegationHelper.PolicyContainsMatchingInstanceRule(delegationPolicy, rule))
+ {
+ delegationPolicy.Rules.Add(BuildDelegationInstanceRule(policydata, rule));
+ }
+ }
+
+ return delegationPolicy;
+ }
+
+ ///
+ /// Builds a XacmlRule representation based on the Rule input
+ ///
+ /// container with data to inclue in policy
+ /// The rule to be delegated
+ public static XacmlRule BuildDelegationInstanceRule(PolicyParameters policyData, InstanceRule rule)
+ {
+ rule.RuleId = Guid.NewGuid().ToString();
+
+ XacmlRule delegationRule = new XacmlRule(rule.RuleId, XacmlEffectType.Permit)
+ {
+ Description = $"Delegation of a right/action from {policyData.FromType}:{policyData.FromId} to {policyData.ToType}:{policyData.ToId}, for the resourceId: {policyData.ResourceId} instanceId: {policyData.InstanceId}, by: {policyData.PerformedByType}:{policyData.PerformedById}",
+ Target = BuildInstanceDelegationRuleTarget(policyData.ToId, policyData.ToType, rule)
+ };
+ return delegationRule;
+ }
+
+ ///
+ /// Builds a XacmlRule representation based on the Rule input
+ ///
+ /// The identifier of the resource, either a resource in the resource registry or altinn app
+ /// The id of the entity offering the delegated the policy
+ /// The type of the entity offering the delegated policy
+ /// The id of the entity having received the delegated policy
+ /// The type of the entity having received the delegated policy
+ /// The id of the entity delegated the policy
+ /// The type of the entity delegated the policy
+ /// The rule to be delegated
+ public static XacmlRule BuildDelegationInstanceRule(string resourceId, string fromId, string fromType, string toId, string toType, string performedById, string performedByType, InstanceRule rule)
+ {
+ rule.RuleId = Guid.NewGuid().ToString();
+
+ XacmlRule delegationRule = new XacmlRule(rule.RuleId, XacmlEffectType.Permit)
+ {
+ Description = $"Delegation of a right/action from {fromType}:{fromId} to {toType}:{toId}, for the resource: {resourceId}, by: {performedByType}:{performedById}",
+ Target = BuildInstanceDelegationRuleTarget(toId, toType, rule)
+ };
+ return delegationRule;
+ }
+
+ ///
+ /// Builds a XacmlRule representation based on the Rule input
+ ///
+ /// The identifier of the resource, either a resource in the resource registry or altinn app
+ /// The party id of the entity offering the delegated the policy
+ /// The id of the entity having received the delegated policy
+ /// The type of the entity having received the delegated policy
+ /// The rule to be delegated
+ public static XacmlRule BuildDelegationRule(string resourceId, int offeredByPartyId, string coveredBy, string toType, Rule rule)
+ {
+ rule.RuleId = Guid.NewGuid().ToString();
+
+ XacmlRule delegationRule = new XacmlRule(rule.RuleId, XacmlEffectType.Permit)
+ {
+ Description = $"Delegation of a right/action from {offeredByPartyId} to {coveredBy}, for the resource: {resourceId}, by user; {rule.DelegatedByUserId}",
+ Target = BuildDelegationRuleTarget(coveredBy, toType, rule)
+ };
+ return delegationRule;
+ }
+
+ ///
+ /// Builds a XacmlTarget representation based on the Rule input
+ ///
+ /// The the entity having received the delegated policy
+ /// The type of identifier received the delegated policy user, party or system user
+ /// The rule to be delegated
+ public static XacmlTarget BuildDelegationRuleTarget(string coveredBy, string toType, Rule rule)
+ {
+ List targetList = new List();
+
+ // Build Subject
+ List subjectAllOfs = new List();
+
+ subjectAllOfs.Add(new XacmlAllOf(new List
+ {
+ new XacmlMatch(
+ new Uri(XacmlConstants.AttributeMatchFunction.StringEqual),
+ new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), coveredBy),
+ new XacmlAttributeDesignator(new Uri(XacmlConstants.MatchAttributeCategory.Subject), new Uri(toType), new Uri(XacmlConstants.DataTypes.XMLString), false))
+ }));
+
+ // Build Resource
+ List resourceMatches = new List();
+ foreach (AttributeMatch resourceMatch in rule.Resource)
+ {
+ resourceMatches.Add(
+ new XacmlMatch(
+ new Uri(XacmlConstants.AttributeMatchFunction.StringEqual),
+ new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), resourceMatch.Value),
+ new XacmlAttributeDesignator(new Uri(XacmlConstants.MatchAttributeCategory.Resource), new Uri(resourceMatch.Id), new Uri(XacmlConstants.DataTypes.XMLString), false)));
+ }
+
+ List resourceAllOfs = new List { new XacmlAllOf(resourceMatches) };
+
+ // Build Action
+ List actionAllOfs = new List();
+ actionAllOfs.Add(new XacmlAllOf(new List
+ {
+ new XacmlMatch(
+ new Uri(XacmlConstants.AttributeMatchFunction.StringEqual),
+ new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), rule.Action.Value),
+ new XacmlAttributeDesignator(new Uri(XacmlConstants.MatchAttributeCategory.Action), new Uri(XacmlConstants.MatchAttributeIdentifiers.ActionId), new Uri(XacmlConstants.DataTypes.XMLString), false))
+ }));
+
+ targetList.Add(new XacmlAnyOf(subjectAllOfs));
+ targetList.Add(new XacmlAnyOf(resourceAllOfs));
+ targetList.Add(new XacmlAnyOf(actionAllOfs));
+
+ return new XacmlTarget(targetList);
+ }
+
+ ///
+ /// Builds a XacmlTarget representation based on the Rule input
+ ///
+ /// The the entity having received the delegated policy
+ /// The type of identifier received the delegated policy user, party or system user
+ /// The rule to be delegated
+ public static XacmlTarget BuildInstanceDelegationRuleTarget(string toId, string toType, InstanceRule rule)
+ {
+ List targetList = new List();
+
+ // Build Subject
+ List subjectAllOfs = new List();
+
+ subjectAllOfs.Add(new XacmlAllOf(new List
+ {
+ new XacmlMatch(
+ new Uri(XacmlConstants.AttributeMatchFunction.StringEqualIgnoreCase),
+ new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), toId),
+ new XacmlAttributeDesignator(new Uri(XacmlConstants.MatchAttributeCategory.Subject), new Uri(toType), new Uri(XacmlConstants.DataTypes.XMLString), false))
+ }));
+
+ // Build Resource
+ List resourceMatches = [];
+ resourceMatches.AddRange(rule.Resource.Select(resourceMatch => new XacmlMatch(
+ new Uri(XacmlConstants.AttributeMatchFunction.StringEqualIgnoreCase),
+ new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), resourceMatch.Value.ValueSpan.ToString()),
+ new XacmlAttributeDesignator(new Uri(XacmlConstants.MatchAttributeCategory.Resource), new Uri(resourceMatch.Value.PrefixSpan.ToString()), new Uri(XacmlConstants.DataTypes.XMLString), false))));
+ List resourceAllOfs = new List { new XacmlAllOf(resourceMatches) };
+
+ // Build Action
+ List actionAllOfs = new List();
+ actionAllOfs.Add(new XacmlAllOf(new List
+ {
+ new XacmlMatch(
+ new Uri(XacmlConstants.AttributeMatchFunction.StringEqualIgnoreCase),
+ new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), rule.Action.ValueSpan.ToString()),
+ new XacmlAttributeDesignator(new Uri(XacmlConstants.MatchAttributeCategory.Action), new Uri(rule.Action.PrefixSpan.ToString()), new Uri(XacmlConstants.DataTypes.XMLString), false))
+ }));
+
+ targetList.Add(new XacmlAnyOf(subjectAllOfs));
+ targetList.Add(new XacmlAnyOf(resourceAllOfs));
+ targetList.Add(new XacmlAnyOf(actionAllOfs));
+
+ return new XacmlTarget(targetList);
+ }
+
+ ///
+ /// Builds a XacmlMatch model
+ ///
+ /// The compare function type
+ /// The attribute data type
+ /// The attribute value
+ /// The attribute id
+ /// The attribute category
+ /// Whether the attribute value must be present
+ public static XacmlMatch BuildDelegationPolicyMatch(string function, string datatype, string attributeValue, string attributeId, string category, bool mustBePresent = false)
+ {
+ return new XacmlMatch(
+ new Uri(function),
+ new XacmlAttributeValue(new Uri(datatype), attributeValue),
+ new XacmlAttributeDesignator(new Uri(category), new Uri(attributeId), new Uri(datatype), mustBePresent));
+ }
+
+ ///
+ /// Gets the entire policy as a list of .
+ ///
+ /// The policy
+ /// The language (not in use yet; exactly how is yet to be determined)
+ /// List of resource policies
+ public static List GetResourcePoliciesFromXacmlPolicy(XacmlPolicy policy, string language)
+ {
+ Dictionary resourcePolicies = new Dictionary();
+
+ foreach (XacmlRule rule in policy.Rules)
+ {
+ if (rule.Effect.Equals(XacmlEffectType.Permit) && rule.Target != null)
+ {
+ List roles = GetRolesFromRule(rule);
+ if (roles.Count == 0)
+ {
+ continue;
+ }
+
+ List policyKeys = GetResourcePoliciesFromRule(resourcePolicies, rule);
+ List actions = GetActionsFromRule(rule, roles);
+
+ foreach (string policyKey in policyKeys)
+ {
+ ResourcePolicy resourcePolicy = resourcePolicies.GetValueOrDefault(policyKey);
+
+ if (policy.Description != null && resourcePolicy.Description == null)
+ {
+ resourcePolicy.Description = policy.Description;
+ }
+
+ AddActionsToResourcePolicy(actions, resourcePolicy);
+ }
+ }
+ }
+
+ return resourcePolicies.Values.ToList();
+ }
+
+ ///
+ /// Gets the authentication level requirement from the obligation expression of the XacmlPolicy if specified
+ ///
+ /// The policy
+ /// Minimum authentication level requirement
+ public static int GetMinimumAuthenticationLevelFromXacmlPolicy(XacmlPolicy policy)
+ {
+ foreach (XacmlObligationExpression oblExpr in policy.ObligationExpressions)
+ {
+ foreach (XacmlAttributeAssignmentExpression attrExpr in oblExpr.AttributeAssignmentExpressions)
+ {
+ if (attrExpr.Category.OriginalString == AltinnXacmlConstants.MatchAttributeCategory.MinimumAuthenticationLevel &&
+ attrExpr.Property is XacmlAttributeValue attrValue &&
+ int.TryParse(attrValue.Value, out int minAuthLevel))
+ {
+ return minAuthLevel;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ ///
+ /// Decomposes the provided XacmlPolicy with individual XacmlRules pr. Subject, Resource and Action AllOf combinations
+ ///
+ /// The XacmlPolicy to decompose
+ /// A decomposed XacmlPolicy
+ public static XacmlPolicy GetDecomposedXacmlPolicy(XacmlPolicy policy)
+ {
+ XacmlPolicy decomposedPolicy = new XacmlPolicy(new Uri($"{policy.PolicyId}_decomposed"), policy.RuleCombiningAlgId, policy.Target);
+ decomposedPolicy.Description = $"Decomposed policy of policyid: {policy.PolicyId}. Original description: {policy.Description}";
+
+ foreach (XacmlRule rule in policy.Rules)
+ {
+ ICollection subjectAllOfs = GetAllOfsByCategory(rule, XacmlConstants.MatchAttributeCategory.Subject);
+ ICollection resourceAllOfs = GetAllOfsByCategory(rule, XacmlConstants.MatchAttributeCategory.Resource);
+ ICollection actionAllOfs = GetAllOfsByCategory(rule, XacmlConstants.MatchAttributeCategory.Action);
+
+ int decomposedRuleCount = 0;
+ foreach (XacmlAllOf subject in subjectAllOfs)
+ {
+ foreach (XacmlAllOf resource in resourceAllOfs)
+ {
+ foreach (XacmlAllOf action in actionAllOfs)
+ {
+ decomposedRuleCount++;
+
+ XacmlRule decomposedRule = new XacmlRule($"{rule.RuleId}_{decomposedRuleCount}", rule.Effect);
+ decomposedRule.Description = $"Decomposed rule from policyid: {policy.PolicyId} ruleid: {rule.RuleId}. Original description: {rule.Description}";
+ decomposedRule.Target = new XacmlTarget(new List
+ {
+ new XacmlAnyOf(new List { subject }),
+ new XacmlAnyOf(new List { resource }),
+ new XacmlAnyOf(new List { action })
+ });
+
+ decomposedPolicy.Rules.Add(decomposedRule);
+ }
+ }
+ }
+ }
+
+ return decomposedPolicy;
+ }
+
+ ///
+ /// Builds a collection of XacmlContextAttributes which can be used for a decision request, based on a list of subject attributes and an already decomposed XacmlRule (has a single combination of subject, resource and action AllOfs)
+ ///
+ /// The list of subject values to add to the context (Roles, access groups, delegation recipients etc.)
+ /// The decomposed XacmlRule (has a single combination of subject, resource and action AllOfs)
+ /// A collection of XacmlContextAttributes which can be used for a decision request
+ public static ICollection GetContextAttributes(List subjects, XacmlRule decomposedRule)
+ {
+ ICollection resourceAllOfs = GetAllOfsByCategory(decomposedRule, XacmlConstants.MatchAttributeCategory.Resource);
+ ICollection actionAllOfs = GetAllOfsByCategory(decomposedRule, XacmlConstants.MatchAttributeCategory.Action);
+
+ List resource = GetAttributeMatchFromXacmlAllOfs(resourceAllOfs.FirstOrDefault());
+ List action = GetAttributeMatchFromXacmlAllOfs(actionAllOfs.FirstOrDefault());
+
+ return GetContextAttributes(subjects, resource, action);
+ }
+
+ ///
+ /// Takes an already decomposed XacmlRule (has a single combination of subject, resource and action AllOfs) and builds a collection of XacmlContextAttributes which can be used for a decision request
+ ///
+ /// The list of subject values to add to the context (Roles, access groups, delegation recipients etc.)
+ /// The list of attribute values identifying a single resource to add to the context (Org/App, ResourceRegistryId)
+ /// The list of action attribute values identifying a single action to add to the context (Read, Write etc.)
+ /// A collection of XacmlContextAttributes which can be used for a decision request
+ public static ICollection GetContextAttributes(List subjects, List resource, List action)
+ {
+ ICollection contextAttributes = new Collection();
+
+ ICollection subjectsAttributes = new Collection();
+ foreach (AttributeMatch subjectMatch in subjects)
+ {
+ XacmlAttribute subjectAttribute = new XacmlAttribute(new Uri(subjectMatch.Id), true);
+ subjectAttribute.AttributeValues.Add(new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), subjectMatch.Value));
+ subjectsAttributes.Add(subjectAttribute);
+ }
+
+ ICollection resourceAttributes = new Collection();
+ foreach (AttributeMatch resourceMatch in resource)
+ {
+ XacmlAttribute resourceAttribute = new XacmlAttribute(new Uri(resourceMatch.Id), true);
+ resourceAttribute.AttributeValues.Add(new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), resourceMatch.Value));
+ resourceAttributes.Add(resourceAttribute);
+ }
+
+ ICollection actionAttributes = new Collection();
+ foreach (AttributeMatch actionMatch in action)
+ {
+ XacmlAttribute actionAttribute = new XacmlAttribute(new Uri(actionMatch.Id), true);
+ actionAttribute.AttributeValues.Add(new XacmlAttributeValue(new Uri(XacmlConstants.DataTypes.XMLString), actionMatch.Value));
+ actionAttributes.Add(actionAttribute);
+ }
+
+ contextAttributes.Add(new XacmlContextAttributes(new Uri(XacmlConstants.MatchAttributeCategory.Subject), subjectsAttributes));
+ contextAttributes.Add(new XacmlContextAttributes(new Uri(XacmlConstants.MatchAttributeCategory.Resource), resourceAttributes));
+ contextAttributes.Add(new XacmlContextAttributes(new Uri(XacmlConstants.MatchAttributeCategory.Action), actionAttributes));
+ return contextAttributes;
+ }
+
+ ///
+ /// Creates a collection of Rights (single Resource and Action combinations) from the provided collection of XacmlRules
+ ///
+ /// The collection of XacmlRules
+ /// A collection of Rights
+ public static ICollection GetRightsFromXacmlRules(ICollection xacmlRules)
+ {
+ Dictionary rights = new Dictionary();
+
+ foreach (XacmlRule rule in xacmlRules)
+ {
+ ICollection resourceAllOfs = GetAllOfsByCategory(rule, XacmlConstants.MatchAttributeCategory.Resource);
+ ICollection actionAllOfs = GetAllOfsByCategory(rule, XacmlConstants.MatchAttributeCategory.Action);
+
+ foreach (XacmlAllOf resource in resourceAllOfs)
+ {
+ foreach (XacmlAllOf action in actionAllOfs)
+ {
+ Right right = new Right
+ {
+ RightSources = new List(),
+ Resource = GetAttributeMatchFromXacmlAllOfs(resource),
+ Action = GetAttributeMatchFromXacmlAllOfs(action).FirstOrDefault()
+ };
+
+ if (!rights.ContainsKey(right.RightKey))
+ {
+ rights.Add(right.RightKey, right);
+ }
+ }
+ }
+ }
+
+ return rights.Values;
+ }
+
+ ///
+ /// Gets a collection of distinct AttributeId used in XacmlMatch instances matching the specified attribute category.
+ ///
+ /// The xacml rule to find match attribute ids in
+ /// The attribute category to match
+ /// Collection of AttributeId
+ public static ICollection GetRuleMatchAttributeIdsForCategory(XacmlRule rule, string category)
+ {
+ SortedList attributeIds = new SortedList();
+
+ foreach (XacmlAnyOf anyOf in rule.Target.AnyOf)
+ {
+ foreach (XacmlAllOf allOf in anyOf.AllOf)
+ {
+ foreach (XacmlMatch xacmlMatch in allOf.Matches)
+ {
+ if (xacmlMatch.AttributeDesignator.Category.Equals(category) && !attributeIds.ContainsKey(xacmlMatch.AttributeDesignator.AttributeId.OriginalString))
+ {
+ attributeIds.Add(xacmlMatch.AttributeDesignator.AttributeId.OriginalString, xacmlMatch.AttributeDesignator.AttributeId.OriginalString);
+ }
+ }
+ }
+ }
+
+ return attributeIds.Keys.ToList();
+ }
+
+ ///
+ /// Gets a nested list of AttributeMatche models for all XacmlMatch instances matching the specified attribute category.
+ ///
+ /// The xacml rule to process
+ /// The attribute category to match
+ /// Nested list of PolicyAttributeMatch models
+ public static List> GetRulePolicyAttributeMatchesForCategory(XacmlRule rule, string category)
+ {
+ List> ruleAttributeMatches = new();
+
+ foreach (XacmlAnyOf anyOf in rule.Target.AnyOf)
+ {
+ foreach (XacmlAllOf allOf in anyOf.AllOf)
+ {
+ List anyOfAttributeMatches = new();
+ foreach (XacmlMatch xacmlMatch in allOf.Matches)
+ {
+ if (xacmlMatch.AttributeDesignator.Category.Equals(category))
+ {
+ anyOfAttributeMatches.Add(new PolicyAttributeMatch { Id = xacmlMatch.AttributeDesignator.AttributeId.OriginalString, Value = xacmlMatch.AttributeValue.Value });
+ }
+ }
+
+ if (anyOfAttributeMatches.Any())
+ {
+ ruleAttributeMatches.Add(anyOfAttributeMatches);
+ }
+ }
+ }
+
+ return ruleAttributeMatches;
+ }
+
+ ///
+ /// Check of a given app definition exist as a subject in a list of subjects
+ ///
+ /// The subject list to check
+ /// the app to check for
+ /// true if the app deffinion exist in list of subjects and false if not
+ public static bool ContainsDelegatorAppInSubject(List> ruleSubjects, IEnumerable delegaterApp)
+ {
+ bool result = false;
+
+ foreach (List