├── src ├── pkgs │ └── Altinn.Register.Contracts │ │ ├── version.txt │ │ ├── Version.props │ │ ├── Directory.Build.props │ │ ├── test │ │ └── Altinn.Register.Contracts.Tests │ │ │ ├── Snapshots │ │ │ ├── SystemUserTests.MinimalSystemUser.verified.json │ │ │ ├── UnknownPartyTests.MinimalUnknownParty.verified.json │ │ │ ├── EnterpriseUserTests.MinimalEnterpriseUser.verified.json │ │ │ ├── PersonTests.MinimalPerson.verified.json │ │ │ ├── SelfIdentifiedUserTests.MinimalSelfIdentifiedUser.verified.json │ │ │ ├── OrganizationTests.MinimalOrganization.verified.json │ │ │ ├── ExternalRoleAssignmentTests.ExternalRoleAssignment_KnownSource.verified.json │ │ │ ├── ExternalRoleAssignmentTests.ExternalRoleAssignment_UnknownSource.verified.json │ │ │ ├── SelfIdentifiedUserTests.MaximalSelfIdentifiedUser.verified.json │ │ │ ├── SystemUserTests.MaximalSystemUser.verified.json │ │ │ ├── SystemUserTests.MaximalSystemUser_Agent.verified.json │ │ │ ├── EnterpriseUserTests.MaximalEnterpriseUser.verified.json │ │ │ ├── UnknownPartyTests.UnknownPartyWithExtensions.verified.json │ │ │ └── OrganizationTests.MaximalOrganization.verified.json │ │ │ ├── ModuleInitializer.cs │ │ │ └── Altinn.Register.Contracts.Tests.csproj │ │ └── src │ │ └── Altinn.Register.Contracts │ │ ├── SelfIdentifiedUser.cs │ │ ├── V1 │ │ ├── PartyNamesLookup.cs │ │ ├── PartyNamesLookupResult.cs │ │ ├── PartyComponentOptions.cs │ │ ├── PersonNameComponents.cs │ │ ├── PartyType.cs │ │ └── PartyName.cs │ │ ├── EnterpriseUser.cs │ │ ├── IExternalRoleRef.cs │ │ ├── MailingAddress.cs │ │ ├── SystemUser.cs │ │ ├── ExternalRoleAssignment.cs │ │ ├── SystemUserType.cs │ │ ├── PartyRef.cs │ │ └── Altinn.Register.Contracts.csproj └── apps │ └── Altinn.Register │ ├── conf.json │ ├── test │ ├── Altinn.Register.Persistence.Tests │ │ ├── Snapshots │ │ │ ├── .gitignore │ │ │ ├── .editorconfig │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(id).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(uuid).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(pers.id).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(org.id).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(user.id).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(user.name).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(id).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(uuid).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(pers.id).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(user.id).verified.sql │ │ │ ├── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(org.id).verified.sql │ │ │ └── PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(user.name).verified.sql │ │ ├── Utils │ │ │ └── EnumMembersDataAttribute.cs │ │ ├── Altinn.Register.Persistence.Tests.csproj │ │ └── ModuleInitializer.cs │ ├── Altinn.Register.Tests │ │ ├── skd-org.pfx │ │ ├── ttd-org.pfx │ │ ├── jwtselfsignedcert.pfx │ │ ├── appsettings.json │ │ ├── Utils │ │ │ ├── AtomicCounter.cs │ │ │ ├── AtomicBool.cs │ │ │ └── AsyncEnumerableTestExtensions.cs │ │ ├── Testdata │ │ │ ├── 50006237.json │ │ │ ├── 50004219.json │ │ │ ├── 50004216.json │ │ │ ├── 50012347.json │ │ │ └── 50012345.json │ │ ├── IntegrationTests │ │ │ └── Utils │ │ │ │ └── TestAccessTokenGenerator.cs │ │ ├── Mocks │ │ │ ├── AuthorizationClientMock.cs │ │ │ ├── PublicSigningKeyProviderMock.cs │ │ │ └── DelegatingHandlerStub.cs │ │ ├── UnitTests │ │ │ ├── StringExtensionsTests.cs │ │ │ ├── ImportJobQueueStatusTests.cs │ │ │ ├── OpaqueTests.cs │ │ │ ├── PartyTypesSetTests.cs │ │ │ ├── EventBaseTests.cs │ │ │ └── CommandBaseTests.cs │ │ ├── Jobs │ │ │ └── JobRegistrationTests.cs │ │ ├── skd-org.pem │ │ └── ttd-org.pem │ ├── Altinn.Register.TestUtils.V2 │ │ ├── AsyncLock.cs │ │ ├── Http │ │ │ ├── IFakeRequestBuilder.cs │ │ │ ├── IFilterFakeRequest.cs │ │ │ ├── Filters │ │ │ │ ├── HttpMethodFilter.cs │ │ │ │ ├── RouteValueFilter.cs │ │ │ │ └── QueryParamFilter.cs │ │ │ ├── IFakeRequestFilter.cs │ │ │ ├── ISetFakeRequestHandler.cs │ │ │ ├── StreamExtensions.cs │ │ │ └── IFakeRequestHandler.cs │ │ ├── IAsyncResource.cs │ │ ├── AsyncTestBase.cs │ │ ├── FluentAssertionsExtensions │ │ │ └── Initializer.cs │ │ └── Assertions │ │ │ └── CustomAssertions.cs │ ├── Altinn.Register.TestUtils.V3 │ │ ├── Database │ │ │ └── PostgresUserType.cs │ │ ├── Traits │ │ │ ├── KnownCategories.cs │ │ │ ├── IntegrationTestAttribute.cs │ │ │ └── CategoryAttribute.cs │ │ ├── TestContextExtensions.cs │ │ ├── Tracing │ │ │ └── ActivityDisplayFilterCollection.cs │ │ ├── TestBase.cs │ │ └── Altinn.Register.TestUtils.V3.csproj │ ├── Altinn.Register.IntegrationTests │ │ ├── Tracing │ │ │ └── IntegrationTestsActivities.cs │ │ ├── AssemblyFixtures.cs │ │ ├── TestServices │ │ │ ├── TestAccessTokenGenerator.cs │ │ │ ├── TestPublicSigningKeyProvider.cs │ │ │ └── TestOpenIdConnectConfigurationManager.cs │ │ └── Altinn.Register.IntegrationTests.csproj │ ├── .editorconfig │ └── Altinn.Register.TestUtils │ │ └── MassTransit │ │ └── AsyncElementListExtensions.cs │ ├── src │ ├── Altinn.Register.Persistence │ │ ├── Migration │ │ │ ├── v0.00-schema │ │ │ │ ├── 00-schema.sql │ │ │ │ └── 01-default-privileges.sql │ │ │ ├── v0.04-external-roles │ │ │ │ ├── 00-hstore-extension.sql │ │ │ │ ├── 01-identifier-domain.sql │ │ │ │ ├── 02-translated-text-domain.sql │ │ │ │ ├── 03-external-role-definition-table.sql │ │ │ │ ├── 04-external-role-table.sql │ │ │ │ └── 05-data-external-roles.sql │ │ │ ├── v0.06-schema-grant │ │ │ │ └── 00-schema-grant.sql │ │ │ ├── v0.17-party-display-name │ │ │ │ ├── 01-party-display-name.sql │ │ │ │ └── 00-person-short-name.sql │ │ │ ├── v0.41-cra-role-source │ │ │ │ └── 00-new-role-source.sql │ │ │ ├── v0.15-ffor-role-definition │ │ │ │ └── 00-data-ffor-role-definition.sql │ │ │ ├── v0.37-system-user-type │ │ │ │ ├── 00-system-user-type-enum.sql │ │ │ │ └── 01-system-user-table.sql │ │ │ ├── v0.02-party-source-values │ │ │ │ └── 00-party-source-values.sql │ │ │ ├── v0.07-addresses │ │ │ │ ├── 03-drop-old-types.sql │ │ │ │ └── 00-address-types.sql │ │ │ ├── v0.08-leases │ │ │ │ └── 00-lease-table.sql │ │ │ ├── v0.30-partyid-opt │ │ │ │ └── 00-partyid-opt.sql │ │ │ ├── v0.01-party │ │ │ │ ├── 00-enums.sql │ │ │ │ ├── 01-identifiers.sql │ │ │ │ ├── 05-person-table.sql │ │ │ │ ├── 04-party-source-ref-table.sql │ │ │ │ ├── 02-address.sql │ │ │ │ └── 06-organization-table.sql │ │ │ ├── v0.22-role-assignment-indexes │ │ │ │ └── 00-role-assignment-indexes.sql │ │ │ ├── v0.42-guardianship-roles │ │ │ │ └── 00-identifier.sql │ │ │ ├── v0.36-import-job-state │ │ │ │ └── 00-import-job-state-table.sql │ │ │ ├── v0.13-seq-lock │ │ │ │ └── 01-party-table.sql │ │ │ ├── v0.03-party-source-values │ │ │ │ └── 00-party-source-values.sql │ │ │ ├── v0.38-deleted-user │ │ │ │ └── 03-remove-usernames-from-deleted-parties.sql │ │ │ ├── v0.19-si-user-enum │ │ │ │ └── 00-party-type-enum.sql │ │ │ ├── v0.27-import-job-state │ │ │ │ └── 00-import-job-party-state-table.sql │ │ │ ├── v0.29-new-partytypes │ │ │ │ └── 00-party-type.sql │ │ │ ├── v0.10-role-code │ │ │ │ └── 00-identifier-domain.sql │ │ │ ├── v0.18-role-identifiers │ │ │ │ └── 00-identifier.sql │ │ │ ├── v0.16-identifier-update │ │ │ │ └── 00-identifier.sql │ │ │ ├── v0.09-import-job │ │ │ │ └── 00-import-job-table.sql │ │ │ ├── v0.05-party-source-ref-index │ │ │ │ └── 00-party-source-ref-index.sql │ │ │ ├── v0.11-lease-done │ │ │ │ └── 00-lease-table.sql │ │ │ ├── v0.20-si-user │ │ │ │ └── 00-type-identifier-check.sql │ │ │ ├── v0.21-party-constraints │ │ │ │ └── 00-party-constraints.sql │ │ │ ├── v0.31-username │ │ │ │ └── 00-username.sql │ │ │ └── v0.23-user │ │ │ │ └── 00-user-table.sql │ │ ├── PostgreSqlVacuumIndexCleanup.cs │ │ ├── DbArgTypes │ │ │ └── ArgUpsertExternalRoleAssignment.cs │ │ ├── PartyListFilters.cs │ │ ├── UnitOfWork │ │ │ └── ISavePoint.cs │ │ └── AsyncEnumerables │ │ │ └── IAsyncResourceOwner.cs │ ├── Altinn.Register.AppHost │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Altinn.Register.AppHost.csproj │ │ └── ResourceExtensions.cs │ ├── Directory.Build.props │ ├── Altinn.Register.Core │ │ ├── PartyImport │ │ │ └── A2 │ │ │ │ ├── A2Change.cs │ │ │ │ ├── A2PartyChangePage.cs │ │ │ │ ├── A2UserProfileChangePage.cs │ │ │ │ ├── A2UserProfileType.cs │ │ │ │ ├── A2PartyChange.cs │ │ │ │ ├── A2PartyExternalRoleAssignment.cs │ │ │ │ └── A2UserProfileChange.cs │ │ ├── ImportJobs │ │ │ └── ImportJobProcessingStatus.cs │ │ ├── Parties │ │ │ ├── PartySource.cs │ │ │ ├── UpsertUserRecordResult.cs │ │ │ ├── IPartyPersistenceCleanupService.cs │ │ │ ├── Records │ │ │ │ ├── SystemUserRecordType.cs │ │ │ │ ├── EnterpriseUserRecord.cs │ │ │ │ ├── SelfIdentifiedUserRecord.cs │ │ │ │ ├── MailingAddressRecord.cs │ │ │ │ ├── SystemUserRecord.cs │ │ │ │ └── PartyRecordType.cs │ │ │ └── PartySourceRef.cs │ │ ├── Utils │ │ │ ├── IAsyncSideEffectEnumerable.cs │ │ │ └── EnumerableExtensions.cs │ │ └── UnitOfWork │ │ │ ├── UnitOfWorkStatus.cs │ │ │ ├── IUnitOfWorkServiceFactory.cs │ │ │ └── IUnitOfWorkParticipantFactory.cs │ ├── Altinn.Register │ │ ├── Configuration │ │ │ ├── PlatformSettings.cs │ │ │ ├── A2PartyImportSettings.cs │ │ │ └── GeneralSettings.cs │ │ ├── appsettings.Staging.json │ │ ├── ModelBinding │ │ │ └── ISingleton.cs │ │ ├── appsettings.Development.json │ │ ├── PartyImport │ │ │ ├── A2 │ │ │ │ ├── ImportPartyUserIdJobState.cs │ │ │ │ ├── ImportA2PartyCommand.cs │ │ │ │ ├── ImportA2UserIdForPartyCommand.cs │ │ │ │ └── ImportA2UserProfileCommand.cs │ │ │ ├── SystemUser │ │ │ │ ├── ImportSystemUserConsumer.cs │ │ │ │ └── ImportSystemUserCommand.cs │ │ │ ├── UpsertPartyCommand.cs │ │ │ ├── UpsertPartyUserCommand.cs │ │ │ ├── UpsertUserRecordCommand.cs │ │ │ └── UpsertPartyTracking.cs │ │ ├── Models │ │ │ └── OrgContactPointLookup.cs │ │ ├── Clients │ │ │ ├── BridgeOrgContactPointLookup.cs │ │ │ └── Interfaces │ │ │ │ ├── IPersonClient.cs │ │ │ │ └── IAuthorizationClient.cs │ │ ├── ApiDescriptions │ │ │ ├── PartyFieldIncludesSchemaFilter.cs │ │ │ └── PartyComponentOptionSchemaFilter.cs │ │ ├── Services │ │ │ ├── Interfaces │ │ │ │ └── IOrgContactPoint.cs │ │ │ └── Implementation │ │ │ │ └── OrgContactPointService.cs │ │ ├── Conventions │ │ │ ├── IControllerModelCondition.cs │ │ │ └── IActionModelCondition.cs │ │ ├── Mapping │ │ │ └── PartyMapper.User.cs │ │ ├── Core │ │ │ ├── PersonLookupSettings.cs │ │ │ ├── TooManyFailedLookupsException.cs │ │ │ └── IPersonLookup.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Controllers │ │ │ └── ErrorController.cs │ │ ├── Utils │ │ │ ├── ContractsMapper.cs │ │ │ └── HttpReciliencyExtensions.cs │ │ ├── LeaseNames.cs │ │ └── JobNames.cs │ ├── Aspire.Npgsql │ │ └── Altinn.Authorization.Aspire.Npgsql.csproj │ ├── ServiceDefaults.MassTransit │ │ ├── AltinnMassTransitOptions.cs │ │ ├── AltinnEndpointNameFormatter.cs │ │ ├── Migration │ │ │ └── v0.00-base │ │ │ │ └── 00-schema.sql │ │ ├── IBusLifetime.cs │ │ ├── MassTransitTransportHelper.InMemoryTransportHelper.cs │ │ └── IMassTransitBuilder.cs │ ├── ServiceDefaults.Jobs │ │ ├── RecurringJobHostedSettings.cs │ │ ├── IHasJobName.cs │ │ ├── IJob.cs │ │ ├── Job.cs │ │ ├── IJobCondition.cs │ │ ├── IJobRegistration.cs │ │ └── Altinn.Authorization.ServiceDefaults.Jobs.csproj │ ├── ServiceDefaults.MassTransit.Abstractions │ │ ├── Altinn.Authorization.ServiceDefaults.MassTransit.Abstractions.csproj │ │ ├── EventBase.cs │ │ ├── CommandBase.cs │ │ └── ICommandSender.cs │ ├── Altinn.Register.Contracts.MassTransit │ │ ├── Parties │ │ │ ├── PartyUpdatedEvent.cs │ │ │ └── PartyReference.cs │ │ ├── Altinn.Register.Contracts.MassTransit.csproj │ │ └── ExternalRoles │ │ │ ├── ExternalRoleReference.cs │ │ │ ├── ExternalRoleAssignmentAddedEvent.cs │ │ │ └── ExternalRoleAssignmentRemovedEvent.cs │ └── ServiceDefaults.Leases │ │ ├── Altinn.Authorization.ServiceDefaults.Leases.csproj │ │ ├── Microsoft.Extensions.DependencyInjection │ │ └── LeaseServiceCollectionExtensions.cs │ │ ├── LeaseTicket.cs │ │ └── LeaseReleaseResult.cs │ └── Directory.Build.targets ├── .release-please-manifest.json ├── .github ├── containerscan │ └── allowedlist.yaml ├── scripts │ ├── get-metadata.mts │ ├── _yargs.mts │ ├── package.json │ ├── publish-to-nuget.mts │ ├── publish-to-github-packages.mts │ └── tsconfig.json ├── disabled-workflows │ └── assign-issues-to-projects.yml └── workflows │ └── container-scan.yml ├── renovate.json ├── container-scan.md ├── .vscode └── settings.json ├── docker-compose.yml ├── release-please-config.json ├── Directory.Build.props ├── Dockerfile └── .justfile /src/pkgs/Altinn.Register.Contracts/version.txt: -------------------------------------------------------------------------------- 1 | 1.4.1 2 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | {"src/pkgs/Altinn.Register.Contracts":"1.4.1"} 2 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "deps": ["pkg:Altinn.Register.Contracts"] 3 | } 4 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/.gitignore: -------------------------------------------------------------------------------- 1 | *.received.* 2 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.00-schema/00-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA IF NOT EXISTS register; 2 | 3 | -------------------------------------------------------------------------------- /.github/containerscan/allowedlist.yaml: -------------------------------------------------------------------------------- 1 | general: 2 | vulnerabilities: 3 | - CVE-2019-0820 4 | - CVE-2024-12797 5 | - CVE-2024-12797 6 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.04-external-roles/00-hstore-extension.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION hstore; 2 | 3 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/Version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.4.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.06-schema-grant/00-schema-grant.sql: -------------------------------------------------------------------------------- 1 | -- grant schema 2 | GRANT USAGE ON SCHEMA register TO "${APP-USER}"; 3 | 4 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/skd-org.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-register/main/src/apps/Altinn.Register/test/Altinn.Register.Tests/skd-org.pfx -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/ttd-org.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-register/main/src/apps/Altinn.Register/test/Altinn.Register.Tests/ttd-org.pfx -------------------------------------------------------------------------------- /.github/scripts/get-metadata.mts: -------------------------------------------------------------------------------- 1 | import { verticals } from "./_meta.mts"; 2 | import { inspect } from "node:util"; 3 | 4 | console.log(inspect(verticals, { depth: 10, colors: true })); 5 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.17-party-display-name/01-party-display-name.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE register.party 2 | RENAME COLUMN name TO display_name; 3 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/AsyncLock.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils; 2 | 3 | internal sealed class AsyncLock() 4 | : AsyncConcurrencyLimiter(1) 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/jwtselfsignedcert.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-register/main/src/apps/Altinn.Register/test/Altinn.Register.Tests/jwtselfsignedcert.pfx -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>Altinn/renovate-config", 5 | "local>Altinn/altinn-authorization-tmp:renovate" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.AppHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.41-cra-role-source/00-new-role-source.sql: -------------------------------------------------------------------------------- 1 | ALTER TYPE register.external_role_source ADD VALUE 'cra'; -- Norwegian Civil Rights Authority (Sivilrettsforvaltningen) 2 | -------------------------------------------------------------------------------- /container-scan.md: -------------------------------------------------------------------------------- 1 | # Status for container scans 2 | 3 | [![Register scan](https://github.com/altinn/altinn-register/actions/workflows/container-scan.yml/badge.svg)](https://github.com/Altinn/altinn-register/actions/workflows/container-scan.yml) 4 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.15-ffor-role-definition/00-data-ffor-role-definition.sql: -------------------------------------------------------------------------------- 1 | UPDATE register.external_role_definition 2 | SET code = 'FFØR' 3 | WHERE "source" = 'ccr' AND identifier = 'ffor'; 4 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.37-system-user-type/00-system-user-type-enum.sql: -------------------------------------------------------------------------------- 1 | -- Enum: register.system_user_type 2 | CREATE TYPE register.system_user_type AS ENUM( 3 | 'standard', 4 | 'agent' 5 | ); 6 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.02-party-source-values/00-party-source-values.sql: -------------------------------------------------------------------------------- 1 | -- Enum: register.party_source 2 | ALTER TYPE register.party_source RENAME VALUE 'folkeregistered' TO 'folkeregisteret'; 3 | 4 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/Database/PostgresUserType.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Database; 2 | 3 | public enum PostgresUserType 4 | { 5 | Owner, 6 | Migrator, 7 | Seeder, 8 | App, 9 | } 10 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/Traits/KnownCategories.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Traits; 2 | 3 | public static class KnownCategories 4 | { 5 | public static string IntegrationTest { get; } = "IntegrationTest"; 6 | } 7 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.AppHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "Aspire.Hosting.Dcp": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.04-external-roles/01-identifier-domain.sql: -------------------------------------------------------------------------------- 1 | -- Domain: register.identifier 2 | CREATE DOMAIN register.identifier AS text CONSTRAINT identifier_valid CHECK (value ~ '^[a-z][a-z0-9_]{2,28}[a-z0-9]$'); 3 | 4 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.07-addresses/03-drop-old-types.sql: -------------------------------------------------------------------------------- 1 | -- Domain: register.address 2 | DROP DOMAIN register.address; 3 | 4 | -- Composite: register._address (private - implementation detail) 5 | DROP TYPE register._address; 6 | 7 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.08-leases/00-lease-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.lease 2 | CREATE TABLE register.lease( 3 | id text PRIMARY KEY NOT NULL, 4 | token uuid NOT NULL, 5 | expires timestamp with time zone NOT NULL 6 | ) 7 | TABLESPACE pg_default; 8 | 9 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | true 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/.editorconfig: -------------------------------------------------------------------------------- 1 | # Verify settings 2 | [*.{sql}] 3 | charset = "utf-8-bom" 4 | end_of_line = lf 5 | indent_size = unset 6 | indent_style = unset 7 | insert_final_newline = false 8 | tab_width = unset 9 | trim_trailing_whitespace = false 10 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.37-system-user-type/01-system-user-table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE register.system_user( 2 | uuid uuid NOT NULL PRIMARY KEY REFERENCES register.party(uuid) ON DELETE CASCADE, 3 | "type" register.system_user_type NOT NULL 4 | ) 5 | TABLESPACE pg_default; 6 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/SystemUserTests.MinimalSystemUser.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "system-user", 3 | "partyUuid": "00000000-0000-0000-0000-000000000001", 4 | "versionId": 1, 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 6 | } 7 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.30-partyid-opt/00-partyid-opt.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE register.party 2 | ADD CONSTRAINT party_id_check CHECK ((id IS NULL) <> (party_type IN ('person', 'organization', 'self-identified-user'))); 3 | 4 | ALTER TABLE register.party 5 | ALTER COLUMN id DROP NOT NULL; 6 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "GeneralSettings": { 3 | "BridgeApiEndpoint": "https://at24.altinn.cloud/sblbridge/register/api/", 4 | "OpenIdWellKnownEndpoint": "http://localhost:5040/authentication/api/v1/openid/", 5 | "JwtCookieName": "AltinnStudioRuntime" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/UnknownPartyTests.MinimalUnknownParty.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "unknown-party", 3 | "partyUuid": "00000000-0000-0000-0000-000000000001", 4 | "versionId": 1, 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 6 | } 7 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/EnterpriseUserTests.MinimalEnterpriseUser.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "enterprise-user", 3 | "partyUuid": "00000000-0000-0000-0000-000000000001", 4 | "versionId": 1, 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 6 | } 7 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.IntegrationTests/Tracing/IntegrationTestsActivities.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Altinn.Register.IntegrationTests.Tracing; 4 | 5 | internal class IntegrationTestsActivities 6 | { 7 | public static ActivitySource Source { get; } = new("Altinn.Register.IntegrationTests"); 8 | } 9 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/PersonTests.MinimalPerson.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "person", 3 | "personIdentifier": "25871999336", 4 | "partyUuid": "00000000-0000-0000-0000-000000000001", 5 | "versionId": 1, 6 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 7 | } 8 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/SelfIdentifiedUserTests.MinimalSelfIdentifiedUser.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "self-identified-user", 3 | "partyUuid": "00000000-0000-0000-0000-000000000001", 4 | "versionId": 1, 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "xml.format.preserveSpace": [ 3 | "xsl:text", 4 | "xsl:comment", 5 | "xsl:processing-instruction", 6 | "literallayout", 7 | "programlisting", 8 | "screen", 9 | "synopsis", 10 | "pre", 11 | "xd:pre", 12 | "BaseArtifactsPath", 13 | "ArtifactsPath", 14 | "VersionFilePath" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.01-party/00-enums.sql: -------------------------------------------------------------------------------- 1 | -- Enum: register.party_type 2 | CREATE TYPE register.party_type AS ENUM( 3 | 'person', 4 | 'organization' 5 | ); 6 | 7 | -- Enum: register.party_source 8 | CREATE TYPE register.party_source AS ENUM( 9 | 'folkeregistered', 10 | 'enhetsregisteret' 11 | ); 12 | 13 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/OrganizationTests.MinimalOrganization.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "organization", 3 | "organizationIdentifier": "123456785", 4 | "partyUuid": "00000000-0000-0000-0000-000000000001", 5 | "versionId": 1, 6 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 7 | } 8 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.IntegrationTests/AssemblyFixtures.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.IntegrationTests.Fixtures; 2 | using Altinn.Register.TestUtils.Database; 3 | using Altinn.Register.TestUtils.Traits; 4 | 5 | [assembly: IntegrationTest] 6 | [assembly: AssemblyFixture(typeof(PostgresServerFixture))] 7 | [assembly: AssemblyFixture(typeof(WebApplicationFixture))] 8 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | v2 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/Traits/IntegrationTestAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Traits; 2 | 3 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] 4 | public sealed class IntegrationTestAttribute() 5 | : CategoryAttribute(KnownCategories.IntegrationTest) 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.04-external-roles/02-translated-text-domain.sql: -------------------------------------------------------------------------------- 1 | -- Domain: register.translated_text 2 | CREATE DOMAIN register.translated_text AS hstore CONSTRAINT translated_text_contains_en CHECK (value ? 'en') CONSTRAINT translated_text_contains_nb CHECK (value ? 'nb') CONSTRAINT translated_text_contains_nn CHECK (value ? 'nn'); 3 | 4 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/IFakeRequestBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Http; 2 | 3 | /// 4 | /// Combination of and . 5 | /// 6 | public interface IFakeRequestBuilder 7 | : IFilterFakeRequest 8 | , ISetFakeRequestHandler 9 | { 10 | } 11 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.17-party-display-name/00-person-short-name.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE register.person 2 | ADD COLUMN short_name text; 3 | 4 | UPDATE register.person 5 | SET short_name = party.name 6 | FROM register.party 7 | WHERE person.uuid = party.uuid; 8 | 9 | ALTER TABLE register.person 10 | ALTER COLUMN short_name SET NOT NULL; 11 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Utils/AtomicCounter.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Tests.Utils; 2 | 3 | internal sealed class AtomicCounter 4 | { 5 | private uint _value = 0; 6 | 7 | public void Increment() 8 | { 9 | Interlocked.Increment(ref _value); 10 | } 11 | 12 | public uint Value 13 | => Volatile.Read(ref _value); 14 | } 15 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.01-party/01-identifiers.sql: -------------------------------------------------------------------------------- 1 | -- Domain: register.person_identifier 2 | CREATE DOMAIN register.person_identifier AS text CONSTRAINT fmtchk CHECK (value ~ '^[0-9]{11}$'); 3 | 4 | -- Domain: register.organization_identifier 5 | CREATE DOMAIN register.organization_identifier AS text CONSTRAINT fmtchk CHECK (value ~ '^[0-9]{9}$'); 6 | 7 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.22-role-assignment-indexes/00-role-assignment-indexes.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX ix_from_party 2 | ON register.external_role_assignment (from_party); 3 | 4 | CREATE INDEX ix_to_party 5 | ON register.external_role_assignment (to_party); 6 | 7 | CREATE INDEX ix_from_party_source 8 | ON register.external_role_assignment (from_party, "source"); 9 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/PartyImport/A2/A2Change.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.PartyImport.A2; 2 | 3 | /// 4 | /// Represents a base class for changes in Altinn 2. 5 | /// 6 | public abstract record A2Change 7 | { 8 | /// 9 | /// Gets the id of this change. 10 | /// 11 | public required uint ChangeId { get; init; } 12 | } 13 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.42-guardianship-roles/00-identifier.sql: -------------------------------------------------------------------------------- 1 | ALTER DOMAIN register.identifier 2 | DROP CONSTRAINT identifier_valid; 3 | 4 | ALTER DOMAIN register.identifier 5 | ADD CONSTRAINT identifier_valid CHECK (value ~ '^[a-z][a-z0-9]+(?:-[a-z0-9]+)*$'); 6 | 7 | ALTER DOMAIN register.identifier 8 | ADD CONSTRAINT identifier_max_length CHECK (char_length(value) <= 64); 9 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Configuration/PlatformSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Configuration; 2 | 3 | /// 4 | /// Platform configuration settings 5 | /// 6 | public class PlatformSettings 7 | { 8 | /// 9 | /// Gets or sets the authorization endpoint 10 | /// 11 | public string ApiAuthorizationEndpoint { get; set; } = string.Empty; 12 | } 13 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/SelfIdentifiedUser.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Contracts; 4 | 5 | /// 6 | /// Represents a self-identified user party in Altinn Register. 7 | /// 8 | [PolymorphicFieldValueRecord] 9 | public sealed record SelfIdentifiedUser() 10 | : Party(PartyType.SelfIdentifiedUser) 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /.github/scripts/_yargs.mts: -------------------------------------------------------------------------------- 1 | import _yargs from "yargs"; 2 | import { hideBin } from "yargs/helpers"; 3 | 4 | const fixArgv = (argv?: string[]) => { 5 | argv ??= process.argv; 6 | argv = hideBin(process.argv); 7 | 8 | if (argv!.length > 0 && argv![0].trim() === "--") { 9 | argv = argv!.slice(1); 10 | } 11 | 12 | return argv; 13 | }; 14 | 15 | export const yargs = (argv?: string[]) => _yargs(fixArgv(argv)); 16 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.36-import-job-state/00-import-job-state-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.import_job_state 2 | CREATE TABLE register.import_job_state( 3 | job_id text NOT NULL, -- cannot reference import_job.id because import_job is handled in a different transaction 4 | state_type text NOT NULL, 5 | state_value jsonb NOT NULL, 6 | PRIMARY KEY (job_id) 7 | ) 8 | TABLESPACE pg_default; 9 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.00-schema/01-default-privileges.sql: -------------------------------------------------------------------------------- 1 | -- grant on tables 2 | ALTER DEFAULT PRIVILEGES FOR USER "${YUNIQL-USER}" IN SCHEMA register GRANT 3 | SELECT 4 | , INSERT, UPDATE, REFERENCES, DELETE, TRUNCATE, TRIGGER ON TABLES TO "${APP-USER}"; 5 | 6 | -- grant on sequences 7 | ALTER DEFAULT PRIVILEGES FOR USER "${YUNIQL-USER}" IN SCHEMA register GRANT ALL ON SEQUENCES TO "${APP-USER}"; 8 | 9 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Aspire.Npgsql/Altinn.Authorization.Aspire.Npgsql.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | $(NoWarn);CS1591 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit/AltinnMassTransitOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 2 | 3 | /// 4 | /// Options for altinn masstransit 5 | /// 6 | public class AltinnMassTransitOptions 7 | { 8 | /// 9 | /// Gets or sets the activity propagation type. 10 | /// 11 | public string ActivityPropagation { get; set; } = "Link"; 12 | } 13 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/PartyImport/A2/A2PartyChangePage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | 3 | namespace Altinn.Register.Core.PartyImport.A2; 4 | 5 | /// 6 | /// A page of s. 7 | /// 8 | public sealed class A2PartyChangePage(ImmutableArray parties, uint lastKnownChangeId) 9 | : A2ChangePage(parties, lastKnownChangeId) 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "System": "Warning", 7 | "Microsoft.Extensions.ServiceDiscovery": "Debug" 8 | }, 9 | "ApplicationInsights": { 10 | "LogLevel": { 11 | "Default": "None", 12 | "Altinn.Register": "Information" 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.13-seq-lock/01-party-table.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION register.update_version_id() 2 | RETURNS TRIGGER AS $BODY$ 3 | BEGIN 4 | NEW.version_id = register.tx_nextval('register.party_version_id_seq'); 5 | RETURN NEW; 6 | END 7 | $BODY$ 8 | LANGUAGE plpgsql; 9 | 10 | ALTER TABLE register.party 11 | ALTER COLUMN version_id SET DEFAULT register.tx_nextval('register.party_version_id_seq'); 12 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.03-party-source-values/00-party-source-values.sql: -------------------------------------------------------------------------------- 1 | -- Enum: register.party_source 2 | -- ccr - (Norwegian) Central Coordinating Register for Legal Entities - Enhetsregisteret 3 | ALTER TYPE register.party_source RENAME VALUE 'enhetsregisteret' TO 'ccr'; 4 | 5 | -- npr - (Norwegian) National Population Register - Folkeregisteret 6 | ALTER TYPE register.party_source RENAME VALUE 'folkeregisteret' TO 'npr'; 7 | 8 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.38-deleted-user/03-remove-usernames-from-deleted-parties.sql: -------------------------------------------------------------------------------- 1 | WITH deleted_parties_users_with_usernames AS ( 2 | SELECT p."uuid" 3 | FROM register.party p 4 | INNER JOIN register.user u USING ("uuid") 5 | WHERE p.is_deleted AND u.username IS NOT NULL 6 | ) 7 | UPDATE register.user u 8 | SET username = NULL 9 | FROM deleted_parties_users_with_usernames dpu 10 | WHERE u."uuid" = dpu."uuid"; 11 | -------------------------------------------------------------------------------- /.github/disabled-workflows/assign-issues-to-projects.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign to Project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to Team Platform project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@main 14 | with: 15 | project-url: https://github.com/orgs/Altinn/projects/75 16 | github-token: ${{ secrets.ASSIGN_PROJECT_TOKEN }} 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/PartyImport/A2/A2UserProfileChangePage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | 3 | namespace Altinn.Register.Core.PartyImport.A2; 4 | 5 | /// 6 | /// A page of s. 7 | /// 8 | public sealed class A2UserProfileChangePage(ImmutableArray profiles, uint lastKnownChangeId) 9 | : A2ChangePage(profiles, lastKnownChangeId) 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit/AltinnEndpointNameFormatter.cs: -------------------------------------------------------------------------------- 1 | using MassTransit; 2 | 3 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 4 | 5 | /// 6 | /// A for altinn services. 7 | /// 8 | internal sealed class AltinnEndpointNameFormatter(AltinnServiceDescriptor descriptor) 9 | : KebabCaseEndpointNameFormatter(prefix: descriptor.Name, includeNamespace: false) 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/ImportJobs/ImportJobProcessingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.ImportJobs; 2 | 3 | /// 4 | /// Import job processing status, consisting of the highest processed item. 5 | /// 6 | public readonly record struct ImportJobProcessingStatus 7 | { 8 | /// 9 | /// Gets the highest processed item. 10 | /// 11 | public readonly required ulong ProcessedMax { get; init; } 12 | } 13 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.19-si-user-enum/00-party-type-enum.sql: -------------------------------------------------------------------------------- 1 | -- Enum: register.party_type 2 | -- CREATE TYPE register.party_type AS ENUM( 3 | -- 'person', 4 | -- 'organization' 5 | -- ); 6 | 7 | ALTER TYPE register.party_type ADD VALUE 'self-identified-user'; 8 | 9 | -- Enum: register.party_type 10 | -- CREATE TYPE register.party_type AS ENUM( 11 | -- 'person', 12 | -- 'organization', 13 | -- 'self-identified-user' 14 | -- ); 15 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.04-external-roles/03-external-role-definition-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.external_role_definition 2 | CREATE TABLE register.external_role_definition( 3 | source register.party_source NOT NULL, 4 | identifier register.identifier NOT NULL, 5 | name register.translated_text NOT NULL, 6 | description register.translated_text NOT NULL, 7 | PRIMARY KEY (source, identifier) 8 | ) 9 | TABLESPACE pg_default; 10 | 11 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/ModelBinding/ISingleton.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.ModelBinding; 2 | 3 | /// 4 | /// A contract for creating singleton instances in a type-safe manner. . 5 | /// 6 | internal interface ISingleton 7 | where TSelf : ISingleton 8 | { 9 | /// 10 | /// Gets the singleton instance of the implementing type. 11 | /// 12 | public static abstract TSelf Instance { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Jobs/RecurringJobHostedSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Jobs; 2 | 3 | /// 4 | /// Settings for . 5 | /// 6 | public class RecurringJobHostedSettings 7 | { 8 | /// 9 | /// Gets or sets whether to disable the scheduler entirely. If the scheduler is disabled, only lifecycle jobs will run. 10 | /// 11 | public bool DisableScheduler { get; set; } = false; 12 | } 13 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.01-party/05-person-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.person 2 | CREATE TABLE register.person( 3 | uuid uuid PRIMARY KEY NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 4 | first_name text NOT NULL, 5 | middle_name text, 6 | last_name text NOT NULL, 7 | address register.address, 8 | mailing_address register.address, 9 | date_of_birth date, 10 | date_of_death date 11 | ) 12 | TABLESPACE pg_default; 13 | 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Jobs/IHasJobName.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Authorization.ServiceDefaults.Jobs; 2 | 3 | /// 4 | /// Interface used to explicitly set the job name on a job type. 5 | /// 6 | /// Self type. 7 | public interface IHasJobName 8 | where TSelf : IHasJobName 9 | { 10 | /// 11 | /// Gets the name of the job. 12 | /// 13 | public static abstract string JobName { get; } 14 | } 15 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/V1/PartyNamesLookup.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts.V1; 2 | 3 | /// 4 | /// Represents a list of lookup criteria when looking for a Party. 5 | /// 6 | public class PartyNamesLookup 7 | { 8 | /// 9 | /// Gets or sets the list of identifiers for the parties to look for. 10 | /// 11 | [JsonPropertyName("parties")] 12 | public IReadOnlyList? Parties { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Configuration/A2PartyImportSettings.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Altinn.Register.Configuration; 6 | 7 | /// 8 | /// Settings for A2 party import. 9 | /// 10 | public class A2PartyImportSettings 11 | { 12 | /// 13 | /// Gets or sets the (root) bridge api endpoint for A2. 14 | /// 15 | [Required] 16 | public Uri? BridgeApiEndpoint { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit.Abstractions/Altinn.Authorization.ServiceDefaults.MassTransit.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | enable 6 | Altinn.Authorization.ServiceDefaults.MassTransit 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/IFilterFakeRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Http; 2 | 3 | /// 4 | /// Interface used to add filters to a . 5 | /// 6 | public interface IFilterFakeRequest 7 | { 8 | /// 9 | /// Adds a filter to the . 10 | /// 11 | /// The filter. 12 | void AddFilter(IFakeRequestFilter filter); 13 | } 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/PartySource.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.Parties; 2 | 3 | /// 4 | /// Represents a party source. 5 | /// 6 | public enum PartySource 7 | { 8 | /// 9 | /// The Norwegian Central Coordinating Register for Legal Entities. 10 | /// 11 | CentralCoordinatingRegister, 12 | 13 | /// 14 | /// The Norwegian National Population Register. 15 | /// 16 | NationalPopulationRegister, 17 | } 18 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/V1/PartyNamesLookupResult.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts.V1; 2 | 3 | /// 4 | /// Represents a list of party names for each corresponding identifier 5 | /// 6 | public class PartyNamesLookupResult 7 | { 8 | /// 9 | /// Gets or sets the list of identifiers for the parties to look for. 10 | /// 11 | [JsonPropertyName("partyNames")] 12 | public IReadOnlyList? PartyNames { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/.editorconfig: -------------------------------------------------------------------------------- 1 | # Inherit from parent 2 | root = false 3 | 4 | [*.cs] 5 | 6 | # SA1600: Elements should be documented 7 | dotnet_diagnostic.SA1600.severity = none 8 | 9 | # CA1707: Identifiers should not contain underscores 10 | dotnet_diagnostic.CA1707.severity = none 11 | 12 | # FAA0002: Replace Xunit assertion with Fluent Assertions equivalent 13 | dotnet_diagnostic.FAA0002.severity = none 14 | 15 | # SA1312: Variable names should begin with lower-case letter 16 | dotnet_diagnostic.SA1312.severity = none 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Utils/AtomicBool.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Tests.Utils; 2 | 3 | internal sealed class AtomicBool(bool initialValue = false) 4 | { 5 | private const byte FALSE = 0; 6 | private const byte TRUE = 1; 7 | 8 | private byte _value = initialValue ? TRUE : FALSE; 9 | 10 | public void Set(bool value) 11 | { 12 | Interlocked.Exchange(ref _value, value ? TRUE : FALSE); 13 | } 14 | 15 | public bool Value 16 | => Volatile.Read(ref _value) == TRUE; 17 | } 18 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | networks: 4 | altinnplatform_network: 5 | external: false 6 | 7 | services: 8 | altinn_register: 9 | container_name: altinn-register 10 | image: altinnregister:latest 11 | restart: always 12 | networks: 13 | - altinnplatform_network 14 | environment: 15 | - ASPNETCORE_ENVIRONMENT=Development 16 | - ASPNETCORE_URLS=http://+:5020 17 | ports: 18 | - "5020:5020" 19 | build: 20 | context: . 21 | dockerfile: Dockerfile 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/UpsertUserRecordResult.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.Parties; 2 | 3 | /// 4 | /// The result of 5 | /// 6 | public sealed record UpsertUserRecordResult 7 | { 8 | /// 9 | /// Gets or sets a value indicating whether the party was updated. 10 | /// 11 | public required bool PartyUpdated { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.27-import-job-state/00-import-job-party-state-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.import_job_party_state 2 | CREATE TABLE register.import_job_party_state( 3 | job_id text NOT NULL, -- cannot reference import_job.id because import_job is handled in a different transaction 4 | party_uuid uuid NOT NULL REFERENCES register.party (uuid) ON DELETE CASCADE, 5 | state_type text NOT NULL, 6 | state_value jsonb NOT NULL, 7 | PRIMARY KEY (job_id, party_uuid) 8 | ) 9 | TABLESPACE pg_default; 10 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.01-party/04-party-source-ref-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.party_source_ref 2 | CREATE TABLE register.party_source_ref( 3 | party_uuid uuid NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 4 | source register.party_source NOT NULL, 5 | source_identifier text NOT NULL, 6 | source_created timestamp with time zone, 7 | source_updated timestamp with time zone, 8 | PRIMARY KEY (party_uuid, source, source_identifier) 9 | ) 10 | TABLESPACE pg_default; 11 | 12 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Testdata/50006237.json: -------------------------------------------------------------------------------- 1 | { 2 | "PartyTypeName": 3, 3 | "SSN": "", 4 | "OrgNumber": "", 5 | "Person": null, 6 | "Organization": null, 7 | "PartyId": 50006237, 8 | "PartyUUID": "4fe860c4-bc65-4d2f-a288-f825b460f26b", 9 | "UnitType": null, 10 | "LastChangedInAltinn": "2010-03-02T01:53:44.87+01:00", 11 | "LastChangedInExternalRegister": null, 12 | "Name": "TestSelfIdentifiedUser", 13 | "IsDeleted": false, 14 | "OnlyHierarchyElementWithNoAccess": false, 15 | "ChildParties": null 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "System": "Warning", 7 | "Npgsql.Command": "Warning", 8 | "Polly": "Warning", 9 | "Altinn.Authorization.ServiceDefaults.Npgsql.Yuniql": "Trace", 10 | "Altinn.Register": "Debug" 11 | }, 12 | "ApplicationInsights": { 13 | "LogLevel": { 14 | "Default": "None", 15 | "Altinn.Register": "None" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/IPartyPersistenceCleanupService.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.Parties; 2 | 3 | /// 4 | /// A service for cleaning up party persistence storage. 5 | /// 6 | public interface IPartyPersistenceCleanupService 7 | { 8 | /// 9 | /// Runs periodic cleanup of party storage. 10 | /// 11 | /// A . 12 | public Task RunPeriodicPartyCleanup(CancellationToken cancellationToken = default); 13 | } 14 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bootstrap-sha": "733e1608b17fe9eb9abd0d936ed8dcb0e1073590", 3 | "separate-pull-requests": true, 4 | "pull-request-title-pattern": "release${scope}:${component} ${version}", 5 | "release-type": "simple", 6 | "packages": { 7 | "src/pkgs/Altinn.Register.Contracts": { 8 | "component": "Altinn.Register.Contracts", 9 | "extra-files": [ 10 | { 11 | "type": "xml", 12 | "path": "Version.props", 13 | "xpath": "//Project/PropertyGroup/Version" 14 | } 15 | ] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.01-party/02-address.sql: -------------------------------------------------------------------------------- 1 | -- Composite: register._address (private - implementation detail) 2 | CREATE TYPE register._address AS ( 3 | municipal_number text, 4 | municipal_name text, 5 | street_name text, 6 | house_number text, 7 | house_letter text, 8 | apartment_number text, 9 | postal_code text, 10 | city text 11 | ); 12 | 13 | -- Domain: register.address 14 | -- No checks at this time, but create a domain so they can be added later 15 | CREATE DOMAIN register.address AS register._address; 16 | 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.01-party/06-organization-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.organization 2 | CREATE TABLE register.organization( 3 | uuid uuid PRIMARY KEY NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 4 | unit_status text, 5 | unit_type text, 6 | telephone_number text, 7 | mobile_number text, 8 | fax_number text, 9 | email_address text, 10 | internet_address text, 11 | mailing_address register.address, 12 | business_address register.address 13 | ) 14 | TABLESPACE pg_default; 15 | 16 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit/Migration/v0.00-base/00-schema.sql: -------------------------------------------------------------------------------- 1 | -- create schema 2 | CREATE SCHEMA IF NOT EXISTS "${SCHEMA}"; 3 | 4 | -- grant schema 5 | GRANT USAGE ON SCHEMA "${SCHEMA}" TO "${APP-USER}"; 6 | 7 | -- grant on tables 8 | ALTER DEFAULT PRIVILEGES FOR USER "${YUNIQL-USER}" IN SCHEMA "${SCHEMA}" GRANT 9 | SELECT 10 | , INSERT, UPDATE, REFERENCES, DELETE, TRUNCATE, TRIGGER ON TABLES TO "${APP-USER}"; 11 | 12 | -- grant on sequences 13 | ALTER DEFAULT PRIVILEGES FOR USER "${YUNIQL-USER}" IN SCHEMA "${SCHEMA}" GRANT ALL ON SEQUENCES TO "${APP-USER}"; 14 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/ModuleInitializer.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Altinn.Register.Contracts.Tests; 4 | 5 | internal class ModuleInitializer 6 | { 7 | private static int _initialized = 0; 8 | 9 | [ModuleInitializer] 10 | public static void Init() 11 | { 12 | if (Interlocked.Exchange(ref _initialized, 1) == 0) 13 | { 14 | VerifierSettings.UseSplitModeForUniqueDirectory(); 15 | UseProjectRelativeDirectory("Snapshots"); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/A2/ImportPartyUserIdJobState.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Register.Core.ImportJobs; 4 | 5 | namespace Altinn.Register.PartyImport.A2; 6 | 7 | /// 8 | /// State for the import job that imports user-ids from A2 for parties already imported into A3. 9 | /// 10 | internal class ImportPartyUserIdJobState 11 | : IImportJobState 12 | { 13 | /// 14 | static string IImportJobState.StateType => $"{nameof(ImportPartyUserIdJobState)}@0"; 15 | } 16 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/EnterpriseUser.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Contracts; 4 | 5 | /// 6 | /// Represents an enterprise user party in Altinn Register. 7 | /// 8 | [PolymorphicFieldValueRecord] 9 | public sealed record EnterpriseUser() 10 | : Party(PartyType.EnterpriseUser) 11 | { 12 | /// 13 | /// Gets the owner of the system user. 14 | /// 15 | [JsonPropertyName("owner")] 16 | public required FieldValue Owner { get; init; } 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Contracts.MassTransit/Parties/PartyUpdatedEvent.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ServiceDefaults.MassTransit; 2 | using MassTransit; 3 | 4 | namespace Altinn.Register.Contracts.Parties; 5 | 6 | /// 7 | /// An event that is published when a party is updated. 8 | /// 9 | [MessageUrn("event:altinn-register:party-updated")] 10 | public sealed record PartyUpdatedEvent 11 | : EventBase 12 | { 13 | /// 14 | /// Gets the party that was updated. 15 | /// 16 | public required PartyReference Party { get; init; } 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Models/OrgContactPointLookup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Altinn.Register.Models; 5 | 6 | /// 7 | /// A class describing the query model for contact points for organizations 8 | /// 9 | public class OrgContactPointLookup 10 | { 11 | /// 12 | /// Gets or sets the list of organization numbers to lookup contact points for 13 | /// 14 | [JsonPropertyName("organizationNumbers")] 15 | public List OrganizationNumbers { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/V1/PartyComponentOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts.V1; 2 | 3 | /// 4 | /// Specifies the components that should be included when retrieving party's information. 5 | /// 6 | [Flags] 7 | public enum PartyComponentOptions : uint 8 | { 9 | /// 10 | /// No additional components are included. 11 | /// 12 | None = 0, 13 | 14 | /// 15 | /// Includes the party's first name, middle name, and last name. 16 | /// 17 | NameComponents = 1 << 0, 18 | } 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit/IBusLifetime.cs: -------------------------------------------------------------------------------- 1 | using MassTransit; 2 | 3 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 4 | 5 | /// 6 | /// Represents the lifetime of a masstransit bus. 7 | /// 8 | public interface IBusLifetime 9 | { 10 | /// 11 | /// Waits for the bus to be ready. 12 | /// 13 | /// A . 14 | /// Information about the bus. 15 | Task WaitForBus(CancellationToken cancellationToken = default); 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/IAsyncResource.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils; 2 | 3 | /// 4 | /// Represents a resource that is created and destroyed asynchronously. 5 | /// 6 | /// The self type. 7 | public interface IAsyncResource 8 | : IAsyncDisposable 9 | where TSelf : IAsyncResource 10 | { 11 | /// 12 | /// Creates a new instance of the resource. 13 | /// 14 | /// The resource. 15 | public abstract static ValueTask New(); 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.29-new-partytypes/00-party-type.sql: -------------------------------------------------------------------------------- 1 | -- Enum: register.party_type 2 | -- CREATE TYPE register.party_type AS ENUM( 3 | -- 'person', 4 | -- 'organization', 5 | -- 'self-identified-user' 6 | -- ); 7 | 8 | ALTER TYPE register.party_type ADD VALUE 'system-user'; 9 | ALTER TYPE register.party_type ADD VALUE 'enterprise-user'; 10 | 11 | -- Enum: register.party_type 12 | -- CREATE TYPE register.party_type AS ENUM( 13 | -- 'person', 14 | -- 'organization', 15 | -- 'self-identified-user', 16 | -- 'system-user', 17 | -- 'enterprise-user' 18 | -- ); 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Clients/BridgeOrgContactPointLookup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Altinn.Register.Models; 5 | 6 | /// 7 | /// A class describing the query model for contact points for organizations 8 | /// 9 | public class BridgeOrgContactPointLookup 10 | { 11 | /// 12 | /// Gets or sets the list of organization numbers to lookup contact points for 13 | /// 14 | [JsonPropertyName("organisationNumbers")] 15 | public List OrganisationNumbers { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/Records/SystemUserRecordType.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Altinn.Register.Core.Parties.Records; 4 | 5 | /// 6 | /// Represents a system user record type. 7 | /// 8 | public enum SystemUserRecordType 9 | { 10 | /// 11 | /// A system user for own use. 12 | /// 13 | [JsonStringEnumMemberName("standard")] 14 | Standard, 15 | 16 | /// 17 | /// A system user for client relations. 18 | /// 19 | [JsonStringEnumMemberName("agent")] 20 | Agent, 21 | } 22 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/Records/EnterpriseUserRecord.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Core.Parties.Records; 4 | 5 | /// 6 | /// A database record for an enterprise user. 7 | /// 8 | [PolymorphicFieldValueRecord] 9 | public sealed record EnterpriseUserRecord 10 | : PartyRecord 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | public EnterpriseUserRecord() 16 | : base(PartyRecordType.EnterpriseUser) 17 | { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/ExternalRoleAssignmentTests.ExternalRoleAssignment_KnownSource.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "role": { 3 | "source": "ccr", 4 | "identifier": "daglig-leder", 5 | "urn": "urn:altinn:external-role:ccr:daglig-leder" 6 | }, 7 | "to": { 8 | "partyUuid": "00000000-0000-0000-0000-000000000002", 9 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000002" 10 | }, 11 | "from": { 12 | "partyUuid": "00000000-0000-0000-0000-000000000001", 13 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.10-role-code/00-identifier-domain.sql: -------------------------------------------------------------------------------- 1 | -- Domain: register.identifier 2 | -- CREATE DOMAIN register.identifier AS text CONSTRAINT identifier_valid CHECK (value ~ '^[a-z][a-z0-9_]{2,28}[a-z0-9]$'); 3 | ALTER DOMAIN register.identifier 4 | DROP CONSTRAINT identifier_valid; 5 | 6 | ALTER DOMAIN register.identifier 7 | ADD CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9_]{0,28}[a-z0-9])?$'); 8 | 9 | -- Domain: register.identifier 10 | -- CREATE DOMAIN register.identifier AS text CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9_]{0,28}[a-z0-9])?$'); 11 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.18-role-identifiers/00-identifier.sql: -------------------------------------------------------------------------------- 1 | -- Domain: register.identifier 2 | -- CREATE DOMAIN register.identifier AS text CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9-]{0,28}[a-z0-9])?$'); 3 | ALTER DOMAIN register.identifier 4 | DROP CONSTRAINT identifier_valid; 5 | 6 | ALTER DOMAIN register.identifier 7 | ADD CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9-]{0,48}[a-z0-9])?$'); 8 | 9 | -- Domain: register.identifier 10 | -- CREATE DOMAIN register.identifier AS text CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9-]{0,48}[a-z0-9])?$'); 11 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/IExternalRoleRef.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Contracts; 4 | 5 | /// 6 | /// Represents a reference to an external role. 7 | /// 8 | public interface IExternalRoleRef 9 | { 10 | /// 11 | /// Gets the source of the external role. 12 | /// 13 | public NonExhaustiveEnum Source { get; } 14 | 15 | /// 16 | /// Gets the source-unique identifier of the external role. 17 | /// 18 | public string Identifier { get; } 19 | } 20 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.16-identifier-update/00-identifier.sql: -------------------------------------------------------------------------------- 1 | -- Domain: register.identifier 2 | -- CREATE DOMAIN register.identifier AS text CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9_]{0,28}[a-z0-9])?$'); 3 | ALTER DOMAIN register.identifier 4 | DROP CONSTRAINT identifier_valid; 5 | 6 | ALTER DOMAIN register.identifier 7 | ADD CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9-]{0,28}[a-z0-9])?$'); 8 | 9 | -- Domain: register.identifier 10 | -- CREATE DOMAIN register.identifier AS text CONSTRAINT identifier_valid CHECK (value ~ '^[a-z]([a-z0-9-]{0,28}[a-z0-9])?$'); 11 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Leases/Altinn.Authorization.ServiceDefaults.Leases.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.IntegrationTests/TestServices/TestAccessTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography.X509Certificates; 2 | using Altinn.Common.AccessTokenClient.Services; 3 | 4 | namespace Altinn.Register.IntegrationTests.TestServices; 5 | 6 | internal class TestAccessTokenGenerator 7 | : IAccessTokenGenerator 8 | { 9 | public string GenerateAccessToken(string issuer, string app) 10 | => $"{issuer}:{app}"; 11 | 12 | public string GenerateAccessToken(string issuer, string app, X509Certificate2 certificate) 13 | { 14 | throw new NotImplementedException(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Contracts.MassTransit/Altinn.Register.Contracts.MassTransit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | Altinn.Register.Contracts 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/ExternalRoleAssignmentTests.ExternalRoleAssignment_UnknownSource.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "role": { 3 | "source": "new-source", 4 | "identifier": "daglig-leder", 5 | "urn": "urn:altinn:external-role:new-source:daglig-leder" 6 | }, 7 | "to": { 8 | "partyUuid": "00000000-0000-0000-0000-000000000002", 9 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000002" 10 | }, 11 | "from": { 12 | "partyUuid": "00000000-0000-0000-0000-000000000001", 13 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/PartyImport/A2/A2UserProfileType.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.PartyImport.A2; 2 | 3 | /// 4 | /// Represents the type of a user profile in Altinn 2. 5 | /// 6 | public enum A2UserProfileType 7 | { 8 | /// 9 | /// A person (SSN identified) user. 10 | /// 11 | Person, 12 | 13 | /// 14 | /// An enterprise (org number identified) user. 15 | /// 16 | EnterpriseUser, 17 | 18 | /// 19 | /// A self-identified (username identified) user. 20 | /// 21 | SelfIdentifiedUser, 22 | } 23 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/Records/SelfIdentifiedUserRecord.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Core.Parties.Records; 4 | 5 | /// 6 | /// A database record for a self-identified user. 7 | /// 8 | [PolymorphicFieldValueRecord] 9 | public sealed record SelfIdentifiedUserRecord 10 | : PartyRecord 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | public SelfIdentifiedUserRecord() 16 | : base(PartyRecordType.SelfIdentifiedUser) 17 | { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/PostgreSqlVacuumIndexCleanup.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Persistence; 2 | 3 | /// 4 | /// Vacuum index cleanup options. 5 | /// 6 | internal enum PostgreSqlVacuumIndexCleanup 7 | : byte 8 | { 9 | /// 10 | /// Vacuum index is disabled. 11 | /// 12 | Off = default, 13 | 14 | /// 15 | /// Vacuum index is enabled based on internal heuristics. This is the default. 16 | /// 17 | Auto = 1, 18 | 19 | /// 20 | /// Vacuum index is always enabled. 21 | /// 22 | On = 2, 23 | } 24 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.IntegrationTests/TestServices/TestPublicSigningKeyProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Security.Cryptography.X509Certificates; 3 | 4 | namespace Altinn.Register.IntegrationTests.TestServices; 5 | 6 | internal class TestPublicSigningKeyProvider 7 | : X509CertificateBasedSigningKeyProvider 8 | { 9 | private readonly ConcurrentDictionary _certificates = new(); 10 | 11 | protected override Task GetCertificate(string issuer, CancellationToken cancellationToken) 12 | { 13 | throw new NotImplementedException(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scripts", 3 | "version": "0.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "", 7 | "private": true, 8 | "devDependencies": { 9 | "@actions/core": "^1.11.1", 10 | "@octokit/action": "^8.0.0", 11 | "@types/node": "^22.19.0", 12 | "arktype": "^2.1.28", 13 | "chalk": "^5.3.0", 14 | "globby": "^14.0.2", 15 | "slugify": "^1.6.6", 16 | "tsx": "^4.20.3", 17 | "typescript": "^5.6.3", 18 | "ws": "^8.18.0", 19 | "yaml": "^2.7.0", 20 | "yargs": "^18.0.0", 21 | "zod": "^4.0.0", 22 | "zod-validation-error": "^4.0.0", 23 | "zx": "^8.6.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/IntegrationTests/Utils/TestAccessTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System.Security.Cryptography.X509Certificates; 4 | using Altinn.Common.AccessTokenClient.Services; 5 | 6 | namespace Altinn.Register.Tests.IntegrationTests.Utils; 7 | 8 | internal class TestAccessTokenGenerator 9 | : IAccessTokenGenerator 10 | { 11 | public string GenerateAccessToken(string issuer, string app) 12 | => $"{issuer}:{app}"; 13 | 14 | public string GenerateAccessToken(string issuer, string app, X509Certificate2 certificate) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Utils/IAsyncSideEffectEnumerable.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace Altinn.Register.Core.Utils; 6 | 7 | /// 8 | /// Represents an async enumerable that can be executed just for its side effects. 9 | /// 10 | /// The item type. 11 | public interface IAsyncSideEffectEnumerable 12 | : IAsyncEnumerable 13 | { 14 | /// Gets an awaiter used to await this . 15 | /// An awaiter instance. 16 | TaskAwaiter GetAwaiter(); 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Utils/EnumMembersDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Reflection; 3 | using Xunit.Sdk; 4 | 5 | namespace Altinn.Register.Persistence.Tests.Utils; 6 | 7 | public class EnumMembersDataAttribute 8 | : DataAttribute 9 | where TEnum : struct, Enum 10 | { 11 | private static readonly ImmutableArray _values = [..Enum.GetValues()]; 12 | 13 | public override IEnumerable GetData(MethodInfo testMethod) 14 | { 15 | foreach (var value in _values) 16 | { 17 | yield return [value]; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/Filters/HttpMethodFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Http.Filters; 2 | 3 | internal sealed class HttpMethodFilter 4 | : IFakeRequestFilter 5 | { 6 | public static IFakeRequestFilter Get(HttpMethod method) 7 | => new HttpMethodFilter(method); 8 | 9 | private readonly HttpMethod _method; 10 | 11 | private HttpMethodFilter(HttpMethod method) 12 | { 13 | _method = method; 14 | } 15 | 16 | public string Description => $"has method {_method}"; 17 | 18 | public bool Matches(FakeHttpRequestMessage request) 19 | => request.Method == _method; 20 | } 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/IFakeRequestFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Http; 2 | 3 | /// 4 | /// A request filter. 5 | /// 6 | public interface IFakeRequestFilter 7 | { 8 | /// 9 | /// A human readable description of the filter. Used in exception messages when a request does not match any filter. 10 | /// 11 | string Description { get; } 12 | 13 | /// 14 | /// Determines whether the request matches the filter. 15 | /// 16 | /// The request. 17 | bool Matches(FakeHttpRequestMessage request); 18 | } 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Altinn.Register.Persistence.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | 6 | false 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/UnitOfWork/UnitOfWorkStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.UnitOfWork; 2 | 3 | /// 4 | /// The status of a unit of work. 5 | /// 6 | public enum UnitOfWorkStatus 7 | { 8 | /// 9 | /// The unit of work is active. 10 | /// 11 | Active, 12 | 13 | /// 14 | /// The unit of work is committed. 15 | /// 16 | Committed, 17 | 18 | /// 19 | /// The unit of work is rolled back. 20 | /// 21 | RolledBack, 22 | 23 | /// 24 | /// The unit of work is disposed. 25 | /// 26 | Disposed, 27 | } 28 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/DbArgTypes/ArgUpsertExternalRoleAssignment.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Persistence.DbArgTypes; 2 | 3 | /// 4 | /// Argument to the database function register.upsert_external_role_assignments. 5 | /// 6 | internal readonly record struct ArgUpsertExternalRoleAssignment 7 | { 8 | /// 9 | /// Gets the party that the role is assigned to. 10 | /// 11 | public required readonly Guid ToParty { get; init; } 12 | 13 | /// 14 | /// Gets the identifier of the role that is assigned. 15 | /// 16 | public required readonly string Identifier { get; init; } 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.AppHost/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "https": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "applicationUrl": "https://localhost:17139;http://localhost:15189", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development", 11 | "DOTNET_ENVIRONMENT": "Development", 12 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21212", 13 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22181" 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/TestContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Diagnostics; 2 | 3 | namespace Altinn.Register.TestUtils; 4 | 5 | /// 6 | /// Extensions for . 7 | /// 8 | public static class TestContextExtensions 9 | { 10 | public static async ValueTask GetRequiredFixture(this ITestContext testContext) 11 | { 12 | var fixture = await testContext.GetFixture(); 13 | if (fixture is null) 14 | { 15 | ThrowHelper.ThrowInvalidOperationException($"Missing required fixture of type {typeof(T).Name}."); 16 | } 17 | 18 | return fixture; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Mocks/AuthorizationClientMock.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ProblemDetails; 2 | using Altinn.Register.Services.Interfaces; 3 | 4 | namespace Altinn.Register.Tests.Mocks; 5 | 6 | public class AuthorizationClientMock 7 | : IAuthorizationClient 8 | { 9 | public Task> ValidateSelectedParty(int userId, int partyId, CancellationToken cancellationToken) 10 | { 11 | cancellationToken.ThrowIfCancellationRequested(); 12 | 13 | Result isValid = true; 14 | 15 | if (userId == 2) 16 | { 17 | isValid = false; 18 | } 19 | 20 | return Task.FromResult(isValid); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.04-external-roles/04-external-role-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.external_role 2 | CREATE TABLE register.external_role( 3 | source register.party_source NOT NULL, 4 | identifier register.identifier NOT NULL, 5 | from_party uuid NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 6 | to_party uuid NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 7 | PRIMARY KEY (source, identifier, from_party, to_party), 8 | FOREIGN KEY (source, identifier) REFERENCES register.external_role_definition(source, identifier) ON DELETE CASCADE ON UPDATE CASCADE 9 | ) 10 | TABLESPACE pg_default; 11 | 12 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/ApiDescriptions/PartyFieldIncludesSchemaFilter.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.Core.Parties; 2 | using Altinn.Register.ModelBinding; 3 | 4 | namespace Altinn.Register.ApiDescriptions; 5 | 6 | /// 7 | /// Schema filter for . 8 | /// 9 | public sealed class PartyFieldIncludesSchemaFilter 10 | : FlagsEnumSchemaFilter 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | public PartyFieldIncludesSchemaFilter() 16 | : base(PartyFieldIncludesModelBinder.Model) 17 | { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/Records/MailingAddressRecord.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.Parties.Records; 2 | 3 | /// 4 | /// Represents a mailing address. 5 | /// 6 | public record MailingAddressRecord 7 | { 8 | /// 9 | /// Gets the address part of the mailing address. 10 | /// 11 | public string? Address { get; init; } 12 | 13 | /// 14 | /// Gets the postal code of the mailing address. 15 | /// 16 | public string? PostalCode { get; init; } 17 | 18 | /// 19 | /// Gets the city of the mailing address. 20 | /// 21 | public string? City { get; init; } 22 | } 23 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/SystemUser/ImportSystemUserConsumer.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using CommunityToolkit.Diagnostics; 4 | using MassTransit; 5 | 6 | namespace Altinn.Register.PartyImport.SystemUser; 7 | 8 | /// 9 | /// Consumer for the . 10 | /// 11 | public sealed class ImportSystemUserConsumer 12 | : IConsumer 13 | { 14 | /// 15 | public Task Consume(ConsumeContext context) 16 | { 17 | ThrowHelper.ThrowInvalidOperationException("Not implemented yet for failing system users."); 18 | return Task.CompletedTask; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/SelfIdentifiedUserTests.MaximalSelfIdentifiedUser.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "self-identified-user", 3 | "partyUuid": "00000000-0000-0000-0000-000000000001", 4 | "versionId": 1, 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001", 6 | "partyId": 12345678, 7 | "displayName": "Display Name", 8 | "createdAt": "2020-01-02T03:04:05+00:00", 9 | "modifiedAt": "2022-05-06T07:08:09+00:00", 10 | "isDeleted": false, 11 | "deletedAt": null, 12 | "user": { 13 | "userId": 50, 14 | "username": "username", 15 | "userIds": [ 16 | 50, 17 | 30, 18 | 1 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/UpsertPartyCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.MassTransit; 4 | using Altinn.Register.Core.Parties.Records; 5 | 6 | namespace Altinn.Register.PartyImport; 7 | 8 | /// 9 | /// A command for upserting a party. 10 | /// 11 | public sealed record UpsertPartyCommand 12 | : CommandBase 13 | { 14 | /// 15 | /// Gets the party to import. 16 | /// 17 | public required PartyRecord Party { get; init; } 18 | 19 | /// 20 | /// Gets the tracking information for the import. 21 | /// 22 | public UpsertPartyTracking Tracking { get; init; } 23 | } 24 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Altinn.Register.Contracts.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/Tracing/ActivityDisplayFilterCollection.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Tracing; 2 | 3 | internal sealed class ActivityDisplayFilterCollection 4 | { 5 | private readonly List _filters = new(); 6 | 7 | public void Add(SpanDisplayFilter filter) => _filters.Add(filter); 8 | 9 | internal bool ShouldDisplay(SpanTree.SpanNode c) 10 | { 11 | foreach (var filter in _filters) 12 | { 13 | if (!filter(c)) 14 | { 15 | return false; 16 | } 17 | } 18 | 19 | return true; 20 | } 21 | } 22 | 23 | internal delegate bool SpanDisplayFilter(SpanTree.SpanNode node); 24 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Utils/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | namespace Altinn.Register.Core.Utils; 4 | 5 | /// 6 | /// Extension methods for 7 | /// 8 | public static class EnumerableExtensions 9 | { 10 | /// 11 | /// Returns an empty enumerable if the source is null. 12 | /// 13 | /// The item type. 14 | /// The source enumerable. 15 | /// , or an empty enumerable. 16 | public static IEnumerable OrEmpty(this IEnumerable? source) 17 | { 18 | return source ?? []; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.09-import-job/00-import-job-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.import_job 2 | CREATE TABLE register.import_job( 3 | id text PRIMARY KEY NOT NULL, 4 | source_max bigint NOT NULL, 5 | enqueued_max bigint NOT NULL, 6 | processed_max bigint NOT NULL, 7 | CONSTRAINT enqueued_max_less_than_or_equal_to_source_max CHECK (enqueued_max <= source_max), 8 | CONSTRAINT processed_max_less_than_or_equal_to_enqueued_max CHECK (processed_max <= enqueued_max), 9 | CONSTRAINT source_max_positive CHECK (source_max >= 0), 10 | CONSTRAINT enqueued_max_positive CHECK (enqueued_max >= 0), 11 | CONSTRAINT processed_max_positive CHECK (processed_max >= 0) 12 | ) 13 | TABLESPACE pg_default; 14 | 15 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.IntegrationTests/Altinn.Register.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | true 6 | v3 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Configuration/GeneralSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Configuration; 2 | 3 | /// 4 | /// General configuration settings 5 | /// 6 | public class GeneralSettings 7 | { 8 | /// 9 | /// Gets or sets the bridge api endpoint 10 | /// 11 | public string BridgeApiEndpoint { get; set; } = string.Empty; 12 | 13 | /// 14 | /// Open Id Connect Well known endpoint 15 | /// 16 | public string OpenIdWellKnownEndpoint { get; set; } = string.Empty; 17 | 18 | /// 19 | /// Name of the cookie for where JWT is stored 20 | /// 21 | public string JwtCookieName { get; set; } = string.Empty; 22 | } 23 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/PartyListFilters.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Altinn.Register.Persistence; 4 | 5 | /// 6 | /// Represents the filters that can be applied when querying a list of parties. 7 | /// 8 | [Flags] 9 | [JsonConverter(typeof(JsonStringEnumConverter))] 10 | public enum PartyListFilters 11 | : byte 12 | { 13 | /// 14 | /// Do not apply any additional filters to the party list. 15 | /// 16 | [JsonStringEnumMemberName("none")] 17 | None = 0, 18 | 19 | /// 20 | /// Filter based on one or more party types. 21 | /// 22 | [JsonStringEnumMemberName("type")] 23 | PartyType = 1 << 0, 24 | } 25 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/SystemUserTests.MaximalSystemUser.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "system-user", 3 | "owner": { 4 | "partyUuid": "00000000-0000-0000-0000-000000000002", 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000002" 6 | }, 7 | "systemUserType": "first-party-system-user", 8 | "partyUuid": "00000000-0000-0000-0000-000000000001", 9 | "versionId": 1, 10 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001", 11 | "partyId": 12345678, 12 | "displayName": "Display Name", 13 | "createdAt": "2020-01-02T03:04:05+00:00", 14 | "modifiedAt": "2022-05-06T07:08:09+00:00", 15 | "isDeleted": false, 16 | "deletedAt": null, 17 | "user": null 18 | } 19 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/SystemUserTests.MaximalSystemUser_Agent.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "system-user", 3 | "owner": { 4 | "partyUuid": "00000000-0000-0000-0000-000000000002", 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000002" 6 | }, 7 | "systemUserType": "client-party-system-user", 8 | "partyUuid": "00000000-0000-0000-0000-000000000001", 9 | "versionId": 1, 10 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001", 11 | "partyId": 12345678, 12 | "displayName": "Display Name", 13 | "createdAt": "2020-01-02T03:04:05+00:00", 14 | "modifiedAt": "2022-05-06T07:08:09+00:00", 15 | "isDeleted": false, 16 | "deletedAt": null, 17 | "user": null 18 | } 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Contracts.MassTransit/Parties/PartyReference.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Altinn.Register.Contracts.Parties; 4 | 5 | /// 6 | /// Represents a reference to a party. 7 | /// 8 | public sealed record PartyReference 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The party UUID. 14 | [SetsRequiredMembers] 15 | public PartyReference(Guid partyUuid) 16 | { 17 | PartyUuid = partyUuid; 18 | } 19 | 20 | /// 21 | /// Gets the UUID of the party. 22 | /// 23 | public required Guid PartyUuid { get; init; } 24 | } 25 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Services/Interfaces/IOrgContactPoint.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.Models; 2 | 3 | namespace Altinn.Register.Services.Interfaces; 4 | 5 | /// 6 | /// Class describing the methods required for organization number contact point service 7 | /// 8 | public interface IOrgContactPoint 9 | { 10 | /// 11 | /// Method for retrieving contact points for an org 12 | /// 13 | /// Organization lookup object 14 | /// The cancellation token 15 | /// The orgs contact points 16 | Task GetContactPoints(OrgContactPointLookup lookup, CancellationToken cancellationToken = default); 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/Records/SystemUserRecord.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Core.Parties.Records; 4 | 5 | /// 6 | /// A database record for a system user. 7 | /// 8 | [PolymorphicFieldValueRecord] 9 | public sealed record SystemUserRecord 10 | : PartyRecord 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | public SystemUserRecord() 16 | : base(PartyRecordType.SystemUser) 17 | { 18 | } 19 | 20 | /// 21 | /// Gets the type of the system user. 22 | /// 23 | public required FieldValue SystemUserType { get; init; } 24 | } 25 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/PartyImport/A2/A2PartyChange.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Altinn.Register.Core.PartyImport.A2; 4 | 5 | /// 6 | /// Represents a change-event for a party in Altinn 2. 7 | /// 8 | public sealed record A2PartyChange 9 | : A2Change 10 | { 11 | /// 12 | /// Gets the party id that changed. 13 | /// 14 | public required int PartyId { get; init; } 15 | 16 | /// 17 | /// Gets the party uuid that changed. 18 | /// 19 | public required Guid PartyUuid { get; init; } 20 | 21 | /// 22 | /// Gets the time of the change. 23 | /// 24 | public required DateTimeOffset ChangeTime { get; init; } 25 | } 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Utils/AsyncEnumerableTestExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Altinn.Register.Tests.Utils; 4 | 5 | internal static class AsyncEnumerableTestExtensions 6 | { 7 | public static async IAsyncEnumerable Yielding(this IAsyncEnumerable source, [EnumeratorCancellation] CancellationToken cancellationToken = default) 8 | { 9 | await using var enumerator = source.GetAsyncEnumerator(cancellationToken); 10 | await Task.Yield(); 11 | 12 | while (await enumerator.MoveNextAsync()) 13 | { 14 | yield return enumerator.Current; 15 | await Task.Yield(); 16 | } 17 | 18 | // before the dispose 19 | await Task.Yield(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.05-party-source-ref-index/00-party-source-ref-index.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.party_source_ref 2 | -- CREATE TABLE register.party_source_ref( 3 | -- party_uuid uuid NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 4 | -- source register.party_source NOT NULL, 5 | -- source_identifier text NOT NULL, 6 | -- source_created timestamp with time zone, 7 | -- source_updated timestamp with time zone, 8 | -- PRIMARY KEY (party_uuid, source, source_identifier) 9 | -- ) 10 | -- TABLESPACE pg_default; 11 | CREATE INDEX party_source_ref_party_uuid_idx ON register.party_source_ref(party_uuid); 12 | 13 | CREATE INDEX party_source_ref_source_id_idx ON register.party_source_ref(source, source_identifier); 14 | 15 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils/MassTransit/AsyncElementListExtensions.cs: -------------------------------------------------------------------------------- 1 | using MassTransit.Testing; 2 | 3 | namespace Altinn.Register.TestUtils.MassTransit; 4 | 5 | /// 6 | /// Extension methods for . 7 | /// 8 | public static class AsyncElementListExtensions 9 | { 10 | public static async IAsyncEnumerable SelectExisting( 11 | this IAsyncElementList list, 12 | FilterDelegate filter) 13 | where T : class, IAsyncListElement 14 | { 15 | using var cts = new CancellationTokenSource(); 16 | cts.Cancel(); 17 | 18 | await foreach (var item in list.SelectAsync(filter, cts.Token)) 19 | { 20 | yield return item; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Mocks/PublicSigningKeyProviderMock.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography.X509Certificates; 2 | using Altinn.Common.AccessToken.Services; 3 | using Microsoft.IdentityModel.Tokens; 4 | 5 | namespace Altinn.Register.Tests.Mocks; 6 | 7 | public class PublicSigningKeyProviderMock : IPublicSigningKeyProvider 8 | { 9 | public Task> GetSigningKeys(string issuer) 10 | { 11 | List signingKeys = new List(); 12 | 13 | X509Certificate2 cert = X509CertificateLoader.LoadCertificateFromFile($"{issuer}-org.pem"); 14 | SecurityKey key = new X509SecurityKey(cert); 15 | 16 | signingKeys.Add(key); 17 | 18 | return Task.FromResult(signingKeys.AsEnumerable()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/MailingAddress.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts; 2 | 3 | /// 4 | /// Represents a mailing address. 5 | /// 6 | public record MailingAddress 7 | { 8 | /// 9 | /// Gets the address part of the mailing address. 10 | /// 11 | [JsonPropertyName("address")] 12 | public string? Address { get; init; } 13 | 14 | /// 15 | /// Gets the postal code of the mailing address. 16 | /// 17 | [JsonPropertyName("postalCode")] 18 | public string? PostalCode { get; init; } 19 | 20 | /// 21 | /// Gets the city of the mailing address. 22 | /// 23 | [JsonPropertyName("city")] 24 | public string? City { get; init; } 25 | } 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.11-lease-done/00-lease-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.lease 2 | -- CREATE TABLE register.lease( 3 | -- id text PRIMARY KEY NOT NULL, 4 | -- token uuid NOT NULL, 5 | -- expires timestamp with time zone NOT NULL 6 | -- ) 7 | -- TABLESPACE pg_default; 8 | 9 | ALTER TABLE register.lease 10 | ADD COLUMN acquired timestamp with time zone; 11 | 12 | ALTER TABLE register.lease 13 | ADD COLUMN released timestamp with time zone; 14 | 15 | -- Table: register.lease 16 | -- CREATE TABLE register.lease( 17 | -- id text PRIMARY KEY NOT NULL, 18 | -- token uuid NOT NULL, 19 | -- expires timestamp with time zone NOT NULL, 20 | -- acquired timestamp with time zone, 21 | -- released timestamp with time zone 22 | -- ) 23 | -- TABLESPACE pg_default; 24 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/V1/PersonNameComponents.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts.V1; 2 | 3 | /// 4 | /// Represents the components of a person's name. 5 | /// 6 | public record PersonNameComponents 7 | { 8 | /// 9 | /// Gets or sets the first name. 10 | /// 11 | [JsonPropertyName("firstName")] 12 | public string? FirstName { get; init; } 13 | 14 | /// 15 | /// Gets or sets the middle name. 16 | /// 17 | [JsonPropertyName("middleName")] 18 | public string? MiddleName { get; init; } 19 | 20 | /// 21 | /// Gets or sets the sure name. 22 | /// 23 | [JsonPropertyName("lastName")] 24 | public string? LastName { get; init; } 25 | } 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/A2/ImportA2PartyCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.MassTransit; 4 | 5 | namespace Altinn.Register.PartyImport.A2; 6 | 7 | /// 8 | /// A command for importing a party from A2. 9 | /// 10 | public sealed record ImportA2PartyCommand 11 | : CommandBase 12 | { 13 | /// 14 | /// Gets the party UUID. 15 | /// 16 | public required Guid PartyUuid { get; init; } 17 | 18 | /// 19 | /// Gets the change ID. 20 | /// 21 | public required uint ChangeId { get; init; } 22 | 23 | /// 24 | /// Gets when the change was registered. 25 | /// 26 | public required DateTimeOffset ChangedTime { get; init; } 27 | } 28 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/SystemUser.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Contracts; 4 | 5 | /// 6 | /// Represents a system user party in Altinn Register. 7 | /// 8 | [PolymorphicFieldValueRecord] 9 | public sealed record SystemUser() 10 | : Party(PartyType.SystemUser) 11 | { 12 | /// 13 | /// Gets the owner of the system user. 14 | /// 15 | [JsonPropertyName("owner")] 16 | public required FieldValue Owner { get; init; } 17 | 18 | /// 19 | /// Gets the type of the system user. 20 | /// 21 | [JsonPropertyName("systemUserType")] 22 | public required FieldValue> SystemUserType { get; init; } 23 | } 24 | -------------------------------------------------------------------------------- /.github/scripts/publish-to-nuget.mts: -------------------------------------------------------------------------------- 1 | import { Chalk } from "chalk"; 2 | import { globby } from "globby"; 3 | import { $, retry } from "zx"; 4 | import path from "node:path"; 5 | 6 | const c = new Chalk({ level: 3 }); 7 | 8 | const apiKey = process.env.NUGET_APIKEY; 9 | const filesGlob = process.env.FILES_GLOB; 10 | 11 | if (!apiKey || !filesGlob) { 12 | console.error("Missing required environment variables"); 13 | process.exit(1); 14 | } 15 | 16 | for (const file of await globby(filesGlob)) { 17 | const name = path.basename(file); 18 | const fullPath = path.resolve(file); 19 | 20 | console.log(`Publishing ${c.yellow(name)}`); 21 | await retry( 22 | 5, 23 | () => 24 | $`dotnet nuget push --skip-duplicate "${fullPath}" --api-key "${apiKey}" --source "https://api.nuget.org/v3/index.json"` 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Conventions/IControllerModelCondition.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Microsoft.AspNetCore.Mvc.ApplicationModels; 4 | 5 | namespace Altinn.Register.Conventions; 6 | 7 | /// 8 | /// A condition that determines whether a controller should be disabled. 9 | /// 10 | public interface IControllerModelCondition 11 | { 12 | /// 13 | /// Determines whether the controller should be disabled. 14 | /// 15 | /// The in question. 16 | /// The . 17 | /// if the controller should be disabled. 18 | bool ShouldDisable(ControllerModel controller, IServiceProvider services); 19 | } 20 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/UnitTests/StringExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.Core; 2 | 3 | using Xunit; 4 | 5 | namespace Altinn.Register.Tests.UnitTests 6 | { 7 | public class StringExtensionsTests 8 | { 9 | [Theory] 10 | [InlineData("fràe", "frae")] 11 | [InlineData("frãe", "frae")] 12 | [InlineData("REèb", "reeb")] 13 | [InlineData("ôröe", "Oroe")] 14 | public void IsSimilarTo_TestPositive(string text1, string text2) 15 | { 16 | Assert.True(text1.IsSimilarTo(text2)); 17 | } 18 | 19 | [Theory] 20 | [InlineData("Åjue", "Ajue")] 21 | public void IsSimilarTo_TestNegative(string text1, string text2) 22 | { 23 | Assert.False(text1.IsSimilarTo(text2)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Clients/Interfaces/IPersonClient.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Register.Contracts.V1; 4 | 5 | namespace Altinn.Register.Clients.Interfaces; 6 | 7 | /// 8 | /// Interface handling methods for operations related to persons 9 | /// 10 | public interface IPersonClient 11 | { 12 | /// 13 | /// Method that fetches a person based on a national identity number of the person. 14 | /// 15 | /// The national identity number of the person to retrieve. 16 | /// The cancellation token. 17 | /// The identified person. 18 | Task GetPerson(string nationalIdentityNumber, CancellationToken cancellationToken = default); 19 | } 20 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/V1/PartyType.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts.V1; 2 | 3 | /// 4 | /// Enum containing values for the different types of parties 5 | /// 6 | public enum PartyType 7 | { 8 | /// 9 | /// Party Type is Person 10 | /// 11 | Person = 1, 12 | 13 | /// 14 | /// Party Type is Organization 15 | /// 16 | Organisation = 2, 17 | 18 | /// 19 | /// Party Type is Self Identified user 20 | /// 21 | SelfIdentified = 3, 22 | 23 | /// 24 | /// Party Type is sub unit 25 | /// 26 | SubUnit = 4, 27 | 28 | /// 29 | /// Party Type is bankruptcy estate 30 | /// 31 | BankruptcyEstate = 5 32 | } 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit.Abstractions/EventBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using MassTransit; 3 | 4 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 5 | 6 | /// 7 | /// Base record for a mass transit event. 8 | /// 9 | [ExcludeFromTopology] 10 | [ExcludeFromImplementedTypes] 11 | public abstract record EventBase 12 | : CorrelatedBy 13 | { 14 | private readonly Guid _id = Guid.CreateVersion7(); 15 | 16 | /// 17 | /// Gets the event ID. 18 | /// 19 | [JsonInclude] 20 | [JsonPropertyName("eventId")] 21 | public Guid EventId 22 | { 23 | get => _id; 24 | private init => _id = value; 25 | } 26 | 27 | /// 28 | Guid CorrelatedBy.CorrelationId => EventId; 29 | } 30 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Mapping/PartyMapper.User.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System.Diagnostics.CodeAnalysis; 4 | using Altinn.Register.Contracts; 5 | using Altinn.Register.Core.Parties.Records; 6 | 7 | namespace Altinn.Register.Mapping; 8 | 9 | /// 10 | /// Mappers for converting from 11 | /// to . 12 | /// 13 | internal static partial class PartyMapper 14 | { 15 | [return: NotNullIfNotNull(nameof(source))] 16 | private static PartyUser? MapPartyUser(PartyUserRecord? source) 17 | { 18 | if (source is null) 19 | { 20 | return null; 21 | } 22 | 23 | return new PartyUser( 24 | userId: source.UserId, 25 | username: source.Username, 26 | userIds: source.UserIds); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/container-scan.yml: -------------------------------------------------------------------------------- 1 | name: Register Scan 2 | 3 | on: 4 | schedule: 5 | - cron: '0 8 * * 1,4' 6 | push: 7 | branches: [ main ] 8 | paths: 9 | - 'src/**' 10 | - 'Dockerfile' 11 | pull_request: 12 | branches: [ main ] 13 | types: [opened, synchronize, reopened] 14 | paths: 15 | - 'src/**' 16 | - 'Dockerfile' 17 | jobs: 18 | scan: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 22 | - name: Build the Docker image 23 | run: docker build . --tag altinn-register:${{github.sha}} 24 | 25 | - uses: Azure/container-scan@f9af925b897d8af5f7e0026b8bca9346261abc93 # v0.1 26 | with: 27 | image-name: altinn-register:${{ github.sha }} 28 | env: 29 | DOCKLE_HOST: "unix:///var/run/docker.sock" 30 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | 13.0 7 | 8 | true 9 | $(MSBuildThisFileDirectory)artifacts\ 10 | Pack 11 | *nupkg 12 | MIT 13 | true 14 | false 15 | Altinn Platform Contributors 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit.Abstractions/CommandBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using MassTransit; 3 | 4 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 5 | 6 | /// 7 | /// Base record for a mass transit command. 8 | /// 9 | [ExcludeFromTopology] 10 | [ExcludeFromImplementedTypes] 11 | public abstract record CommandBase 12 | : CorrelatedBy 13 | { 14 | private readonly Guid _id = Guid.CreateVersion7(); 15 | 16 | /// 17 | /// Gets the command ID. 18 | /// 19 | [JsonInclude] 20 | [JsonPropertyName("commandId")] 21 | public Guid CommandId 22 | { 23 | get => _id; 24 | private init => _id = value; 25 | } 26 | 27 | /// 28 | Guid CorrelatedBy.CorrelationId => CommandId; 29 | } 30 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/EnterpriseUserTests.MaximalEnterpriseUser.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "enterprise-user", 3 | "owner": { 4 | "partyUuid": "00000000-0000-0000-0000-000000000002", 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000002" 6 | }, 7 | "partyUuid": "00000000-0000-0000-0000-000000000001", 8 | "versionId": 1, 9 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001", 10 | "partyId": 12345678, 11 | "displayName": "Display Name", 12 | "createdAt": "2020-01-02T03:04:05+00:00", 13 | "modifiedAt": "2022-05-06T07:08:09+00:00", 14 | "isDeleted": false, 15 | "deletedAt": null, 16 | "user": { 17 | "userId": 50, 18 | "username": "username", 19 | "userIds": [ 20 | 50, 21 | 30, 22 | 1 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/ExternalRoleAssignment.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts; 2 | 3 | /// 4 | /// Represents the assignment of an external role to a party. 5 | /// 6 | public sealed record ExternalRoleAssignment 7 | { 8 | /// 9 | /// Gets the role being assigned. 10 | /// 11 | [JsonPropertyName("role")] 12 | public required ExternalRoleRef Role { get; init; } 13 | 14 | /// 15 | /// Gets the party the role is assigned to. 16 | /// 17 | [JsonPropertyName("to")] 18 | public required PartyRef ToParty { get; init; } 19 | 20 | /// 21 | /// Gets the party the role is assigned from. 22 | /// 23 | [JsonPropertyName("from")] 24 | public required PartyRef FromParty { get; init; } 25 | } 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/UnitOfWork/ISavePoint.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Persistence.UnitOfWork; 2 | 3 | /// 4 | /// Represents a save point in a transaction. 5 | /// 6 | internal interface ISavePoint 7 | : IAsyncDisposable 8 | { 9 | /// 10 | /// Rolls the transaction back to before the save point was created. 11 | /// 12 | /// A . 13 | Task RollbackAsync(CancellationToken cancellationToken = default); 14 | 15 | /// 16 | /// Releases the save point. This effectively commits it to the transaction. 17 | /// 18 | /// A . 19 | Task ReleaseAsync(CancellationToken cancellationToken = default); 20 | } 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Clients/Interfaces/IAuthorizationClient.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ProblemDetails; 2 | 3 | namespace Altinn.Register.Services.Interfaces; 4 | 5 | /// 6 | /// Interface for authorization functionality. 7 | /// 8 | public interface IAuthorizationClient 9 | { 10 | /// 11 | /// Verifies that the selected party is contained in the user's party list. 12 | /// 13 | /// The user id. 14 | /// The party id. 15 | /// The cancellation token. 16 | /// Boolean indicating whether or not the user can represent the selected party. 17 | Task> ValidateSelectedParty(int userId, int partyId, CancellationToken cancellationToken = default); 18 | } 19 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Jobs/JobRegistrationTests.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.Jobs; 4 | 5 | namespace Altinn.Register.Tests.Jobs; 6 | 7 | public class JobRegistrationTests 8 | { 9 | [Theory] 10 | [InlineData(typeof(DefaultJobName), nameof(DefaultJobName))] 11 | [InlineData(typeof(CustomJobName), "i-am-custom-job")] 12 | public void GetJobNameForType_ReturnsExpectedName(Type type, string expectedName) 13 | { 14 | var actualName = JobRegistration.GetJobNameForType(type); 15 | 16 | actualName.Should().Be(expectedName); 17 | } 18 | 19 | private class DefaultJobName 20 | { 21 | } 22 | 23 | private class CustomJobName 24 | : IHasJobName 25 | { 26 | public static string JobName => "i-am-custom-job"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.AppHost/Altinn.Register.AppHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exe 6 | true 7 | 3f04644d-2f40-42f9-b0d7-1e5696018795 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/SystemUser/ImportSystemUserCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.MassTransit; 4 | using MassTransit; 5 | 6 | namespace Altinn.Register.PartyImport.SystemUser; 7 | 8 | /// 9 | /// A command sent for each system user that could not be imported. 10 | /// 11 | /// 12 | /// This exists to get retries and error handling. 13 | /// 14 | public sealed record ImportSystemUserCommand 15 | : CommandBase 16 | { 17 | /// 18 | /// Gets the system user ID that could not be imported. 19 | /// 20 | public required Guid SystemUserId { get; init; } 21 | 22 | /// 23 | /// Gets the tracking information for the import. 24 | /// 25 | public required UpsertPartyTracking Tracking { get; init; } 26 | } 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/TestBase.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.TestUtils.Tracing; 2 | 3 | namespace Altinn.Register.TestUtils; 4 | 5 | /// 6 | /// Base class for tests. 7 | /// 8 | public abstract class TestBase 9 | : IAsyncLifetime 10 | { 11 | private readonly ActivityCollector _collector; 12 | 13 | protected TestBase() 14 | { 15 | _collector = new ActivityCollector(); 16 | } 17 | 18 | protected virtual ValueTask InitializeAsync() 19 | => ValueTask.CompletedTask; 20 | 21 | protected virtual ValueTask DisposeAsync() 22 | { 23 | _collector.Dispose(); 24 | 25 | return ValueTask.CompletedTask; 26 | } 27 | 28 | ValueTask IAsyncDisposable.DisposeAsync() 29 | => DisposeAsync(); 30 | 31 | ValueTask IAsyncLifetime.InitializeAsync() 32 | => InitializeAsync(); 33 | } 34 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/UpsertPartyUserCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.MassTransit; 4 | using Altinn.Register.Core.Parties.Records; 5 | 6 | namespace Altinn.Register.PartyImport; 7 | 8 | /// 9 | /// A command for upserting the user-info for a party. 10 | /// 11 | public sealed record UpsertPartyUserCommand 12 | : CommandBase 13 | { 14 | /// 15 | /// Gets the party UUID. 16 | /// 17 | public required Guid PartyUuid { get; init; } 18 | 19 | /// 20 | /// Gets the user record to upsert. 21 | /// 22 | public required PartyUserRecord User { get; init; } 23 | 24 | /// 25 | /// Gets the tracking information for the import. 26 | /// 27 | public required UpsertPartyTracking Tracking { get; init; } 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Jobs/IJob.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Authorization.ServiceDefaults.Jobs; 2 | 3 | /// 4 | /// A job that can be run on a schedule or on host lifecycle events. 5 | /// 6 | public interface IJob 7 | { 8 | /// 9 | /// Checks if the job should run at this time. 10 | /// 11 | /// A . 12 | /// , if the job should be allowed to run at this time, otherwise . 13 | public ValueTask ShouldRun(CancellationToken cancellationToken = default); 14 | 15 | /// 16 | /// Runs the job. 17 | /// 18 | /// A . 19 | public Task RunAsync(CancellationToken cancellationToken = default); 20 | } 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Testdata/50004219.json: -------------------------------------------------------------------------------- 1 | { 2 | "PartyTypeName": 2, 3 | "SSN": "", 4 | "OrgNumber": "810418192", 5 | "Person": null, 6 | "Organization": { 7 | "OrgNumber": "810418192", 8 | "Name": "KOLSAAS OG FLAAM", 9 | "UnitType": "AS", 10 | "TelephoneNumber": null, 11 | "MobileNumber": null, 12 | "FaxNumber": null, 13 | "EMailAddress": "test@test.test", 14 | "InternetAddress": null, 15 | "MailingAddress": null, 16 | "MailingPostalCode": null, 17 | "MailingPostalCity": null, 18 | "BusinessAddress": null, 19 | "BusinessPostalCode": null, 20 | "BusinessPostalCity": null 21 | }, 22 | "PartyId": 50004219, 23 | "PartyUuid": "d0c063a2-9cb3-4724-9d47-e44cec590d2c", 24 | "UnitType": "AS", 25 | "Name": "KOLSAAS OG FLAAM", 26 | "IsDeleted": true, 27 | "OnlyHierarchyElementWithNoAccess": false, 28 | "ChildParties": null 29 | } -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Conventions/IActionModelCondition.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Microsoft.AspNetCore.Mvc.ApplicationModels; 4 | 5 | namespace Altinn.Register.Conventions; 6 | 7 | /// 8 | /// A condition that determines whether a action should be disabled. 9 | /// 10 | public interface IActionModelCondition 11 | { 12 | /// 13 | /// Determines whether the action should be disabled. 14 | /// 15 | /// The in question. 16 | /// The in question. 17 | /// The . 18 | /// if the controller should be disabled. 19 | bool ShouldDisable(ActionModel action, ControllerModel controller, IServiceProvider services); 20 | } 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Leases/Microsoft.Extensions.DependencyInjection/LeaseServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ServiceDefaults.Leases; 2 | using Microsoft.Extensions.DependencyInjection.Extensions; 3 | 4 | namespace Microsoft.Extensions.DependencyInjection; 5 | 6 | /// 7 | /// Extension methods for . 8 | /// 9 | public static class LeaseServiceCollectionExtensions 10 | { 11 | /// 12 | /// Adds a to the service collection. 13 | /// 14 | /// The . 15 | /// . 16 | public static IServiceCollection AddLeaseManager(this IServiceCollection services) 17 | { 18 | services.TryAddTransient(); 19 | 20 | return services; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/AsyncTestBase.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Altinn.Register.TestUtils; 4 | 5 | /// 6 | /// Base class for tests that needs async resources. 7 | /// 8 | public abstract class AsyncTestBase 9 | : IAsyncLifetime 10 | , IAsyncDisposable 11 | { 12 | Task IAsyncLifetime.DisposeAsync() 13 | => DisposeAsync().AsTask(); 14 | 15 | ValueTask IAsyncDisposable.DisposeAsync() 16 | => DisposeAsync(); 17 | 18 | Task IAsyncLifetime.InitializeAsync() 19 | => InitializeAsync().AsTask(); 20 | 21 | /// 22 | protected virtual ValueTask DisposeAsync() 23 | => ValueTask.CompletedTask; 24 | 25 | /// 26 | protected virtual ValueTask InitializeAsync() 27 | => ValueTask.CompletedTask; 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/ISetFakeRequestHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Http; 2 | 3 | /// 4 | /// Interface used to set a delegate that handles requests for this . 5 | /// 6 | public interface ISetFakeRequestHandler 7 | { 8 | /// 9 | /// Sets a delegate that handles requests for this . 10 | /// 11 | /// The handler delegate. 12 | void SetHandler(FakeRequestDelegate handler); 13 | } 14 | 15 | /// 16 | /// Represents a delegate that handles a request. 17 | /// 18 | /// The request context. 19 | /// A . 20 | public delegate Task FakeRequestDelegate(FakeRequestContext context, CancellationToken cancellationToken); 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/UnitOfWork/IUnitOfWorkServiceFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.UnitOfWork; 2 | 3 | /// 4 | /// Marker interface for a factory that creates services that participate in a unit of work. 5 | /// 6 | public interface IUnitOfWorkServiceFactory 7 | { 8 | } 9 | 10 | /// 11 | /// Factory that creates services that participate in a unit of work. 12 | /// 13 | /// The service type. 14 | public interface IUnitOfWorkServiceFactory 15 | : IUnitOfWorkServiceFactory 16 | where TService : class 17 | { 18 | /// 19 | /// Creates a service that participates in a unit of work. 20 | /// 21 | /// The unit of work. 22 | /// A service that participates in a unit of work. 23 | TService Create(IUnitOfWork unitOfWork); 24 | } 25 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Leases/LeaseTicket.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Authorization.ServiceDefaults.Leases; 2 | 3 | /// 4 | /// A ticket representing a lease. 5 | /// 6 | public sealed record LeaseTicket 7 | { 8 | /// 9 | /// Gets the lease id. 10 | /// 11 | public string LeaseId { get; } 12 | 13 | /// 14 | /// Gets the lease token. 15 | /// 16 | public Guid Token { get; } 17 | 18 | /// 19 | /// Gets when the lease expires. 20 | /// 21 | public DateTimeOffset Expires { get; } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | public LeaseTicket(string leaseId, Guid token, DateTimeOffset expires) 27 | { 28 | LeaseId = leaseId; 29 | Token = token; 30 | Expires = expires; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/SystemUserType.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ModelUtils; 2 | 3 | namespace Altinn.Register.Contracts; 4 | 5 | /// 6 | /// Represents a system-user type. 7 | /// 8 | /// 9 | /// This enum is explicitly made such that () is not a valid value. 10 | /// 11 | [StringEnumConverter] 12 | public enum SystemUserType 13 | : uint 14 | { 15 | /// 16 | /// A "standard" system user, used for system-access for own relations. 17 | /// 18 | [JsonStringEnumMemberName("first-party-system-user")] 19 | FirstPartySystemUser = 1, 20 | 21 | /// 22 | /// An "agent" system user, used for system-access for client relations. 23 | /// 24 | [JsonStringEnumMemberName("client-party-system-user")] 25 | ClientPartySystemUser, 26 | } 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Core/PersonLookupSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core 2 | { 3 | /// 4 | /// Represents settings related to the person lookup endpoint. 5 | /// 6 | public class PersonLookupSettings 7 | { 8 | /// 9 | /// The maximum number of times a user can fail to enter correct match. 10 | /// 11 | public int MaximumFailedAttempts { get; set; } = 1; 12 | 13 | /// 14 | /// The number of seconds the failed attempts counter will be kept in the cache. 15 | /// 16 | public int FailedAttemptsCacheLifetimeSeconds { get; set; } = 3600; 17 | 18 | /// 19 | /// The number of seconds a successfully retrieved person object should be cached. 20 | /// 21 | public int PersonCacheLifetimeSeconds { get; set; } = 3600; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Jobs/Job.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Authorization.ServiceDefaults.Jobs; 2 | 3 | /// 4 | /// Base class for jobs that can be run on a schedule or on host lifecycle events. 5 | /// 6 | public abstract class Job 7 | : IJob 8 | { 9 | /// 10 | ValueTask IJob.ShouldRun(CancellationToken cancellationToken) 11 | => ShouldRun(cancellationToken); 12 | 13 | /// 14 | Task IJob.RunAsync(CancellationToken cancellationToken) 15 | => RunAsync(cancellationToken); 16 | 17 | /// 18 | protected virtual ValueTask ShouldRun(CancellationToken cancellationToken = default) 19 | => ValueTask.FromResult(true); 20 | 21 | /// 22 | protected abstract Task RunAsync(CancellationToken cancellationToken = default); 23 | } 24 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.04-external-roles/05-data-external-roles.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO register.external_role_definition(source, identifier, name, description) 2 | VALUES ('ccr'::register.party_source, 'bedr'::register.identifier, 'en=>"Has as the registration entity", nb=>"Har som registreringsenhet", nn=>"Har som registreringseininga"'::register.translated_text, 'en=>"Has as the registration entity", nb=>"Har som registreringsenhet", nn=>"Har som registreringseininga"'::register.translated_text); 3 | 4 | INSERT INTO register.external_role_definition(source, identifier, name, description) 5 | VALUES ('ccr'::register.party_source, 'aafy'::register.identifier, 'en=>"Has as registration entity", nb=>"Har som registreringsenhet", nn=>"Har som registreringseininga"'::register.translated_text, 'en=>"Has as registration entity", nb=>"Har som registreringsenhet", nn=>"Har som registreringseininga"'::register.translated_text); 6 | 7 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.20-si-user/00-type-identifier-check.sql: -------------------------------------------------------------------------------- 1 | -- CONSTRAINT type_identifier_check CHECK ((party_type = 'person'::register.party_type AND person_identifier IS NOT NULL AND organization_identifier IS NULL) OR (party_type = 'organization'::register.party_type AND person_identifier IS NULL AND organization_identifier IS NOT NULL)) 2 | 3 | ALTER TABLE register.party 4 | DROP CONSTRAINT type_identifier_check; 5 | 6 | ALTER TABLE register.party 7 | ADD CONSTRAINT type_identifier_check CHECK ( 8 | (party_type = 'person'::register.party_type AND person_identifier IS NOT NULL AND organization_identifier IS NULL) 9 | OR 10 | (party_type = 'organization'::register.party_type AND person_identifier IS NULL AND organization_identifier IS NOT NULL) 11 | OR 12 | (party_type = 'self-identified-user'::register.party_type AND person_identifier IS NULL AND organization_identifier IS NULL) 13 | ); 14 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit/MassTransitTransportHelper.InMemoryTransportHelper.cs: -------------------------------------------------------------------------------- 1 | using MassTransit; 2 | 3 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 4 | 5 | /// 6 | /// Configuration helper for MassTransit. 7 | /// 8 | internal abstract partial class MassTransitTransportHelper 9 | { 10 | private sealed class InMemoryTransportHelper(MassTransitSettings settings, string busName) 11 | : MassTransitTransportHelper(settings, busName) 12 | { 13 | public override void ConfigureBus(IBusRegistrationConfigurator configurator, Action configureBus) 14 | { 15 | configurator.UsingInMemory((ctx, cfg) => 16 | { 17 | cfg.UseInMemoryScheduler(); 18 | 19 | configureBus(ctx, cfg); 20 | cfg.ConfigureEndpoints(ctx); 21 | }); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/UnknownPartyTests.UnknownPartyWithExtensions.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "unknown-party", 3 | "partyUuid": "00000000-0000-0000-0000-000000000001", 4 | "versionId": 1, 5 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001", 6 | "partyId": 12345678, 7 | "displayName": "Display Name", 8 | "createdAt": "2020-01-02T03:04:05+00:00", 9 | "modifiedAt": "2022-05-06T07:08:09+00:00", 10 | "isDeleted": false, 11 | "deletedAt": null, 12 | "user": { 13 | "userId": 50, 14 | "username": "username", 15 | "userIds": [ 16 | 50, 17 | 30, 18 | 1 19 | ] 20 | }, 21 | "custom-field": "custom value", 22 | "custom-array": [ 23 | "value1", 24 | "value2" 25 | ], 26 | "custom-tuple": [ 27 | "value1", 28 | 42, 29 | true 30 | ], 31 | "custom-object": { 32 | "nested-field": "nested value" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Core/TooManyFailedLookupsException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Altinn.Register.Core 4 | { 5 | /// 6 | /// Represents a situation where a user has performed too many failed lookup requests. 7 | /// 8 | public class TooManyFailedLookupsException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | public TooManyFailedLookupsException() 14 | { 15 | } 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The message that descibes the error. 21 | public TooManyFailedLookupsException(string message) 22 | : base(message) 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/PartyRef.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Diagnostics; 2 | 3 | namespace Altinn.Register.Contracts; 4 | 5 | /// 6 | /// A reference to the owner of a party. 7 | /// 8 | public sealed record PartyRef 9 | { 10 | private readonly Guid _uuid; 11 | private readonly PartyUrn.PartyUuid _urn = null!; 12 | 13 | /// 14 | /// Gets the UUID of the party. 15 | /// 16 | [JsonPropertyName("partyUuid")] 17 | public required Guid Uuid 18 | { 19 | get => _uuid; 20 | init 21 | { 22 | Guard.IsNotDefault(value); 23 | _uuid = value; 24 | _urn = PartyUrn.PartyUuid.Create(value); 25 | } 26 | } 27 | 28 | /// 29 | /// Gets the canonical URN of the party. 30 | /// 31 | [JsonPropertyName("urn")] 32 | public PartyUrn.PartyUuid Urn 33 | => _urn; 34 | } 35 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/A2/ImportA2UserIdForPartyCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.MassTransit; 4 | using Altinn.Register.Core.Parties.Records; 5 | 6 | namespace Altinn.Register.PartyImport.A2; 7 | 8 | /// 9 | /// A command for importing user-ids from A2 for parties already imported into A3. 10 | /// 11 | public sealed record ImportA2UserIdForPartyCommand 12 | : CommandBase 13 | { 14 | /// 15 | /// Gets the party UUID. 16 | /// 17 | public required Guid PartyUuid { get; init; } 18 | 19 | /// 20 | /// Gets the of the party. 21 | /// 22 | public required PartyRecordType PartyType { get; init; } 23 | 24 | /// 25 | /// Gets the tracking information for the import. 26 | /// 27 | public required UpsertPartyTracking Tracking { get; init; } 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | 3 | namespace Altinn.Register.TestUtils.Http; 4 | 5 | internal static class StreamExtensions 6 | { 7 | public static ValueTask WriteAsync(this Stream stream, ReadOnlySequence buffer, CancellationToken cancellationToken = default) 8 | { 9 | if (buffer.IsSingleSegment) 10 | { 11 | return stream.WriteAsync(buffer.First, cancellationToken); 12 | } 13 | 14 | return WriteMultiSegmentAsync(stream, buffer, cancellationToken); 15 | } 16 | 17 | private static async ValueTask WriteMultiSegmentAsync(Stream stream, ReadOnlySequence buffer, CancellationToken cancellationToken) 18 | { 19 | var position = buffer.Start; 20 | while (buffer.TryGet(ref position, out var segment)) 21 | { 22 | await stream.WriteAsync(segment, cancellationToken); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/scripts/publish-to-github-packages.mts: -------------------------------------------------------------------------------- 1 | import { Chalk } from "chalk"; 2 | import { globby } from "globby"; 3 | import { $, retry } from "zx"; 4 | import path from "node:path"; 5 | import { Octokit } from "@octokit/action"; 6 | 7 | const c = new Chalk({ level: 3 }); 8 | 9 | const ghToken = process.env.GITHUB_TOKEN; 10 | const filesGlob = process.env.FILES_GLOB; 11 | 12 | const github = new Octokit({ 13 | auth: ghToken, 14 | }); 15 | 16 | if (!ghToken || !filesGlob) { 17 | console.error("Missing required environment variables"); 18 | process.exit(1); 19 | } 20 | 21 | for (const file of await globby(filesGlob)) { 22 | const name = path.basename(file); 23 | const fullPath = path.resolve(file); 24 | 25 | console.log(`Publishing ${c.yellow(name)}`); 26 | await retry( 27 | 5, 28 | () => 29 | $`dotnet nuget push --skip-duplicate "${fullPath}" --api-key "${ghToken}" --source "github"` 30 | ); 31 | 32 | console.log(`Published ${c.green(name)}`); 33 | } 34 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/Altinn.Register.Contracts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | Altinn.Register.Contracts 6 | Altinn;Models;Contracts;Register;Authorization 7 | 8 | Altinn.Register.Contracts is a package for models used by platform register and profile. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Environment setup & latest features 4 | "lib": ["ESNext"], 5 | "target": "ESNext", 6 | "module": "Preserve", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedIndexedAccess": true, 22 | "noImplicitOverride": true, 23 | "isolatedModules": true, 24 | 25 | // Some stricter flags (disabled by default) 26 | "noUnusedLocals": false, 27 | "noUnusedParameters": false, 28 | "noPropertyAccessFromIndexSignature": false, 29 | 30 | // Paths 31 | "rootDir": ".", 32 | "baseUrl": ".", 33 | "outDir": "out/" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/Filters/RouteValueFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Http.Filters; 2 | 3 | internal sealed class RouteValueFilter 4 | : IFakeRequestFilter 5 | { 6 | public static IFakeRequestFilter Create(string key, Predicate predicate) 7 | => new RouteValueFilter(key, predicate); 8 | 9 | private readonly string _key; 10 | private readonly Predicate _predicate; 11 | 12 | private RouteValueFilter(string key, Predicate predicate) 13 | { 14 | _key = key; 15 | _predicate = predicate; 16 | } 17 | 18 | public string Description => $"has route value '{_key}' that matches predicate"; 19 | 20 | public bool Matches(FakeHttpRequestMessage request) 21 | { 22 | if (!request.RouteData.Values.TryGetValue(_key, out var value)) 23 | { 24 | return false; 25 | } 26 | 27 | return _predicate(value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine@sha256:f271ed7d0fd9c5a7ed0acafed8a2bc978bb65c19dcd2eeea0415adef142ffc87 AS build 2 | WORKDIR /app 3 | 4 | # Copy everything and build 5 | COPY . . 6 | RUN cd ./src/apps/Altinn.Register/src/Altinn.Register \ 7 | && dotnet build Altinn.Register.csproj -c Release -o /app_output \ 8 | && dotnet publish Altinn.Register.csproj -c Release -o /app_output 9 | 10 | FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine@sha256:5e8dca92553951e42caed00f2568771b0620679f419a28b1335da366477d7f98 AS final 11 | EXPOSE 5020 12 | WORKDIR /app 13 | COPY --from=build /app_output . 14 | 15 | # setup the user and group 16 | # the user will have no password, using shell /bin/false and using the group dotnet 17 | RUN addgroup -g 3000 dotnet && adduser -u 1000 -G dotnet -D -s /bin/false dotnet 18 | # update permissions of files if neccessary before becoming dotnet user 19 | USER dotnet 20 | RUN mkdir /tmp/logtelemetry 21 | 22 | ENTRYPOINT ["dotnet", "Altinn.Register.dll"] 23 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.07-addresses/00-address-types.sql: -------------------------------------------------------------------------------- 1 | -- Composite: register.co_mailing_address (private - implementation detail) 2 | CREATE TYPE register.co_mailing_address AS ( 3 | address text, 4 | postal_code text, 5 | city text 6 | ); 7 | 8 | -- Composite: register.co_street_address (private - implementation detail) 9 | CREATE TYPE register.co_street_address AS ( 10 | municipal_number text, 11 | municipal_name text, 12 | street_name text, 13 | house_number text, 14 | house_letter text, 15 | postal_code text, 16 | city text 17 | ); 18 | 19 | -- Domain: register.mailing_address 20 | -- No checks at this time, but create a domain so they can be added later 21 | CREATE DOMAIN register.mailing_address AS register.co_mailing_address; 22 | 23 | -- Domain: register.street_address 24 | -- No checks at this time, but create a domain so they can be added later 25 | CREATE DOMAIN register.street_address AS register.co_street_address; 26 | 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Altinn.Register": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "http://localhost:5020/swagger", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development", 9 | "ASPNETCORE_URLS": "http://localhost:5020" 10 | }, 11 | "applicationUrl": "http://localhost:5020" 12 | }, 13 | "Init Altinn.Register": { 14 | "commandName": "Project", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development", 17 | "ALTINN__RUNINITONLY": "true" 18 | } 19 | } 20 | }, 21 | "$schema": "http://json.schemastore.org/launchsettings.json", 22 | "iisSettings": { 23 | "windowsAuthentication": false, 24 | "anonymousAuthentication": true, 25 | "iisExpress": { 26 | "applicationUrl": "http://localhost:52233", 27 | "sslPort": 44346 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/ModuleInitializer.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Altinn.Authorization.ModelUtils.EnumUtils; 3 | using Altinn.Register.Core.Parties; 4 | 5 | namespace Altinn.Register.Persistence.Tests; 6 | 7 | public static class ModuleInitializer 8 | { 9 | private static readonly FlagsEnumModel _includesModel = FlagsEnumModel.Create(); 10 | 11 | private static int _initialized = 0; 12 | 13 | [ModuleInitializer] 14 | public static void Init() 15 | { 16 | if (Interlocked.Exchange(ref _initialized, 1) == 0) 17 | { 18 | VerifierSettings.NameForParameter(_includesModel.Format); 19 | VerifierSettings.NameForParameter(static f => f.ToString()); 20 | 21 | VerifierSettings.UseSplitModeForUniqueDirectory(); 22 | UseProjectRelativeDirectory("Snapshots"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookupOne(id) 3 | 4 | WITH top_level_uuids AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."id" = @partyId 8 | ), 9 | uuids AS ( 10 | SELECT 11 | "uuid" AS "uuid", 12 | NULL::uuid AS parent_uuid, 13 | version_id AS sort_first, 14 | NULL::uuid AS sort_second 15 | FROM top_level_uuids 16 | ) 17 | SELECT 18 | party.uuid p_uuid, 19 | party.id p_id, 20 | party.party_type p_party_type, 21 | party.display_name p_display_name, 22 | party.person_identifier p_person_identifier, 23 | party.organization_identifier p_organization_identifier 24 | FROM uuids AS uuids 25 | INNER JOIN register.party AS party USING (uuid) 26 | ORDER BY 27 | uuids.sort_first, 28 | uuids.sort_second NULLS FIRST 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/ApiDescriptions/PartyComponentOptionSchemaFilter.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.Contracts.V1; 2 | using Altinn.Register.ModelBinding; 3 | using Microsoft.OpenApi.Any; 4 | using Microsoft.OpenApi.Models; 5 | 6 | using Swashbuckle.AspNetCore.SwaggerGen; 7 | 8 | namespace Altinn.Register.ApiDescriptions; 9 | 10 | /// 11 | /// Schema filter for . 12 | /// 13 | public sealed class PartyComponentOptionSchemaFilter 14 | : SchemaFilter 15 | { 16 | /// 17 | protected override void Apply(OpenApiSchema schema, SchemaFilterContext context) 18 | { 19 | schema.Enum = null; 20 | schema.Format = null; 21 | schema.Type = "array"; 22 | schema.Items = new OpenApiSchema 23 | { 24 | Type = "string", 25 | Enum = PartyComponentOptionModelBinder.AllowedValues.Select(v => (IOpenApiAny)new OpenApiString(v)).ToList(), 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/A2/ImportA2UserProfileCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.MassTransit; 4 | 5 | namespace Altinn.Register.PartyImport.A2; 6 | 7 | /// 8 | /// A command for importing an A2 user profile. 9 | /// 10 | public sealed record ImportA2UserProfileCommand 11 | : CommandBase 12 | { 13 | /// 14 | /// Gets the user ID. 15 | /// 16 | public required ulong UserId { get; init; } 17 | 18 | /// 19 | /// Gets the owner party UUID. 20 | /// 21 | public required Guid OwnerPartyUuid { get; init; } 22 | 23 | /// 24 | /// Gets whether the user profile was deleted at the update time. 25 | /// 26 | public required bool IsDeleted { get; init; } 27 | 28 | /// 29 | /// Gets tracking information for the import job. 30 | /// 31 | public required UpsertPartyTracking Tracking { get; init; } 32 | } 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(uuid).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookupOne(uuid) 3 | 4 | WITH top_level_uuids AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."uuid" = @partyUuid 8 | ), 9 | uuids AS ( 10 | SELECT 11 | "uuid" AS "uuid", 12 | NULL::uuid AS parent_uuid, 13 | version_id AS sort_first, 14 | NULL::uuid AS sort_second 15 | FROM top_level_uuids 16 | ) 17 | SELECT 18 | party.uuid p_uuid, 19 | party.id p_id, 20 | party.party_type p_party_type, 21 | party.display_name p_display_name, 22 | party.person_identifier p_person_identifier, 23 | party.organization_identifier p_organization_identifier 24 | FROM uuids AS uuids 25 | INNER JOIN register.party AS party USING (uuid) 26 | ORDER BY 27 | uuids.sort_first, 28 | uuids.sort_second NULLS FIRST 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.21-party-constraints/00-party-constraints.sql: -------------------------------------------------------------------------------- 1 | -- CONSTRAINT type_identifier_check CHECK ( 2 | -- (party_type = 'person'::register.party_type AND person_identifier IS NOT NULL AND organization_identifier IS NULL) 3 | -- OR 4 | -- (party_type = 'organization'::register.party_type AND person_identifier IS NULL AND organization_identifier IS NOT NULL) 5 | -- OR 6 | -- (party_type = 'self-identified-user'::register.party_type AND person_identifier IS NULL AND organization_identifier IS NULL) 7 | -- ); 8 | 9 | ALTER TABLE register.party 10 | DROP CONSTRAINT type_identifier_check; 11 | 12 | ALTER TABLE register.party 13 | ADD CONSTRAINT person_identifier_check CHECK ( 14 | (person_identifier IS NULL) <> (party_type = 'person'::register.party_type) 15 | ); 16 | 17 | ALTER TABLE register.party 18 | ADD CONSTRAINT organization_identifier_check CHECK ( 19 | (organization_identifier IS NULL) <> (party_type = 'organization'::register.party_type) 20 | ); 21 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.31-username/00-username.sql: -------------------------------------------------------------------------------- 1 | -- CREATE TABLE register.user ( 2 | -- id BIGSERIAL PRIMARY KEY NOT NULL, 3 | -- uuid uuid NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 4 | -- user_id BIGINT NOT NULL, 5 | -- is_active BOOLEAN NOT NULL DEFAULT TRUE 6 | -- ) 7 | -- TABLESPACE pg_default; 8 | ALTER TABLE register.user 9 | ADD COLUMN username TEXT; 10 | 11 | ALTER TABLE register.user 12 | ADD CONSTRAINT only_active_user_username CHECK ((username IS NULL) OR (is_active = TRUE)); 13 | 14 | CREATE UNIQUE INDEX uq_user_username ON register.user (username) TABLESPACE pg_default 15 | WHERE 16 | is_active = TRUE; 17 | 18 | -- CREATE TABLE register.user ( 19 | -- id BIGSERIAL PRIMARY KEY NOT NULL, 20 | -- uuid uuid NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 21 | -- user_id BIGINT NOT NULL, 22 | -- is_active BOOLEAN NOT NULL DEFAULT TRUE, 23 | -- username TEXT 24 | -- ) 25 | -- TABLESPACE pg_default; 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/Traits/CategoryAttribute.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Diagnostics; 2 | using Xunit.v3; 3 | 4 | namespace Altinn.Register.TestUtils.Traits; 5 | 6 | /// 7 | /// Attribute used to decorate a test method, test class, or assembly with an arbitrary category name ("trait"). 8 | /// 9 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] 10 | public class CategoryAttribute 11 | : Attribute 12 | , ITraitAttribute 13 | { 14 | internal static string TraitName { get; } = "Category"; 15 | 16 | public CategoryAttribute(string name) 17 | { 18 | Guard.IsNotNullOrWhiteSpace(name); 19 | 20 | Name = name; 21 | } 22 | 23 | /// 24 | /// Get the category name. 25 | /// 26 | public string Name { get; } 27 | 28 | /// 29 | public IReadOnlyCollection> GetTraits() => 30 | [new(TraitName, Name)]; 31 | } 32 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/AsyncEnumerables/IAsyncResourceOwner.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Persistence.AsyncEnumerables; 2 | 3 | /// 4 | /// A manager of async resource lifetimes. 5 | /// 6 | internal interface IAsyncResourceOwner 7 | : IAsyncDisposable 8 | { 9 | /// 10 | /// Adopts ownership of a set of , such that when this 11 | /// resource owner is disposed, the is also disposed. 12 | /// 13 | /// The resources to adopt. 14 | public void Adopt(ReadOnlySpan resources); 15 | 16 | /// 17 | /// Adopts ownership of a , such that when this 18 | /// resource owner is disposed, the is also disposed. 19 | /// 20 | /// The resource to adopt. 21 | public void Adopt(IAsyncDisposable resource) 22 | => Adopt([resource]); 23 | } 24 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(pers.id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookupOne(pers.id) 3 | 4 | WITH top_level_uuids AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."person_identifier" = @personIdentifier 8 | ), 9 | uuids AS ( 10 | SELECT 11 | "uuid" AS "uuid", 12 | NULL::uuid AS parent_uuid, 13 | version_id AS sort_first, 14 | NULL::uuid AS sort_second 15 | FROM top_level_uuids 16 | ) 17 | SELECT 18 | party.uuid p_uuid, 19 | party.id p_id, 20 | party.party_type p_party_type, 21 | party.display_name p_display_name, 22 | party.person_identifier p_person_identifier, 23 | party.organization_identifier p_organization_identifier 24 | FROM uuids AS uuids 25 | INNER JOIN register.party AS party USING (uuid) 26 | ORDER BY 27 | uuids.sort_first, 28 | uuids.sort_second NULLS FIRST 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/UnitTests/ImportJobQueueStatusTests.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.Core.ImportJobs; 2 | 3 | namespace Altinn.Register.Tests.UnitTests; 4 | 5 | public class ImportJobQueueStatusTests 6 | { 7 | [Fact] 8 | public void SourceMaxNull() 9 | { 10 | var status = new ImportJobQueueStatus 11 | { 12 | EnqueuedMax = 42, 13 | SourceMax = null, 14 | }; 15 | 16 | status.SourceMax.Should().BeNull(); 17 | } 18 | 19 | [Fact] 20 | public void SourceMaxZero() 21 | { 22 | var status = new ImportJobQueueStatus 23 | { 24 | EnqueuedMax = 42, 25 | SourceMax = 0, 26 | }; 27 | 28 | status.SourceMax.Should().Be(0); 29 | } 30 | 31 | [Fact] 32 | public void SourceMaxPositive() 33 | { 34 | var status = new ImportJobQueueStatus 35 | { 36 | EnqueuedMax = 42, 37 | SourceMax = 42, 38 | }; 39 | status.SourceMax.Should().Be(42); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(org.id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookupOne(org.id) 3 | 4 | WITH top_level_uuids AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."organization_identifier" = @organizationIdentifier 8 | ), 9 | uuids AS ( 10 | SELECT 11 | "uuid" AS "uuid", 12 | NULL::uuid AS parent_uuid, 13 | version_id AS sort_first, 14 | NULL::uuid AS sort_second 15 | FROM top_level_uuids 16 | ) 17 | SELECT 18 | party.uuid p_uuid, 19 | party.id p_id, 20 | party.party_type p_party_type, 21 | party.display_name p_display_name, 22 | party.person_identifier p_person_identifier, 23 | party.organization_identifier p_organization_identifier 24 | FROM uuids AS uuids 25 | INNER JOIN register.party AS party USING (uuid) 26 | ORDER BY 27 | uuids.sort_first, 28 | uuids.sort_second NULLS FIRST 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.AppHost/ResourceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Altinn.Register.AppHost; 4 | 5 | /// 6 | /// Extension metods for reasource builders. 7 | /// 8 | [ExcludeFromCodeCoverage] 9 | internal static class ResourceExtensions 10 | { 11 | /// 12 | /// Marks all endpoints on the resource as public. 13 | /// 14 | /// The resource type. 15 | /// The builder. 16 | /// . 17 | public static IResourceBuilder WithPublicEndpoints(this IResourceBuilder builder) 18 | where T : IResourceWithEndpoints 19 | { 20 | if (builder.Resource.TryGetAnnotationsOfType(out var endpoints)) 21 | { 22 | foreach (var endpoint in endpoints) 23 | { 24 | endpoint.IsProxied = false; 25 | } 26 | } 27 | 28 | return builder; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit.Abstractions/ICommandSender.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 2 | 3 | /// 4 | /// A command sender. 5 | /// 6 | public interface ICommandSender 7 | { 8 | /// 9 | /// Sends a command. 10 | /// 11 | /// The command type. 12 | /// The command. 13 | /// A . 14 | Task Send(T command, CancellationToken cancellationToken = default) 15 | where T : CommandBase; 16 | 17 | /// 18 | /// Sends a collection of commands. 19 | /// 20 | /// The command type. 21 | /// The commands. 22 | /// A . 23 | Task Send(IEnumerable commands, CancellationToken cancellationToken = default) 24 | where T : CommandBase; 25 | } 26 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Testdata/50004216.json: -------------------------------------------------------------------------------- 1 | { 2 | "PartyTypeName": 2, 3 | "SSN": "", 4 | "OrgNumber": "311654306", 5 | "Person": null, 6 | "Organization": { 7 | "OrgNumber": "311654306", 8 | "Name": "TYNSET OG OPPDAL", 9 | "UnitType": "ANS", 10 | "UnitStatus": "N", 11 | "TelephoneNumber": "22077000", 12 | "MobileNumber": "99000000", 13 | "FaxNumber": "22077108", 14 | "EMailAddress": "tynset_og_oppdal@example.com", 15 | "InternetAddress": "tynset-og-oppdal.example.com", 16 | "MailingAddress": "Postboks 6662 St. Bergens plass", 17 | "MailingPostalCode": "1666", 18 | "MailingPostalCity": "Bergen", 19 | "BusinessAddress": "Postboks 6662 St. Olavs plass", 20 | "BusinessPostalCode": "0555", 21 | "BusinessPostalCity": "Oslo" 22 | }, 23 | "PartyId": 50004216, 24 | "PartyUuid": "7aa53da8-836c-4812-afcb-76d39f5ebb0e", 25 | "UnitType": "ANS", 26 | "Name": "TYNSET OG OPPDAL", 27 | "IsDeleted": false, 28 | "OnlyHierarchyElementWithNoAccess": false, 29 | "ChildParties": null 30 | } 31 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/FluentAssertionsExtensions/Initializer.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Runtime.CompilerServices; 3 | using FluentAssertions; 4 | 5 | namespace Altinn.Register.TestUtils.FluentAssertionsExtensions; 6 | 7 | /// 8 | /// Setup for FluentAssertions. 9 | /// 10 | [ExcludeFromCodeCoverage] 11 | public static class Initializer 12 | { 13 | /// 14 | /// Initializes the fluent assertions. 15 | /// 16 | [ModuleInitializer] 17 | [SuppressMessage("Usage", "CA2255:The 'ModuleInitializer' attribute should not be used in libraries", Justification = "Test setup")] 18 | public static void Initialize() 19 | { 20 | AssertionOptions.AssertEquivalencyUsing(options => 21 | { 22 | options.Using(new JsonDocumentConversionStep()); 23 | options.Using(new JsonStringConversionStep()); 24 | options.Using(new JsonElementEquivalencyStep()); 25 | 26 | return options; 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Testdata/50012347.json: -------------------------------------------------------------------------------- 1 | { 2 | "PartyId": 50012347, 3 | "PartyTypeName": 1, 4 | "OrgNumber": null, 5 | "SSN": "01039012345", 6 | "UnitType": null, 7 | "Name": "Sophie Salt", 8 | "IsDeleted": false, 9 | "PartyUuid": "4AB951A4-CBFE-4309-83CA-263F75BC3802", 10 | "OnlyHierarchyElementWithNoAccess": false, 11 | "Person": { 12 | "SSN": "01039012345", 13 | "Name": "Sophie Salt", 14 | "FirstName": "Sophie", 15 | "MiddleName": "", 16 | "LastName": "Salt", 17 | "TelephoneNumber": "12345678", 18 | "MobileNumber": "87654321", 19 | "MailingAddress": "Grev Wedels Plass 9 0157 Oslo", 20 | "MailingPostalCode": "0157", 21 | "MailingPostalCity": "Oslo", 22 | "AddressMunicipalNumber": "0301", 23 | "AddressMunicipalName": "Oslo", 24 | "AddressStreetName": "Grev Wedels Plass", 25 | "AddressHouseNumber": "9", 26 | "AddressHouseLetter": null, 27 | "AddressPostalCode": "0151", 28 | "AddressCity": "Oslo" 29 | }, 30 | "Organization": null, 31 | "ChildParties": null 32 | } 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V3/Altinn.Register.TestUtils.V3.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | Altinn.Register.TestUtils 6 | false 7 | true 8 | v3 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Services/Implementation/OrgContactPointService.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.Clients.Interfaces; 2 | using Altinn.Register.Models; 3 | using Altinn.Register.Services.Interfaces; 4 | 5 | namespace Altinn.Register.Services.Implementation; 6 | 7 | /// 8 | /// An implementation of 9 | /// 10 | public class OrgContactPointService : IOrgContactPoint 11 | { 12 | private readonly IOrganizationClient _organizationClient; 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | public OrgContactPointService(IOrganizationClient organizationClient) 18 | { 19 | _organizationClient = organizationClient; 20 | } 21 | 22 | /// 23 | public async Task GetContactPoints(OrgContactPointLookup lookup, CancellationToken cancellationToken = default) 24 | { 25 | return await _organizationClient.GetContactPoints(lookup, cancellationToken); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Mocks/DelegatingHandlerStub.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Altinn.Register.Tests.Mocks 8 | { 9 | public class DelegatingHandlerStub : DelegatingHandler 10 | { 11 | private readonly Func> _handlerFunc; 12 | 13 | public DelegatingHandlerStub() 14 | { 15 | _handlerFunc = (request, cancellationToken) => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); 16 | } 17 | 18 | public DelegatingHandlerStub(Func> handlerFunc) 19 | { 20 | _handlerFunc = handlerFunc; 21 | } 22 | 23 | protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 24 | { 25 | return _handlerFunc(request, cancellationToken); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(user.id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookupOne(user.id) 3 | 4 | WITH top_level_uuids AS ( 5 | SELECT "user"."uuid", party.version_id 6 | FROM register."user" AS "user" 7 | INNER JOIN register.party AS party USING (uuid) 8 | WHERE "user".user_id = @userId 9 | ), 10 | uuids AS ( 11 | SELECT 12 | "uuid" AS "uuid", 13 | NULL::uuid AS parent_uuid, 14 | version_id AS sort_first, 15 | NULL::uuid AS sort_second 16 | FROM top_level_uuids 17 | ) 18 | SELECT 19 | party.uuid p_uuid, 20 | party.id p_id, 21 | party.party_type p_party_type, 22 | party.display_name p_display_name, 23 | party.person_identifier p_person_identifier, 24 | party.organization_identifier p_organization_identifier 25 | FROM uuids AS uuids 26 | INNER JOIN register.party AS party USING (uuid) 27 | ORDER BY 28 | uuids.sort_first, 29 | uuids.sort_second NULLS FIRST 30 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/UnitOfWork/IUnitOfWorkParticipantFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | 3 | namespace Altinn.Register.Core.UnitOfWork; 4 | 5 | /// 6 | /// A factory for creating participants in a unit of work. 7 | /// 8 | public interface IUnitOfWorkParticipantFactory 9 | { 10 | /// 11 | /// Gets the public service types that the factory creates. 12 | /// 13 | ImmutableArray ServiceTypes { get; } 14 | 15 | /// 16 | /// Factory method for creating a participant in a unit of work. 17 | /// 18 | /// A . 19 | /// A . 20 | /// A . 21 | /// A . 22 | ValueTask Create(IUnitOfWorkHandle handle, IServiceProvider serviceProvider, CancellationToken cancellationToken = default); 23 | } 24 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookupOne(user.name).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookupOne(user.name) 3 | 4 | WITH top_level_uuids AS ( 5 | SELECT "user"."uuid", party.version_id 6 | FROM register."user" AS "user" 7 | INNER JOIN register.party AS party USING (uuid) 8 | WHERE "user".username = @username 9 | ), 10 | uuids AS ( 11 | SELECT 12 | "uuid" AS "uuid", 13 | NULL::uuid AS parent_uuid, 14 | version_id AS sort_first, 15 | NULL::uuid AS sort_second 16 | FROM top_level_uuids 17 | ) 18 | SELECT 19 | party.uuid p_uuid, 20 | party.id p_id, 21 | party.party_type p_party_type, 22 | party.display_name p_display_name, 23 | party.person_identifier p_person_identifier, 24 | party.organization_identifier p_organization_identifier 25 | FROM uuids AS uuids 26 | INNER JOIN register.party AS party USING (uuid) 27 | ORDER BY 28 | uuids.sort_first, 29 | uuids.sort_second NULLS FIRST 30 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/IFakeRequestHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.TestUtils.Http; 2 | 3 | /// 4 | /// A handler for requests. 5 | /// 6 | public interface IFakeRequestHandler 7 | { 8 | /// 9 | /// Gets a human readable description of the handler. 10 | /// 11 | string Description { get; } 12 | 13 | /// 14 | /// Gets a value indicating whether this handler can handle the request. 15 | /// 16 | /// The request context. 17 | /// , if this handler can handle , otherwise . 18 | bool CanHandle(FakeRequestContext context); 19 | 20 | /// 21 | /// Handles the request. 22 | /// 23 | /// The request context. 24 | /// A . 25 | Task Handle(FakeRequestContext context, CancellationToken cancellationToken); 26 | } 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/Testdata/50012345.json: -------------------------------------------------------------------------------- 1 | { 2 | "PartyId": 50012345, 3 | "PartyTypeName": 1, 4 | "OrgNumber": null, 5 | "SSN": "25871999336", 6 | "UnitType": null, 7 | "Name": "Ola Bla Nordmann", 8 | "IsDeleted": false, 9 | "PartyUuid": "195BD1DC-F136-48FE-9BA4-959949A1B067", 10 | "OnlyHierarchyElementWithNoAccess": false, 11 | "Person": { 12 | "SSN": "25871999336", 13 | "Name": "Ola Bla Nordmann", 14 | "FirstName": "Ola", 15 | "MiddleName": "Bla", 16 | "LastName": "Nordmann", 17 | "TelephoneNumber": "12345678", 18 | "MobileNumber": "87654321", 19 | "MailingAddress": "Blåbæreveien 7 8450 Stokmarknes", 20 | "MailingPostalCode": "8450", 21 | "MailingPostalCity": "Stokmarknes", 22 | "AddressMunicipalNumber": "1866", 23 | "AddressMunicipalName": "Hadsel", 24 | "AddressStreetName": "Blåbærveien", 25 | "AddressHouseNumber": "7", 26 | "AddressHouseLetter": "G", 27 | "AddressPostalCode": "8450", 28 | "AddressCity": "Stokarknes" 29 | }, 30 | "Organization": null, 31 | "ChildParties": null 32 | } 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Http/Filters/QueryParamFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.WebUtilities; 2 | 3 | namespace Altinn.Register.TestUtils.Http.Filters; 4 | 5 | internal sealed class QueryParamFilter 6 | : IFakeRequestFilter 7 | { 8 | public static IFakeRequestFilter Create(string key, string value) 9 | => new QueryParamFilter(key, value); 10 | 11 | private readonly string _key; 12 | private readonly string _value; 13 | 14 | private QueryParamFilter(string key, string value) 15 | { 16 | _key = key; 17 | _value = value; 18 | } 19 | 20 | public string Description => $"has query parameter {_key} with value {_value}"; 21 | 22 | public bool Matches(FakeHttpRequestMessage request) 23 | { 24 | var queryString = request.RequestUri?.Query; 25 | if (queryString is null) 26 | { 27 | return false; 28 | } 29 | 30 | var parsed = QueryHelpers.ParseQuery(queryString); 31 | return parsed.TryGetValue(_key, out var values) && values.Contains(_value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/PartySourceRef.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.Parties; 2 | 3 | /// 4 | /// A party source-ref entity. 5 | /// 6 | public record PartySourceRef 7 | { 8 | /// 9 | /// Gets the UUID of the party. 10 | /// 11 | public required Guid PartyUuid { get; init; } 12 | 13 | /// 14 | /// Gets the party source of this source-ref. 15 | /// 16 | public required PartySource PartySource { get; init; } 17 | 18 | /// 19 | /// Gets the source identifier of this source-ref. 20 | /// 21 | public required string SourceIdentifier { get; init; } 22 | 23 | /// 24 | /// Gets when the source created the referenced entity if available. 25 | /// 26 | public required DateTimeOffset? SourceCreated { get; init; } 27 | 28 | /// 29 | /// Gets when the source last updated the referenced entity if available. 30 | /// 31 | public required DateTimeOffset? SourceUpdated { get; init; } 32 | } 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/UpsertUserRecordCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Authorization.ServiceDefaults.MassTransit; 4 | 5 | namespace Altinn.Register.PartyImport; 6 | 7 | /// 8 | /// A command for upserting a user record. 9 | /// 10 | public sealed record UpsertUserRecordCommand 11 | : CommandBase 12 | { 13 | /// 14 | /// The owner party UUID. 15 | /// 16 | public required Guid PartyUuid { get; init; } 17 | 18 | /// 19 | /// Gets the user ID. 20 | /// 21 | public required ulong UserId { get; init; } 22 | 23 | /// 24 | /// Gets the username, if any. 25 | /// 26 | public required string? Username { get; init; } 27 | 28 | /// 29 | /// Gets whether the user is active. 30 | /// 31 | public required bool IsActive { get; init; } 32 | 33 | /// 34 | /// Gets the tracking information for the import. 35 | /// 36 | public UpsertPartyTracking Tracking { get; init; } 37 | } 38 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/src/Altinn.Register.Contracts/V1/PartyName.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Contracts.V1; 2 | 3 | /// 4 | /// Represents the party lookup result 5 | /// 6 | public record PartyName 7 | { 8 | /// 9 | /// Gets or sets the social security number for this result. 10 | /// 11 | [JsonPropertyName("ssn")] 12 | public string? Ssn { get; set; } 13 | 14 | /// 15 | /// Gets or sets the organization number for this result. 16 | /// 17 | [JsonPropertyName("orgNo")] 18 | public string? OrgNo { get; set; } 19 | 20 | /// 21 | /// Gets or sets the party name for this result. 22 | /// 23 | [JsonPropertyName("name")] 24 | public string? Name { get; set; } 25 | 26 | /// 27 | /// Gets or sets the components of a person's name for this result. 28 | /// 29 | [JsonPropertyName("personName")] 30 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 31 | public PersonNameComponents? PersonName { get; set; } 32 | } 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookup(id) 3 | 4 | WITH uuids_by_party_id AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."id" = ANY (@partyIds) 8 | ), 9 | top_level_uuids AS ( 10 | SELECT "uuid", version_id FROM uuids_by_party_id 11 | ), 12 | uuids AS ( 13 | SELECT 14 | "uuid" AS "uuid", 15 | NULL::uuid AS parent_uuid, 16 | version_id AS sort_first, 17 | NULL::uuid AS sort_second 18 | FROM top_level_uuids 19 | ) 20 | SELECT 21 | party.uuid p_uuid, 22 | party.id p_id, 23 | party.party_type p_party_type, 24 | party.display_name p_display_name, 25 | party.person_identifier p_person_identifier, 26 | party.organization_identifier p_organization_identifier 27 | FROM uuids AS uuids 28 | INNER JOIN register.party AS party USING (uuid) 29 | ORDER BY 30 | uuids.sort_first, 31 | uuids.sort_second NULLS FIRST 32 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.IntegrationTests/TestServices/TestOpenIdConnectConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Diagnostics; 2 | using Microsoft.IdentityModel.Protocols; 3 | using Microsoft.IdentityModel.Protocols.OpenIdConnect; 4 | using Microsoft.IdentityModel.Tokens; 5 | 6 | namespace Altinn.Register.IntegrationTests.TestServices; 7 | 8 | internal class TestOpenIdConnectConfigurationManager 9 | : IConfigurationManager 10 | { 11 | private readonly TestJwtService _jwt; 12 | 13 | public TestOpenIdConnectConfigurationManager(TestJwtService certs) 14 | { 15 | _jwt = certs; 16 | } 17 | 18 | public Task GetConfigurationAsync(CancellationToken cancel) 19 | { 20 | var config = new OpenIdConnectConfiguration(); 21 | var cert = _jwt.GetCertificate(); 22 | config.SigningKeys.Add(new X509SecurityKey(cert)); 23 | 24 | return Task.FromResult(config); 25 | } 26 | 27 | public void RequestRefresh() 28 | { 29 | ThrowHelper.ThrowNotSupportedException(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Core/IPersonLookup.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using Altinn.Register.Contracts.V1; 3 | 4 | namespace Altinn.Register.Core 5 | { 6 | /// 7 | /// Describes the methods required by a person check service. 8 | /// 9 | public interface IPersonLookup 10 | { 11 | /// 12 | /// Operation for checking if a given national identity number is connected to a person. 13 | /// 14 | /// The national identity number to check. 15 | /// The last name of the person. Must match the last name of the person. 16 | /// The unique id of the user performing the check. 17 | /// A cancellation token. 18 | /// The identified if last name was correct. 19 | Task GetPerson(string nationalIdentityNumber, string lastName, int activeUser, CancellationToken cancellationToken = default); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(uuid).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookup(uuid) 3 | 4 | WITH uuids_by_party_uuid AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."uuid" = ANY (@partyUuids) 8 | ), 9 | top_level_uuids AS ( 10 | SELECT "uuid", version_id FROM uuids_by_party_uuid 11 | ), 12 | uuids AS ( 13 | SELECT 14 | "uuid" AS "uuid", 15 | NULL::uuid AS parent_uuid, 16 | version_id AS sort_first, 17 | NULL::uuid AS sort_second 18 | FROM top_level_uuids 19 | ) 20 | SELECT 21 | party.uuid p_uuid, 22 | party.id p_id, 23 | party.party_type p_party_type, 24 | party.display_name p_display_name, 25 | party.person_identifier p_person_identifier, 26 | party.organization_identifier p_organization_identifier 27 | FROM uuids AS uuids 28 | INNER JOIN register.party AS party USING (uuid) 29 | ORDER BY 30 | uuids.sort_first, 31 | uuids.sort_second NULLS FIRST 32 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Jobs/IJobCondition.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | 3 | namespace Altinn.Authorization.ServiceDefaults.Jobs; 4 | 5 | /// 6 | /// A condition that can be applied to one or more jobs, determining if they are allowed to run or not. 7 | /// 8 | public interface IJobCondition 9 | { 10 | /// 11 | /// Gets the name of the job condition. Used for logging and diagnostics. 12 | /// 13 | public string Name { get; } 14 | 15 | /// 16 | /// Gets tags this condition is applied to. If the list is empty, the condition applies to all jobs. 17 | /// 18 | public ImmutableArray JobTags { get; } 19 | 20 | /// 21 | /// Checks if the job is allowed to run. 22 | /// 23 | /// A . 24 | /// , if the job should be allowed to run at this time, otherwise . 25 | public ValueTask ShouldRun(CancellationToken cancellationToken = default); 26 | } 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Jobs/IJobRegistration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | 3 | namespace Altinn.Authorization.ServiceDefaults.Jobs; 4 | 5 | /// 6 | /// Represents a job registration that defines the configuration and metadata for a scheduled job. 7 | /// 8 | public interface IJobRegistration 9 | { 10 | /// 11 | /// Gets the name of the job. 12 | /// 13 | public string JobName { get; } 14 | 15 | /// 16 | /// Gets the name of the lease that should be acquired before running the job. 17 | /// 18 | public string? LeaseName { get; } 19 | 20 | /// 21 | /// Gets the interval at which the job should run. 22 | /// 23 | public TimeSpan Interval { get; } 24 | 25 | /// 26 | /// Gets the that the job should run at. 27 | /// 28 | public JobHostLifecycles RunAt { get; } 29 | 30 | /// 31 | /// Gets the tags associated with the job. 32 | /// 33 | public ImmutableArray Tags { get; } 34 | } 35 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/PartyImport/A2/A2PartyExternalRoleAssignment.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.PartyImport.A2; 2 | 3 | /// 4 | /// Represents an external role assignment from a party. 5 | /// 6 | public sealed record A2PartyExternalRoleAssignment 7 | : IEquatable 8 | { 9 | /// 10 | /// Gets the party uuid of the receiving party. 11 | /// 12 | public required Guid ToPartyUuid { get; init; } 13 | 14 | /// 15 | /// Gets the role code. 16 | /// 17 | public required string RoleCode { get; init; } 18 | 19 | /// 20 | public override int GetHashCode() 21 | => HashCode.Combine( 22 | ToPartyUuid, 23 | string.GetHashCode(RoleCode, StringComparison.OrdinalIgnoreCase)); 24 | 25 | /// 26 | public bool Equals(A2PartyExternalRoleAssignment? other) 27 | => other is not null 28 | && ToPartyUuid == other.ToPartyUuid 29 | && string.Equals(RoleCode, other.RoleCode, StringComparison.OrdinalIgnoreCase); 30 | } 31 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Persistence/Migration/v0.23-user/00-user-table.sql: -------------------------------------------------------------------------------- 1 | -- Table: register.user 2 | 3 | -- note: The id column is an internal identifier for the user in the register system. It only exists to be a primary 4 | -- key for the user table. It should never be exposed to the outside world. The uuid column is what ties the user to 5 | -- a party, but given that the user table contains historical user data, the uuid column is not a unique identifier, 6 | -- and cannot be used as the primary key. The user_id column is Altinn 2 user id. 7 | CREATE TABLE register.user ( 8 | id BIGSERIAL PRIMARY KEY NOT NULL, 9 | uuid uuid NOT NULL REFERENCES register.party(uuid) ON DELETE CASCADE ON UPDATE CASCADE, 10 | user_id BIGINT NOT NULL, 11 | is_active BOOLEAN NOT NULL DEFAULT TRUE 12 | ) 13 | TABLESPACE pg_default; 14 | 15 | CREATE INDEX ix_user_uuid ON register.user (uuid) TABLESPACE pg_default; 16 | 17 | CREATE UNIQUE INDEX uq_user_id ON register.user (user_id) INCLUDE (uuid) TABLESPACE pg_default; 18 | 19 | CREATE UNIQUE INDEX uq_user_active_uuid ON register.user (uuid) TABLESPACE pg_default 20 | WHERE 21 | is_active = TRUE; 22 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/UnitTests/OpaqueTests.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System.Text.Json; 4 | using Altinn.Register.Models; 5 | 6 | namespace Altinn.Register.Tests.UnitTests; 7 | 8 | public class OpaqueTests 9 | { 10 | public static TheoryData StringData => new() 11 | { 12 | string.Empty, 13 | "f", 14 | "fo", 15 | "foo", 16 | "ÿïø øû迸", 17 | }; 18 | 19 | [Theory] 20 | [MemberData(nameof(StringData))] 21 | public void RoundTrips(string s) 22 | { 23 | var data = new Opaque(s); 24 | var serialized = data.ToString(); 25 | var parsed = Opaque.Parse(serialized, null); 26 | 27 | parsed.Value.Should().Be(s); 28 | } 29 | 30 | [Theory] 31 | [MemberData(nameof(StringData))] 32 | public void RoundTripsJson(string s) 33 | { 34 | var data = new Opaque(s); 35 | var json = JsonSerializer.Serialize(data); 36 | var parsed = JsonSerializer.Deserialize>(json); 37 | 38 | Assert.NotNull(parsed); 39 | parsed.Value.Should().Be(s); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Contracts.MassTransit/ExternalRoles/ExternalRoleReference.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Altinn.Register.Contracts.ExternalRoles; 4 | 5 | /// 6 | /// Represents a reference to an external role. 7 | /// 8 | public sealed record ExternalRoleReference 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The source of the external role. 14 | /// The identifier of the external role. 15 | [SetsRequiredMembers] 16 | public ExternalRoleReference(ExternalRoleSource source, string identifier) 17 | { 18 | Source = source; 19 | Identifier = identifier; 20 | } 21 | 22 | /// 23 | /// Gets the source of the external role. 24 | /// 25 | public required ExternalRoleSource Source { get; init; } 26 | 27 | /// 28 | /// Gets the identifier of the external role. 29 | /// 30 | public required string Identifier { get; init; } 31 | } 32 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(pers.id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookup(pers.id) 3 | 4 | WITH uuids_by_person_identifier AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."person_identifier" = ANY (@personIdentifiers) 8 | ), 9 | top_level_uuids AS ( 10 | SELECT "uuid", version_id FROM uuids_by_person_identifier 11 | ), 12 | uuids AS ( 13 | SELECT 14 | "uuid" AS "uuid", 15 | NULL::uuid AS parent_uuid, 16 | version_id AS sort_first, 17 | NULL::uuid AS sort_second 18 | FROM top_level_uuids 19 | ) 20 | SELECT 21 | party.uuid p_uuid, 22 | party.id p_id, 23 | party.party_type p_party_type, 24 | party.display_name p_display_name, 25 | party.person_identifier p_person_identifier, 26 | party.organization_identifier p_organization_identifier 27 | FROM uuids AS uuids 28 | INNER JOIN register.party AS party USING (uuid) 29 | ORDER BY 30 | uuids.sort_first, 31 | uuids.sort_second NULLS FIRST 32 | -------------------------------------------------------------------------------- /src/pkgs/Altinn.Register.Contracts/test/Altinn.Register.Contracts.Tests/Snapshots/OrganizationTests.MaximalOrganization.verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "partyType": "organization", 3 | "organizationIdentifier": "123456785", 4 | "unitStatus": "Unit Status", 5 | "unitType": "Unit Type", 6 | "telephoneNumber": "Telephone Number", 7 | "mobileNumber": "Mobile Number", 8 | "faxNumber": "Fax Number", 9 | "emailAddress": "email@address.example.com", 10 | "internetAddress": "internet.address.example.com", 11 | "mailingAddress": { 12 | "address": "Mailing Address Lines", 13 | "postalCode": "1234", 14 | "city": "City Name" 15 | }, 16 | "businessAddress": { 17 | "address": "Business Address Lines", 18 | "postalCode": "1234", 19 | "city": "City Name" 20 | }, 21 | "partyUuid": "00000000-0000-0000-0000-000000000001", 22 | "versionId": 1, 23 | "urn": "urn:altinn:party:uuid:00000000-0000-0000-0000-000000000001", 24 | "partyId": 12345678, 25 | "displayName": "Display Name", 26 | "createdAt": "2020-01-02T03:04:05+00:00", 27 | "modifiedAt": "2022-05-06T07:08:09+00:00", 28 | "isDeleted": false, 29 | "deletedAt": null, 30 | "user": null 31 | } 32 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.MassTransit/IMassTransitBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Altinn.Authorization.ServiceDefaults.MassTransit; 5 | 6 | /// 7 | /// Builder for configuring mass transit. 8 | /// 9 | public interface IMassTransitBuilder 10 | { 11 | /// 12 | /// Gets the service collection. 13 | /// 14 | public IServiceCollection Services { get; } 15 | } 16 | 17 | /// 18 | /// Implementation of . 19 | /// 20 | internal class MassTransitBuilder 21 | : IMassTransitBuilder 22 | { 23 | private readonly IConfiguration _configuration; 24 | 25 | /// 26 | /// Initializes a new instance of . 27 | /// 28 | public MassTransitBuilder(IServiceCollection serviceProvider, IConfiguration configuration) 29 | { 30 | Services = serviceProvider; 31 | _configuration = configuration; 32 | } 33 | 34 | /// 35 | public IServiceCollection Services { get; } 36 | } 37 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Controllers/ErrorController.cs: -------------------------------------------------------------------------------- 1 | using Asp.Versioning; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace Altinn.Register.Controllers 6 | { 7 | /// 8 | /// Handles the presentation of unhandled exceptions during the execution of a request. 9 | /// 10 | [ApiController] 11 | [ApiVersion(1.0)] 12 | [ApiExplorerSettings(IgnoreApi = true)] 13 | [AllowAnonymous] 14 | [Route("register/api/v{version:apiVersion}/error")] 15 | public class ErrorController : ControllerBase 16 | { 17 | /// 18 | /// Create a response with a new instance with limited information. 19 | /// 20 | /// 21 | /// This method cannot be called directly. It is used by the API framework as a way to output ProblemDetails 22 | /// if there has been an unhandled exception. 23 | /// 24 | /// A new instance. 25 | [Route("")] 26 | public IActionResult Error() => Problem(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Jobs/Altinn.Authorization.ServiceDefaults.Jobs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(user.id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookup(user.id) 3 | 4 | WITH uuids_by_user_id AS ( 5 | SELECT "user"."uuid", party.version_id 6 | FROM register."user" AS "user" 7 | INNER JOIN register.party AS party USING (uuid) 8 | WHERE "user".user_id = ANY (@userIds) 9 | ), 10 | top_level_uuids AS ( 11 | SELECT "uuid", version_id FROM uuids_by_user_id 12 | ), 13 | uuids AS ( 14 | SELECT 15 | "uuid" AS "uuid", 16 | NULL::uuid AS parent_uuid, 17 | version_id AS sort_first, 18 | NULL::uuid AS sort_second 19 | FROM top_level_uuids 20 | ) 21 | SELECT 22 | party.uuid p_uuid, 23 | party.id p_id, 24 | party.party_type p_party_type, 25 | party.display_name p_display_name, 26 | party.person_identifier p_person_identifier, 27 | party.organization_identifier p_organization_identifier 28 | FROM uuids AS uuids 29 | INNER JOIN register.party AS party USING (uuid) 30 | ORDER BY 31 | uuids.sort_first, 32 | uuids.sort_second NULLS FIRST 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(org.id).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookup(org.id) 3 | 4 | WITH uuids_by_organization_identifier AS ( 5 | SELECT party."uuid", party.version_id 6 | FROM register.party AS party 7 | WHERE party."organization_identifier" = ANY (@organizationIdentifiers) 8 | ), 9 | top_level_uuids AS ( 10 | SELECT "uuid", version_id FROM uuids_by_organization_identifier 11 | ), 12 | uuids AS ( 13 | SELECT 14 | "uuid" AS "uuid", 15 | NULL::uuid AS parent_uuid, 16 | version_id AS sort_first, 17 | NULL::uuid AS sort_second 18 | FROM top_level_uuids 19 | ) 20 | SELECT 21 | party.uuid p_uuid, 22 | party.id p_id, 23 | party.party_type p_party_type, 24 | party.display_name p_display_name, 25 | party.person_identifier p_person_identifier, 26 | party.organization_identifier p_organization_identifier 27 | FROM uuids AS uuids 28 | INNER JOIN register.party AS party USING (uuid) 29 | ORDER BY 30 | uuids.sort_first, 31 | uuids.sort_second NULLS FIRST 32 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Persistence.Tests/Snapshots/PartyQueryTests.VerifyQuerySnapshot_includes=display-name,identifiers_filterBy=lookup(user.name).verified.sql: -------------------------------------------------------------------------------- 1 | -- include: display-name,identifiers 2 | -- filter: lookup(user.name) 3 | 4 | WITH uuids_by_username AS ( 5 | SELECT "user"."uuid", party.version_id 6 | FROM register."user" AS "user" 7 | INNER JOIN register.party AS party USING (uuid) 8 | WHERE "user".username = ANY (@usernames) 9 | ), 10 | top_level_uuids AS ( 11 | SELECT "uuid", version_id FROM uuids_by_username 12 | ), 13 | uuids AS ( 14 | SELECT 15 | "uuid" AS "uuid", 16 | NULL::uuid AS parent_uuid, 17 | version_id AS sort_first, 18 | NULL::uuid AS sort_second 19 | FROM top_level_uuids 20 | ) 21 | SELECT 22 | party.uuid p_uuid, 23 | party.id p_id, 24 | party.party_type p_party_type, 25 | party.display_name p_display_name, 26 | party.person_identifier p_person_identifier, 27 | party.organization_identifier p_organization_identifier 28 | FROM uuids AS uuids 29 | INNER JOIN register.party AS party USING (uuid) 30 | ORDER BY 31 | uuids.sort_first, 32 | uuids.sort_second NULLS FIRST 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/PartyImport/UpsertPartyTracking.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System.Diagnostics.CodeAnalysis; 4 | using CommunityToolkit.Diagnostics; 5 | 6 | namespace Altinn.Register.PartyImport; 7 | 8 | /// 9 | /// Tracking information for a party import. 10 | /// 11 | public readonly record struct UpsertPartyTracking 12 | { 13 | /// 14 | /// Initializes a new instance of the record. 15 | /// 16 | /// The job name. 17 | /// The progress. 18 | [SetsRequiredMembers] 19 | public UpsertPartyTracking(string jobName, ulong progress) 20 | { 21 | Guard.IsNotNull(jobName); 22 | Guard.IsGreaterThan(progress, 0); 23 | 24 | JobName = jobName; 25 | Progress = progress; 26 | } 27 | 28 | /// 29 | /// The name of the job that imported the party. 30 | /// 31 | public required string JobName { get; init; } 32 | 33 | /// 34 | /// The change ID of the party. 35 | /// 36 | public required ulong Progress { get; init; } 37 | } 38 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Utils/ContractsMapper.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Register.Contracts.ExternalRoles; 2 | using Altinn.Register.Contracts.Parties; 3 | using Altinn.Register.Core.Parties.Records; 4 | 5 | namespace Altinn.Register.Utils; 6 | 7 | /// 8 | /// Utility class for mapping models to contracts. 9 | /// 10 | internal static class ContractsMapper 11 | { 12 | /// 13 | /// Maps a party uuid to a party reference contract. 14 | /// 15 | /// The party UUID. 16 | /// A . 17 | public static PartyReference ToPartyReferenceContract(this Guid partyUuid) 18 | => new(partyUuid); 19 | 20 | /// 21 | /// Maps a to a . 22 | /// 23 | /// The external role assignment event. 24 | /// A . 25 | public static ExternalRoleReference ToPartyExternalRoleReferenceContract(this ExternalRoleAssignmentEvent evt) 26 | => new(evt.RoleSource, evt.RoleIdentifier); 27 | } 28 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/UnitTests/PartyTypesSetTests.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Register.Core.Parties.Records; 4 | using Altinn.Register.Models; 5 | 6 | namespace Altinn.Register.Tests.UnitTests; 7 | 8 | public class PartyTypesSetTests 9 | { 10 | [Fact] 11 | public void Contains_AllTypes() 12 | { 13 | PartyTypes allBitsSet = ~PartyTypes.None; 14 | var partyTypesSet = new PartyTypesSet(allBitsSet); 15 | 16 | var allPartyTypes = Enum.GetValues(); 17 | partyTypesSet.Should().HaveCount(allPartyTypes.Length); 18 | 19 | foreach (var partyType in allPartyTypes) 20 | { 21 | partyTypesSet.Contains(partyType).Should().BeTrue("Expected PartyTypesSet to contain party type {0}", partyType); 22 | } 23 | 24 | var enumerated = partyTypesSet.ToList(); 25 | enumerated.Should().HaveCount(allPartyTypes.Length, "Expected enumeration to yield all party types"); 26 | 27 | foreach (var partyType in allPartyTypes) 28 | { 29 | enumerated.Should().Contain(partyType, "Expected enumeration to contain party type {0}", partyType); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/Parties/Records/PartyRecordType.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Altinn.Authorization.ModelUtils; 3 | 4 | namespace Altinn.Register.Core.Parties.Records; 5 | 6 | /// 7 | /// Represents a party type. 8 | /// 9 | [StringEnumConverter(JsonKnownNamingPolicy.KebabCaseLower)] 10 | public enum PartyRecordType 11 | { 12 | /// 13 | /// Person party type. 14 | /// 15 | [JsonStringEnumMemberName("person")] 16 | Person = 1, 17 | 18 | /// 19 | /// Organization party type. 20 | /// 21 | [JsonStringEnumMemberName("organization")] 22 | Organization, 23 | 24 | /// 25 | /// Self-identified user party type. 26 | /// 27 | [JsonStringEnumMemberName("self-identified-user")] 28 | SelfIdentifiedUser, 29 | 30 | /// 31 | /// System user party type. 32 | /// 33 | [JsonStringEnumMemberName("system-user")] 34 | SystemUser, 35 | 36 | /// 37 | /// Enterprise user party type. 38 | /// 39 | [JsonStringEnumMemberName("enterprise-user")] 40 | EnterpriseUser, 41 | } 42 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Contracts.MassTransit/ExternalRoles/ExternalRoleAssignmentAddedEvent.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ServiceDefaults.MassTransit; 2 | using Altinn.Register.Contracts.Parties; 3 | using MassTransit; 4 | 5 | namespace Altinn.Register.Contracts.ExternalRoles; 6 | 7 | /// 8 | /// An event that is published when an external role assignment is added. 9 | /// 10 | [MessageUrn("event:altinn-register:external-role-assignment-added")] 11 | public sealed record ExternalRoleAssignmentAddedEvent 12 | : EventBase 13 | { 14 | /// 15 | /// Gets the version ID of the event. 16 | /// 17 | public required ulong VersionId { get; init; } 18 | 19 | /// 20 | /// Gets the role that was assigned. 21 | /// 22 | public required ExternalRoleReference Role { get; init; } 23 | 24 | /// 25 | /// Gets the party that the role was assigned from. 26 | /// 27 | public required PartyReference From { get; init; } 28 | 29 | /// 30 | /// Gets the party that the role was assigned to. 31 | /// 32 | public required PartyReference To { get; init; } 33 | } 34 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/LeaseNames.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using Altinn.Register.PartyImport.A2; 4 | using Altinn.Register.PartyImport.SystemUser; 5 | 6 | namespace Altinn.Register; 7 | 8 | /// 9 | /// Lease names for register. 10 | /// 11 | internal static class LeaseNames 12 | { 13 | /// 14 | /// Lease name for . 15 | /// 16 | internal const string A2PartyImport = "a2-party-import"; 17 | 18 | /// 19 | /// Lease name for . 20 | /// 21 | internal const string A2PartyUserIdImport = "a2-party-userid-import"; 22 | 23 | /// 24 | /// Lease name for . 25 | /// 26 | internal const string A2ProfileImport = "a2-profile-import"; 27 | 28 | /// 29 | /// Lease name for . 30 | /// 31 | internal const string SystemUserImport = "systemuser-import"; 32 | 33 | /// 34 | /// Lease name for party cleanup job. 35 | /// 36 | internal const string PartyCleanup = "db:party-cleanup"; 37 | } 38 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/skd-org.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDAjCCAeqgAwIBAgIIQtFLr5On1tIwDQYJKoZIhvcNAQELBQAwDjEMMAoGA1UE 3 | AxMDc2tkMB4XDTIwMDUyNTEyMjE0NFoXDTMwMDUyNDEyMjE0NFowDjEMMAoGA1UE 4 | AxMDc2tkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3PMrdoHBX0X/ 5 | Jkr7abPlsdOBB2Gka+zXf63gADz3xvzyE3LSAV75VgT1LgJD+DuljJj/lNpXwUxe 6 | Dae4ypLYCH0E+NZBnUQbcNry9BB3wbe8yXZIy/7vwbV23YxXxnipWAbfQnVQEIWC 7 | QpR0Ew0rq/Tbn17+ImonzbwM7XiVADdUjzAlJ9Tfesr/lnOqEozDlhfEHTc2z4bF 8 | 3AvfvwED5s5/O1GMjzGohrVUF3jVLIRbZHsLSZljNbdawMqMh4Fh5s9+iNkYavQS 9 | W4rIirCmQuGT0VgP6mHXmmXHsjGeuXzXcP334XIRZcTBjGL+a/+3Gn1+imezcfVR 10 | +KOFqIj0WQIDAQABo2QwYjAgBgNVHQ4BAf8EFgQUKOVCRgdUqH1PyiLziJhkEVfI 11 | 9lgwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH/BBYwFAYI 12 | KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBwxA1sGsW1BXmn 13 | HpZmTQBCoHWywUk2gWfJTjgmSTPdh55KlO+F9+1bCOx2+HYrGMEwKmyq5JR3W9zT 14 | zPfqd5wPJClqL0LrZG0k2Rc1BTbqDysbMFn/ygC5iLgez8pByDa0V3oJfoUVWnES 15 | uPFjAwCrlLPvyq/KeT6KtcynrDwg9dPgqirx0TAY11LlgkRwZHSGhtK2q8KWI6nU 16 | jsrYw7rH55KNzzJETTd0P6es644+WI2/XRUlPTJWIgnAaXH2hS+wLNrtB1DoVPey 17 | XNxeSUEFH2eqdDAnTjCD2Imk1KDNnnKG9/89i6AvB8SkyjB6cx4ZRNWW92vGcfur 18 | y5BlJ7Cr 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/ttd-org.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDAzCCAeugAwIBAgIJANTdO8o3I8x5MA0GCSqGSIb3DQEBCwUAMA4xDDAKBgNV 3 | BAMTA3R0ZDAeFw0yMDA1MjUxMjIxMzdaFw0zMDA1MjQxMjIxMzdaMA4xDDAKBgNV 4 | BAMTA3R0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcfTsXwwLyC 5 | UkIz06eadWJvG3yrzT+ZB2Oy/WPaZosDnPcnZvCDueN+oy0zTx5TyH5gCi1FvzX2 6 | 7G2eZEKwQaRPv0yuM+McHy1rXxMSOlH/ebP9KJj3FDMUgZl1DCAjJxSAANdTwdrq 7 | ydVg1Crp37AQx8IIEjnBhXsfQh1uPGt1XwgeNyjl00IejxvQOPzd1CofYWwODVtQ 8 | l3PKn1SEgOGcB6wuHNRlnZPCIelQmqxWkcEZiu/NU+kst3NspVUQG2Jf2AF8UWgC 9 | rnrhMQR0Ra1Vi7bWpu6QIKYkN9q0NRHeRSsELOvTh1FgDySYJtNd2xDRSf6IvOiu 10 | tSipl1NZlV0CAwEAAaNkMGIwIAYDVR0OAQH/BBYEFIwq/KbSMzLETdo9NNxj0rz4 11 | qMqVMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQG 12 | CCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAE56UmH5gEYbe 13 | 1kVw7nrfH0R9FyVZGeQQWBn4/6Ifn+eMS9mxqe0Lq74Ue1zEzvRhRRqWYi9JlKNf 14 | 7QQNrc+DzCceIa1U6cMXgXKuXquVHLmRfqvKHbWHJfIkaY8Mlfy++77UmbkvIzly 15 | T1HVhKKp6Xx0r5koa6frBh4Xo/vKBlEyQxWLWF0RPGpGErnYIosJ41M3Po3nw3lY 16 | f7lmH47cdXatcntj2Ho/b2wGi9+W29teVCDfHn2/0oqc7K0EOY9c2ODLjUvQyPZR 17 | OD2yykpyh9x/YeYHFDYdLDJ76/kIdxN43kLU4/hTrh9tMb1PZF+/4DshpAlRoQuL 18 | o8I8avQm/A== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Contracts.MassTransit/ExternalRoles/ExternalRoleAssignmentRemovedEvent.cs: -------------------------------------------------------------------------------- 1 | using Altinn.Authorization.ServiceDefaults.MassTransit; 2 | using Altinn.Register.Contracts.Parties; 3 | using MassTransit; 4 | 5 | namespace Altinn.Register.Contracts.ExternalRoles; 6 | 7 | /// 8 | /// An event that is published when an external role assignment is removed. 9 | /// 10 | [MessageUrn("event:altinn-register:external-role-assignment-removed")] 11 | public sealed record ExternalRoleAssignmentRemovedEvent 12 | : EventBase 13 | { 14 | /// 15 | /// Gets the version ID of the event. 16 | /// 17 | public required ulong VersionId { get; init; } 18 | 19 | /// 20 | /// Gets the role that was assigned. 21 | /// 22 | public required ExternalRoleReference Role { get; init; } 23 | 24 | /// 25 | /// Gets the party that the role was assigned from. 26 | /// 27 | public required PartyReference From { get; init; } 28 | 29 | /// 30 | /// Gets the party that the role was assigned to. 31 | /// 32 | public required PartyReference To { get; init; } 33 | } 34 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/JobNames.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | namespace Altinn.Register; 4 | 5 | /// 6 | /// Job names for register. 7 | /// 8 | internal static class JobNames 9 | { 10 | /// 11 | /// Job name for A2 party import-party job. 12 | /// 13 | internal const string A2PartyImportParty = $"{LeaseNames.A2PartyImport}:party"; 14 | 15 | /// 16 | /// Job name for A2 party import-external-roles job. 17 | /// 18 | internal const string A2PartyImportCCRRoleAssignments = $"{LeaseNames.A2PartyImport}:ccr-roles"; 19 | 20 | /// 21 | /// Job name for A2 party import-userid job. 22 | /// 23 | internal const string A2PartyUserIdImport = $"{LeaseNames.A2PartyUserIdImport}:userid"; 24 | 25 | /// 26 | /// Job name for A2 profile-changes import job. 27 | /// 28 | internal const string A2ProfileChangesImport = $"{LeaseNames.A2ProfileImport}:profile-changes"; 29 | 30 | /// 31 | /// Job name for system user import job. 32 | /// 33 | internal const string SystemUserImport = $"{LeaseNames.SystemUserImport}:systemuser"; 34 | } 35 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/UnitTests/EventBaseTests.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Altinn.Authorization.ServiceDefaults.MassTransit; 3 | using MassTransit; 4 | 5 | namespace Altinn.Register.Tests.UnitTests; 6 | 7 | public class EventBaseTests 8 | { 9 | private static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web); 10 | 11 | [Fact] 12 | public void TestCommand_RoundTrips_WithCorrelationId() 13 | { 14 | var evt = new TestEvent 15 | { 16 | Foo = "foo", 17 | Bar = "bar", 18 | }; 19 | 20 | var correlationId = ((CorrelatedBy)evt).CorrelationId; 21 | correlationId.Should().NotBeEmpty(); 22 | 23 | var json = JsonSerializer.Serialize(evt, Options); 24 | var roundTripped = JsonSerializer.Deserialize(json, Options); 25 | 26 | roundTripped.Should().BeEquivalentTo(evt); 27 | ((CorrelatedBy)roundTripped).CorrelationId.Should().Be(correlationId); 28 | } 29 | 30 | private sealed record TestEvent 31 | : EventBase 32 | { 33 | public required string Foo { get; init; } 34 | 35 | public required string Bar { get; init; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.TestUtils.V2/Assertions/CustomAssertions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Altinn.Register.TestUtils.Assertions; 5 | 6 | /// 7 | /// Base class for custom assertions. 8 | /// 9 | /// The type being asserted over. 10 | /// The subject of assertions. 11 | [DebuggerNonUserCode] 12 | [ExcludeFromCodeCoverage] 13 | public abstract class CustomAssertions(T subject) 14 | { 15 | /// 16 | /// Gets the field value which is being asserted. 17 | /// 18 | public T Subject { get; } = subject; 19 | 20 | /// 21 | /// Used in error messages to identify the subject being asserted. 22 | /// 23 | protected abstract string Identifier { get; } 24 | 25 | /// 26 | public override bool Equals(object? obj) => 27 | throw new NotSupportedException("Equals is not part of Fluent Assertions. Did you mean Be() instead?"); 28 | 29 | /// 30 | public override int GetHashCode() => 31 | throw new NotSupportedException("GetHashCode is not part of Fluent Assertions."); 32 | } 33 | -------------------------------------------------------------------------------- /.justfile: -------------------------------------------------------------------------------- 1 | # Cross platform shebang: 2 | shebang := if os() == 'windows' { 3 | 'pwsh.exe' 4 | } else { 5 | '/usr/bin/env pwsh' 6 | } 7 | 8 | # Set shell for non-Windows OSs: 9 | set shell := ["pwsh", "-CommandWithArgs"] 10 | 11 | # Set shell for Windows OSs: 12 | set windows-shell := ["pwsh.exe", "-NoLogo", "-CommandWithArgs"] 13 | 14 | [private] 15 | @default: 16 | just --choose 17 | 18 | # Install node packages required to run scripts - uses pnpm to install the packages 19 | [private] 20 | @install-script-packages: 21 | #!{{shebang}} 22 | pushd .github/scripts 23 | pnpm install 24 | 25 | [private] 26 | @install-script-packages-frozen: 27 | #!{{shebang}} 28 | pushd .github/scripts 29 | pnpm install --frozen-lockfile 30 | 31 | # Print all projects metadata 32 | @get-metadata: install-script-packages-frozen 33 | #!{{shebang}} 34 | node ./.github/scripts/get-metadata.mts 35 | 36 | # Run the script to update solution files 37 | @update-sln-files *ARGS: install-script-packages-frozen 38 | #!{{shebang}} 39 | node ./.github/scripts/update-sln-files.mts -- {{ARGS}} 40 | 41 | @generate-guardianship-migration FILE: install-script-packages-frozen 42 | #!{{shebang}} 43 | node ./.github/scripts/generate-guardianship-migration.mts > "{{FILE}}" 44 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register.Core/PartyImport/A2/A2UserProfileChange.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Register.Core.PartyImport.A2; 2 | 3 | /// 4 | /// Represents a change-event for a user profile in Altinn 2. 5 | /// 6 | public sealed record A2UserProfileChange 7 | : A2Change 8 | { 9 | /// 10 | /// Gets the user id that changed. 11 | /// 12 | public required uint UserId { get; init; } 13 | 14 | /// 15 | /// Gets the user uuid that changed. 16 | /// 17 | public required Guid? UserUuid { get; init; } 18 | 19 | /// 20 | /// Gets the party uuid of the owner of the user profile. 21 | /// 22 | public required Guid OwnerPartyUuid { get; init; } 23 | 24 | /// 25 | /// Gets the user name of the user profile. 26 | /// 27 | public required string? UserName { get; init; } 28 | 29 | /// 30 | /// Gets whether the user profile is deleted or not. 31 | /// 32 | public required bool IsDeleted { get; init; } 33 | 34 | /// 35 | /// Gets the type of the user profile. 36 | /// 37 | public required A2UserProfileType ProfileType { get; init; } 38 | } 39 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/Altinn.Register/Utils/HttpReciliencyExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Http.Resilience; 2 | 3 | namespace Altinn.Register.Utils; 4 | 5 | /// 6 | /// Extensions methods to help with HTTP resiliency. 7 | /// 8 | internal static class HttpReciliencyExtensions 9 | { 10 | /// 11 | /// Configures the standard resilience handler for the HTTP client. 12 | /// 13 | /// A . 14 | /// A configuration delegate. 15 | /// . 16 | public static IHttpClientBuilder ReplaceResilienceHandler(this IHttpClientBuilder builder, Action configure) 17 | { 18 | builder.ConfigureAdditionalHttpMessageHandlers((handlers, _) => 19 | { 20 | for (int i = handlers.Count - 1; i >= 0; i--) 21 | { 22 | if (handlers[i] is ResilienceHandler) 23 | { 24 | handlers.RemoveAt(i); 25 | } 26 | } 27 | }); 28 | 29 | builder.AddStandardResilienceHandler(configure); 30 | 31 | return builder; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/test/Altinn.Register.Tests/UnitTests/CommandBaseTests.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Altinn.Authorization.ServiceDefaults.MassTransit; 3 | using MassTransit; 4 | 5 | namespace Altinn.Register.Tests.UnitTests; 6 | 7 | public class CommandBaseTests 8 | { 9 | private static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web); 10 | 11 | [Fact] 12 | public void TestCommand_RoundTrips_WithCorrelationId() 13 | { 14 | var command = new TestCommand 15 | { 16 | Foo = "foo", 17 | Bar = "bar", 18 | }; 19 | 20 | var correlationId = ((CorrelatedBy)command).CorrelationId; 21 | correlationId.Should().NotBeEmpty(); 22 | 23 | var json = JsonSerializer.Serialize(command, Options); 24 | var roundTripped = JsonSerializer.Deserialize(json, Options); 25 | 26 | roundTripped.Should().BeEquivalentTo(command); 27 | ((CorrelatedBy)roundTripped).CorrelationId.Should().Be(correlationId); 28 | } 29 | 30 | private sealed record TestCommand 31 | : CommandBase 32 | { 33 | public required string Foo { get; init; } 34 | 35 | public required string Bar { get; init; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/apps/Altinn.Register/src/ServiceDefaults.Leases/LeaseReleaseResult.cs: -------------------------------------------------------------------------------- 1 | namespace Altinn.Authorization.ServiceDefaults.Leases; 2 | 3 | /// 4 | /// The result of calling . 5 | /// 6 | public sealed class LeaseReleaseResult 7 | { 8 | /// 9 | /// Gets a value indicating whether or not the lease was released. 10 | /// 11 | public required bool IsReleased { get; init; } 12 | 13 | /// 14 | /// Gets when the lease will expire (regardless of whether it was acquired). 15 | /// 16 | public required DateTimeOffset Expires { get; init; } 17 | 18 | /// 19 | /// Gets when the lease was last acquired at. 20 | /// 21 | /// 22 | /// This does not signify that the lease is currently held. 23 | /// 24 | public required DateTimeOffset? LastAcquiredAt { get; init; } 25 | 26 | /// 27 | /// Gets when the lease was last released at. 28 | /// 29 | /// 30 | /// This does not signify that the lease is not currently held. 31 | /// 32 | public required DateTimeOffset? LastReleasedAt { get; init; } 33 | } 34 | --------------------------------------------------------------------------------