Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auth-api): Add column to delegation type indicating if actor discretion is required #16226

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ export const indexingTestCases: Record<string, TestCase> = {
createClient({
clientId: clientId,
supportsPersonalRepresentatives: true,
supportedDelegationTypes: [AuthDelegationType.PersonalRepresentative],
supportedDelegationTypes: [
`${AuthDelegationType.PersonalRepresentative}:${prRight1}`,
],
}),
{
fromRepresentative: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import request from 'supertest'
import { getModelToken } from '@nestjs/sequelize'
import request from 'supertest'

import { TestApp } from '@island.is/testing/nest'
import {
createCurrentUser,
createNationalId,
} from '@island.is/testing/fixtures'
import { AuthScope } from '@island.is/auth/scopes'
import { DelegationIndex } from '@island.is/auth-api-lib'
import { AuthScope } from '@island.is/auth/scopes'
import { FixtureFactory } from '@island.is/services/auth/testing'
import {
AuthDelegationProvider,
AuthDelegationType,
} from '@island.is/shared/types'
import {
createCurrentUser,
createNationalId,
} from '@island.is/testing/fixtures'
import { TestApp } from '@island.is/testing/nest'

import { setupWithAuth } from '../../../../../test/setup'

Expand Down Expand Up @@ -249,6 +250,7 @@ describe('DelegationIndexController', () => {
describe('With valid delegation provider', () => {
let app: TestApp
let server: request.SuperTest<request.Test>
let factory: FixtureFactory

let delegationIndexModel: typeof DelegationIndex
const delegationProvider = AuthDelegationProvider.CompanyRegistry
Expand All @@ -262,6 +264,8 @@ describe('DelegationIndexController', () => {
app = await setupWithAuth({
user,
})
factory = new FixtureFactory(app)
await factory.createAllDelegationTypes()
server = request(app.getHttpServer())

delegationIndexModel = app.get(getModelToken(DelegationIndex))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

module.exports = {
async up(queryInterface, Sequelize) {
return Promise.all([
queryInterface.addColumn('delegation_type', 'actor_discretion_required', {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false,
}),
])
},

async down(queryInterface) {
return Promise.all([
queryInterface.removeColumn(
'delegation_type',
'actor_discretion_required',
),
])
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict'

module.exports = {
up: (queryInterface) => {
return Promise.all([
queryInterface.addConstraint('delegation_index', {
fields: ['type'],
type: 'foreign key',
name: 'FK_delegation_index_delegation_type',
references: {
table: 'delegation_type',
field: 'id',
},
}),
queryInterface.addConstraint('delegation_index', {
fields: ['provider'],
type: 'foreign key',
name: 'FK_delegation_index_delegation_provider',
references: {
table: 'delegation_provider',
field: 'id',
},
}),
])
},

down: (queryInterface) => {
return Promise.all([
queryInterface.removeConstraint(
'delegation_index',
'FK_delegation_index_delegation_type',
),
queryInterface.removeConstraint(
'delegation_index',
'FK_delegation_index_delegation_provider',
),
])
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = {
up(queryInterface) {
return queryInterface.sequelize.query(`
BEGIN;
UPDATE delegation_type
SET actor_discretion_required = true
WHERE id = 'LegalRepresentative';

COMMIT;
`)
},

down(queryInterface) {
return queryInterface.sequelize.query(`
BEGIN;
UPDATE delegation_type
SET actor_discretion_required = false
WHERE id = 'LegalRepresentative';

COMMIT;
`)
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import union from 'lodash/union'
import { Op } from 'sequelize'

import { Auth, User } from '@island.is/auth-nest-tools'
import { AuditService } from '@island.is/nest/audit'
import { type Logger, LOGGER_PROVIDER } from '@island.is/logging'
import { AuditService } from '@island.is/nest/audit'
import {
AuthDelegationProvider,
AuthDelegationType,
Expand All @@ -30,6 +30,7 @@ import {
import { DelegationDTO } from './dto/delegation.dto'
import { DelegationIndexMeta } from './models/delegation-index-meta.model'
import { DelegationIndex } from './models/delegation-index.model'
import { DelegationTypeModel } from './models/delegation-type.model'
import { DelegationDirection } from './types/delegationDirection'
import {
DelegationRecordType,
Expand Down Expand Up @@ -378,6 +379,7 @@ export class DelegationsIndexService {
type: types,
validTo: { [Op.or]: [{ [Op.gte]: new Date() }, { [Op.is]: null }] },
},
include: [{ model: DelegationTypeModel }],
})
.then((d) => d.map((d) => d.toDTO()))
}
Expand Down Expand Up @@ -647,6 +649,7 @@ export class DelegationsIndexService {
customDelegationScopes: { [Op.contains]: [scope.name] },
validTo: { [Op.or]: [{ [Op.gte]: new Date() }, { [Op.is]: null }] },
},
include: [{ model: DelegationTypeModel }],
})
.then((d) => d.map((d) => d.toDTO()))
}
Expand Down Expand Up @@ -682,6 +685,7 @@ export class DelegationsIndexService {
provider: AuthDelegationProvider.PersonalRepresentativeRegistry,
validTo: { [Op.or]: [{ [Op.gte]: new Date() }, { [Op.is]: null }] },
},
include: [{ model: DelegationTypeModel }],
})
.then((d) => d.map((d) => d.toDTO()))
}
Expand All @@ -708,6 +712,7 @@ export class DelegationsIndexService {
provider: AuthDelegationProvider.CompanyRegistry,
validTo: { [Op.or]: [{ [Op.gte]: new Date() }, { [Op.is]: null }] },
},
include: [{ model: DelegationTypeModel }],
})
.then((d) => d.map((d) => d.toDTO()))
}
Expand Down Expand Up @@ -736,6 +741,7 @@ export class DelegationsIndexService {
provider: AuthDelegationProvider.NationalRegistry,
validTo: { [Op.or]: [{ [Op.gte]: new Date() }, { [Op.is]: null }] },
},
include: [{ model: DelegationTypeModel }],
})
.then((d) => d.map((d) => d.toDTO()))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
import { IsDateString, IsNumber, IsOptional, IsString } from 'class-validator'
import {
IsBoolean,
IsDateString,
IsNumber,
IsOptional,
IsString,
} from 'class-validator'

import { PageInfoDto } from '@island.is/nest/pagination'
import {
Expand All @@ -24,6 +30,11 @@ export class DelegationRecordDTO {
@IsString()
@ApiProperty({ type: String })
type!: AuthDelegationType

@IsOptional()
@IsBoolean()
@ApiProperty({ type: Boolean, nullable: true })
actorDiscretionRequired?: boolean | null
}

export class PaginatedDelegationRecordDTO {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import {
type CreationOptional,
InferAttributes,
InferCreationAttributes,
NonAttribute,
} from 'sequelize'
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import {
type CreationOptional,
InferAttributes,
InferCreationAttributes,
} from 'sequelize'

import { AuthDelegationType } from '@island.is/shared/types'

import { DelegationRecordDTO } from '../dto/delegation-index.dto'
import { DelegationProviderModel } from './delegation-provider.model'
import { DelegationTypeModel } from './delegation-type.model'

@Table({
tableName: 'delegation_index',
Expand Down Expand Up @@ -43,13 +48,15 @@ export class DelegationIndex extends Model<
allowNull: false,
primaryKey: true,
})
@ForeignKey(() => DelegationProviderModel)
provider!: string

@Column({
type: DataType.STRING,
allowNull: false,
primaryKey: true,
})
@ForeignKey(() => DelegationTypeModel)
type!: string

@Column({
Expand All @@ -76,12 +83,16 @@ export class DelegationIndex extends Model<
@UpdatedAt
readonly modified?: Date

@BelongsTo(() => DelegationTypeModel)
delegationType?: NonAttribute<DelegationTypeModel>

toDTO(): DelegationRecordDTO {
return {
fromNationalId: this.fromNationalId,
toNationalId: this.toNationalId,
subjectId: this.subjectId,
type: this.type as AuthDelegationType,
actorDiscretionRequired: this.delegationType?.actorDiscretionRequired,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential undefined 'actorDiscretionRequired' in DTO.

Since delegationType might not be loaded, actorDiscretionRequired could be undefined. Consider providing a default value or handling this case to ensure consistent behavior for consumers of the DTO.

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from 'sequelize'
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
DataType,
Expand All @@ -14,17 +15,16 @@ import {
PrimaryKey,
Table,
UpdatedAt,
BelongsToMany,
} from 'sequelize-typescript'

import { DelegationProviderModel } from './delegation-provider.model'
import { PersonalRepresentativeDelegationTypeModel } from '../../personal-representative/models/personal-representative-delegation-type.model'
import { DelegationTypeDto } from '../dto/delegation-type.dto'
import { ClientDelegationType } from '../../clients/models/client-delegation-type.model'
import { Client } from '../../clients/models/client.model'
import { PersonalRepresentativeDelegationTypeModel } from '../../personal-representative/models/personal-representative-delegation-type.model'
import { ApiScopeDelegationType } from '../../resources/models/api-scope-delegation-type.model'
import { ApiScope } from '../../resources/models/api-scope.model'
import { DelegationTypeDto } from '../dto/delegation-type.dto'
import { DelegationDelegationType } from './delegation-delegation-type.model'
import { DelegationProviderModel } from './delegation-provider.model'

@Table({
tableName: 'delegation_type',
Expand Down Expand Up @@ -65,6 +65,13 @@ export class DelegationTypeModel extends Model<
})
description!: string

@Column({
type: DataType.BOOLEAN,
allowNull: false,
defaultValue: false,
})
actorDiscretionRequired!: boolean

@BelongsToMany(() => Client, () => ClientDelegationType)
clients!: CreationOptional<Client[]>

Expand Down
18 changes: 17 additions & 1 deletion libs/services/auth/testing/src/fixtures/fixture-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ export class FixtureFactory {
providerId: delegationProvider.id,
provider: delegationProvider,
description: `Personal representative delegation type for right type ${id}`,
actorDiscretionRequired: false,
},
},
)
Expand Down Expand Up @@ -568,7 +569,12 @@ export class FixtureFactory {
const delegationTypes = await Promise.all(
rightTypes
? rightTypes.map((rightType) =>
this.createDelegationType({ id: rightType?.code ?? '' }),
this.createDelegationType({
id: `${AuthDelegationType.PersonalRepresentative}:${
rightType?.code ?? ''
}`,
providerId: AuthDelegationProvider.PersonalRepresentativeRegistry,
}),
)
: [],
)
Expand Down Expand Up @@ -697,6 +703,7 @@ export class FixtureFactory {
name = faker.random.word(),
description = faker.random.words(3),
providerId,
actorDiscretionRequired = false,
}: CreateDelegationType) {
const delegationProvider = await this.createDelegationProvider({
id: providerId,
Expand All @@ -711,10 +718,19 @@ export class FixtureFactory {
description,
providerId: delegationProvider.id,
provider: delegationProvider,
actorDiscretionRequired,
},
},
)

return delegationType
}

async createAllDelegationTypes() {
await Promise.all(
Object.values(AuthDelegationType).map(async (delegationType) => {
await this.createDelegationType({ id: delegationType })
}),
)
}
}
7 changes: 5 additions & 2 deletions libs/services/auth/testing/src/fixtures/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ export type CreateDelegationProvider = Optional<
>

export type CreateDelegationType = Optional<
Pick<DelegationTypeModel, 'id' | 'name' | 'description' | 'providerId'>,
'id' | 'name' | 'description' | 'providerId'
Pick<
DelegationTypeModel,
'id' | 'name' | 'description' | 'providerId' | 'actorDiscretionRequired'
>,
'id' | 'name' | 'description' | 'providerId' | 'actorDiscretionRequired'
>
Loading