diff --git a/apps/services/auth/admin-api/infra/auth-admin-api.ts b/apps/services/auth/admin-api/infra/auth-admin-api.ts index 0fc25a62d5d4..cf773f0dbd94 100644 --- a/apps/services/auth/admin-api/infra/auth-admin-api.ts +++ b/apps/services/auth/admin-api/infra/auth-admin-api.ts @@ -4,7 +4,12 @@ import { service, ServiceBuilder, } from '../../../../../infra/src/dsl/dsl' -import { Base, Client, RskProcuring } from '../../../../../infra/src/dsl/xroad' +import { + Base, + Client, + NationalRegistryAuthB2C, + RskProcuring, +} from '../../../../../infra/src/dsl/xroad' const REDIS_NODE_CONFIG = { dev: json([ @@ -84,8 +89,10 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-admin-api'> => { '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET', SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME', SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD', + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: + '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET', }) - .xroad(Base, Client, RskProcuring) + .xroad(Base, Client, RskProcuring, NationalRegistryAuthB2C) .ingress({ primary: { host: { diff --git a/apps/services/auth/admin-api/src/app/app.module.ts b/apps/services/auth/admin-api/src/app/app.module.ts index dc56c6fe0343..5f947672c654 100644 --- a/apps/services/auth/admin-api/src/app/app.module.ts +++ b/apps/services/auth/admin-api/src/app/app.module.ts @@ -9,8 +9,10 @@ import { import { AuthModule } from '@island.is/auth-nest-tools' import { RskRelationshipsClientConfig } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientConfig } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientConfig } from '@island.is/clients/national-registry-v3' import { CompanyRegistryConfig } from '@island.is/clients/rsk/company-registry' import { SyslumennClientConfig } from '@island.is/clients/syslumenn' +import { ZendeskServiceConfig } from '@island.is/clients/zendesk' import { AuditModule } from '@island.is/nest/audit' import { IdsClientConfig, XRoadConfig } from '@island.is/nest/config' import { FeatureFlagConfig } from '@island.is/nest/feature-flags' @@ -31,7 +33,6 @@ import { ProvidersModule } from './v2/providers/providers.module' import { ScopesModule } from './v2/scopes/scopes.module' import { ClientSecretsModule } from './v2/secrets/client-secrets.module' import { TenantsModule } from './v2/tenants/tenants.module' -import { ZendeskServiceConfig } from '@island.is/clients/zendesk' @Module({ imports: [ @@ -61,6 +62,7 @@ import { ZendeskServiceConfig } from '@island.is/clients/zendesk' DelegationConfig, RskRelationshipsClientConfig, NationalRegistryClientConfig, + NationalRegistryV3ClientConfig, CompanyRegistryConfig, FeatureFlagConfig, XRoadConfig, diff --git a/apps/services/auth/delegation-api/src/app/app.module.ts b/apps/services/auth/delegation-api/src/app/app.module.ts index ac10af9d95f1..71b347827378 100644 --- a/apps/services/auth/delegation-api/src/app/app.module.ts +++ b/apps/services/auth/delegation-api/src/app/app.module.ts @@ -9,6 +9,7 @@ import { import { AuthModule } from '@island.is/auth-nest-tools' import { RskRelationshipsClientConfig } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientConfig } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientConfig } from '@island.is/clients/national-registry-v3' import { CompanyRegistryConfig } from '@island.is/clients/rsk/company-registry' import { SyslumennClientConfig } from '@island.is/clients/syslumenn' import { AuditModule } from '@island.is/nest/audit' @@ -48,6 +49,7 @@ import { ScopesModule } from './scopes/scopes.module' FeatureFlagConfig, IdsClientConfig, NationalRegistryClientConfig, + NationalRegistryV3ClientConfig, RskRelationshipsClientConfig, CompanyRegistryConfig, XRoadConfig, diff --git a/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts b/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts index 6f5c526d1188..377a7771410b 100644 --- a/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts +++ b/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts @@ -8,9 +8,11 @@ import { Delegation, DelegationScope, DelegationsIndexService, + NationalRegistryV3FeatureService, } from '@island.is/auth-api-lib' import { RskRelationshipsClient } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { CompanyRegistryClientService } from '@island.is/clients/rsk/company-registry' import { expectMatchingDelegations, @@ -23,189 +25,219 @@ import { accessIncomingTestCases } from '../../../../test/access-incoming-test-c import { setupWithAuth } from '../../../../test/setup' import { filterExpectedDelegations } from './utils' +const fromName: string = faker.name.findName() + describe('MeDelegationsController', () => { - describe.each(Object.keys(accessIncomingTestCases))( - 'Incoming Access with test case: %s', - (caseName) => { - const testCase = accessIncomingTestCases[caseName] - let app: TestApp - let server: request.SuperTest - let factory: FixtureFactory - let delegations: Delegation[] = [] - const fromName = faker.name.findName() - let delegationIndexService: DelegationsIndexService - - beforeAll(async () => { - // Arrange - app = await setupWithAuth({ - user: testCase.user, - customScopeRules: testCase.customScopeRules, - }) - server = request(app.getHttpServer()) - delegationIndexService = app.get(DelegationsIndexService) - const rskRelationshipsClientService = app.get(RskRelationshipsClient) - const nationalRegistryClientService = app.get( - NationalRegistryClientService, - ) - const companyRegistryClientService = app.get( - CompanyRegistryClientService, - ) - jest - .spyOn(nationalRegistryClientService, 'getCustodyChildren') - .mockImplementation(async () => []) - jest - .spyOn(nationalRegistryClientService, 'getIndividual') - .mockImplementation(async (nationalId: string) => - createNationalRegistryUser({ - nationalId, - name: fromName, - }), - ) - jest - .spyOn(rskRelationshipsClientService, 'getIndividualRelationships') - .mockImplementation(async () => null) - jest - .spyOn(companyRegistryClientService, 'getCompany') - .mockImplementation(async () => null) - jest - .spyOn(delegationIndexService, 'indexDelegations') - .mockImplementation() - jest - .spyOn(delegationIndexService, 'indexCustomDelegations') - .mockImplementation() - - factory = new FixtureFactory(app) - await Promise.all( - testCase.domains.map((domain) => factory.createDomain(domain)), - ) - await Promise.all( - (testCase.accessTo ?? []).map((scope) => - factory.createApiScopeUserAccess({ - nationalId: testCase.user.nationalId, - scope, - }), - ), - ) - }) - - beforeEach(async () => { - if (delegations) { - await Delegation.destroy({ - where: { - id: { [Op.in]: delegations.map((delegation) => delegation.id) }, - }, + describe.each([false, true])( + 'national registry v3 featureflag: %s', + (featureFlag) => { + describe.each(Object.keys(accessIncomingTestCases))( + 'Incoming Access with test case: %s', + (caseName) => { + const testCase = accessIncomingTestCases[caseName] + let app: TestApp + let server: request.SuperTest + let factory: FixtureFactory + let delegations: Delegation[] = [] + let delegationIndexService: DelegationsIndexService + + beforeAll(async () => { + // Arrange + app = await setupWithAuth({ + user: testCase.user, + customScopeRules: testCase.customScopeRules, + }) + server = request(app.getHttpServer()) + delegationIndexService = app.get(DelegationsIndexService) + const rskRelationshipsClientService = app.get( + RskRelationshipsClient, + ) + const nationalRegistryClientService = app.get( + NationalRegistryClientService, + ) + const nationalRegistryV3ClientService = app.get( + NationalRegistryV3ClientService, + ) + const companyRegistryClientService = app.get( + CompanyRegistryClientService, + ) + const nationalRegistryV3FeatureService = app.get( + NationalRegistryV3FeatureService, + ) + jest + .spyOn(nationalRegistryClientService, 'getCustodyChildren') + .mockImplementation(async () => []) + jest + .spyOn(nationalRegistryClientService, 'getIndividual') + .mockImplementation(async (nationalId: string) => + createNationalRegistryUser({ + nationalId, + name: fromName, + }), + ) + jest + .spyOn(nationalRegistryV3ClientService, 'getAllDataIndividual') + .mockImplementation(async (nationalId: string) => { + return { kennitala: nationalId, nafn: fromName } + }) + jest + .spyOn( + rskRelationshipsClientService, + 'getIndividualRelationships', + ) + .mockImplementation(async () => null) + jest + .spyOn(companyRegistryClientService, 'getCompany') + .mockImplementation(async () => null) + jest + .spyOn(delegationIndexService, 'indexDelegations') + .mockImplementation() + jest + .spyOn(delegationIndexService, 'indexCustomDelegations') + .mockImplementation() + jest + .spyOn(nationalRegistryV3FeatureService, 'getValue') + .mockImplementation(async () => featureFlag) + factory = new FixtureFactory(app) + await Promise.all( + testCase.domains.map((domain) => factory.createDomain(domain)), + ) + await Promise.all( + (testCase.accessTo ?? []).map((scope) => + factory.createApiScopeUserAccess({ + nationalId: testCase.user.nationalId, + scope, + }), + ), + ) + }) + + beforeEach(async () => { + if (delegations) { + await Delegation.destroy({ + where: { + id: { + [Op.in]: delegations.map((delegation) => delegation.id), + }, + }, + }) + } + + delegations = await Promise.all( + (testCase.delegations ?? []).map((delegation) => + factory.createCustomDelegation({ + toNationalId: testCase.user.nationalId, + fromName, + ...delegation, + }), + ), + ) }) - } - - delegations = await Promise.all( - (testCase.delegations ?? []).map((delegation) => - factory.createCustomDelegation({ - toNationalId: testCase.user.nationalId, - fromName, - ...delegation, - }), - ), - ) - }) - - it('GET /v1/me/delegations?direction=incoming filters delegations', async () => { - // Arrange - const expectedDelegations = filterExpectedDelegations( - delegations, - testCase.expected, - ) - - // Act - const res = await server.get('/v1/me/delegations?direction=incoming') - - // Assert - expect(delegationIndexService.indexDelegations).toHaveBeenCalled() - expect(res.status).toEqual(200) - expectMatchingDelegations(res.body, expectedDelegations) - }) - - if (testCase.expected.length > 0) { - it.each(testCase.expected)( - 'GET /v1/me/delegation/:id finds delegation in domain $name', - async (domain) => { + + it('GET /v1/me/delegations?direction=incoming filters delegations', async () => { // Arrange - const expectedDelegation = filterExpectedDelegations( + const expectedDelegations = filterExpectedDelegations( delegations, testCase.expected, - ).find((delegation) => delegation.domainName === domain.name) - assert(expectedDelegation) + ) // Act const res = await server.get( - `/v1/me/delegations/${expectedDelegation.id}`, + '/v1/me/delegations?direction=incoming', ) // Assert + expect(delegationIndexService.indexDelegations).toHaveBeenCalled() expect(res.status).toEqual(200) - expectMatchingDelegations(res.body, expectedDelegation) - }, - ) - - it.each(testCase.expected)( - 'DELETE /v1/me/delegations/:id removes access to delegation', - async (domain) => { - // Arrange - const delegationScopeModel = app.get( - getModelToken(DelegationScope), - ) - const delegationModel = app.get( - getModelToken(Delegation), - ) - const delegation = delegations.find( - (delegation) => - delegation.domainName === domain.name && - delegation.toNationalId === testCase.user.nationalId, - ) - assert(delegation) + expectMatchingDelegations(res.body, expectedDelegations) + }) - // Act - const res = await server.delete( - `/v1/me/delegations/${delegation.id}`, + if (testCase.expected.length > 0) { + it.each(testCase.expected)( + 'GET /v1/me/delegation/:id finds delegation in domain $name', + async (domain) => { + // Arrange + const expectedDelegation = filterExpectedDelegations( + delegations, + testCase.expected, + ).find((delegation) => delegation.domainName === domain.name) + assert(expectedDelegation) + + // Act + const res = await server.get( + `/v1/me/delegations/${expectedDelegation.id}`, + ) + + // Assert + expect(res.status).toEqual(200) + expectMatchingDelegations(res.body, expectedDelegation) + }, ) - // Assert - expect(res.status).toEqual(204) - expect( - delegationIndexService.indexCustomDelegations, - ).toHaveBeenCalled() - - const delegationAfter = await delegationModel.findByPk( - delegation.id, + it.each(testCase.expected)( + 'DELETE /v1/me/delegations/:id removes access to delegation', + async (domain) => { + // Arrange + const delegationScopeModel = app.get( + getModelToken(DelegationScope), + ) + const delegationModel = app.get( + getModelToken(Delegation), + ) + const delegation = delegations.find( + (delegation) => + delegation.domainName === domain.name && + delegation.toNationalId === testCase.user.nationalId, + ) + assert(delegation) + + // Act + const res = await server.delete( + `/v1/me/delegations/${delegation.id}`, + ) + + // Assert + expect(res.status).toEqual(204) + expect( + delegationIndexService.indexCustomDelegations, + ).toHaveBeenCalled() + + const delegationAfter = await delegationModel.findByPk( + delegation.id, + ) + expect(delegationAfter).toBeNull() + + const scopesAfter = await delegationScopeModel.findAll({ + where: { + delegationId: delegation.id, + scopeName: + delegation.delegationScopes?.map( + (scope) => scope.scopeName, + ) ?? [], + }, + }) + expect(scopesAfter).toHaveLength(0) + }, ) - expect(delegationAfter).toBeNull() - - const scopesAfter = await delegationScopeModel.findAll({ - where: { - delegationId: delegation.id, - scopeName: - delegation.delegationScopes?.map( - (scope) => scope.scopeName, - ) ?? [], + } + + if (testCase.expected.length === 0 && delegations.length > 0) { + it.each(delegations)( + 'GET /v1/me/delegation/:id returns no content response for $name', + async (delegation) => { + // Act + const res = await server.get( + `/v1/me/delegations/${delegation.id}`, + ) + + // Assert + expect(res.status).toEqual(204) + expect(res.body).toMatchObject({}) }, - }) - expect(scopesAfter).toHaveLength(0) - }, - ) - } - - if (testCase.expected.length === 0 && delegations.length > 0) { - it.each(delegations)( - 'GET /v1/me/delegation/:id returns no content response for $name', - async (delegation) => { - // Act - const res = await server.get(`/v1/me/delegations/${delegation.id}`) - - // Assert - expect(res.status).toEqual(204) - expect(res.body).toMatchObject({}) - }, - ) - } + ) + } + }, + ) }, ) }) diff --git a/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts b/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts index f0050b0d9e9e..5c07ba95334d 100644 --- a/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts +++ b/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts @@ -13,6 +13,7 @@ import { Domain, InactiveReason, MergedDelegationDTO, + NationalRegistryV3FeatureService, PersonalRepresentative, PersonalRepresentativeDelegationTypeModel, PersonalRepresentativeRight, @@ -22,6 +23,7 @@ import { UNKNOWN_NAME, } from '@island.is/auth-api-lib' import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { createClient, createDomain, @@ -54,374 +56,190 @@ import { } from '../../../test/stubs/personalRepresentativeStubs' describe('Personal Representative DelegationsController', () => { - describe('Given a user is authenticated', () => { - let app: TestApp - let factory: FixtureFactory - let server: request.SuperTest - let apiScopeModel: typeof ApiScope - let clientModel: typeof Client - let prScopePermission: typeof PersonalRepresentativeScopePermission - let apiScopeDelegationTypeModel: typeof ApiScopeDelegationType - let prModel: typeof PersonalRepresentative - let prRightsModel: typeof PersonalRepresentativeRight - let prRightTypeModel: typeof PersonalRepresentativeRightType - let prTypeModel: typeof PersonalRepresentativeType - let prDelegationTypeModel: typeof PersonalRepresentativeDelegationTypeModel - let delegationTypeModel: typeof DelegationTypeModel - let nationalRegistryApi: NationalRegistryClientService - let delegationProviderModel: typeof DelegationProviderModel - let delegationIndexService: DelegationsIndexService - - const client = createClient({ - clientId: '@island.is/webapp', - }) - - const scopeValid1 = 'scope/valid1' - const scopeValid2 = 'scope/valid2' - const scopeValid1and2 = 'scope/valid1and2' - const scopeUnactiveType = 'scope/unactiveType' - const scopeOutdated = 'scope/outdated' - const disabledScope = 'disabledScope' - - client.allowedScopes = Object.values([ - scopeValid1, - scopeValid2, - scopeValid1and2, - scopeUnactiveType, - scopeOutdated, - disabledScope, - ]).map((s) => ({ - clientId: client.clientId, - scopeName: s, - })) - - const userNationalId = getFakeNationalId() - - const user = createCurrentUser({ - nationalId: userNationalId, - scope: [defaultScopes.testUserHasAccess.name], - client: client.clientId, - }) - - const domain = createDomain() - - beforeAll(async () => { - app = await setupWithAuth({ - user, - }) - server = request(app.getHttpServer()) - - prTypeModel = app.get( - getModelToken(PersonalRepresentativeType), - ) - - await prTypeModel.create(personalRepresentativeType) - - const domainModel = app.get(getModelToken(Domain)) - await domainModel.create(domain) - - apiScopeModel = app.get(getModelToken(ApiScope)) - prModel = app.get( - getModelToken(PersonalRepresentative), - ) - prRightsModel = app.get( - getModelToken(PersonalRepresentativeRight), - ) - prRightTypeModel = app.get( - getModelToken(PersonalRepresentativeRightType), - ) - prScopePermission = app.get( - getModelToken(PersonalRepresentativeScopePermission), - ) - apiScopeDelegationTypeModel = app.get( - getModelToken(ApiScopeDelegationType), - ) - prDelegationTypeModel = app.get< - typeof PersonalRepresentativeDelegationTypeModel - >(getModelToken(PersonalRepresentativeDelegationTypeModel)) - delegationTypeModel = app.get( - getModelToken(DelegationTypeModel), - ) - delegationProviderModel = app.get( - getModelToken(DelegationProviderModel), - ) - clientModel = app.get(getModelToken(Client)) - nationalRegistryApi = app.get(NationalRegistryClientService) - delegationIndexService = app.get(DelegationsIndexService) - factory = new FixtureFactory(app) - }) - - const createDelegationTypeAndProvider = async (rightCode: string[]) => { - const newDelegationProvider = await delegationProviderModel.create({ - id: AuthDelegationProvider.PersonalRepresentativeRegistry, - name: 'Talsmannagrunnur', - description: 'Talsmannagrunnur', - delegationTypes: [], - }) - - await delegationTypeModel.bulkCreate( - rightCode.map((code) => { - return { - id: getPersonalRepresentativeDelegationType(code), - providerId: newDelegationProvider.id, - name: getPersonalRepresentativeDelegationType(code), - description: `Personal representative delegation type for right type ${code}`, - } - }), - ) - } - - afterAll(async () => { - await app.cleanUp() - }) - - describe('and given we have 3 valid, 1 not yet active and 1 outdate right types', () => { - type rightsTypeStatus = 'valid' | 'unactivated' | 'outdated' - type rightsType = [code: string, status: rightsTypeStatus] - const rightsTypes: rightsType[] = [ - ['valid1', 'valid'], - ['valid2', 'valid'], - ['unactivated', 'unactivated'], - ['outdated', 'outdated'], - ] - - beforeAll(async () => { - await prRightTypeModel.bulkCreate( - rightsTypes.map(([code, status]) => { - switch (status) { - case 'valid': - return getPersonalRepresentativeRightType(code) - case 'unactivated': - return getPersonalRepresentativeRightType( - code, - faker.date.soon(7), - ) - case 'outdated': - return getPersonalRepresentativeRightType( - code, - faker.date.recent(5), - faker.date.recent(), - ) - } - }), - ) - await createDelegationTypeAndProvider(rightsTypes.map(([code]) => code)) - - client.supportedDelegationTypes = delegationTypes - await factory.createClient(client) - }) - - afterAll(async () => { - await prRightTypeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, + describe.each([false, true])( + 'national registry v3 featureflag: %s', + (featureFlag) => { + describe('Given a user is authenticated', () => { + let app: TestApp + let factory: FixtureFactory + let server: request.SuperTest + let apiScopeModel: typeof ApiScope + let clientModel: typeof Client + let prScopePermission: typeof PersonalRepresentativeScopePermission + let apiScopeDelegationTypeModel: typeof ApiScopeDelegationType + let prModel: typeof PersonalRepresentative + let prRightsModel: typeof PersonalRepresentativeRight + let prRightTypeModel: typeof PersonalRepresentativeRightType + let prTypeModel: typeof PersonalRepresentativeType + let prDelegationTypeModel: typeof PersonalRepresentativeDelegationTypeModel + let delegationTypeModel: typeof DelegationTypeModel + let nationalRegistryApi: NationalRegistryClientService + let nationalRegistryV3Api: NationalRegistryV3ClientService + let delegationProviderModel: typeof DelegationProviderModel + let delegationIndexService: DelegationsIndexService + + const client = createClient({ + clientId: '@island.is/webapp', }) - await delegationTypeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await delegationProviderModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, + + const scopeValid1 = 'scope/valid1' + const scopeValid2 = 'scope/valid2' + const scopeValid1and2 = 'scope/valid1and2' + const scopeUnactiveType = 'scope/unactiveType' + const scopeOutdated = 'scope/outdated' + const disabledScope = 'disabledScope' + + client.allowedScopes = Object.values([ + scopeValid1, + scopeValid2, + scopeValid1and2, + scopeUnactiveType, + scopeOutdated, + disabledScope, + ]).map((s) => ({ + clientId: client.clientId, + scopeName: s, + })) + + const userNationalId = getFakeNationalId() + + const user = createCurrentUser({ + nationalId: userNationalId, + scope: [defaultScopes.testUserHasAccess.name], + client: client.clientId, }) - }) - describe.each([ - [1, 0, 0, 2, 1], - [2, 0, 0, 1, 0], - [0, 0, 0, 0, 0], - [0, 1, 0, 0, 1], - [0, 0, 1, 0, 0], - [0, 1, 1, 0, 2], - [1, 1, 1, 0, 0], - ])( - 'and given user has %d active representees with valid rights, %d active representees with outdated rights and %d active representees with unactivated', - ( - valid: number, - outdated: number, - unactivated: number, - deceased: number, - nationalRegistryErrors: number, - ) => { - let nationalRegistryApiSpy: jest.SpyInstance - const validRepresentedPersons: NameIdTuple[] = [] - const outdatedRepresentedPersons: NameIdTuple[] = [] - const unactivatedRepresentedPersons: NameIdTuple[] = [] - const errorNationalIdsRepresentedPersons: NameIdTuple[] = [] - const deceasedNationalIds = times(deceased, getFakeNationalId) - const errorNationalIds = times( - nationalRegistryErrors, - getFakeNationalId, + const domain = createDomain() + + beforeAll(async () => { + app = await setupWithAuth({ + user, + }) + server = request(app.getHttpServer()) + + prTypeModel = app.get( + getModelToken(PersonalRepresentativeType), ) - beforeAll(async () => { - for (let i = 0; i < valid; i++) { - const representedPerson: NameIdTuple = [ - getFakeName(), - getFakeNationalId(), - ] - const relationship = getPersonalRepresentativeRelationship( - userNationalId, - representedPerson[1], - ) - validRepresentedPersons.push(representedPerson) - await prModel.create(relationship) - await prRightsModel.create( - getPersonalRepresentativeRights('valid1', relationship.id), - ) - await prDelegationTypeModel.create({ - personalRepresentativeId: relationship.id, - delegationTypeId: - getPersonalRepresentativeDelegationType('valid1'), - }) - } - - for (let i = 0; i < outdated; i++) { - const representedPerson: NameIdTuple = [ - getFakeName(), - getFakeNationalId(), - ] - const relationship = getPersonalRepresentativeRelationship( - userNationalId, - representedPerson[1], - ) - outdatedRepresentedPersons.push(representedPerson) - await prModel.create(relationship) - await prRightsModel.create( - getPersonalRepresentativeRights('outdated', relationship.id), - ) - await prDelegationTypeModel.create({ - personalRepresentativeId: relationship.id, - delegationTypeId: - getPersonalRepresentativeDelegationType('outdated'), - }) - } - - for (let i = 0; i < unactivated; i++) { - const representedPerson: NameIdTuple = [ - getFakeName(), - getFakeNationalId(), - ] - const relationship = getPersonalRepresentativeRelationship( - userNationalId, - representedPerson[1], - ) - unactivatedRepresentedPersons.push(representedPerson) - await prModel.create(relationship) - await prRightsModel.create( - getPersonalRepresentativeRights('unactivated', relationship.id), - ) - await prDelegationTypeModel.create({ - personalRepresentativeId: relationship.id, - delegationTypeId: - getPersonalRepresentativeDelegationType('unactivated'), - }) - } - - for (let i = 0; i < deceased; i++) { - const representedPerson: NameIdTuple = [ - getFakeName(), - deceasedNationalIds[i], - ] - const relationship = getPersonalRepresentativeRelationship( - userNationalId, - representedPerson[1], - ) + await prTypeModel.create(personalRepresentativeType) - await prModel.create(relationship) - await prRightsModel.create( - getPersonalRepresentativeRights('valid1', relationship.id), - ) - await prDelegationTypeModel.create({ - personalRepresentativeId: relationship.id, - delegationTypeId: - getPersonalRepresentativeDelegationType('valid1'), - }) - } - - for (let i = 0; i < nationalRegistryErrors; i++) { - const representedPerson: NameIdTuple = [ - UNKNOWN_NAME, - errorNationalIds[i], - ] - const relationship = getPersonalRepresentativeRelationship( - userNationalId, - representedPerson[1], - ) + const domainModel = app.get(getModelToken(Domain)) + await domainModel.create(domain) - errorNationalIdsRepresentedPersons.push(representedPerson) - // Create Personal Representative model which will have nationalIdRepresentedPerson throw an error - // when national registry api getIndividual is called - await prModel.create(relationship) - await prRightsModel.create( - getPersonalRepresentativeRights('valid1', relationship.id), - ) - await prDelegationTypeModel.create({ - personalRepresentativeId: relationship.id, - delegationTypeId: - getPersonalRepresentativeDelegationType('valid1'), - }) - } - - const nationalRegistryUsers = [ - ...validRepresentedPersons.map(([name, nationalId]) => - createNationalRegistryUser({ name, nationalId }), - ), - ...outdatedRepresentedPersons.map(([name, nationalId]) => - createNationalRegistryUser({ name, nationalId }), - ), - ...unactivatedRepresentedPersons.map(([name, nationalId]) => - createNationalRegistryUser({ name, nationalId }), - ), - ] + apiScopeModel = app.get(getModelToken(ApiScope)) + prModel = app.get( + getModelToken(PersonalRepresentative), + ) + prRightsModel = app.get( + getModelToken(PersonalRepresentativeRight), + ) + prRightTypeModel = app.get( + getModelToken(PersonalRepresentativeRightType), + ) + prScopePermission = app.get< + typeof PersonalRepresentativeScopePermission + >(getModelToken(PersonalRepresentativeScopePermission)) + apiScopeDelegationTypeModel = app.get( + getModelToken(ApiScopeDelegationType), + ) + prDelegationTypeModel = app.get< + typeof PersonalRepresentativeDelegationTypeModel + >(getModelToken(PersonalRepresentativeDelegationTypeModel)) + delegationTypeModel = app.get( + getModelToken(DelegationTypeModel), + ) + delegationProviderModel = app.get( + getModelToken(DelegationProviderModel), + ) + clientModel = app.get(getModelToken(Client)) + nationalRegistryApi = app.get(NationalRegistryClientService) + nationalRegistryV3Api = app.get(NationalRegistryV3ClientService) + delegationIndexService = app.get(DelegationsIndexService) + const nationalRegistryV3FeatureService = app.get( + NationalRegistryV3FeatureService, + ) + jest + .spyOn(nationalRegistryV3FeatureService, 'getValue') + .mockImplementation(async () => featureFlag) + factory = new FixtureFactory(app) + }) - nationalRegistryApiSpy = jest - .spyOn(nationalRegistryApi, 'getIndividual') - .mockImplementation(async (id) => { - if (deceasedNationalIds.includes(id)) { - return null - } + const createDelegationTypeAndProvider = async (rightCode: string[]) => { + const newDelegationProvider = await delegationProviderModel.create({ + id: AuthDelegationProvider.PersonalRepresentativeRegistry, + name: 'Talsmannagrunnur', + description: 'Talsmannagrunnur', + delegationTypes: [], + }) - if ( - errorNationalIds.find( - (errorNationalId) => id === errorNationalId, - ) - ) { - throw new Error('National registry error') - } + await delegationTypeModel.bulkCreate( + rightCode.map((code) => { + return { + id: getPersonalRepresentativeDelegationType(code), + providerId: newDelegationProvider.id, + name: getPersonalRepresentativeDelegationType(code), + description: `Personal representative delegation type for right type ${code}`, + } + }), + ) + } - const user = nationalRegistryUsers.find( - (u) => - u?.nationalId === id && - // Make sure we don't return a user that has been marked as deceased - !deceasedNationalIds.includes(u?.nationalId), - ) + afterAll(async () => { + await app.cleanUp() + }) - return user ?? null - }) + describe('and given we have 3 valid, 1 not yet active and 1 outdate right types', () => { + type rightsTypeStatus = 'valid' | 'unactivated' | 'outdated' + type rightsType = [code: string, status: rightsTypeStatus] + const rightsTypes: rightsType[] = [ + ['valid1', 'valid'], + ['valid2', 'valid'], + ['unactivated', 'unactivated'], + ['outdated', 'outdated'], + ] + + beforeAll(async () => { + await prRightTypeModel.bulkCreate( + rightsTypes.map(([code, status]) => { + switch (status) { + case 'valid': + return getPersonalRepresentativeRightType(code) + case 'unactivated': + return getPersonalRepresentativeRightType( + code, + faker.date.soon(7), + ) + case 'outdated': + return getPersonalRepresentativeRightType( + code, + faker.date.recent(5), + faker.date.recent(), + ) + } + }), + ) + await createDelegationTypeAndProvider( + rightsTypes.map(([code]) => code), + ) + + client.supportedDelegationTypes = delegationTypes + await factory.createClient(client) }) afterAll(async () => { - jest.clearAllMocks() - await prRightsModel.destroy({ + await prRightTypeModel.destroy({ where: {}, cascade: true, truncate: true, force: true, }) - await prModel.destroy({ + await delegationTypeModel.destroy({ where: {}, cascade: true, truncate: true, force: true, }) - await prDelegationTypeModel.destroy({ + await delegationProviderModel.destroy({ where: {}, cascade: true, truncate: true, @@ -429,215 +247,409 @@ describe('Personal Representative DelegationsController', () => { }) }) - describe('when user calls GET /v2/delegations', () => { - const path = '/v2/delegations' - let response: request.Response - let body: MergedDelegationDTO[] + describe.each([ + [1, 0, 0, 2, 1], + [2, 0, 0, 1, 0], + [0, 0, 0, 0, 0], + [0, 1, 0, 0, 1], + [0, 0, 1, 0, 0], + [0, 1, 1, 0, 2], + [1, 1, 1, 0, 0], + ])( + 'and given user has %d active representees with valid rights, %d active representees with outdated rights and %d active representees with unactivated', + ( + valid: number, + outdated: number, + unactivated: number, + deceased: number, + nationalRegistryErrors: number, + ) => { + let nationalRegistryApiSpy: jest.SpyInstance + let nationalRegistryV3ApiSpy: jest.SpyInstance + const validRepresentedPersons: NameIdTuple[] = [] + const outdatedRepresentedPersons: NameIdTuple[] = [] + const unactivatedRepresentedPersons: NameIdTuple[] = [] + const errorNationalIdsRepresentedPersons: NameIdTuple[] = [] + const deceasedNationalIds = times(deceased, getFakeNationalId) + const errorNationalIds = times( + nationalRegistryErrors, + getFakeNationalId, + ) - beforeAll(async () => { - response = await server.get(path) - body = response.body - }) + beforeAll(async () => { + for (let i = 0; i < valid; i++) { + const representedPerson: NameIdTuple = [ + getFakeName(), + getFakeNationalId(), + ] + const relationship = getPersonalRepresentativeRelationship( + userNationalId, + representedPerson[1], + ) + validRepresentedPersons.push(representedPerson) + await prModel.create(relationship) + await prRightsModel.create( + getPersonalRepresentativeRights('valid1', relationship.id), + ) + await prDelegationTypeModel.create({ + personalRepresentativeId: relationship.id, + delegationTypeId: + getPersonalRepresentativeDelegationType('valid1'), + }) + } - it('should have a an OK return status', () => { - expect(response.status).toEqual(200) - }) + for (let i = 0; i < outdated; i++) { + const representedPerson: NameIdTuple = [ + getFakeName(), + getFakeNationalId(), + ] + const relationship = getPersonalRepresentativeRelationship( + userNationalId, + representedPerson[1], + ) + outdatedRepresentedPersons.push(representedPerson) + await prModel.create(relationship) + await prRightsModel.create( + getPersonalRepresentativeRights( + 'outdated', + relationship.id, + ), + ) + await prDelegationTypeModel.create({ + personalRepresentativeId: relationship.id, + delegationTypeId: + getPersonalRepresentativeDelegationType('outdated'), + }) + } - it(`should return ${valid} ${ - valid === 1 ? 'item' : 'items' - } `, () => { - expect(body).toHaveLength(valid + nationalRegistryErrors) - }) + for (let i = 0; i < unactivated; i++) { + const representedPerson: NameIdTuple = [ + getFakeName(), + getFakeNationalId(), + ] + const relationship = getPersonalRepresentativeRelationship( + userNationalId, + representedPerson[1], + ) + unactivatedRepresentedPersons.push(representedPerson) + await prModel.create(relationship) + await prRightsModel.create( + getPersonalRepresentativeRights( + 'unactivated', + relationship.id, + ), + ) + await prDelegationTypeModel.create({ + personalRepresentativeId: relationship.id, + delegationTypeId: + getPersonalRepresentativeDelegationType('unactivated'), + }) + } - it('should have the nationalId of the user as the representer', () => { - expect( - body.every((d) => d.toNationalId === userNationalId), - ).toBeTruthy() - }) + for (let i = 0; i < deceased; i++) { + const representedPerson: NameIdTuple = [ + getFakeName(), + deceasedNationalIds[i], + ] + const relationship = getPersonalRepresentativeRelationship( + userNationalId, + representedPerson[1], + ) - it('should only have the nationalId of the valid representees', () => { - expect(body.map((d) => d.fromNationalId).sort()).toEqual( - [ - ...validRepresentedPersons.map(([_, id]) => id), - ...errorNationalIdsRepresentedPersons.map(([_, id]) => id), - ].sort(), - ) - }) + await prModel.create(relationship) + await prRightsModel.create( + getPersonalRepresentativeRights('valid1', relationship.id), + ) + await prDelegationTypeModel.create({ + personalRepresentativeId: relationship.id, + delegationTypeId: + getPersonalRepresentativeDelegationType('valid1'), + }) + } - it(`should only have ${ - valid + nationalRegistryErrors === 1 ? 'name' : 'names' - } of the valid represented ${ - valid + nationalRegistryErrors === 1 ? 'person' : 'persons' - }`, () => { - expect(body.map((d) => d.fromName).sort()).toEqual([ - ...validRepresentedPersons.map(([name, _]) => name).sort(), - ...errorNationalIdsRepresentedPersons.map(([name]) => name), - ]) - }) + for (let i = 0; i < nationalRegistryErrors; i++) { + const representedPerson: NameIdTuple = [ + UNKNOWN_NAME, + errorNationalIds[i], + ] + const relationship = getPersonalRepresentativeRelationship( + userNationalId, + representedPerson[1], + ) - it(`should have fetched the ${ - valid + deceased + nationalRegistryErrors === 1 ? 'name' : 'names' - } of the valid represented ${ - valid + deceased + nationalRegistryErrors === 1 - ? 'person' - : 'persons' - } from nationalRegistryApi`, () => { - expect(nationalRegistryApiSpy).toHaveBeenCalledTimes( - valid + deceased + nationalRegistryErrors, - ) - }) + errorNationalIdsRepresentedPersons.push(representedPerson) + // Create Personal Representative model which will have nationalIdRepresentedPerson throw an error + // when national registry api getIndividual is called + await prModel.create(relationship) + await prRightsModel.create( + getPersonalRepresentativeRights('valid1', relationship.id), + ) + await prDelegationTypeModel.create({ + personalRepresentativeId: relationship.id, + delegationTypeId: + getPersonalRepresentativeDelegationType('valid1'), + }) + } - it('should have the delegation type claims of PersonalRepresentative', () => { - expect( - body.every( - (d) => - d.types[0] === AuthDelegationType.PersonalRepresentative, - ), - ).toBeTruthy() - }) + const nationalRegistryUsers = [ + ...validRepresentedPersons.map(([name, nationalId]) => + createNationalRegistryUser({ name, nationalId }), + ), + ...outdatedRepresentedPersons.map(([name, nationalId]) => + createNationalRegistryUser({ name, nationalId }), + ), + ...unactivatedRepresentedPersons.map(([name, nationalId]) => + createNationalRegistryUser({ name, nationalId }), + ), + ] + + nationalRegistryApiSpy = jest + .spyOn(nationalRegistryApi, 'getIndividual') + .mockImplementation(async (id) => { + if (deceasedNationalIds.includes(id)) { + return null + } + + if ( + errorNationalIds.find( + (errorNationalId) => id === errorNationalId, + ) + ) { + throw new Error('National registry error') + } + + const user = nationalRegistryUsers.find( + (u) => + u?.nationalId === id && + // Make sure we don't return a user that has been marked as deceased + !deceasedNationalIds.includes(u?.nationalId), + ) + + return user ?? null + }) + + nationalRegistryV3ApiSpy = jest + .spyOn(nationalRegistryV3Api, 'getAllDataIndividual') + .mockImplementation(async (id) => { + if ( + errorNationalIds.find( + (errorNationalId) => id === errorNationalId, + ) + ) { + throw new Error('National registry error') + } + + if (deceasedNationalIds.includes(id)) { + return { + kennitala: id, + afdrif: 'LÉST', + } + } + + const user = nationalRegistryUsers.find( + (u) => u?.nationalId === id, + ) + + return user + ? { + kennitala: id, + nafn: user?.name, + afdrif: null, + } + : null + }) + }) - it('should have made prModels inactive for deceased persons', async () => { - // Arrange - const expectedModels = await prModel.findAll({ - where: { - nationalIdRepresentedPerson: deceasedNationalIds, - inactive: true, - inactiveReason: InactiveReason.DECEASED_PARTY, - }, + afterAll(async () => { + jest.clearAllMocks() + await prRightsModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prDelegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) }) - // Assert - expect(expectedModels.length).toEqual(deceased) + describe('when user calls GET /v2/delegations', () => { + const path = '/v2/delegations' + let response: request.Response + let body: MergedDelegationDTO[] - expectedModels.forEach((model) => { - expect(model.inactive).toEqual(true) - expect(model.inactiveReason).toEqual( - InactiveReason.DECEASED_PARTY, - ) - }) - }) + beforeAll(async () => { + response = await server.get(path) + body = response.body + }) - it('should return delegation if national registry api getIndividual throws an error', async () => { - // Arrange - const expectedModels = await prModel.findAll({ - where: { - nationalIdRepresentedPerson: errorNationalIds, - }, - }) + it('should have a an OK return status', () => { + expect(response.status).toEqual(200) + }) - // Assert - expect(expectedModels.length).toEqual(errorNationalIds.length) - }) + it(`should return ${valid} ${ + valid === 1 ? 'item' : 'items' + } `, () => { + expect(body).toHaveLength(valid + nationalRegistryErrors) + }) - it('should index delegations', () => { - expect(delegationIndexService.indexDelegations).toHaveBeenCalled() - }) - }) - }, - ) - - describe('and given we have a combination of scopes for personal representative', () => { - type scopesType = [name: string, enabled: boolean, rightTypes: string[]] - const scopes: scopesType[] = [ - [scopeValid1, true, ['valid1']], - [scopeValid2, true, ['valid2']], - [scopeValid1and2, true, ['valid1', 'valid2']], - [scopeUnactiveType, true, ['unactivated']], - [scopeOutdated, true, ['outdated']], - [disabledScope, false, ['valid1']], - ] + it('should have the nationalId of the user as the representer', () => { + expect( + body.every((d) => d.toNationalId === userNationalId), + ).toBeTruthy() + }) - beforeAll(async () => { - const apiScopes = scopes.flatMap(([name, enabled, types]) => ({ - name: name, - enabled, - domainName: domain.name, - supportedDelegationTypes: types.map((rt) => - getPersonalRepresentativeDelegationType(rt), - ), - })) - - await Promise.all( - apiScopes.map((scope) => factory.createApiScope(scope)), - ) + it('should only have the nationalId of the valid representees', () => { + expect(body.map((d) => d.fromNationalId).sort()).toEqual( + [ + ...validRepresentedPersons.map(([_, id]) => id), + ...errorNationalIdsRepresentedPersons.map( + ([_, id]) => id, + ), + ].sort(), + ) + }) - await prScopePermission.bulkCreate( - scopes.flatMap(([name, _, types]) => - types.map((rt) => getScopePermission(rt, name)), - ), - ) - }) + it(`should only have ${ + valid + nationalRegistryErrors === 1 ? 'name' : 'names' + } of the valid represented ${ + valid + nationalRegistryErrors === 1 ? 'person' : 'persons' + }`, () => { + expect(body.map((d) => d.fromName).sort()).toEqual([ + ...validRepresentedPersons.map(([name, _]) => name).sort(), + ...errorNationalIdsRepresentedPersons.map(([name]) => name), + ]) + }) - afterAll(async () => { - await prScopePermission.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await apiScopeDelegationTypeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await apiScopeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - }) + it(`should have fetched the ${ + valid + deceased + nationalRegistryErrors === 1 + ? 'name' + : 'names' + } of the valid represented ${ + valid + deceased + nationalRegistryErrors === 1 + ? 'person' + : 'persons' + } from nationalRegistryApi`, () => { + featureFlag + ? expect(nationalRegistryV3ApiSpy).toHaveBeenCalledTimes( + valid + deceased + nationalRegistryErrors, + ) + : expect(nationalRegistryApiSpy).toHaveBeenCalledTimes( + valid + deceased + nationalRegistryErrors, + ) + }) + + it('should have the delegation type claims of PersonalRepresentative', () => { + expect( + body.every( + (d) => + d.types[0] === + AuthDelegationType.PersonalRepresentative, + ), + ).toBeTruthy() + }) + + it('should have made prModels inactive for deceased persons', async () => { + // Arrange + const expectedModels = await prModel.findAll({ + where: { + nationalIdRepresentedPerson: deceasedNationalIds, + inactive: true, + inactiveReason: InactiveReason.DECEASED_PARTY, + }, + }) + + // Assert + expect(expectedModels.length).toEqual(deceased) + + expectedModels.forEach((model) => { + expect(model.inactive).toEqual(true) + expect(model.inactiveReason).toEqual( + InactiveReason.DECEASED_PARTY, + ) + }) + }) - describe.each([ - [['valid1'], [scopeValid1, scopeValid1and2]], - [['valid2'], [scopeValid2, scopeValid1and2]], - [ - ['valid1', 'valid2'], - [scopeValid1, scopeValid2, scopeValid1and2], - ], - [[], []], - // [['unactivated'], []], - // [['outdated'], []], - ])( - 'and given user is representing persons with rights %p', - (rights, expected) => { - const representeeNationalId = getFakeNationalId() + it('should return delegation if national registry api getIndividual throws an error', async () => { + // Arrange + const expectedModels = await prModel.findAll({ + where: { + nationalIdRepresentedPerson: errorNationalIds, + }, + }) + + // Assert + expect(expectedModels.length).toEqual(errorNationalIds.length) + }) + + it('should index delegations', () => { + expect( + delegationIndexService.indexDelegations, + ).toHaveBeenCalled() + }) + }) + }, + ) + + describe('and given we have a combination of scopes for personal representative', () => { + type scopesType = [ + name: string, + enabled: boolean, + rightTypes: string[], + ] + const scopes: scopesType[] = [ + [scopeValid1, true, ['valid1']], + [scopeValid2, true, ['valid2']], + [scopeValid1and2, true, ['valid1', 'valid2']], + [scopeUnactiveType, true, ['unactivated']], + [scopeOutdated, true, ['outdated']], + [disabledScope, false, ['valid1']], + ] beforeAll(async () => { - const relationship = getPersonalRepresentativeRelationship( - userNationalId, - representeeNationalId, + const apiScopes = scopes.flatMap(([name, enabled, types]) => ({ + name: name, + enabled, + domainName: domain.name, + supportedDelegationTypes: types.map((rt) => + getPersonalRepresentativeDelegationType(rt), + ), + })) + + await Promise.all( + apiScopes.map((scope) => factory.createApiScope(scope)), ) - await prModel.create(relationship) - await prRightsModel.bulkCreate( - rights.map((r) => - getPersonalRepresentativeRights(r, relationship.id), + await prScopePermission.bulkCreate( + scopes.flatMap(([name, _, types]) => + types.map((rt) => getScopePermission(rt, name)), ), ) - await prDelegationTypeModel.bulkCreate( - rights.map((r) => ({ - personalRepresentativeId: relationship.id, - delegationTypeId: getPersonalRepresentativeDelegationType(r), - })), - ) }) afterAll(async () => { - await prRightsModel.destroy({ + await prScopePermission.destroy({ where: {}, cascade: true, truncate: true, force: true, }) - await prModel.destroy({ + await apiScopeDelegationTypeModel.destroy({ where: {}, cascade: true, truncate: true, force: true, }) - await prDelegationTypeModel.destroy({ + await apiScopeModel.destroy({ where: {}, cascade: true, truncate: true, @@ -645,34 +657,95 @@ describe('Personal Representative DelegationsController', () => { }) }) - describe('when user calls GET /delegations/scopes', () => { - const path = '/delegations/scopes' - let response: request.Response - let body: string[] + describe.each([ + [['valid1'], [scopeValid1, scopeValid1and2]], + [['valid2'], [scopeValid2, scopeValid1and2]], + [ + ['valid1', 'valid2'], + [scopeValid1, scopeValid2, scopeValid1and2], + ], + [[], []], + // [['unactivated'], []], + // [['outdated'], []], + ])( + 'and given user is representing persons with rights %p', + (rights, expected) => { + const representeeNationalId = getFakeNationalId() + + beforeAll(async () => { + const relationship = getPersonalRepresentativeRelationship( + userNationalId, + representeeNationalId, + ) - beforeAll(async () => { - response = await server.get(`${path}`).query({ - fromNationalId: representeeNationalId, - delegationType: rights.map((r) => - getPersonalRepresentativeDelegationType(r), - ), + await prModel.create(relationship) + await prRightsModel.bulkCreate( + rights.map((r) => + getPersonalRepresentativeRights(r, relationship.id), + ), + ) + await prDelegationTypeModel.bulkCreate( + rights.map((r) => ({ + personalRepresentativeId: relationship.id, + delegationTypeId: + getPersonalRepresentativeDelegationType(r), + })), + ) }) - body = response.body - }) - it('should have a an OK return status', () => { - expect(response.status).toEqual(200) - }) + afterAll(async () => { + await prRightsModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prDelegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + }) - it(`should return ${ - expected.length === 0 ? 'no scopes' : JSON.stringify(expected) - }`, () => { - expect(body.sort()).toEqual(expected.sort()) - }) - }) - }, - ) + describe('when user calls GET /delegations/scopes', () => { + const path = '/delegations/scopes' + let response: request.Response + let body: string[] + + beforeAll(async () => { + response = await server.get(`${path}`).query({ + fromNationalId: representeeNationalId, + delegationType: rights.map((r) => + getPersonalRepresentativeDelegationType(r), + ), + }) + body = response.body + }) + + it('should have a an OK return status', () => { + expect(response.status).toEqual(200) + }) + + it(`should return ${ + expected.length === 0 + ? 'no scopes' + : JSON.stringify(expected) + }`, () => { + expect(body.sort()).toEqual(expected.sort()) + }) + }) + }, + ) + }) + }) }) - }) - }) + }, + ) }) diff --git a/apps/services/auth/ids-api/src/app/delegations/test/delegations-filters.spec.ts b/apps/services/auth/ids-api/src/app/delegations/test/delegations-filters.spec.ts index 526bf4262a4a..eb6c5a885989 100644 --- a/apps/services/auth/ids-api/src/app/delegations/test/delegations-filters.spec.ts +++ b/apps/services/auth/ids-api/src/app/delegations/test/delegations-filters.spec.ts @@ -4,9 +4,13 @@ import faker from 'faker' import { Sequelize } from 'sequelize-typescript' import request from 'supertest' -import { MergedDelegationDTO } from '@island.is/auth-api-lib' +import { + MergedDelegationDTO, + NationalRegistryV3FeatureService, +} from '@island.is/auth-api-lib' import { RskRelationshipsClient } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { FixtureFactory } from '@island.is/services/auth/testing' import { AuthDelegationProvider, @@ -23,166 +27,190 @@ import { testCases } from './delegations-filters-test-cases' import { user } from './delegations-filters-types' describe('DelegationsController', () => { - let sequelize: Sequelize - let app: TestApp - let server: request.SuperTest - let factory: FixtureFactory - let nationalRegistryApi: NationalRegistryClientService - let rskApi: RskRelationshipsClient - beforeAll(async () => { - app = await setupWithAuth({ - user: user, - }) - sequelize = await app.resolve(getConnectionToken() as Type) - - server = request(app.getHttpServer()) - - nationalRegistryApi = app.get(NationalRegistryClientService) - jest - .spyOn(nationalRegistryApi, 'getIndividual') - .mockImplementation(async (nationalId: string) => - createNationalRegistryUser({ - nationalId, - name: faker.name.findName(), - }), - ) - rskApi = app.get(RskRelationshipsClient) - - factory = new FixtureFactory(app) - }) - - afterAll(async () => { - await app.cleanUp() - }) - - describe.each(Object.keys(testCases))( - 'Delegation filtering with test case: %s', - (caseName) => { - const testCase = testCases[caseName] - testCase.user = user - const path = '/v2/delegations' - + describe.each([false, true])( + 'national registry v3 featureflag: %s', + (featureFlag) => { + let sequelize: Sequelize + let app: TestApp + let server: request.SuperTest + let factory: FixtureFactory + let nationalRegistryApi: NationalRegistryClientService + let nationalRegistryV3Api: NationalRegistryV3ClientService + let rskApi: RskRelationshipsClient beforeAll(async () => { - await truncate(sequelize) - - await Promise.all( - testCase.domains.map((domain) => factory.createDomain(domain)), - ) + app = await setupWithAuth({ + user: user, + }) + sequelize = await app.resolve(getConnectionToken() as Type) - await factory.createClient(testCase.client) - - await Promise.all( - testCase.clientAllowedScopes.map((scope) => - factory.createClientAllowedScope(scope), - ), - ) + server = request(app.getHttpServer()) - await Promise.all( - testCase.apiScopes.map((scope) => factory.createApiScope(scope)), - ) - - await Promise.all( - testCase.apiScopeUserAccess.map((access) => - factory.createApiScopeUserAccess(access), - ), - ) - - await Promise.all( - testCase.customDelegations.map((delegation) => - factory.createCustomDelegation(delegation), - ), - ) - - await Promise.all( - testCase.fromLegalRepresentative.map((nationalId) => - factory.createDelegationIndexRecord({ - fromNationalId: nationalId, - toNationalId: testCase.user.nationalId, - type: AuthDelegationType.LegalRepresentative, - provider: AuthDelegationProvider.DistrictCommissionersRegistry, + nationalRegistryApi = app.get(NationalRegistryClientService) + jest + .spyOn(nationalRegistryApi, 'getIndividual') + .mockImplementation(async (nationalId: string) => + createNationalRegistryUser({ + nationalId, + name: faker.name.findName(), }), - ), + ) + nationalRegistryV3Api = app.get(NationalRegistryV3ClientService) + jest + .spyOn(nationalRegistryV3Api, 'getAllDataIndividual') + .mockImplementation(async (nationalId: string) => { + const user = createNationalRegistryUser({ + nationalId: nationalId, + }) + + return { kennitala: user.nationalId, nafn: user.name } + }) + rskApi = app.get(RskRelationshipsClient) + + const nationalRegistryV3FeatureService = app.get( + NationalRegistryV3FeatureService, ) - jest - .spyOn(nationalRegistryApi, 'getCustodyChildren') - .mockImplementation(async () => testCase.fromChildren) + .spyOn(nationalRegistryV3FeatureService, 'getValue') + .mockImplementation(async () => featureFlag) - jest - .spyOn(rskApi, 'getIndividualRelationships') - .mockImplementation(async () => testCase.procuration) + factory = new FixtureFactory(app) }) - let res: request.Response - - it(`GET ${path} returns correct filtered delegations`, async () => { - res = await server.get(path) - - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(testCase.expectedFrom.length) - expect( - res.body.map((d: MergedDelegationDTO) => d.fromNationalId).sort(), - ).toEqual(testCase.expectedFrom.sort()) - if (testCase.expectedTypes) { - expect(res.body[0].types.sort()).toEqual( - testCase.expectedTypes.sort(), - ) - } + afterAll(async () => { + await app.cleanUp() }) - }, - ) - - describe('verify', () => { - const testCase = testCases['legalRepresentative1'] - testCase.user = user - const path = '/v1/delegations/verify' - beforeAll(async () => { - await truncate(sequelize) - - await Promise.all( - testCase.domains.map((domain) => factory.createDomain(domain)), + describe.each(Object.keys(testCases))( + 'Delegation filtering with test case: %s', + (caseName) => { + const testCase = testCases[caseName] + testCase.user = user + const path = '/v2/delegations' + + beforeAll(async () => { + await truncate(sequelize) + + await Promise.all( + testCase.domains.map((domain) => factory.createDomain(domain)), + ) + + await factory.createClient(testCase.client) + + await Promise.all( + testCase.clientAllowedScopes.map((scope) => + factory.createClientAllowedScope(scope), + ), + ) + + await Promise.all( + testCase.apiScopes.map((scope) => factory.createApiScope(scope)), + ) + + await Promise.all( + testCase.apiScopeUserAccess.map((access) => + factory.createApiScopeUserAccess(access), + ), + ) + + await Promise.all( + testCase.customDelegations.map((delegation) => + factory.createCustomDelegation(delegation), + ), + ) + + await Promise.all( + testCase.fromLegalRepresentative.map((nationalId) => + factory.createDelegationIndexRecord({ + fromNationalId: nationalId, + toNationalId: testCase.user.nationalId, + type: AuthDelegationType.LegalRepresentative, + provider: + AuthDelegationProvider.DistrictCommissionersRegistry, + }), + ), + ) + + jest + .spyOn(nationalRegistryApi, 'getCustodyChildren') + .mockImplementation(async () => testCase.fromChildren) + + jest + .spyOn(rskApi, 'getIndividualRelationships') + .mockImplementation(async () => testCase.procuration) + }) + + let res: request.Response + + it(`GET ${path} returns correct filtered delegations`, async () => { + res = await server.get(path) + + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(testCase.expectedFrom.length) + expect( + res.body.map((d: MergedDelegationDTO) => d.fromNationalId).sort(), + ).toEqual(testCase.expectedFrom.sort()) + if (testCase.expectedTypes) { + expect(res.body[0].types.sort()).toEqual( + testCase.expectedTypes.sort(), + ) + } + }) + }, ) - await factory.createClient(testCase.client) + describe('verify', () => { + const testCase = testCases['legalRepresentative1'] + testCase.user = user + const path = '/v1/delegations/verify' - await Promise.all( - testCase.clientAllowedScopes.map((scope) => - factory.createClientAllowedScope(scope), - ), - ) + beforeAll(async () => { + await truncate(sequelize) - await Promise.all( - testCase.apiScopes.map((scope) => factory.createApiScope(scope)), - ) + await Promise.all( + testCase.domains.map((domain) => factory.createDomain(domain)), + ) - await factory.createDelegationIndexRecord({ - fromNationalId: nonExistingLegalRepresentativeNationalId, - toNationalId: testCase.user.nationalId, - type: AuthDelegationType.LegalRepresentative, - provider: AuthDelegationProvider.DistrictCommissionersRegistry, - }) - }) + await factory.createClient(testCase.client) - let res: request.Response - it(`POST ${path} returns verified response`, async () => { - res = await server.post(path).send({ - fromNationalId: testCase.fromLegalRepresentative[0], - delegationTypes: [AuthDelegationType.LegalRepresentative], - }) + await Promise.all( + testCase.clientAllowedScopes.map((scope) => + factory.createClientAllowedScope(scope), + ), + ) - expect(res.status).toEqual(200) - expect(res.body.verified).toEqual(true) - }) + await Promise.all( + testCase.apiScopes.map((scope) => factory.createApiScope(scope)), + ) - it(`POST ${path} returns non-verified response`, async () => { - res = await server.post(path).send({ - fromNationalId: nonExistingLegalRepresentativeNationalId, - delegationTypes: [AuthDelegationType.LegalRepresentative], + await factory.createDelegationIndexRecord({ + fromNationalId: nonExistingLegalRepresentativeNationalId, + toNationalId: testCase.user.nationalId, + type: AuthDelegationType.LegalRepresentative, + provider: AuthDelegationProvider.DistrictCommissionersRegistry, + }) + }) + + let res: request.Response + it(`POST ${path} returns verified response`, async () => { + res = await server.post(path).send({ + fromNationalId: testCase.fromLegalRepresentative[0], + delegationTypes: [AuthDelegationType.LegalRepresentative], + }) + + expect(res.status).toEqual(200) + expect(res.body.verified).toEqual(true) + }) + + it(`POST ${path} returns non-verified response`, async () => { + res = await server.post(path).send({ + fromNationalId: nonExistingLegalRepresentativeNationalId, + delegationTypes: [AuthDelegationType.LegalRepresentative], + }) + + expect(res.status).toEqual(200) + expect(res.body.verified).toEqual(false) + }) }) - - expect(res.status).toEqual(200) - expect(res.body.verified).toEqual(false) - }) - }) + }, + ) }) diff --git a/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts b/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts index 4abd4987bd31..f2dee16513d6 100644 --- a/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts +++ b/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts @@ -1,7 +1,7 @@ import { getModelToken } from '@nestjs/sequelize' +import addDays from 'date-fns/addDays' import request from 'supertest' import { uuid } from 'uuidv4' -import addDays from 'date-fns/addDays' import { ApiScope, @@ -12,8 +12,10 @@ import { DelegationScope, DelegationTypeModel, Domain, + NationalRegistryV3FeatureService, } from '@island.is/auth-api-lib' import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { createClient, createDomain, @@ -33,246 +35,273 @@ import { defaultScopes, setupWithAuth } from '../../../../test/setup' import { getFakeNationalId } from '../../../../test/stubs/genericStubs' describe('DelegationsController', () => { - describe('Given a user is authenticated', () => { - let app: TestApp - let factory: FixtureFactory - let server: request.SuperTest - - let apiScopeModel: typeof ApiScope - let apiScopeDelegationTypeModel: typeof ApiScopeDelegationType - let delegationDelegationTypeModel: typeof DelegationDelegationType - let delegationModel: typeof Delegation - let delegationTypeModel: typeof DelegationTypeModel - let nationalRegistryApi: NationalRegistryClientService - let delegationProviderModel: typeof DelegationProviderModel - let delegationScopesModel: typeof DelegationScope - - const client = createClient({ - clientId: '@island.is/webapp', - }) - - const scopeValid1 = 'scope/valid1' - const scopeValid2 = 'scope/valid2' - const scopeValid1and2 = 'scope/valid1and2' - const scopeUnactiveType = 'scope/unactiveType' - const scopeOutdated = 'scope/outdated' - const disabledScope = 'disabledScope' - - client.allowedScopes = Object.values([ - scopeValid1, - scopeValid2, - scopeValid1and2, - scopeUnactiveType, - scopeOutdated, - disabledScope, - ]).map((s) => ({ - clientId: client.clientId, - scopeName: s, - })) - - const userNationalId = getFakeNationalId() - - const user = createCurrentUser({ - nationalId: userNationalId, - scope: [defaultScopes.testUserHasAccess.name], - client: client.clientId, - }) - - const domain = createDomain() - - beforeAll(async () => { - app = await setupWithAuth({ - user, - }) - server = request(app.getHttpServer()) - - const domainModel = app.get(getModelToken(Domain)) - await domainModel.create(domain) - - apiScopeModel = app.get(getModelToken(ApiScope)) - - apiScopeDelegationTypeModel = app.get( - getModelToken(ApiScopeDelegationType), - ) - delegationTypeModel = app.get( - getModelToken(DelegationTypeModel), - ) - delegationProviderModel = app.get( - getModelToken(DelegationProviderModel), - ) - delegationScopesModel = app.get( - getModelToken(DelegationScope), - ) - delegationModel = app.get(getModelToken(Delegation)) - delegationDelegationTypeModel = app.get( - getModelToken(DelegationDelegationType), - ) - nationalRegistryApi = app.get(NationalRegistryClientService) - factory = new FixtureFactory(app) - }) - - afterAll(async () => { - await app.cleanUp() - }) - - describe('GET with general mandate delegation type', () => { - const representeeNationalId = getFakeNationalId() - let nationalRegistryApiSpy: jest.SpyInstance - const scopeNames = [ - 'api-scope/generalMandate1', - 'api-scope/generalMandate2', - 'api-scope/generalMandate3', - ] - - beforeAll(async () => { - client.supportedDelegationTypes = [ - AuthDelegationType.GeneralMandate, - AuthDelegationType.LegalGuardian, - ] - await factory.createClient(client) - - const delegations = await delegationModel.create({ - id: uuid(), - fromDisplayName: 'Test', - fromNationalId: representeeNationalId, - toNationalId: userNationalId, - toName: 'Test', + describe.each([false, true])( + 'national registry v3 featureflag: %s', + (featureFlag) => { + describe('Given a user is authenticated', () => { + let app: TestApp + let factory: FixtureFactory + let server: request.SuperTest + + let apiScopeModel: typeof ApiScope + let apiScopeDelegationTypeModel: typeof ApiScopeDelegationType + let delegationDelegationTypeModel: typeof DelegationDelegationType + let delegationModel: typeof Delegation + let delegationTypeModel: typeof DelegationTypeModel + let nationalRegistryApi: NationalRegistryClientService + let nationalRegistryV3Api: NationalRegistryV3ClientService + let delegationProviderModel: typeof DelegationProviderModel + let delegationScopesModel: typeof DelegationScope + + const client = createClient({ + clientId: '@island.is/webapp', + }) + + const scopeValid1 = 'scope/valid1' + const scopeValid2 = 'scope/valid2' + const scopeValid1and2 = 'scope/valid1and2' + const scopeUnactiveType = 'scope/unactiveType' + const scopeOutdated = 'scope/outdated' + const disabledScope = 'disabledScope' + + client.allowedScopes = Object.values([ + scopeValid1, + scopeValid2, + scopeValid1and2, + scopeUnactiveType, + scopeOutdated, + disabledScope, + ]).map((s) => ({ + clientId: client.clientId, + scopeName: s, + })) + + const userNationalId = getFakeNationalId() + + const user = createCurrentUser({ + nationalId: userNationalId, + scope: [defaultScopes.testUserHasAccess.name], + client: client.clientId, }) - await delegationProviderModel.create({ - id: AuthDelegationProvider.Custom, - name: 'Custom', - description: 'Custom', + const domain = createDomain() + + beforeAll(async () => { + app = await setupWithAuth({ + user, + }) + server = request(app.getHttpServer()) + + const domainModel = app.get(getModelToken(Domain)) + await domainModel.create(domain) + + apiScopeModel = app.get(getModelToken(ApiScope)) + + apiScopeDelegationTypeModel = app.get( + getModelToken(ApiScopeDelegationType), + ) + delegationTypeModel = app.get( + getModelToken(DelegationTypeModel), + ) + delegationProviderModel = app.get( + getModelToken(DelegationProviderModel), + ) + delegationScopesModel = app.get( + getModelToken(DelegationScope), + ) + delegationModel = app.get( + getModelToken(Delegation), + ) + delegationDelegationTypeModel = app.get< + typeof DelegationDelegationType + >(getModelToken(DelegationDelegationType)) + nationalRegistryApi = app.get(NationalRegistryClientService) + nationalRegistryV3Api = app.get(NationalRegistryV3ClientService) + const nationalRegistryV3FeatureService = app.get( + NationalRegistryV3FeatureService, + ) + jest + .spyOn(nationalRegistryV3FeatureService, 'getValue') + .mockImplementation(async () => featureFlag) + factory = new FixtureFactory(app) }) - await delegationDelegationTypeModel.create({ - delegationId: delegations.id, - delegationTypeId: AuthDelegationType.GeneralMandate, + afterAll(async () => { + await app.cleanUp() }) - await apiScopeModel.bulkCreate( - scopeNames.map((name) => ({ - name, - domainName: domain.name, - enabled: true, - description: `${name}: description`, - displayName: `${name}: display name`, - })), - ) - - // set 2 of 3 scopes to have general mandate delegation type - await apiScopeDelegationTypeModel.bulkCreate([ - { - apiScopeName: scopeNames[0], - delegationType: AuthDelegationType.GeneralMandate, - }, - { - apiScopeName: scopeNames[1], - delegationType: AuthDelegationType.GeneralMandate, - }, - ]) - - nationalRegistryApiSpy = jest - .spyOn(nationalRegistryApi, 'getIndividual') - .mockImplementation(async (id) => { - const user = createNationalRegistryUser({ - nationalId: representeeNationalId, + describe('GET with general mandate delegation type', () => { + const representeeNationalId = getFakeNationalId() + let nationalRegistryApiSpy: jest.SpyInstance + let nationalRegistryV3ApiSpy: jest.SpyInstance + const scopeNames = [ + 'api-scope/generalMandate1', + 'api-scope/generalMandate2', + 'api-scope/generalMandate3', + ] + + beforeAll(async () => { + client.supportedDelegationTypes = [ + AuthDelegationType.GeneralMandate, + AuthDelegationType.LegalGuardian, + ] + await factory.createClient(client) + + const delegations = await delegationModel.create({ + id: uuid(), + fromDisplayName: 'Test', + fromNationalId: representeeNationalId, + toNationalId: userNationalId, + toName: 'Test', + }) + + await delegationProviderModel.create({ + id: AuthDelegationProvider.Custom, + name: 'Custom', + description: 'Custom', + }) + + await delegationDelegationTypeModel.create({ + delegationId: delegations.id, + delegationTypeId: AuthDelegationType.GeneralMandate, }) - return user ?? null + await apiScopeModel.bulkCreate( + scopeNames.map((name) => ({ + name, + domainName: domain.name, + enabled: true, + description: `${name}: description`, + displayName: `${name}: display name`, + })), + ) + + // set 2 of 3 scopes to have general mandate delegation type + await apiScopeDelegationTypeModel.bulkCreate([ + { + apiScopeName: scopeNames[0], + delegationType: AuthDelegationType.GeneralMandate, + }, + { + apiScopeName: scopeNames[1], + delegationType: AuthDelegationType.GeneralMandate, + }, + ]) + + nationalRegistryApiSpy = jest + .spyOn(nationalRegistryApi, 'getIndividual') + .mockImplementation(async (id) => { + const user = createNationalRegistryUser({ + nationalId: representeeNationalId, + }) + + return user ?? null + }) + + nationalRegistryV3ApiSpy = jest + .spyOn(nationalRegistryV3Api, 'getAllDataIndividual') + .mockImplementation(async () => { + const user = createNationalRegistryUser({ + nationalId: representeeNationalId, + }) + + return { kennitala: user.nationalId, nafn: user.name } + }) }) - }) - afterAll(async () => { - await app.cleanUp() - nationalRegistryApiSpy.mockClear() - }) + afterAll(async () => { + await app.cleanUp() + nationalRegistryApiSpy.mockClear() + nationalRegistryV3ApiSpy.mockClear() + }) - it('should return mergedDelegationDTO with the generalMandate', async () => { - const response = await server.get('/v2/delegations') + it('should return mergedDelegationDTO with the generalMandate', async () => { + const response = await server.get('/v2/delegations') - expect(response.status).toEqual(200) - expect(response.body).toHaveLength(1) - }) + expect(response.status).toEqual(200) + expect(response.body).toHaveLength(1) + }) - it('should get all general mandate scopes', async () => { - const response = await server.get('/delegations/scopes').query({ - fromNationalId: representeeNationalId, - delegationType: [AuthDelegationType.GeneralMandate], - }) + it('should get all general mandate scopes', async () => { + const response = await server.get('/delegations/scopes').query({ + fromNationalId: representeeNationalId, + delegationType: [AuthDelegationType.GeneralMandate], + }) - expect(response.status).toEqual(200) - expect(response.body).toEqual([scopeNames[0], scopeNames[1]]) - }) + expect(response.status).toEqual(200) + expect(response.body).toEqual([scopeNames[0], scopeNames[1]]) + }) - it('should only return valid general mandates', async () => { - const newNationalId = getFakeNationalId() - const newDelegation = await delegationModel.create({ - id: uuid(), - fromDisplayName: 'Test', - fromNationalId: newNationalId, - toNationalId: userNationalId, - toName: 'Test', - }) + it('should only return valid general mandates', async () => { + const newNationalId = getFakeNationalId() + const newDelegation = await delegationModel.create({ + id: uuid(), + fromDisplayName: 'Test', + fromNationalId: newNationalId, + toNationalId: userNationalId, + toName: 'Test', + }) - await delegationDelegationTypeModel.create({ - delegationId: newDelegation.id, - delegationTypeId: AuthDelegationType.GeneralMandate, - validTo: addDays(new Date(), -2), - }) + await delegationDelegationTypeModel.create({ + delegationId: newDelegation.id, + delegationTypeId: AuthDelegationType.GeneralMandate, + validTo: addDays(new Date(), -2), + }) - const response = await server.get('/delegations/scopes').query({ - fromNationalId: newNationalId, - delegationType: [AuthDelegationType.GeneralMandate], - }) + const response = await server.get('/delegations/scopes').query({ + fromNationalId: newNationalId, + delegationType: [AuthDelegationType.GeneralMandate], + }) - expect(response.status).toEqual(200) - expect(response.body).toEqual([]) - }) + expect(response.status).toEqual(200) + expect(response.body).toEqual([]) + }) - it('should return all general mandate scopes and other preset scopes', async () => { - const newDelegation = await delegationModel.create({ - id: uuid(), - fromDisplayName: 'Test', - fromNationalId: representeeNationalId, - domainName: domain.name, - toNationalId: userNationalId, - toName: 'Test', - }) + it('should return all general mandate scopes and other preset scopes', async () => { + const newDelegation = await delegationModel.create({ + id: uuid(), + fromDisplayName: 'Test', + fromNationalId: representeeNationalId, + domainName: domain.name, + toNationalId: userNationalId, + toName: 'Test', + }) - await delegationTypeModel.create({ - id: AuthDelegationType.Custom, - name: 'custom', - description: 'custom', - providerId: AuthDelegationProvider.Custom, - }) + await delegationTypeModel.create({ + id: AuthDelegationType.Custom, + name: 'custom', + description: 'custom', + providerId: AuthDelegationProvider.Custom, + }) - await delegationScopesModel.create({ - id: uuid(), - delegationId: newDelegation.id, - scopeName: scopeNames[2], - // set valid from as yesterday and valid to as tomorrow - validFrom: addDays(new Date(), -1), - validTo: addDays(new Date(), 1), - }) + await delegationScopesModel.create({ + id: uuid(), + delegationId: newDelegation.id, + scopeName: scopeNames[2], + // set valid from as yesterday and valid to as tomorrow + validFrom: addDays(new Date(), -1), + validTo: addDays(new Date(), 1), + }) - await apiScopeDelegationTypeModel.create({ - apiScopeName: scopeNames[2], - delegationType: AuthDelegationType.LegalGuardian, - }) + await apiScopeDelegationTypeModel.create({ + apiScopeName: scopeNames[2], + delegationType: AuthDelegationType.LegalGuardian, + }) - const response = await server.get('/delegations/scopes').query({ - fromNationalId: representeeNationalId, - delegationType: [ - AuthDelegationType.GeneralMandate, - AuthDelegationType.LegalGuardian, - ], - }) + const response = await server.get('/delegations/scopes').query({ + fromNationalId: representeeNationalId, + delegationType: [ + AuthDelegationType.GeneralMandate, + AuthDelegationType.LegalGuardian, + ], + }) - expect(response.status).toEqual(200) - expect(response.body).toEqual(expect.arrayContaining(scopeNames)) - expect(response.body).toHaveLength(scopeNames.length) + expect(response.status).toEqual(200) + expect(response.body).toEqual(expect.arrayContaining(scopeNames)) + expect(response.body).toHaveLength(scopeNames.length) + }) + }) }) - }) - }) + }, + ) }) diff --git a/apps/services/auth/personal-representative/infra/personal-representative.ts b/apps/services/auth/personal-representative/infra/personal-representative.ts index 180b75ec9891..42928e86db9f 100644 --- a/apps/services/auth/personal-representative/infra/personal-representative.ts +++ b/apps/services/auth/personal-representative/infra/personal-representative.ts @@ -1,5 +1,10 @@ import { json, service, ServiceBuilder } from '../../../../../infra/src/dsl/dsl' -import { Base, Client, RskProcuring } from '../../../../../infra/src/dsl/xroad' +import { + Base, + Client, + NationalRegistryAuthB2C, + RskProcuring, +} from '../../../../../infra/src/dsl/xroad' const REDIS_NODE_CONFIG = { dev: json([ @@ -62,8 +67,10 @@ export const serviceSetup = '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET', SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME', SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD', + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: + '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET', }) - .xroad(Base, Client, RskProcuring) + .xroad(Base, Client, RskProcuring, NationalRegistryAuthB2C) .ingress({ primary: { host: { diff --git a/apps/services/auth/personal-representative/src/app/app.module.ts b/apps/services/auth/personal-representative/src/app/app.module.ts index e46d8b36eeed..0c89f4dd3a5a 100644 --- a/apps/services/auth/personal-representative/src/app/app.module.ts +++ b/apps/services/auth/personal-representative/src/app/app.module.ts @@ -8,8 +8,10 @@ import { import { AuthModule } from '@island.is/auth-nest-tools' import { RskRelationshipsClientConfig } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientConfig } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientConfig } from '@island.is/clients/national-registry-v3' import { CompanyRegistryConfig } from '@island.is/clients/rsk/company-registry' import { SyslumennClientConfig } from '@island.is/clients/syslumenn' +import { ZendeskServiceConfig } from '@island.is/clients/zendesk' import { AuditModule } from '@island.is/nest/audit' import { ConfigModule, @@ -17,12 +19,11 @@ import { XRoadConfig, } from '@island.is/nest/config' import { FeatureFlagConfig } from '@island.is/nest/feature-flags' -import { ZendeskServiceConfig } from '@island.is/clients/zendesk' import { environment } from '../environments' -import { PersonalRepresentativeTypesModule } from './modules/personalRepresentativeTypes/personalRepresentativeTypes.module' import { AccessLogsModule } from './modules/accessLogs/accessLogs.module' import { PersonalRepresentativesModule } from './modules/personalRepresentatives/personalRepresentatives.module' +import { PersonalRepresentativeTypesModule } from './modules/personalRepresentativeTypes/personalRepresentativeTypes.module' import { RightTypesModule } from './modules/rightTypes/rightTypes.module' @Module({ @@ -38,6 +39,7 @@ import { RightTypesModule } from './modules/rightTypes/rightTypes.module' DelegationConfig, IdsClientConfig, NationalRegistryClientConfig, + NationalRegistryV3ClientConfig, RskRelationshipsClientConfig, CompanyRegistryConfig, XRoadConfig, diff --git a/apps/services/auth/public-api/infra/auth-public-api.ts b/apps/services/auth/public-api/infra/auth-public-api.ts index b27663150b40..609588c46271 100644 --- a/apps/services/auth/public-api/infra/auth-public-api.ts +++ b/apps/services/auth/public-api/infra/auth-public-api.ts @@ -1,5 +1,10 @@ import { json, service, ServiceBuilder } from '../../../../../infra/src/dsl/dsl' -import { Base, Client, RskProcuring } from '../../../../../infra/src/dsl/xroad' +import { + Base, + Client, + NationalRegistryAuthB2C, + RskProcuring, +} from '../../../../../infra/src/dsl/xroad' const REDIS_NODE_CONFIG = { dev: json([ @@ -86,8 +91,10 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-public-api'> => { '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET', SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME', SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD', + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: + '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET', }) - .xroad(Base, Client, RskProcuring) + .xroad(Base, Client, RskProcuring, NationalRegistryAuthB2C) .ingress({ primary: { host: { diff --git a/apps/services/auth/public-api/src/app/app.module.ts b/apps/services/auth/public-api/src/app/app.module.ts index e3719da7e4b9..7fe7ba43de60 100644 --- a/apps/services/auth/public-api/src/app/app.module.ts +++ b/apps/services/auth/public-api/src/app/app.module.ts @@ -9,8 +9,10 @@ import { import { AuthModule } from '@island.is/auth-nest-tools' import { RskRelationshipsClientConfig } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientConfig } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientConfig } from '@island.is/clients/national-registry-v3' import { CompanyRegistryConfig } from '@island.is/clients/rsk/company-registry' import { SyslumennClientConfig } from '@island.is/clients/syslumenn' +import { ZendeskServiceConfig } from '@island.is/clients/zendesk' import { AuditModule } from '@island.is/nest/audit' import { ConfigModule, @@ -23,7 +25,6 @@ import { ProblemModule } from '@island.is/nest/problem' import { environment } from '../environments' import { DelegationsModule } from './modules/delegations/delegations.module' import { PasskeysModule } from './modules/passkeys/passkeys.module' -import { ZendeskServiceConfig } from '@island.is/clients/zendesk' @Module({ imports: [ @@ -42,6 +43,7 @@ import { ZendeskServiceConfig } from '@island.is/clients/zendesk' FeatureFlagConfig, IdsClientConfig, NationalRegistryClientConfig, + NationalRegistryV3ClientConfig, RskRelationshipsClientConfig, CompanyRegistryConfig, XRoadConfig, diff --git a/apps/services/auth/public-api/src/app/modules/delegations/actorDelegations.controller.spec.ts b/apps/services/auth/public-api/src/app/modules/delegations/actorDelegations.controller.spec.ts index d57f16dea37d..a7ad9fb47b1e 100644 --- a/apps/services/auth/public-api/src/app/modules/delegations/actorDelegations.controller.spec.ts +++ b/apps/services/auth/public-api/src/app/modules/delegations/actorDelegations.controller.spec.ts @@ -1,8 +1,8 @@ import { getModelToken } from '@nestjs/sequelize' +import addYears from 'date-fns/addYears' +import kennitala from 'kennitala' import times from 'lodash/times' import request from 'supertest' -import kennitala from 'kennitala' -import addYears from 'date-fns/addYears' import { ClientDelegationType, @@ -14,6 +14,7 @@ import { DelegationScope, DelegationTypeModel, MergedDelegationDTO, + NationalRegistryV3FeatureService, PersonalRepresentative, PersonalRepresentativeDelegationTypeModel, PersonalRepresentativeRight, @@ -26,6 +27,7 @@ import { IndividualDto, NationalRegistryClientService, } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { createClient, createDelegation, @@ -153,923 +155,976 @@ beforeAll(() => { }) describe('ActorDelegationsController', () => { - describe('with auth', () => { - let app: TestApp - let server: request.SuperTest - let delegationModel: typeof Delegation - let delegationDelegationTypeModel: typeof DelegationDelegationType - let clientDelegationTypeModel: typeof ClientDelegationType - let nationalRegistryApi: NationalRegistryClientService - - beforeAll(async () => { - // TestApp setup with auth and database - app = await setupWithAuth({ - user, - userName, - nationalRegistryUser, - client: { - props: client, - scopes: Scopes.slice(0, 4).map((s) => s.name), - }, - }) - server = request(app.getHttpServer()) - - // Get reference on Delegation and Client models to seed DB - delegationModel = app.get(getModelToken(Delegation)) - clientDelegationTypeModel = app.get( - getModelToken(ClientDelegationType), - ) - delegationDelegationTypeModel = app.get( - getModelToken(DelegationDelegationType), - ) - nationalRegistryApi = app.get(NationalRegistryClientService) - }) - - beforeEach(async () => { - return await clientDelegationTypeModel.bulkCreate( - delegationTypes.map((type) => ({ - clientId: client.clientId, - delegationType: type, - })), - { - updateOnDuplicate: ['modified'], - }, - ) - }) - - afterAll(async () => { - await app.cleanUp() - }) - - afterEach(async () => { - await delegationModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - }) - - describe('GET /actor/delegations', () => { - let nationalRegistryApiSpy: jest.SpyInstance - const path = '/v1/actor/delegations' - const query = '?direction=incoming' - const deceasedNationalIds = times(3, () => createNationalId('person')) - const nationalRegistryUsers = [ - nationalRegistryUser, - ...Object.values(mockDelegations).map((delegation) => - createNationalRegistryUser({ - nationalId: delegation.fromNationalId, - }), - ), - ...times(10, () => - createNationalRegistryUser({ - name: getFakeName(), - nationalId: createNationalId('person'), - }), - ), - ] - - beforeAll(async () => { - nationalRegistryApiSpy = jest - .spyOn(nationalRegistryApi, 'getIndividual') - .mockImplementation(async (id) => { - if (deceasedNationalIds.includes(id)) { - return null - } + describe.each([false, true])( + 'national registry v3 featureflag: %s', + (featureFlag) => { + describe('with auth', () => { + let app: TestApp + let server: request.SuperTest + let delegationModel: typeof Delegation + let delegationDelegationTypeModel: typeof DelegationDelegationType + let clientDelegationTypeModel: typeof ClientDelegationType + let nationalRegistryApi: NationalRegistryClientService + let nationalRegistryV3Api: NationalRegistryV3ClientService - const user = nationalRegistryUsers.find((u) => u?.nationalId === id) - - return user ?? null + beforeAll(async () => { + // TestApp setup with auth and database + app = await setupWithAuth({ + user, + userName, + nationalRegistryUser, + client: { + props: client, + scopes: Scopes.slice(0, 4).map((s) => s.name), + }, }) - }) - - it('should return only valid delegations', async () => { - // Arrange - await createDelegationModels( - delegationModel, - Object.values(mockDelegations), - ) - const expectedModels = await findExpectedMergedDelegationModels( - delegationModel, - [ - mockDelegations.incoming.id, - mockDelegations.incomingBothValidAndNotAllowed.id, - mockDelegations.incomingWithOtherDomain.id, - ], - [Scopes[0].name], - ) - - // Act - const res = await server.get(`${path}${query}`) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(3) - expectMatchingMergedDelegations( - res.body, - updateDelegationFromNameToPersonName( - expectedModels, - nationalRegistryUsers, - ), - ) - }) + server = request(app.getHttpServer()) - it('should return only delegations with scopes the client has access to', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incomingWithOtherDomain, - ]) - const expectedModel = await findExpectedMergedDelegationModels( - delegationModel, - mockDelegations.incomingWithOtherDomain.id, - [Scopes[0].name], - ) - - // Act - const res = await server.get(`${path}${query}`) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expectMatchingMergedDelegations( - res.body[0], - updateDelegationFromNameToPersonName( - expectedModel, - nationalRegistryUsers, - ), - ) - }) - - it('should return custom delegations and general mandate when the delegationTypes filter has both types and delegation exists for both', async () => { - // Arrange - const delegation = createDelegation({ - fromNationalId: nationalRegistryUser.nationalId, - toNationalId: user.nationalId, - scopes: [], + // Get reference on Delegation and Client models to seed DB + delegationModel = app.get( + getModelToken(Delegation), + ) + clientDelegationTypeModel = app.get( + getModelToken(ClientDelegationType), + ) + delegationDelegationTypeModel = app.get< + typeof DelegationDelegationType + >(getModelToken(DelegationDelegationType)) + nationalRegistryApi = app.get(NationalRegistryClientService) + nationalRegistryV3Api = app.get(NationalRegistryV3ClientService) }) - await delegationModel.create(delegation) - - await delegationDelegationTypeModel.create({ - delegationId: delegation.id, - delegationTypeId: AuthDelegationType.GeneralMandate, + beforeEach(async () => { + return await clientDelegationTypeModel.bulkCreate( + delegationTypes.map((type) => ({ + clientId: client.clientId, + delegationType: type, + })), + { + updateOnDuplicate: ['modified'], + }, + ) }) - await createDelegationModels(delegationModel, [ - mockDelegations.incomingWithOtherDomain, - ]) - - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.GeneralMandate}`, - ) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(2) - expect( - res.body - .map((d: MergedDelegationDTO) => d.types) - .flat() - .sort(), - ).toEqual( - [AuthDelegationType.Custom, AuthDelegationType.GeneralMandate].sort(), - ) - }) - - it('should return a merged object with both Custom and GeneralMandate types', async () => { - // Arrange - const delegation = createDelegation({ - fromNationalId: - mockDelegations.incomingWithOtherDomain.fromNationalId, - toNationalId: user.nationalId, - domainName: null, - scopes: [], + afterAll(async () => { + await app.cleanUp() }) - await delegationModel.create(delegation) - - await delegationDelegationTypeModel.create({ - delegationId: delegation.id, - delegationTypeId: AuthDelegationType.GeneralMandate, + afterEach(async () => { + await delegationModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) }) - await createDelegationModels(delegationModel, [ - mockDelegations.incomingWithOtherDomain, - ]) - - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.GeneralMandate}`, - ) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expect( - res.body - .map((d: MergedDelegationDTO) => d.types) - .flat() - .sort(), - ).toEqual( - [AuthDelegationType.Custom, AuthDelegationType.GeneralMandate].sort(), - ) - }) - - it('should return only delegations related to the provided otherUser national id', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incoming, - ]) - const expectedModel = await findExpectedMergedDelegationModels( - delegationModel, - mockDelegations.incoming.id, - [Scopes[0].name], - ) - - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&otherUser=${mockDelegations.incoming.fromNationalId}`, - ) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expectMatchingMergedDelegations( - res.body[0], - updateDelegationFromNameToPersonName( - expectedModel, - nationalRegistryUsers, - ), - ) - }) - - it('should return only delegations related to the provided otherUser national id without the general mandate since there is none', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incoming, - ]) - const expectedModel = await findExpectedMergedDelegationModels( - delegationModel, - mockDelegations.incoming.id, - [Scopes[0].name], - ) - - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes${AuthDelegationType.GeneralMandate}&otherUser=${mockDelegations.incoming.fromNationalId}`, - ) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expectMatchingMergedDelegations( - res.body[0], - updateDelegationFromNameToPersonName( - expectedModel, - nationalRegistryUsers, - ), - ) - }) - - it('should return empty array when provided otherUser national id is not related to any delegation', async () => { - // Arrange - const unrelatedNationalId = createNationalId('person') + describe('GET /actor/delegations', () => { + let nationalRegistryApiSpy: jest.SpyInstance + let nationalRegistryV3ApiSpy: jest.SpyInstance + const path = '/v1/actor/delegations' + const query = '?direction=incoming' + const deceasedNationalIds = times(3, () => createNationalId('person')) + const nationalRegistryUsers = [ + nationalRegistryUser, + ...Object.values(mockDelegations).map((delegation) => + createNationalRegistryUser({ + nationalId: delegation.fromNationalId, + }), + ), + ...times(10, () => + createNationalRegistryUser({ + name: getFakeName(), + nationalId: createNationalId('person'), + }), + ), + ] - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&otherUser=${unrelatedNationalId}`, - ) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) - - it('should return custom delegations and not deceased delegations, when the delegationTypes filter is custom type', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incoming, - createDelegation({ - fromNationalId: deceasedNationalIds[0], - toNationalId: user.nationalId, - scopes: [Scopes[0].name], - today, - }), - createDelegation({ - fromNationalId: deceasedNationalIds[1], - toNationalId: user.nationalId, - scopes: [Scopes[0].name], - today, - }), - ]) - - // We expect the first model to be returned, but not the second or third since they are tied to a deceased person - const expectedModel = await findExpectedMergedDelegationModels( - delegationModel, - mockDelegations.incoming.id, - [Scopes[0].name], - ) + beforeAll(async () => { + nationalRegistryApiSpy = jest + .spyOn(nationalRegistryApi, 'getIndividual') + .mockImplementation(async (id) => { + if (deceasedNationalIds.includes(id)) { + return null + } + + const user = nationalRegistryUsers.find( + (u) => u?.nationalId === id, + ) + + return user ?? null + }) + const nationalRegistryV3FeatureService = app.get( + NationalRegistryV3FeatureService, + ) + jest + .spyOn(nationalRegistryV3FeatureService, 'getValue') + .mockImplementation(async () => featureFlag) + nationalRegistryV3ApiSpy = jest + .spyOn(nationalRegistryV3Api, 'getAllDataIndividual') + .mockImplementation(async (id) => { + if (deceasedNationalIds.includes(id)) { + return { + kennitala: id, + afdrif: 'LÉST', + } + } + + const user = nationalRegistryUsers.find( + (u) => u?.nationalId === id, + ) + + return user + ? { + kennitala: id, + nafn: user?.name, + afdrif: null, + } + : null + }) + }) - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, - ) + it('should return only valid delegations', async () => { + // Arrange + await createDelegationModels( + delegationModel, + Object.values(mockDelegations), + ) + const expectedModels = await findExpectedMergedDelegationModels( + delegationModel, + [ + mockDelegations.incoming.id, + mockDelegations.incomingBothValidAndNotAllowed.id, + mockDelegations.incomingWithOtherDomain.id, + ], + [Scopes[0].name], + ) + + // Act + const res = await server.get(`${path}${query}`) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(3) + expectMatchingMergedDelegations( + res.body, + updateDelegationFromNameToPersonName( + expectedModels, + nationalRegistryUsers, + ), + ) + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expectMatchingMergedDelegations( - res.body[0], - updateDelegationFromNameToPersonName( - expectedModel, - nationalRegistryUsers, - ), - ) - - // Verify - const expectedModifiedModels = await delegationModel.findAll({ - where: { - toNationalId: user.nationalId, - }, - include: [ - { - model: DelegationScope, - as: 'delegationScopes', - }, - ], - }) + it('should return only delegations with scopes the client has access to', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incomingWithOtherDomain, + ]) + const expectedModel = await findExpectedMergedDelegationModels( + delegationModel, + mockDelegations.incomingWithOtherDomain.id, + [Scopes[0].name], + ) + + // Act + const res = await server.get(`${path}${query}`) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expectMatchingMergedDelegations( + res.body[0], + updateDelegationFromNameToPersonName( + expectedModel, + nationalRegistryUsers, + ), + ) + }) - expect(expectedModifiedModels.length).toEqual(1) - }) + it('should return custom delegations and general mandate when the delegationTypes filter has both types and delegation exists for both', async () => { + // Arrange + const delegation = createDelegation({ + fromNationalId: nationalRegistryUser.nationalId, + toNationalId: user.nationalId, + scopes: [], + }) - it('should not mix up companies and individuals when processing deceased delegations [BUG]', async () => { - // Arrange - const incomingCompany = createDelegation({ - fromNationalId: createNationalId('company'), - toNationalId: user.nationalId, - scopes: [Scopes[0].name], - today, - }) - await createDelegationModels(delegationModel, [ - // The order of these is important to trigger the previous bug. - incomingCompany, - mockDelegations.incoming, - ]) - - // We expect both models to be returned. - const expectedModels = await findExpectedMergedDelegationModels( - delegationModel, - [mockDelegations.incoming.id, incomingCompany.id], - [Scopes[0].name], - ) + await delegationModel.create(delegation) - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, - ) + await delegationDelegationTypeModel.create({ + delegationId: delegation.id, + delegationTypeId: AuthDelegationType.GeneralMandate, + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(2) - expectMatchingMergedDelegations( - res.body, - updateDelegationFromNameToPersonName( - expectedModels, - nationalRegistryUsers, - ), - ) - }) + await createDelegationModels(delegationModel, [ + mockDelegations.incomingWithOtherDomain, + ]) - it('should return delegations which only has scopes with special scope rules [BUG]', async () => { - // Arrange - const specialDelegation = createDelegation({ - fromNationalId: nationalRegistryUser.nationalId, - toNationalId: user.nationalId, - scopes: [Scopes[3].name], - today, - }) - await createDelegationModels(delegationModel, [specialDelegation]) + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.GeneralMandate}`, + ) - // We expect the delegation to be returned. - const expectedModels = await findExpectedMergedDelegationModels( - delegationModel, - [specialDelegation.id], - ) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(2) + expect( + res.body + .map((d: MergedDelegationDTO) => d.types) + .flat() + .sort(), + ).toEqual( + [ + AuthDelegationType.Custom, + AuthDelegationType.GeneralMandate, + ].sort(), + ) + }) - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, - ) + it('should return a merged object with both Custom and GeneralMandate types', async () => { + // Arrange + const delegation = createDelegation({ + fromNationalId: + mockDelegations.incomingWithOtherDomain.fromNationalId, + toNationalId: user.nationalId, + domainName: null, + scopes: [], + }) - // Assert - expect(res.status).toEqual(200) - expectMatchingMergedDelegations( - res.body, - updateDelegationFromNameToPersonName( - expectedModels, - nationalRegistryUsers, - ), - ) - }) + await delegationModel.create(delegation) - it('should return delegations when the delegationTypes filter is empty', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incomingWithOtherDomain, - ]) - const expectedModel = await findExpectedMergedDelegationModels( - delegationModel, - mockDelegations.incomingWithOtherDomain.id, - [Scopes[0].name], - ) + await delegationDelegationTypeModel.create({ + delegationId: delegation.id, + delegationTypeId: AuthDelegationType.GeneralMandate, + }) - // Act - const res = await server.get(`${path}${query}&delegationTypes=`) + await createDelegationModels(delegationModel, [ + mockDelegations.incomingWithOtherDomain, + ]) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expectMatchingMergedDelegations( - res.body[0], - updateDelegationFromNameToPersonName( - expectedModel, - nationalRegistryUsers, - ), - ) - }) + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.GeneralMandate}`, + ) - it('should not return custom delegations when the delegationTypes filter does not include custom type', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incomingWithOtherDomain, - ]) - - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.ProcurationHolder}`, - ) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expect( + res.body + .map((d: MergedDelegationDTO) => d.types) + .flat() + .sort(), + ).toEqual( + [ + AuthDelegationType.Custom, + AuthDelegationType.GeneralMandate, + ].sort(), + ) + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) + it('should return only delegations related to the provided otherUser national id', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incoming, + ]) + const expectedModel = await findExpectedMergedDelegationModels( + delegationModel, + mockDelegations.incoming.id, + [Scopes[0].name], + ) + + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&otherUser=${mockDelegations.incoming.fromNationalId}`, + ) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expectMatchingMergedDelegations( + res.body[0], + updateDelegationFromNameToPersonName( + expectedModel, + nationalRegistryUsers, + ), + ) + }) - it('should return no delegation when the client does not have access to any scope', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incomingOnlyOtherDomain, - ]) + it('should return only delegations related to the provided otherUser national id without the general mandate since there is none', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incoming, + ]) + const expectedModel = await findExpectedMergedDelegationModels( + delegationModel, + mockDelegations.incoming.id, + [Scopes[0].name], + ) + + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes${AuthDelegationType.GeneralMandate}&otherUser=${mockDelegations.incoming.fromNationalId}`, + ) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expectMatchingMergedDelegations( + res.body[0], + updateDelegationFromNameToPersonName( + expectedModel, + nationalRegistryUsers, + ), + ) + }) - // Act - const res = await server.get(`${path}${query}`) + it('should return empty array when provided otherUser national id is not related to any delegation', async () => { + // Arrange + const unrelatedNationalId = createNationalId('person') - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&otherUser=${unrelatedNationalId}`, + ) - it('should return 400 BadRequest if required query paramter is missing', async () => { - // Act - const res = await server.get(path) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) - // Assert - expect(res.status).toEqual(400) - expect(res.body).toMatchObject({ - status: 400, - type: 'https://httpstatuses.org/400', - title: 'Bad Request', - detail: - "'direction' can only be set to incoming for the /actor alias", - }) - }) + it('should return custom delegations and not deceased delegations, when the delegationTypes filter is custom type', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incoming, + createDelegation({ + fromNationalId: deceasedNationalIds[0], + toNationalId: user.nationalId, + scopes: [Scopes[0].name], + today, + }), + createDelegation({ + fromNationalId: deceasedNationalIds[1], + toNationalId: user.nationalId, + scopes: [Scopes[0].name], + today, + }), + ]) + + // We expect the first model to be returned, but not the second or third since they are tied to a deceased person + const expectedModel = await findExpectedMergedDelegationModels( + delegationModel, + mockDelegations.incoming.id, + [Scopes[0].name], + ) + + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, + ) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expectMatchingMergedDelegations( + res.body[0], + updateDelegationFromNameToPersonName( + expectedModel, + nationalRegistryUsers, + ), + ) + + // Verify + const expectedModifiedModels = await delegationModel.findAll({ + where: { + toNationalId: user.nationalId, + }, + include: [ + { + model: DelegationScope, + as: 'delegationScopes', + }, + ], + }) - it('should not return delegation with no scope longer allowed for delegation', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incomingWithNoAllowed, - ]) + expect(expectedModifiedModels.length).toEqual(1) + }) - // Act - const res = await server.get(`${path}${query}`) + it('should not mix up companies and individuals when processing deceased delegations [BUG]', async () => { + // Arrange + const incomingCompany = createDelegation({ + fromNationalId: createNationalId('company'), + toNationalId: user.nationalId, + scopes: [Scopes[0].name], + today, + }) + await createDelegationModels(delegationModel, [ + // The order of these is important to trigger the previous bug. + incomingCompany, + mockDelegations.incoming, + ]) + + // We expect both models to be returned. + const expectedModels = await findExpectedMergedDelegationModels( + delegationModel, + [mockDelegations.incoming.id, incomingCompany.id], + [Scopes[0].name], + ) + + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, + ) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(2) + expectMatchingMergedDelegations( + res.body, + updateDelegationFromNameToPersonName( + expectedModels, + nationalRegistryUsers, + ), + ) + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) + it('should return delegations which only has scopes with special scope rules [BUG]', async () => { + // Arrange + const specialDelegation = createDelegation({ + fromNationalId: nationalRegistryUser.nationalId, + toNationalId: user.nationalId, + scopes: [Scopes[3].name], + today, + }) + await createDelegationModels(delegationModel, [specialDelegation]) + + // We expect the delegation to be returned. + const expectedModels = await findExpectedMergedDelegationModels( + delegationModel, + [specialDelegation.id], + ) + + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, + ) + + // Assert + expect(res.status).toEqual(200) + expectMatchingMergedDelegations( + res.body, + updateDelegationFromNameToPersonName( + expectedModels, + nationalRegistryUsers, + ), + ) + }) - it('should not return delegation when client does not support custom delegations', async () => { - // Arrange - await createDelegationModels(delegationModel, [ - mockDelegations.incoming, - ]) - await clientDelegationTypeModel.destroy({ - where: { - clientId: client.clientId, - delegationType: AuthDelegationType.Custom, - }, - }) + it('should return delegations when the delegationTypes filter is empty', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incomingWithOtherDomain, + ]) + const expectedModel = await findExpectedMergedDelegationModels( + delegationModel, + mockDelegations.incomingWithOtherDomain.id, + [Scopes[0].name], + ) + + // Act + const res = await server.get(`${path}${query}&delegationTypes=`) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expectMatchingMergedDelegations( + res.body[0], + updateDelegationFromNameToPersonName( + expectedModel, + nationalRegistryUsers, + ), + ) + }) - // Act - const res = await server.get(`${path}${query}`) + it('should not return custom delegations when the delegationTypes filter does not include custom type', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incomingWithOtherDomain, + ]) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.ProcurationHolder}`, + ) - describe('with legal guardian delegations', () => { - let getForsja: jest.SpyInstance - let clientInstance: any - - const mockForKt = (kt: string): void => { - jest.spyOn(kennitala, 'info').mockReturnValue({ - kt, - age: 16, - birthday: addYears(Date.now(), -15), - birthdayReadable: '', - type: 'person', - valid: true, + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) }) - jest.spyOn(clientInstance, 'getIndividual').mockResolvedValueOnce({ - nationalId: kt, - name: nationalRegistryUser.name, - } as IndividualDto) - - jest - .spyOn(clientInstance, 'getCustodyChildren') - .mockResolvedValueOnce([kt]) - } - - beforeEach(() => { - clientInstance = app.get(NationalRegistryClientService) - getForsja = jest - .spyOn(clientInstance, 'getCustodyChildren') - .mockResolvedValue([nationalRegistryUser.nationalId]) - }) - - afterAll(() => { - getForsja.mockRestore() - }) + it('should return no delegation when the client does not have access to any scope', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incomingOnlyOtherDomain, + ]) - it('should return delegations', async () => { - const kt = '1111089030' - - // Arrange - mockForKt(kt) - - const expectedDelegation = DelegationDTOMapper.toMergedDelegationDTO({ - fromName: nationalRegistryUser.name, - fromNationalId: kt, - provider: AuthDelegationProvider.NationalRegistry, - toNationalId: user.nationalId, - type: [ - AuthDelegationType.LegalGuardian, - AuthDelegationType.LegalGuardianMinor, - ], - } as Omit & { type: AuthDelegationType | AuthDelegationType[] }) - - // Act - const res = await server.get(`${path}${query}`) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expect(res.body[0]).toEqual(expectedDelegation) - }) + // Act + const res = await server.get(`${path}${query}`) - it('should not return delegations when client does not support legal guardian delegations', async () => { - await clientDelegationTypeModel.destroy({ - where: { - clientId: client.clientId, - delegationType: AuthDelegationType.LegalGuardian, - }, + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) }) - // Act - const res = await server.get(`${path}${query}`) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) - - it('should return a legal guardian delegation since the type is included in the delegationTypes filter', async () => { - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.LegalGuardian}`, - ) + it('should return 400 BadRequest if required query paramter is missing', async () => { + // Act + const res = await server.get(path) + + // Assert + expect(res.status).toEqual(400) + expect(res.body).toMatchObject({ + status: 400, + type: 'https://httpstatuses.org/400', + title: 'Bad Request', + detail: + "'direction' can only be set to incoming for the /actor alias", + }) + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expect(res.body[0].types[0]).toEqual(AuthDelegationType.LegalGuardian) - }) + it('should not return delegation with no scope longer allowed for delegation', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incomingWithNoAllowed, + ]) - it('should not return a legal guardian delegation since the type is not included in the delegationTypes filter', async () => { - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, - ) + // Act + const res = await server.get(`${path}${query}`) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) - }) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) - describe('with procuring delegations', () => { - let getIndividualRelationships: jest.SpyInstance - beforeAll(() => { - const client = app.get(RskRelationshipsClient) - getIndividualRelationships = jest - .spyOn(client, 'getIndividualRelationships') - .mockResolvedValue({ - name: nationalRegistryUser.name, - nationalId: nationalRegistryUser.nationalId, - relationships: [ - { - nationalId: nationalRegistryUser.nationalId, - name: nationalRegistryUser.name, - }, - ], + it('should not return delegation when client does not support custom delegations', async () => { + // Arrange + await createDelegationModels(delegationModel, [ + mockDelegations.incoming, + ]) + await clientDelegationTypeModel.destroy({ + where: { + clientId: client.clientId, + delegationType: AuthDelegationType.Custom, + }, }) - }) - afterAll(() => { - getIndividualRelationships.mockRestore() - }) + // Act + const res = await server.get(`${path}${query}`) - it('should return delegations', async () => { - // Arrange - const expectedDelegation = { - fromName: nationalRegistryUser.name, - fromNationalId: nationalRegistryUser.nationalId, - provider: 'fyrirtaekjaskra', - toNationalId: user.nationalId, - type: 'ProcurationHolder', - } as DelegationDTO - // Act - const res = await server.get(`${path}${query}`) - - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expect(res.body[0]).toEqual( - DelegationDTOMapper.toMergedDelegationDTO(expectedDelegation), - ) - }) - - it('should not return delegations when client does not support procuring holder delegations', async () => { - // Arrange - await clientDelegationTypeModel.destroy({ - where: { - clientId: client.clientId, - delegationType: AuthDelegationType.ProcurationHolder, - }, + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) }) - // Act - const res = await server.get(`${path}${query}`) + describe('with legal guardian delegations', () => { + let getForsja: jest.SpyInstance + let clientInstance: any + + const mockForKt = (kt: string): void => { + jest.spyOn(kennitala, 'info').mockReturnValue({ + kt, + age: 16, + birthday: addYears(Date.now(), -15), + birthdayReadable: '', + type: 'person', + valid: true, + }) + + jest + .spyOn(clientInstance, 'getIndividual') + .mockResolvedValueOnce({ + nationalId: kt, + name: nationalRegistryUser.name, + } as IndividualDto) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) + jest + .spyOn(clientInstance, 'getCustodyChildren') + .mockResolvedValueOnce([kt]) + } - it('should return a procuring holder delegation since the type is included in the delegationTypes filter', async () => { - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.ProcurationHolder}&delegationTypes=${AuthDelegationType.PersonalRepresentative}`, - ) + beforeEach(() => { + clientInstance = app.get(NationalRegistryClientService) + getForsja = jest + .spyOn(clientInstance, 'getCustodyChildren') + .mockResolvedValue([nationalRegistryUser.nationalId]) + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expect(res.body[0].types[0]).toEqual( - AuthDelegationType.ProcurationHolder, - ) - }) + afterAll(() => { + getForsja.mockRestore() + }) - it('should not return a procuring holder delegation since the type is not included in the delegationTypes filter', async () => { - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.LegalGuardian}&delegationTypes=${AuthDelegationType.PersonalRepresentative}`, - ) + it('should return delegations', async () => { + const kt = '1111089030' + + // Arrange + mockForKt(kt) + + const expectedDelegation = + DelegationDTOMapper.toMergedDelegationDTO({ + fromName: nationalRegistryUser.name, + fromNationalId: kt, + provider: AuthDelegationProvider.NationalRegistry, + toNationalId: user.nationalId, + type: [ + AuthDelegationType.LegalGuardian, + AuthDelegationType.LegalGuardianMinor, + ], + } as Omit & { type: AuthDelegationType | AuthDelegationType[] }) + + // Act + const res = await server.get(`${path}${query}`) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expect(res.body[0]).toEqual(expectedDelegation) + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) - }) + it('should not return delegations when client does not support legal guardian delegations', async () => { + await clientDelegationTypeModel.destroy({ + where: { + clientId: client.clientId, + delegationType: AuthDelegationType.LegalGuardian, + }, + }) - describe('when user is a personal representative with one representee', () => { - let prModel: typeof PersonalRepresentative - let prRightsModel: typeof PersonalRepresentativeRight - let prRightTypeModel: typeof PersonalRepresentativeRightType - let prTypeModel: typeof PersonalRepresentativeType - let delegationTypeModel: typeof DelegationTypeModel - let delegationProviderModel: typeof DelegationProviderModel - let prDelegationTypeModel: typeof PersonalRepresentativeDelegationTypeModel + // Act + const res = await server.get(`${path}${query}`) - beforeAll(async () => { - prTypeModel = app.get( - 'PersonalRepresentativeTypeRepository', - ) - prModel = app.get( - 'PersonalRepresentativeRepository', - ) - prRightTypeModel = app.get( - 'PersonalRepresentativeRightTypeRepository', - ) - prRightsModel = app.get( - 'PersonalRepresentativeRightRepository', - ) - delegationTypeModel = app.get( - getModelToken(DelegationTypeModel), - ) - delegationProviderModel = app.get( - getModelToken(DelegationProviderModel), - ) - prDelegationTypeModel = app.get< - typeof PersonalRepresentativeDelegationTypeModel - >(getModelToken(PersonalRepresentativeDelegationTypeModel)) - - const prType = await prTypeModel.create({ - code: 'prTypeCode', - name: 'prTypeName', - description: 'prTypeDescription', - }) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) - const pr = await prModel.create({ - nationalIdPersonalRepresentative: user.nationalId, - nationalIdRepresentedPerson: nationalRegistryUser.nationalId, - personalRepresentativeTypeCode: prType.code, - contractId: '1', - externalUserId: '1', - }) + it('should return a legal guardian delegation since the type is included in the delegationTypes filter', async () => { + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.LegalGuardian}`, + ) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expect(res.body[0].types[0]).toEqual( + AuthDelegationType.LegalGuardian, + ) + }) - const dt = await delegationTypeModel.create({ - id: getPersonalRepresentativeDelegationType('prRightType'), - providerId: AuthDelegationProvider.PersonalRepresentativeRegistry, - name: `Personal Representative: prRightType`, - description: `Personal representative delegation type for right type prRightType`, - }) + it('should not return a legal guardian delegation since the type is not included in the delegationTypes filter', async () => { + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}`, + ) - const prRightType = await prRightTypeModel.create({ - code: 'prRightType', - description: 'prRightTypeDescription', + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) }) - const prr = await prRightsModel.create({ - rightTypeCode: prRightType.code, - personalRepresentativeId: pr.id, - }) + describe('with procuring delegations', () => { + let getIndividualRelationships: jest.SpyInstance + beforeAll(() => { + const client = app.get(RskRelationshipsClient) + getIndividualRelationships = jest + .spyOn(client, 'getIndividualRelationships') + .mockResolvedValue({ + name: nationalRegistryUser.name, + nationalId: nationalRegistryUser.nationalId, + relationships: [ + { + nationalId: nationalRegistryUser.nationalId, + name: nationalRegistryUser.name, + }, + ], + }) + }) - await prDelegationTypeModel.create({ - id: prr.id, - delegationTypeId: dt.id, - personalRepresentativeId: pr.id, - }) - }) + afterAll(() => { + getIndividualRelationships.mockRestore() + }) - describe('when fetched', () => { - let response: request.Response - let body: MergedDelegationDTO[] + it('should return delegations', async () => { + // Arrange + const expectedDelegation = { + fromName: nationalRegistryUser.name, + fromNationalId: nationalRegistryUser.nationalId, + provider: 'fyrirtaekjaskra', + toNationalId: user.nationalId, + type: 'ProcurationHolder', + } as DelegationDTO + // Act + const res = await server.get(`${path}${query}`) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expect(res.body[0]).toEqual( + DelegationDTOMapper.toMergedDelegationDTO(expectedDelegation), + ) + }) - beforeAll(async () => { - response = await server.get(`${path}${query}`) - body = response.body - }) + it('should not return delegations when client does not support procuring holder delegations', async () => { + // Arrange + await clientDelegationTypeModel.destroy({ + where: { + clientId: client.clientId, + delegationType: AuthDelegationType.ProcurationHolder, + }, + }) - it('should have a an OK return status', () => { - expect(response.status).toEqual(200) - }) + // Act + const res = await server.get(`${path}${query}`) - it('should return a single entity', () => { - expect(body.length).toEqual(1) - }) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) - it('should have the nationalId of the user as the representer', () => { - expect( - body.some((d) => d.toNationalId === user.nationalId), - ).toBeTruthy() - }) + it('should return a procuring holder delegation since the type is included in the delegationTypes filter', async () => { + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.ProcurationHolder}&delegationTypes=${AuthDelegationType.PersonalRepresentative}`, + ) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expect(res.body[0].types[0]).toEqual( + AuthDelegationType.ProcurationHolder, + ) + }) - it('should have the nationalId of the correct representee', () => { - expect( - body.some( - (d) => d.fromNationalId === nationalRegistryUser.nationalId, - ), - ).toBeTruthy() - }) + it('should not return a procuring holder delegation since the type is not included in the delegationTypes filter', async () => { + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.LegalGuardian}&delegationTypes=${AuthDelegationType.PersonalRepresentative}`, + ) - it('should have the name of the correct representee', () => { - expect( - body.some((d) => d.fromName === nationalRegistryUser.name), - ).toBeTruthy() + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) }) - it('should have the delegation type claim of PersonalRepresentative', () => { - expect( - body.some( - (d) => d.types[0] === AuthDelegationType.PersonalRepresentative, - ), - ).toBeTruthy() - }) - }) + describe('when user is a personal representative with one representee', () => { + let prModel: typeof PersonalRepresentative + let prRightsModel: typeof PersonalRepresentativeRight + let prRightTypeModel: typeof PersonalRepresentativeRightType + let prTypeModel: typeof PersonalRepresentativeType + let delegationTypeModel: typeof DelegationTypeModel + let delegationProviderModel: typeof DelegationProviderModel + let prDelegationTypeModel: typeof PersonalRepresentativeDelegationTypeModel + + beforeAll(async () => { + prTypeModel = app.get( + 'PersonalRepresentativeTypeRepository', + ) + prModel = app.get( + 'PersonalRepresentativeRepository', + ) + prRightTypeModel = app.get< + typeof PersonalRepresentativeRightType + >('PersonalRepresentativeRightTypeRepository') + prRightsModel = app.get( + 'PersonalRepresentativeRightRepository', + ) + delegationTypeModel = app.get( + getModelToken(DelegationTypeModel), + ) + delegationProviderModel = app.get( + getModelToken(DelegationProviderModel), + ) + prDelegationTypeModel = app.get< + typeof PersonalRepresentativeDelegationTypeModel + >(getModelToken(PersonalRepresentativeDelegationTypeModel)) + + const prType = await prTypeModel.create({ + code: 'prTypeCode', + name: 'prTypeName', + description: 'prTypeDescription', + }) + + const pr = await prModel.create({ + nationalIdPersonalRepresentative: user.nationalId, + nationalIdRepresentedPerson: nationalRegistryUser.nationalId, + personalRepresentativeTypeCode: prType.code, + contractId: '1', + externalUserId: '1', + }) + + const dt = await delegationTypeModel.create({ + id: getPersonalRepresentativeDelegationType('prRightType'), + providerId: + AuthDelegationProvider.PersonalRepresentativeRegistry, + name: `Personal Representative: prRightType`, + description: `Personal representative delegation type for right type prRightType`, + }) + + const prRightType = await prRightTypeModel.create({ + code: 'prRightType', + description: 'prRightTypeDescription', + }) + + const prr = await prRightsModel.create({ + rightTypeCode: prRightType.code, + personalRepresentativeId: pr.id, + }) + + await prDelegationTypeModel.create({ + id: prr.id, + delegationTypeId: dt.id, + personalRepresentativeId: pr.id, + }) + }) - it('should not return delegations when client does not support personal representative delegations', async () => { - // Prepare - await clientDelegationTypeModel.destroy({ - where: { - clientId: client.clientId, - delegationType: AuthDelegationType.PersonalRepresentative, - }, - }) + describe('when fetched', () => { + let response: request.Response + let body: MergedDelegationDTO[] + + beforeAll(async () => { + response = await server.get(`${path}${query}`) + body = response.body + }) + + it('should have a an OK return status', () => { + expect(response.status).toEqual(200) + }) + + it('should return a single entity', () => { + expect(body.length).toEqual(1) + }) + + it('should have the nationalId of the user as the representer', () => { + expect( + body.some((d) => d.toNationalId === user.nationalId), + ).toBeTruthy() + }) + + it('should have the nationalId of the correct representee', () => { + expect( + body.some( + (d) => d.fromNationalId === nationalRegistryUser.nationalId, + ), + ).toBeTruthy() + }) + + it('should have the name of the correct representee', () => { + expect( + body.some((d) => d.fromName === nationalRegistryUser.name), + ).toBeTruthy() + }) + + it('should have the delegation type claim of PersonalRepresentative', () => { + expect( + body.some( + (d) => + d.types[0] === AuthDelegationType.PersonalRepresentative, + ), + ).toBeTruthy() + }) + }) - // Act - const res = await server.get(`${path}${query}`) + it('should not return delegations when client does not support personal representative delegations', async () => { + // Prepare + await clientDelegationTypeModel.destroy({ + where: { + clientId: client.clientId, + delegationType: AuthDelegationType.PersonalRepresentative, + }, + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) + // Act + const res = await server.get(`${path}${query}`) - it('should return a personal representative delegation since the type is included in the delegationTypes filter', async () => { - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.ProcurationHolder}&delegationTypes=${AuthDelegationType.PersonalRepresentative}`, - ) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(1) - expect(res.body[0].types[0]).toEqual( - AuthDelegationType.PersonalRepresentative, - ) - }) + it('should return a personal representative delegation since the type is included in the delegationTypes filter', async () => { + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.ProcurationHolder}&delegationTypes=${AuthDelegationType.PersonalRepresentative}`, + ) + + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(1) + expect(res.body[0].types[0]).toEqual( + AuthDelegationType.PersonalRepresentative, + ) + }) - it('should not return a personal representative delegation since the type is not included in the delegationTypes filter', async () => { - // Act - const res = await server.get( - `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.LegalGuardian}&delegationTypes=${AuthDelegationType.ProcurationHolder}`, - ) + it('should not return a personal representative delegation since the type is not included in the delegationTypes filter', async () => { + // Act + const res = await server.get( + `${path}${query}&delegationTypes=${AuthDelegationType.Custom}&delegationTypes=${AuthDelegationType.LegalGuardian}&delegationTypes=${AuthDelegationType.ProcurationHolder}`, + ) - // Assert - expect(res.status).toEqual(200) - expect(res.body).toHaveLength(0) - }) + // Assert + expect(res.status).toEqual(200) + expect(res.body).toHaveLength(0) + }) - afterAll(async () => { - await prRightsModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await prRightTypeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await prModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await prTypeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await prDelegationTypeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await delegationTypeModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, - }) - await delegationProviderModel.destroy({ - where: {}, - cascade: true, - truncate: true, - force: true, + afterAll(async () => { + await prRightsModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prRightTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await prDelegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await delegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await delegationProviderModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + }) }) }) }) - }) - }) + }, + ) describe('without auth and permission', () => { it.each` diff --git a/charts/identity-server/values.dev.yaml b/charts/identity-server/values.dev.yaml index e409d5e63565..37227fdaaedd 100644 --- a/charts/identity-server/values.dev.yaml +++ b/charts/identity-server/values.dev.yaml @@ -227,6 +227,10 @@ services-auth-admin-api: IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' IDENTITY_SERVER_ISSUER_URL_LIST: '["https://identity-server.dev01.devland.is","https://identity-server.staging01.devland.is","https://innskra.island.is"]' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-DEV/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -297,6 +301,7 @@ services-auth-admin-api: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' @@ -605,6 +610,10 @@ services-auth-personal-representative: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-DEV/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -670,6 +679,7 @@ services-auth-personal-representative: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' @@ -752,6 +762,10 @@ services-auth-public-api: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-DEV/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=345 -r dd-trace/init' PASSKEY_CORE_ALLOWED_ORIGINS: '["https://island.is","android:apk-key-hash:JgPeo_F6KYk-ngRa26tO2SsAtMiTBQCc7WtSgN-jRX0","android:apk-key-hash:EsLTUu5kaY7XPmMl2f7nbq4amu-PNzdYu3FecNf90wU"]' PASSKEY_CORE_CHALLENGE_TTL_MS: '120000' @@ -832,6 +846,7 @@ services-auth-public-api: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/identity-server/values.prod.yaml b/charts/identity-server/values.prod.yaml index 7a6fc66e89f3..1ceb0720407e 100644 --- a/charts/identity-server/values.prod.yaml +++ b/charts/identity-server/values.prod.yaml @@ -224,6 +224,10 @@ services-auth-admin-api: IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' IDENTITY_SERVER_ISSUER_URL_LIST: '["https://innskra.island.is"]' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '8271bbc2-d8de-480f-8540-ea43fc40b7ae' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentity.b2clogin.com/skraidentity.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS/GOV/6503760649/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentity.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SYSLUMENN_HOST: 'https://api.syslumenn.is/api' @@ -294,6 +298,7 @@ services-auth-admin-api: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' @@ -602,6 +607,10 @@ services-auth-personal-representative: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '8271bbc2-d8de-480f-8540-ea43fc40b7ae' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentity.b2clogin.com/skraidentity.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS/GOV/6503760649/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentity.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SYSLUMENN_HOST: 'https://api.syslumenn.is/api' @@ -667,6 +676,7 @@ services-auth-personal-representative: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' @@ -749,6 +759,10 @@ services-auth-public-api: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '8271bbc2-d8de-480f-8540-ea43fc40b7ae' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentity.b2clogin.com/skraidentity.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS/GOV/6503760649/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentity.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=345 -r dd-trace/init' PASSKEY_CORE_ALLOWED_ORIGINS: '["https://island.is","android:apk-key-hash:JgPeo_F6KYk-ngRa26tO2SsAtMiTBQCc7WtSgN-jRX0","android:apk-key-hash:EsLTUu5kaY7XPmMl2f7nbq4amu-PNzdYu3FecNf90wU"]' PASSKEY_CORE_CHALLENGE_TTL_MS: '120000' @@ -829,6 +843,7 @@ services-auth-public-api: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/identity-server/values.staging.yaml b/charts/identity-server/values.staging.yaml index 57c2727f4daa..a08cbe1f20a0 100644 --- a/charts/identity-server/values.staging.yaml +++ b/charts/identity-server/values.staging.yaml @@ -227,6 +227,10 @@ services-auth-admin-api: IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' IDENTITY_SERVER_ISSUER_URL_LIST: '["https://identity-server.staging01.devland.is","https://innskra.island.is"]' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-TEST/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -297,6 +301,7 @@ services-auth-admin-api: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' @@ -605,6 +610,10 @@ services-auth-personal-representative: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-TEST/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -670,6 +679,7 @@ services-auth-personal-representative: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' @@ -752,6 +762,10 @@ services-auth-public-api: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-TEST/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=345 -r dd-trace/init' PASSKEY_CORE_ALLOWED_ORIGINS: '["https://island.is","android:apk-key-hash:JgPeo_F6KYk-ngRa26tO2SsAtMiTBQCc7WtSgN-jRX0","android:apk-key-hash:EsLTUu5kaY7XPmMl2f7nbq4amu-PNzdYu3FecNf90wU"]' PASSKEY_CORE_CHALLENGE_TTL_MS: '120000' @@ -832,6 +846,7 @@ services-auth-public-api: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/services/services-auth-admin-api/values.dev.yaml b/charts/services/services-auth-admin-api/values.dev.yaml index f2b108d4a7ce..a792c06696f7 100644 --- a/charts/services/services-auth-admin-api/values.dev.yaml +++ b/charts/services/services-auth-admin-api/values.dev.yaml @@ -29,6 +29,10 @@ env: IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' IDENTITY_SERVER_ISSUER_URL_LIST: '["https://identity-server.dev01.devland.is","https://identity-server.staging01.devland.is","https://innskra.island.is"]' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-DEV/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -99,6 +103,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/services/services-auth-admin-api/values.prod.yaml b/charts/services/services-auth-admin-api/values.prod.yaml index f392a1400099..cc6563a905ed 100644 --- a/charts/services/services-auth-admin-api/values.prod.yaml +++ b/charts/services/services-auth-admin-api/values.prod.yaml @@ -29,6 +29,10 @@ env: IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' IDENTITY_SERVER_ISSUER_URL_LIST: '["https://innskra.island.is"]' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '8271bbc2-d8de-480f-8540-ea43fc40b7ae' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentity.b2clogin.com/skraidentity.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS/GOV/6503760649/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentity.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SYSLUMENN_HOST: 'https://api.syslumenn.is/api' @@ -99,6 +103,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/services/services-auth-admin-api/values.staging.yaml b/charts/services/services-auth-admin-api/values.staging.yaml index 1ca982a2abb7..e67c4abfceac 100644 --- a/charts/services/services-auth-admin-api/values.staging.yaml +++ b/charts/services/services-auth-admin-api/values.staging.yaml @@ -29,6 +29,10 @@ env: IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' IDENTITY_SERVER_ISSUER_URL_LIST: '["https://identity-server.staging01.devland.is","https://innskra.island.is"]' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-TEST/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -99,6 +103,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/services/services-auth-personal-representative/values.dev.yaml b/charts/services/services-auth-personal-representative/values.dev.yaml index 973eb041bc32..aaca50f4c0c0 100644 --- a/charts/services/services-auth-personal-representative/values.dev.yaml +++ b/charts/services/services-auth-personal-representative/values.dev.yaml @@ -28,6 +28,10 @@ env: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-DEV/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -93,6 +97,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' diff --git a/charts/services/services-auth-personal-representative/values.prod.yaml b/charts/services/services-auth-personal-representative/values.prod.yaml index f9f74f8d92c1..e65c0aafe6fa 100644 --- a/charts/services/services-auth-personal-representative/values.prod.yaml +++ b/charts/services/services-auth-personal-representative/values.prod.yaml @@ -28,6 +28,10 @@ env: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '8271bbc2-d8de-480f-8540-ea43fc40b7ae' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentity.b2clogin.com/skraidentity.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS/GOV/6503760649/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentity.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' SYSLUMENN_HOST: 'https://api.syslumenn.is/api' @@ -93,6 +97,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' diff --git a/charts/services/services-auth-personal-representative/values.staging.yaml b/charts/services/services-auth-personal-representative/values.staging.yaml index 21ec99d0547a..16fadeb06ab7 100644 --- a/charts/services/services-auth-personal-representative/values.staging.yaml +++ b/charts/services/services-auth-personal-representative/values.staging.yaml @@ -28,6 +28,10 @@ env: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-TEST/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' SERVERSIDE_FEATURES_ON: '' SYSLUMENN_HOST: 'https://api.syslumenn.is/staging' @@ -93,6 +97,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' ZENDESK_CONTACT_FORM_EMAIL: '/k8s/api/ZENDESK_CONTACT_FORM_EMAIL' diff --git a/charts/services/services-auth-public-api/values.dev.yaml b/charts/services/services-auth-public-api/values.dev.yaml index 4398430d7eb2..9b4cc091ee46 100644 --- a/charts/services/services-auth-public-api/values.dev.yaml +++ b/charts/services/services-auth-public-api/values.dev.yaml @@ -28,6 +28,10 @@ env: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-DEV/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=345 -r dd-trace/init' PASSKEY_CORE_ALLOWED_ORIGINS: '["https://island.is","android:apk-key-hash:JgPeo_F6KYk-ngRa26tO2SsAtMiTBQCc7WtSgN-jRX0","android:apk-key-hash:EsLTUu5kaY7XPmMl2f7nbq4amu-PNzdYu3FecNf90wU"]' PASSKEY_CORE_CHALLENGE_TTL_MS: '120000' @@ -108,6 +112,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/services/services-auth-public-api/values.prod.yaml b/charts/services/services-auth-public-api/values.prod.yaml index cedab910898a..13e897e62429 100644 --- a/charts/services/services-auth-public-api/values.prod.yaml +++ b/charts/services/services-auth-public-api/values.prod.yaml @@ -28,6 +28,10 @@ env: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '8271bbc2-d8de-480f-8540-ea43fc40b7ae' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentity.b2clogin.com/skraidentity.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS/GOV/6503760649/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentity.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=345 -r dd-trace/init' PASSKEY_CORE_ALLOWED_ORIGINS: '["https://island.is","android:apk-key-hash:JgPeo_F6KYk-ngRa26tO2SsAtMiTBQCc7WtSgN-jRX0","android:apk-key-hash:EsLTUu5kaY7XPmMl2f7nbq4amu-PNzdYu3FecNf90wU"]' PASSKEY_CORE_CHALLENGE_TTL_MS: '120000' @@ -108,6 +112,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/charts/services/services-auth-public-api/values.staging.yaml b/charts/services/services-auth-public-api/values.staging.yaml index af05b8333772..ba19f2a7a3bf 100644 --- a/charts/services/services-auth-public-api/values.staging.yaml +++ b/charts/services/services-auth-public-api/values.staging.yaml @@ -28,6 +28,10 @@ env: IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LOG_LEVEL: 'info' + NATIONAL_REGISTRY_B2C_CLIENT_ID: '6cf94113-d326-4e4d-b97c-1fea12d2f5e1' + NATIONAL_REGISTRY_B2C_ENDPOINT: 'https://skraidentitydev.b2clogin.com/skraidentitydev.onmicrosoft.com/b2c_1_midlun_flow/oauth2/v2.0/token' + NATIONAL_REGISTRY_B2C_PATH: 'IS-TEST/GOV/10001/SKRA-Cloud-Protected/Midlun-v1' + NATIONAL_REGISTRY_B2C_SCOPE: 'https://skraidentitydev.onmicrosoft.com/midlun/.default' NODE_OPTIONS: '--max-old-space-size=345 -r dd-trace/init' PASSKEY_CORE_ALLOWED_ORIGINS: '["https://island.is","android:apk-key-hash:JgPeo_F6KYk-ngRa26tO2SsAtMiTBQCc7WtSgN-jRX0","android:apk-key-hash:EsLTUu5kaY7XPmMl2f7nbq4amu-PNzdYu3FecNf90wU"]' PASSKEY_CORE_CHALLENGE_TTL_MS: '120000' @@ -108,6 +112,7 @@ secrets: CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' DB_PASS: '/k8s/servicesauth/DB_PASSWORD' IDENTITY_SERVER_CLIENT_SECRET: '/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET' + NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET' NATIONAL_REGISTRY_IDS_CLIENT_SECRET: '/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET' SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD' SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME' diff --git a/libs/auth-api-lib/src/index.ts b/libs/auth-api-lib/src/index.ts index c31273fdae15..6c4d00f2b791 100644 --- a/libs/auth-api-lib/src/index.ts +++ b/libs/auth-api-lib/src/index.ts @@ -61,6 +61,7 @@ export * from './lib/delegations/utils/scopes' export * from './lib/delegations/admin/delegation-admin-custom.service' export * from './lib/delegations/constants/names' export * from './lib/delegations/constants/zendesk' +export * from './lib/delegations/national-registry-v3-feature.service' // Resources module export * from './lib/resources/resources.module' diff --git a/libs/auth-api-lib/src/lib/delegations/alive-status.service.ts b/libs/auth-api-lib/src/lib/delegations/alive-status.service.ts new file mode 100644 index 000000000000..e1e4aeceb170 --- /dev/null +++ b/libs/auth-api-lib/src/lib/delegations/alive-status.service.ts @@ -0,0 +1,192 @@ +import { Inject, Injectable, Logger } from '@nestjs/common' +import * as kennitala from 'kennitala' + +import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' +import { CompanyRegistryClientService } from '@island.is/clients/rsk/company-registry' +import { LOGGER_PROVIDER } from '@island.is/logging' + +import { UNKNOWN_NAME } from './constants/names' +import { partitionWithIndex } from './utils/partitionWithIndex' + +export type NameInfo = { + nationalId: string + name: string +} + +type IdentityInfo = NameInfo & { isDeceased: boolean } + +const decesead = 'LÉST' + +@Injectable() +export class AliveStatusService { + constructor( + private readonly nationalRegistryClient: NationalRegistryClientService, + private readonly nationalRegistryV3Client: NationalRegistryV3ClientService, + private readonly companyRegistryClient: CompanyRegistryClientService, + @Inject(LOGGER_PROVIDER) + private logger: Logger, + ) {} + + /** + * Divides nationalIds into alive and deceased + * - Makes calls for every nationalId to NationalRegistry to check if the person exists. + * - Divides the nationalIds into alive and deceased, based on + * 1. All companies will be divided into alive. + * 2. If the person exists in NationalRegistry, then the person is alive. + */ + public async getStatus( + nameInfos: NameInfo[], + useNationalRegistryV3: boolean, + ): Promise<{ + aliveNationalIds: string[] + deceasedNationalIds: string[] + aliveNameInfo: NameInfo[] + }> { + if (nameInfos.length === 0) { + return { + aliveNationalIds: [], + deceasedNationalIds: [], + aliveNameInfo: [], + } + } + let identitiesValuesNoError: IdentityInfo[] = [] + try { + const identities = await ( + await Promise.allSettled( + this.getIdentities(nameInfos, useNationalRegistryV3), + ) + ).map((promiseResult) => + promiseResult.status === 'fulfilled' + ? promiseResult.value + : new Error('Error getting identity'), + ) + + identitiesValuesNoError = identities.filter(this.isNotError) + + const deceasedNationalIds = identitiesValuesNoError + .filter((identity) => identity.isDeceased) + .map((identity) => identity.nationalId) + + return { + aliveNationalIds: nameInfos + .filter((info) => !deceasedNationalIds.includes(info.nationalId)) + .map((info) => info.nationalId), + deceasedNationalIds: deceasedNationalIds, + aliveNameInfo: identitiesValuesNoError, + } + } catch (error) { + this.logger.error(`Error getting live status.`, error) + + // We do not want to fail the whole request if we cannot get the live status. + // Therefore, we return all nationalIds as alive. + return { + aliveNationalIds: nameInfos.map((info) => info.nationalId), + deceasedNationalIds: [], + aliveNameInfo: identitiesValuesNoError, + } + } + } + + private getIdentities( + nameInfos: NameInfo[], + useNationalRegistryV3: boolean, + ): Promise[] { + const [companies, individuals] = partitionWithIndex(nameInfos, (nameInfo) => + kennitala.isCompany(nameInfo.nationalId), + ) + + const companyPromises = companies.map((nameInfo) => + this.getCompanyIdentity(nameInfo), + ) + + const individualPromises = individuals.map((nameInfo) => + this.getIndividualIdentity(nameInfo, useNationalRegistryV3), + ) + + return [...companyPromises, ...individualPromises] + } + + private getCompanyIdentity(companyInfo: NameInfo): Promise { + // All companies will be divided into alive + return this.companyRegistryClient + .getCompany(companyInfo.nationalId) + .then((company) => { + if (company && this.isNotError(company)) { + return { + nationalId: company.nationalId, + name: company.name, + isDeceased: false, + } + } else { + return { + nationalId: companyInfo.nationalId, + name: companyInfo.name ?? UNKNOWN_NAME, + isDeceased: false, + } + } + }) + } + + private async getIndividualIdentity( + individualInfo: NameInfo, + useNationalRegistryV3: boolean, + ): Promise { + if (useNationalRegistryV3) { + return await this.nationalRegistryV3Client + .getAllDataIndividual(individualInfo.nationalId) + .then((individual) => { + if ( + individual && + this.isNotError(individual) && + individual?.kennitala && + individual?.kennitala !== null + ) { + return { + nationalId: individual.kennitala, + name: individual.nafn ?? individualInfo.name ?? UNKNOWN_NAME, + isDeceased: individual.afdrif === decesead, + } + } else { + // Pass through although Þjóðskrá API throws an error + return { + nationalId: individualInfo.nationalId, + name: individualInfo.name ?? UNKNOWN_NAME, + isDeceased: false, + } + } + }) + } else { + return await this.getIndividualIdentityV2(individualInfo) + } + } + + private getIndividualIdentityV2( + individualInfo: NameInfo, + ): Promise { + return this.nationalRegistryClient + .getIndividual(individualInfo.nationalId) + .then((individual) => { + if (individual === null) { + return { + nationalId: individualInfo.nationalId, + name: individualInfo.name ?? UNKNOWN_NAME, + isDeceased: true, + } + } else { + return { + nationalId: individual?.nationalId ?? individualInfo.nationalId, + name: individual?.name ?? individualInfo.name ?? UNKNOWN_NAME, + isDeceased: false, + } + } + }) + } + + /** + * Checks if item is not an instance of Error + */ + private isNotError(item: T | Error): item is T { + return item instanceof Error === false + } +} diff --git a/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts b/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts index d51a25e54cf9..ee60fb61c692 100644 --- a/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts @@ -1,18 +1,12 @@ -import { Inject, Injectable, Logger } from '@nestjs/common' -import { InjectModel } from '@nestjs/sequelize' +import { Inject, Injectable } from '@nestjs/common' import { ConfigType } from '@nestjs/config' +import { InjectModel } from '@nestjs/sequelize' +import startOfDay from 'date-fns/startOfDay' import * as kennitala from 'kennitala' import uniqBy from 'lodash/uniqBy' import { Op } from 'sequelize' -import startOfDay from 'date-fns/startOfDay' import { User } from '@island.is/auth-nest-tools' -import { - IndividualDto, - NationalRegistryClientService, -} from '@island.is/clients/national-registry-v2' -import { CompanyRegistryClientService } from '@island.is/clients/rsk/company-registry' -import { LOGGER_PROVIDER } from '@island.is/logging' import { AuditService } from '@island.is/nest/audit' import { AuthDelegationType } from '@island.is/shared/types' import { isDefined } from '@island.is/shared/utils' @@ -20,17 +14,18 @@ import { isDefined } from '@island.is/shared/utils' import { ApiScopeDelegationType } from '../resources/models/api-scope-delegation-type.model' import { ApiScopeUserAccess } from '../resources/models/api-scope-user-access.model' import { ApiScope } from '../resources/models/api-scope.model' +import { AliveStatusService, NameInfo } from './alive-status.service' import { UNKNOWN_NAME } from './constants/names' +import { DelegationConfig } from './DelegationConfig' import { ApiScopeInfo } from './delegations-incoming.service' import { DelegationDTO } from './dto/delegation.dto' import { MergedDelegationDTO } from './dto/merged-delegation.dto' +import { DelegationDelegationType } from './models/delegation-delegation-type.model' import { DelegationScope } from './models/delegation-scope.model' import { Delegation } from './models/delegation.model' +import { NationalRegistryV3FeatureService } from './national-registry-v3-feature.service' import { DelegationValidity } from './types/delegationValidity' -import { partitionWithIndex } from './utils/partitionWithIndex' import { getScopeValidityWhereClause } from './utils/scopes' -import { DelegationDelegationType } from './models/delegation-delegation-type.model' -import { DelegationConfig } from './DelegationConfig' type FindAllValidIncomingOptions = { nationalId: string @@ -38,11 +33,6 @@ type FindAllValidIncomingOptions = { validity?: DelegationValidity } -type FromNameInfo = { - nationalId: string - name: string -} - /** * Service class for incoming delegations. * This class supports domain based delegations. @@ -54,13 +44,11 @@ export class DelegationsIncomingCustomService { private delegationModel: typeof Delegation, @InjectModel(ApiScopeUserAccess) private apiScopeUserAccessModel: typeof ApiScopeUserAccess, - private nationalRegistryClient: NationalRegistryClientService, - private companyRegistryClient: CompanyRegistryClientService, - @Inject(LOGGER_PROVIDER) - private logger: Logger, + private aliveStatusService: AliveStatusService, @Inject(DelegationConfig.KEY) private delegationConfig: ConfigType, private auditService: AuditService, + private readonly nationalRegistryV3FeatureService: NationalRegistryV3FeatureService, ) {} async findAllValidIncoming( @@ -70,6 +58,7 @@ export class DelegationsIncomingCustomService { validity = DelegationValidity.NOW, }: FindAllValidIncomingOptions, useMaster = false, + user?: User, ): Promise { const { delegations, fromNameInfo } = await this.findAllIncoming( { @@ -78,6 +67,7 @@ export class DelegationsIncomingCustomService { domainName, }, useMaster, + user, ) const delegationDTOs = delegations.map((d) => d.toDTO()) @@ -95,6 +85,7 @@ export class DelegationsIncomingCustomService { async findAllValidGeneralMandate( { nationalId }: FindAllValidIncomingOptions, useMaster = false, + user?: User, ): Promise { const { delegations, fromNameInfo } = await this.findAllIncomingGeneralMandates( @@ -102,6 +93,7 @@ export class DelegationsIncomingCustomService { nationalId, }, useMaster, + user, ) return delegations.map((delegation) => { @@ -133,10 +125,14 @@ export class DelegationsIncomingCustomService { return [] } - const { delegations, fromNameInfo } = await this.findAllIncoming({ - nationalId: user.nationalId, - validity: DelegationValidity.NOW, - }) + const { delegations, fromNameInfo } = await this.findAllIncoming( + { + nationalId: user.nationalId, + validity: DelegationValidity.NOW, + }, + false, + user, + ) const validDelegations = delegations .map((d) => { @@ -219,9 +215,13 @@ export class DelegationsIncomingCustomService { } const { delegations, fromNameInfo } = - await this.findAllIncomingGeneralMandates({ - nationalId: user.nationalId, - }) + await this.findAllIncomingGeneralMandates( + { + nationalId: user.nationalId, + }, + false, + user, + ) const mergedDelegationDTOs = uniqBy( delegations.map((d) => @@ -243,7 +243,8 @@ export class DelegationsIncomingCustomService { private async findAllIncomingGeneralMandates( { nationalId }: FindAllValidIncomingOptions, useMaster = false, - ): Promise<{ delegations: Delegation[]; fromNameInfo: FromNameInfo[] }> { + user?: User, + ): Promise<{ delegations: Delegation[]; fromNameInfo: NameInfo[] }> { const startOfToday = startOfDay(new Date()) const delegations = await this.delegationModel.findAll({ @@ -268,10 +269,22 @@ export class DelegationsIncomingCustomService { }) // Check live status, i.e. dead or alive for delegations - const { aliveDelegations, deceasedDelegations, fromNameInfo } = - await this.getLiveStatusFromDelegations(delegations) + const isNationalRegistryV3DeceasedStatusEnabled = + await this.nationalRegistryV3FeatureService.getValue(user) + + const { aliveNationalIds, deceasedNationalIds, aliveNameInfo } = + await this.aliveStatusService.getStatus( + delegations.map((d) => ({ + nationalId: d.fromNationalId, + name: d.fromDisplayName, + })), + isNationalRegistryV3DeceasedStatusEnabled, + ) - if (deceasedDelegations.length > 0) { + if (deceasedNationalIds.length > 0) { + const deceasedDelegations = delegations.filter((d) => + deceasedNationalIds.includes(d.fromNationalId), + ) // Delete all deceased delegations by deleting them and their scopes. const deletePromises = deceasedDelegations.map((delegation) => delegation.destroy(), @@ -286,7 +299,12 @@ export class DelegationsIncomingCustomService { }) } - return { delegations: aliveDelegations, fromNameInfo } + return { + delegations: delegations.filter((d) => + aliveNationalIds.includes(d.fromNationalId), + ), + fromNameInfo: aliveNameInfo, + } } private async findAllIncoming( @@ -298,7 +316,8 @@ export class DelegationsIncomingCustomService { validity: DelegationValidity }, useMaster = false, - ): Promise<{ delegations: Delegation[]; fromNameInfo: FromNameInfo[] }> { + user?: User, + ): Promise<{ delegations: Delegation[]; fromNameInfo: NameInfo[] }> { let whereOptions = getScopeValidityWhereClause(validity) if (domainName) whereOptions = { ...whereOptions, domainName: domainName } @@ -336,10 +355,22 @@ export class DelegationsIncomingCustomService { }) // Check live status, i.e. dead or alive for delegations - const { aliveDelegations, deceasedDelegations, fromNameInfo } = - await this.getLiveStatusFromDelegations(delegations) + const isNationalRegistryV3DeceasedStatusEnabled = + await this.nationalRegistryV3FeatureService.getValue(user) + + const { aliveNationalIds, deceasedNationalIds, aliveNameInfo } = + await this.aliveStatusService.getStatus( + delegations.map((d) => ({ + nationalId: d.fromNationalId, + name: d.fromDisplayName, + })), + isNationalRegistryV3DeceasedStatusEnabled, + ) - if (deceasedDelegations.length > 0) { + if (deceasedNationalIds.length > 0) { + const deceasedDelegations = delegations.filter((d) => + deceasedNationalIds.includes(d.fromNationalId), + ) // Delete all deceased delegations by deleting them and their scopes. const deletePromises = deceasedDelegations.map((delegation) => delegation.destroy(), @@ -354,7 +385,12 @@ export class DelegationsIncomingCustomService { }) } - return { delegations: aliveDelegations, fromNameInfo } + return { + delegations: delegations.filter((d) => + aliveNationalIds.includes(d.fromNationalId), + ), + fromNameInfo: aliveNameInfo, + } } private checkIfScopeIsValid( @@ -389,103 +425,14 @@ export class DelegationsIncomingCustomService { return false } - /** - * Divides delegations into alive and deceased delegations - * - Makes calls for every delegation to NationalRegistry to check if the person exists. - * - Divides the delegations into alive and deceased delegations, based on - * 1. All companies will be divided into alive delegations. - * 2. If the person exists in NationalRegistry, then the delegation is alive. - */ - private async getLiveStatusFromDelegations( - delegations: Delegation[], - ): Promise<{ - aliveDelegations: Delegation[] - deceasedDelegations: Delegation[] - fromNameInfo: FromNameInfo[] - }> { - if (delegations.length === 0) { - return { - aliveDelegations: [], - deceasedDelegations: [], - fromNameInfo: [], - } - } - - const delegationsPromises = delegations.map(({ fromNationalId }) => - kennitala.isCompany(fromNationalId) - ? this.companyRegistryClient - .getCompany(fromNationalId) - .catch(this.handlerGetError) - : this.nationalRegistryClient - .getIndividual(fromNationalId) - .catch(this.handlerGetError), - ) - - try { - // Check if delegations is linked to a person, i.e. not deceased - const identities = await Promise.all(delegationsPromises) - const identitiesValuesNoError = identities - .filter(this.isNotError) - .filter(isDefined) - .map((identity) => ({ - nationalId: identity.nationalId, - name: identity.name ?? UNKNOWN_NAME, - })) - - // Divide delegations into alive or deceased delegations. - const [aliveDelegations, deceasedDelegations] = partitionWithIndex( - delegations, - ({ fromNationalId }, index) => - // All companies will be divided into aliveDelegations - kennitala.isCompany(fromNationalId) || - // Pass through although Þjóðskrá API throws an error since it is not required to view the delegation. - identities[index] instanceof Error || - // Make sure we can match the person to the delegation, i.e. not deceased - (identities[index] as IndividualDto)?.nationalId === fromNationalId, - ) - - return { - aliveDelegations, - deceasedDelegations, - fromNameInfo: identitiesValuesNoError, - } - } catch (error) { - this.logger.error( - `Error getting live status from delegations. Delegations: ${delegations.map( - (d) => d.id, - )}`, - error, - ) - - // We do not want to fail the whole request if we cannot get the live status from delegations. - // Therefore, we return all delegations as alive delegations. - return { - aliveDelegations: delegations, - deceasedDelegations: [], - fromNameInfo: [], - } - } - } - - private handlerGetError(error: null | Error) { - return error - } - - /** - * Checks if item is not an instance of Error - */ - private isNotError(item: T | Error): item is T { - return item instanceof Error === false - } - /** * Finds person by nationalId. */ private getPersonByNationalId( - identities: Array, + identities: Array | undefined, nationalId: string, ) { - return identities.find((identity) => identity?.nationalId === nationalId) + return identities?.find((identity) => identity?.nationalId === nationalId) } private async findAccessControlList( diff --git a/libs/auth-api-lib/src/lib/delegations/delegations-incoming-representative.service.ts b/libs/auth-api-lib/src/lib/delegations/delegations-incoming-representative.service.ts index b4380ad4aef1..42d9ccb0a540 100644 --- a/libs/auth-api-lib/src/lib/delegations/delegations-incoming-representative.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/delegations-incoming-representative.service.ts @@ -1,9 +1,6 @@ import { Inject, Logger } from '@nestjs/common' -import { - IndividualDto, - NationalRegistryClientService, -} from '@island.is/clients/national-registry-v2' +import { User } from '@island.is/auth-nest-tools' import { LOGGER_PROVIDER } from '@island.is/logging' import { AuditService } from '@island.is/nest/audit' import { @@ -14,10 +11,11 @@ import { isDefined } from '@island.is/shared/utils' import { PersonalRepresentativeDTO } from '../personal-representative/dto/personal-representative.dto' import { PersonalRepresentativeService } from '../personal-representative/services/personalRepresentative.service' +import { AliveStatusService } from './alive-status.service' import { UNKNOWN_NAME } from './constants/names' import { ApiScopeInfo } from './delegations-incoming.service' import { DelegationDTO } from './dto/delegation.dto' -import { partitionWithIndex } from './utils/partitionWithIndex' +import { NationalRegistryV3FeatureService } from './national-registry-v3-feature.service' type FindAllIncomingOptions = { nationalId: string @@ -28,10 +26,11 @@ type FindAllIncomingOptions = { export class DelegationsIncomingRepresentativeService { constructor( private prService: PersonalRepresentativeService, - private nationalRegistryClient: NationalRegistryClientService, + private aliveStatusService: AliveStatusService, @Inject(LOGGER_PROVIDER) private logger: Logger, private auditService: AuditService, + private readonly nationalRegistryV3FeatureService: NationalRegistryV3FeatureService, ) {} async findAllIncoming( @@ -41,6 +40,7 @@ export class DelegationsIncomingRepresentativeService { requireApiScopes, }: FindAllIncomingOptions, useMaster = false, + user?: User, ): Promise { if ( requireApiScopes && @@ -95,42 +95,35 @@ export class DelegationsIncomingRepresentativeService { } }) - const personPromises = personalRepresentatives.map( - ({ nationalIdRepresentedPerson }) => - this.nationalRegistryClient - .getIndividual(nationalIdRepresentedPerson) - .catch(this.handlerGetIndividualError), - ) + const isNationalRegistryV3DeceasedStatusEnabled = + await this.nationalRegistryV3FeatureService.getValue(user) - const persons = await Promise.all(personPromises) - const personsValues = persons.filter((person) => person !== undefined) - const personsValuesNoError = personsValues.filter(this.isNotError) - - // Divide personal representatives into alive or deceased. - const [alive, deceased] = partitionWithIndex( - personalRepresentatives, - ({ nationalIdRepresentedPerson }, index) => - // Pass through although Þjóðskrá API throws an error since it is not required to view the personal representative. - persons[index] instanceof Error || - // Make sure we can match the person to the personal representatives, i.e. not deceased - (persons[index] as IndividualDto)?.nationalId === - nationalIdRepresentedPerson, - ) + const { aliveNationalIds, deceasedNationalIds, aliveNameInfo } = + await this.aliveStatusService.getStatus( + personalRepresentatives.map((d) => ({ + nationalId: d.nationalIdRepresentedPerson, + name: UNKNOWN_NAME, + })), + isNationalRegistryV3DeceasedStatusEnabled, + ) - if (deceased.length > 0) { + if (deceasedNationalIds.length > 0) { + const deceased = personalRepresentatives.filter((pr) => + deceasedNationalIds.includes(pr.nationalIdRepresentedPerson), + ) await this.makePersonalRepresentativesInactive(deceased) } - return alive - .map((pr) => { - const person = this.getPersonByNationalId( - personsValuesNoError, - pr.nationalIdRepresentedPerson, - ) + const alive = personalRepresentatives.filter((pr) => + aliveNationalIds.includes(pr.nationalIdRepresentedPerson), + ) - return toDelegationDTO(person?.name ?? UNKNOWN_NAME, pr) - }) - .filter(isDefined) + return alive.map((pr) => { + const person = aliveNameInfo.find( + (n) => n.nationalId === pr.nationalIdRepresentedPerson, + ) + return toDelegationDTO(person?.name ?? UNKNOWN_NAME, pr) + }) } catch (error) { this.logger.error('Error in findAllRepresentedPersons', error) } @@ -154,25 +147,4 @@ export class DelegationsIncomingRepresentativeService { system: true, }) } - - private handlerGetIndividualError(error: null | Error) { - return error - } - - /** - * Finds person by nationalId. - */ - private getPersonByNationalId( - persons: Array, - nationalId: string, - ) { - return persons.find((person) => person?.nationalId === nationalId) - } - - /** - * Checks if item is not an instance of Error - */ - private isNotError(item: T | Error): item is T { - return item instanceof Error === false - } } diff --git a/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts b/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts index 1792164ddb48..b2e0775d3b48 100644 --- a/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts @@ -2,10 +2,6 @@ import { BadRequestException, Injectable } from '@nestjs/common' import { InjectModel } from '@nestjs/sequelize' import { User } from '@island.is/auth-nest-tools' -import { - IndividualDto, - NationalRegistryClientService, -} from '@island.is/clients/national-registry-v2' import { SyslumennService } from '@island.is/clients/syslumenn' import { logger } from '@island.is/logging' import { FeatureFlagService, Features } from '@island.is/nest/feature-flags' @@ -19,6 +15,7 @@ import { ClientDelegationType } from '../clients/models/client-delegation-type.m import { Client } from '../clients/models/client.model' import { ApiScopeDelegationType } from '../resources/models/api-scope-delegation-type.model' import { ApiScope } from '../resources/models/api-scope.model' +import { AliveStatusService, NameInfo } from './alive-status.service' import { UNKNOWN_NAME } from './constants/names' import { DelegationDTOMapper } from './delegation-dto.mapper' import { DelegationProviderService } from './delegation-provider.service' @@ -27,8 +24,10 @@ import { DelegationsIncomingCustomService } from './delegations-incoming-custom. import { DelegationsIncomingRepresentativeService } from './delegations-incoming-representative.service' import { DelegationsIncomingWardService } from './delegations-incoming-ward.service' import { DelegationsIndexService } from './delegations-index.service' +import { DelegationRecordDTO } from './dto/delegation-index.dto' import { DelegationDTO } from './dto/delegation.dto' import { MergedDelegationDTO } from './dto/merged-delegation.dto' +import { NationalRegistryV3FeatureService } from './national-registry-v3-feature.service' type ClientDelegationInfo = Pick< Client, @@ -66,9 +65,10 @@ export class DelegationsIncomingService { private delegationsIncomingWardService: DelegationsIncomingWardService, private delegationsIndexService: DelegationsIndexService, private delegationProviderService: DelegationProviderService, - private nationalRegistryClient: NationalRegistryClientService, + private aliveStatusService: AliveStatusService, private readonly featureFlagService: FeatureFlagService, private readonly syslumennService: SyslumennService, + private readonly nationalRegistryV3FeatureService: NationalRegistryV3FeatureService, ) {} async findAllValid( @@ -96,22 +96,34 @@ export class DelegationsIncomingService { ) delegationPromises.push( - this.delegationsIncomingCustomService.findAllValidIncoming({ - nationalId: user.nationalId, - domainName, - }), + this.delegationsIncomingCustomService.findAllValidIncoming( + { + nationalId: user.nationalId, + domainName, + }, + false, + user, + ), ) delegationPromises.push( - this.delegationsIncomingCustomService.findAllValidGeneralMandate({ - nationalId: user.nationalId, - }), + this.delegationsIncomingCustomService.findAllValidGeneralMandate( + { + nationalId: user.nationalId, + }, + false, + user, + ), ) delegationPromises.push( - this.delegationsIncomingRepresentativeService.findAllIncoming({ - nationalId: user.nationalId, - }), + this.delegationsIncomingRepresentativeService.findAllIncoming( + { + nationalId: user.nationalId, + }, + false, + user, + ), ) const delegationSets = await Promise.all(delegationPromises) @@ -211,11 +223,15 @@ export class DelegationsIncomingService { ) { delegationPromises.push( this.delegationsIncomingRepresentativeService - .findAllIncoming({ - nationalId: user.nationalId, - clientAllowedApiScopes, - requireApiScopes: client.requireApiScopes, - }) + .findAllIncoming( + { + nationalId: user.nationalId, + clientAllowedApiScopes, + requireApiScopes: client.requireApiScopes, + }, + false, + user, + ) .then((ds) => ds.map((d) => DelegationDTOMapper.toMergedDelegationDTO(d)), ), @@ -335,11 +351,64 @@ export class DelegationsIncomingService { clientAllowedApiScopes, requireApiScopes, ) - const merged = records.map((d) => - DelegationDTOMapper.recordToMergedDelegationDTO(d), - ) - await Promise.all(merged.map((d) => this.updateName(d))) + const isNationalRegistryV3DeceasedStatusEnabled = + await this.nationalRegistryV3FeatureService.getValue(user) + + const { aliveNationalIds, deceasedNationalIds, aliveNameInfo } = + await this.aliveStatusService.getStatus( + Array.from( + new Set( + records.map((d) => ({ + nationalId: d.fromNationalId, + name: UNKNOWN_NAME, + })), + ), + ), + isNationalRegistryV3DeceasedStatusEnabled, + ) + + if (deceasedNationalIds.length > 0) { + const deceasedDelegations = records.filter((d) => + deceasedNationalIds.includes(d.fromNationalId), + ) + // Delete all deceased delegations from index + const deletePromises = deceasedDelegations.map((delegation) => { + this.delegationsIndexService.removeDelegationRecord( + { + fromNationalId: delegation.fromNationalId, + toNationalId: delegation.toNationalId, + type: delegation.type, + provider: AuthDelegationProvider.DistrictCommissionersRegistry, + }, + user, + ) + }) + + const results = await Promise.allSettled(deletePromises) + results.forEach((result, index) => { + if (result.status === 'rejected') { + logger.error('Failed to remove delegation record', { + error: result.reason, + delegation: deceasedDelegations[index], + }) + } + }) + } + + const aliveNationalIdSet = new Set(aliveNationalIds) + const merged = records.reduce( + (acc: MergedDelegationDTO[], d: DelegationRecordDTO) => { + if (aliveNationalIdSet.has(d.fromNationalId)) { + acc.push({ + ...DelegationDTOMapper.recordToMergedDelegationDTO(d), + fromName: this.getNameFromNameInfo(d.fromNationalId, aliveNameInfo), + }) + } + return acc + }, + [], + ) return merged } @@ -381,17 +450,12 @@ export class DelegationsIncomingService { }) } - private async updateName( - mergedDelegation: MergedDelegationDTO, - ): Promise { - try { - const fromIndividual: IndividualDto | null = - await this.nationalRegistryClient.getIndividual( - mergedDelegation.fromNationalId, - ) - mergedDelegation.fromName = fromIndividual?.name ?? UNKNOWN_NAME - } catch (error) { - mergedDelegation.fromName = UNKNOWN_NAME - } + private getNameFromNameInfo( + nationalId: string, + nameInfo: NameInfo[], + ): string { + return ( + nameInfo.find((n) => n.nationalId === nationalId)?.name ?? UNKNOWN_NAME + ) } } diff --git a/libs/auth-api-lib/src/lib/delegations/delegations.module.ts b/libs/auth-api-lib/src/lib/delegations/delegations.module.ts index 21aae6fe43e4..031423b76886 100644 --- a/libs/auth-api-lib/src/lib/delegations/delegations.module.ts +++ b/libs/auth-api-lib/src/lib/delegations/delegations.module.ts @@ -3,10 +3,11 @@ import { SequelizeModule } from '@nestjs/sequelize' import { RskRelationshipsClientModule } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientModule } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientModule } from '@island.is/clients/national-registry-v3' import { CompanyRegistryClientModule } from '@island.is/clients/rsk/company-registry' import { SyslumennClientModule } from '@island.is/clients/syslumenn' -import { FeatureFlagModule } from '@island.is/nest/feature-flags' import { ZendeskModule } from '@island.is/clients/zendesk' +import { FeatureFlagModule } from '@island.is/nest/feature-flags' import { ClientAllowedScope } from '../clients/models/client-allowed-scope.model' import { Client } from '../clients/models/client.model' @@ -19,6 +20,7 @@ import { ResourcesModule } from '../resources/resources.module' import { UserIdentitiesModule } from '../user-identities/user-identities.module' import { UserSystemNotificationModule } from '../user-notification' import { DelegationAdminCustomService } from './admin/delegation-admin-custom.service' +import { AliveStatusService } from './alive-status.service' import { DelegationProviderService } from './delegation-provider.service' import { DelegationScopeService } from './delegation-scope.service' import { IncomingDelegationsCompanyService } from './delegations-incoming-company.service' @@ -37,12 +39,14 @@ import { DelegationScope } from './models/delegation-scope.model' import { DelegationTypeModel } from './models/delegation-type.model' import { Delegation } from './models/delegation.model' import { NamesService } from './names.service' +import { NationalRegistryV3FeatureService } from './national-registry-v3-feature.service' @Module({ imports: [ ResourcesModule, PersonalRepresentativeModule, NationalRegistryClientModule, + NationalRegistryV3ClientModule, RskRelationshipsClientModule, CompanyRegistryClientModule, ZendeskModule, @@ -79,6 +83,8 @@ import { NamesService } from './names.service' DelegationsIndexService, DelegationProviderService, DelegationAdminCustomService, + AliveStatusService, + NationalRegistryV3FeatureService, ], exports: [ DelegationsService, diff --git a/libs/auth-api-lib/src/lib/delegations/national-registry-v3-feature.service.ts b/libs/auth-api-lib/src/lib/delegations/national-registry-v3-feature.service.ts new file mode 100644 index 000000000000..e750dbc12126 --- /dev/null +++ b/libs/auth-api-lib/src/lib/delegations/national-registry-v3-feature.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common' + +import { User } from '@island.is/auth-nest-tools' +import { FeatureFlagService, Features } from '@island.is/nest/feature-flags' + +@Injectable() +export class NationalRegistryV3FeatureService { + constructor(private readonly featureFlagService: FeatureFlagService) {} + + getValue(user?: User): Promise { + return this.featureFlagService.getValue( + Features.isNationalRegistryV3DeceasedStatusEnabled, + false, + user, + ) + } +} diff --git a/libs/feature-flags/src/lib/features.ts b/libs/feature-flags/src/lib/features.ts index ae839cdd1690..fc77efa64398 100644 --- a/libs/feature-flags/src/lib/features.ts +++ b/libs/feature-flags/src/lib/features.ts @@ -106,6 +106,9 @@ export enum Features { // General mandate delegation type isGeneralMandateDelegationEnabled = 'isGeneralMandateDelegationEnabled', + + // Should auth api use national registry v3 for checking deceased status + isNationalRegistryV3DeceasedStatusEnabled = 'isNationalRegistryV3DeceasedStatusEnabled', } export enum ServerSideFeature {