Skip to content

Commit

Permalink
Add legal representative delegation type and return in delegation list.
Browse files Browse the repository at this point in the history
  • Loading branch information
valurefugl committed Aug 30, 2024
1 parent bc38e3e commit 556f437
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -287,4 +287,17 @@ export const testCases: Record<string, TestCase> = {
],
},
),
// Returns available delegations for legal representatives
legalRepresentative1: new TestCase(
createClient({
clientId: clientId,
supportedDelegationTypes: [AuthDelegationType.LegalRepresentative],
}),
{
fromLegalRepresentative: [person1, person2],
protectedScopes: [],
expectedFrom: [person1, person2],
expectedTypes: [AuthDelegationType.LegalRepresentative],
},
),
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const customScope2 = 'cu2'
const customScopeOtherDomain = 'cu-od1'
const representativeScope1 = 'pr1'
const representativeScope2 = 'pr2'
const legalRepresentativeScope1 = 'lr1'
const legalRepresentativeScope2 = 'lr2'

export const legalGuardianScopes = [legalGuardianScope1, legalGuardianScope2]
export const procurationHolderScopes = [
Expand All @@ -37,12 +39,17 @@ export const procurationHolderScopes = [
]
export const customScopes = [customScope1, customScope2, customScopeOtherDomain]
export const representativeScopes = [representativeScope1, representativeScope2]
export const legalRepresentativeScopes = [
legalRepresentativeScope1,
legalRepresentativeScope2,
]

export interface ITestCaseOptions {
fromChildren?: string[]
fromCompanies?: string[]
fromCustom?: string[]
fromRepresentative?: string[]
fromLegalRepresentative?: string[]
scopes?: string[]
protectedScopes?: string[]
scopeAccess?: [string, string][]
Expand All @@ -59,6 +66,7 @@ export class TestCase {
fromCompanies: string[]
fromCustom: string[]
fromRepresentative: string[]
fromLegalRepresentative: string[]
scopes: string[]
protectedScopes: string[]
scopeAccess: [string, string][]
Expand All @@ -71,11 +79,13 @@ export class TestCase {
this.fromCompanies = options.fromCompanies ?? []
this.fromCustom = options.fromCustom ?? []
this.fromRepresentative = options.fromRepresentative ?? []
this.fromLegalRepresentative = options.fromLegalRepresentative ?? []
this.scopes = options.scopes ?? [
...legalGuardianScopes,
...procurationHolderScopes,
...customScopes,
...representativeScopes,
...legalRepresentativeScopes,
]
this.protectedScopes = options.protectedScopes ?? []
this.scopeAccess = options.scopeAccess ?? []
Expand Down Expand Up @@ -160,6 +170,9 @@ export class TestCase {
if (representativeScopes.includes(scopeName)) {
result.push(AuthDelegationType.PersonalRepresentative)
}
if (legalRepresentativeScopes.includes(scopeName)) {
result.push(AuthDelegationType.LegalRepresentative)
}
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { MergedDelegationDTO } from '@island.is/auth-api-lib'
import { RskRelationshipsClient } from '@island.is/clients-rsk-relationships'
import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2'
import { FixtureFactory } from '@island.is/services/auth/testing'
import {
AuthDelegationProvider,
AuthDelegationType,
} from '@island.is/shared/types'
import { createNationalRegistryUser } from '@island.is/testing/fixtures'
import { TestApp, truncate } from '@island.is/testing/nest'

Expand Down Expand Up @@ -86,6 +90,17 @@ describe('DelegationsController', () => {
),
)

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)
Expand Down
4 changes: 1 addition & 3 deletions apps/services/auth/ids-api/test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,7 @@ export const setupWithAuth = async ({
.useValue({
getValue: (feature: Features) =>
!features || features.includes(feature),
})
.overrideProvider(DelegationsIndexService)
.useClass(MockDelegationsIndexService),
}),
hooks: [
useAuth({ auth: user }),
useDatabase({ type: 'postgres', provider: SequelizeConfigService }),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict'

module.exports = {
up(queryInterface) {
return queryInterface.sequelize.query(`
BEGIN;
INSERT INTO delegation_provider
(id, name, description)
VALUES
('syslumenn', 'Sýslumenn', 'Provider for district commissioners registry');
INSERT INTO delegation_type
(id, provider, name, description)
VALUES
('LegalRepresentative', 'syslumenn', 'Legal Representative', 'Legal Representative delegation type');
INSERT INTO api_scope_delegation_types
(api_scope_name, delegation_type)
VALUES
('@island.is/documents', 'LegalRepresentative');
INSERT INTO client_delegation_types
(client_id, delegation_type)
VALUES
('@island.is/web', 'LegalRepresentative');
COMMIT;
`)
},

down(queryInterface) {
return queryInterface.sequelize.query(`
BEGIN;
DELETE FROM client_delegation_types
WHERE client_id = '@island.is/web' AND delegation_type = 'LegalRepresentative';
DELETE FROM api_scope_delegation_types
WHERE api_scope_name = '@island.is/documents' AND delegation_type = 'LegalRepresentative';
DELETE FROM delegation_type
WHERE id = 'LegalRepresentative';
DELETE FROM delegation_provider
WHERE id = 'syslumenn';
COMMIT;
`)
},
}
13 changes: 13 additions & 0 deletions libs/auth-api-lib/src/lib/delegations/delegation-dto.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { AuthDelegationType } from '@island.is/shared/types'

import { DelegationRecordDTO } from './dto/delegation-index.dto'
import { DelegationDTO } from './dto/delegation.dto'
import { MergedDelegationDTO } from './dto/merged-delegation.dto'

Expand All @@ -13,4 +16,14 @@ export class DelegationDTOMapper {
scopes: dto.scopes,
}
}

public static recordToMergedDelegationDTO(
dto: DelegationRecordDTO,
): MergedDelegationDTO {
return {
fromNationalId: dto.fromNationalId,
toNationalId: dto.toNationalId,
types: [dto.type as AuthDelegationType],
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { BadRequestException, Injectable } from '@nestjs/common'
import { InjectModel } from '@nestjs/sequelize'

import { User } from '@island.is/auth-nest-tools'
import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2'
import { FeatureFlagService, Features } from '@island.is/nest/feature-flags'
import {
AuthDelegationProvider,
AuthDelegationType,
} from '@island.is/shared/types'
import { isDefined } from '@island.is/shared/utils'

import { ClientAllowedScope } from '../clients/models/client-allowed-scope.model'
import { ClientDelegationType } from '../clients/models/client-delegation-type.model'
Expand All @@ -22,6 +25,8 @@ import { DelegationsIndexService } from './delegations-index.service'
import { DelegationDTO } from './dto/delegation.dto'
import { MergedDelegationDTO } from './dto/merged-delegation.dto'

const UNKNOWN_NAME = 'Óþekkt nafn'

type ClientDelegationInfo = Pick<
Client,
'supportedDelegationTypes' | 'requireApiScopes'
Expand Down Expand Up @@ -58,6 +63,8 @@ export class DelegationsIncomingService {
private delegationsIncomingWardService: DelegationsIncomingWardService,
private delegationsIndexService: DelegationsIndexService,
private delegationProviderService: DelegationProviderService,
private nationalRegistryClient: NationalRegistryClientService,
private readonly featureFlagService: FeatureFlagService,
) {}

async findAllValid(
Expand Down Expand Up @@ -116,20 +123,20 @@ export class DelegationsIncomingService {
const client = await this.getClientDelegationInfo(user)
if (!client?.supportedDelegationTypes) return []

const types: ClientDelegationType[] =
client.supportedDelegationTypes.filter(
const types: AuthDelegationType[] = client.supportedDelegationTypes
.filter(
(dt) =>
!delegationTypes ||
delegationTypes.includes(dt.delegationType as AuthDelegationType),
)
.map((t) => t.delegationType as AuthDelegationType)

if (types.length == 0) return []

const providers = await this.delegationProviderService.findProviders(
types.map((t) => t.delegationType),
)
const providers = await this.delegationProviderService.findProviders(types)

const clientAllowedApiScopes = await this.getClientAllowedApiScopes(user)
const clientAllowedApiScopes: ApiScopeInfo[] =
await this.getClientAllowedApiScopes(user)

const delegationPromises = []

Expand Down Expand Up @@ -187,6 +194,27 @@ export class DelegationsIncomingService {
)
}

if (
providers.includes(AuthDelegationProvider.DistrictCommissionersRegistry)
) {
const isLegalRepresentativeDelegationEnabled =
await this.featureFlagService.getValue(
Features.isLegalRepresentativeDelegationEnabled,
true,
user,
)
if (isLegalRepresentativeDelegationEnabled) {
delegationPromises.push(
this.getAvailableDistrictCommissionersRegistryDelegations(
user,
types,
clientAllowedApiScopes,
client.requireApiScopes,
),
)
}
}

const delegationSets = await Promise.all(delegationPromises)

let delegations = ([] as MergedDelegationDTO[])
Expand Down Expand Up @@ -224,6 +252,56 @@ export class DelegationsIncomingService {
return [...mergedDelegationMap.values()]
}

private async getAvailableDistrictCommissionersRegistryDelegations(
user: User,
types: AuthDelegationType[],
clientAllowedApiScopes: ApiScopeInfo[],
requireApiScopes?: boolean,
): Promise<MergedDelegationDTO[]> {
const records =
await this.delegationsIndexService.getAvailableDistrictCommissionersRegistryRecords(
user,
types,
clientAllowedApiScopes,
requireApiScopes,
)
const merged = records.map((d) =>
DelegationDTOMapper.recordToMergedDelegationDTO(d),
)

const persons = (
await Promise.all(
merged.map((d) =>
this.nationalRegistryClient
.getIndividual(d.fromNationalId)
.catch((error) => error),
),
)
)
.filter(this.isNotError)
.filter(isDefined)
.map((individual) => ({
nationalId: individual.nationalId,
name: individual.name ?? UNKNOWN_NAME,
}))

merged.forEach((d) => {
const person = persons.find((p) => p.nationalId === d.fromNationalId)
if (person) {
d.fromName = person.name
}
})

return merged
}

/**
* Checks if item is not an instance of Error
*/
private isNotError<T>(item: T | Error): item is T {
return item instanceof Error === false
}

private getClientDelegationInfo(
user: User,
): Promise<ClientDelegationInfo | null> {
Expand Down
31 changes: 31 additions & 0 deletions libs/auth-api-lib/src/lib/delegations/delegations-index.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { IncomingDelegationsCompanyService } from './delegations-incoming-compan
import { DelegationsIncomingCustomService } from './delegations-incoming-custom.service'
import { DelegationsIncomingRepresentativeService } from './delegations-incoming-representative.service'
import { DelegationsIncomingWardService } from './delegations-incoming-ward.service'
import { ApiScopeInfo } from './delegations-incoming.service'
import {
DelegationRecordDTO,
DelegationRecordInputDTO,
Expand Down Expand Up @@ -302,6 +303,36 @@ export class DelegationsIndexService {
})
}

async getAvailableDistrictCommissionersRegistryRecords(
user: User,
types: AuthDelegationType[],
clientAllowedApiScopes: ApiScopeInfo[],
requireApiScopes?: boolean,
): Promise<DelegationRecordDTO[]> {
if (requireApiScopes) {
const noSupportedScope = !clientAllowedApiScopes.some(
(s) =>
s.supportedDelegationTypes?.some(
(dt) => dt.delegationType == AuthDelegationType.LegalRepresentative,
) && !s.isAccessControlled,
)
if (noSupportedScope) {
return []
}
}

return await this.delegationIndexModel
.findAll({
where: {
toNationalId: user.nationalId,
provider: AuthDelegationProvider.DistrictCommissionersRegistry,
type: types,
validTo: { [Op.or]: [{ [Op.gte]: new Date() }, { [Op.is]: null }] },
},
})
.then((d) => d.map((d) => d.toDTO()))
}

/*
* Private methods
* */
Expand Down
Loading

0 comments on commit 556f437

Please sign in to comment.