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(portals-admin-ids-admin): Add LegalRepresentative delegation type #16069

Merged
merged 8 commits into from
Sep 19, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {
AdminPatchClientDto,
Client,
clientBaseAttributes,
ClientDelegationType,
ClientGrantType,
defaultAcrValue,
RefreshTokenExpiration,
SequelizeConfigService,
SUPER_USER_DELEGATION_TYPES,
translateRefreshTokenExpiration,
} from '@island.is/auth-api-lib'
import { User } from '@island.is/auth-nest-tools'
Expand Down Expand Up @@ -51,6 +53,10 @@ const createTestClientData = async (app: TestApp, user: User) => {
AuthDelegationType.LegalGuardian,
AuthDelegationProvider.NationalRegistry,
],
[
AuthDelegationType.LegalRepresentative,
AuthDelegationProvider.DistrictCommissionersRegistry,
],
].map(async ([delegationType, provider]) =>
fixtureFactory.createDelegationType({
id: delegationType,
Expand Down Expand Up @@ -620,6 +626,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalGuardian,
AuthDelegationType.LegalRepresentative,
],
}

Expand All @@ -636,6 +643,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalGuardian,
AuthDelegationType.LegalRepresentative,
]),
)
})
Expand Down Expand Up @@ -986,6 +994,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
}

Expand All @@ -1000,6 +1009,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
supportsCustomDelegation: true,
supportsLegalGuardians: true,
Expand Down Expand Up @@ -1028,6 +1038,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
supportsCustomDelegation: true,
supportsLegalGuardians: true,
Expand All @@ -1041,6 +1052,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
supportsCustomDelegation: true,
supportsLegalGuardians: true,
Expand All @@ -1054,6 +1066,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
}

Expand All @@ -1068,6 +1081,66 @@ describe('MeClientsController with auth', () => {
supportsProcuringHolders: false,
})
})

it.each`
action
${'added'}
${'removed'}
`(
'should not have $action super user delegation type as normal',
async ({ action }) => {
// Arrange
const app = await setupApp({
AppModule,
SequelizeConfigService,
user,
dbType: 'postgres',
})
const server = request(app.getHttpServer())
await createTestClientData(app, user)

// Act
const res = await Promise.all(
SUPER_USER_DELEGATION_TYPES.map((delegationType) =>
server
.patch(
`/v2/me/tenants/${tenantId}/clients/${encodeURIComponent(
clientId,
)}`,
)
.send({
[`${action}DelegationTypes`]: [delegationType],
}),
),
)

// Assert
res.forEach((r) => {
expect(r.status).toEqual(403)
expect(r.body).toEqual({
type: 'https://httpstatuses.org/403',
title: 'Forbidden',
status: 403,
detail:
'User does not have access to update admin controlled fields.',
})
})

// DB assert
const clientDelegationTypeModel = app.get(
getModelToken(ClientDelegationType),
)
const clientDelegationTypes = await clientDelegationTypeModel.findAll(
{
where: {
clientId,
},
},
)

expect(clientDelegationTypes.length).toEqual(0)
},
)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ApiScopeDelegationType,
AdminPatchScopeDto,
ApiScope,
SUPER_USER_DELEGATION_TYPES,
} from '@island.is/auth-api-lib'
import { FixtureFactory } from '@island.is/services/auth/testing'
import {
Expand Down Expand Up @@ -127,6 +128,10 @@ const createTestData = async ({
AuthDelegationType.LegalGuardian,
AuthDelegationProvider.NationalRegistry,
],
[
AuthDelegationType.LegalRepresentative,
AuthDelegationProvider.DistrictCommissionersRegistry,
],
].map(async ([delegationType, provider]) =>
fixtureFactory.createDelegationType({
id: delegationType,
Expand Down Expand Up @@ -374,6 +379,8 @@ interface PatchTestCase {
allowExplicitDelegationGrant?: boolean
grantToPersonalRepresentatives?: boolean
isAccessControlled?: boolean
addedDelegationTypes?: AuthDelegationType[]
removedDelegationTypes?: AuthDelegationType[]
}
expected: {
status: number
Expand Down Expand Up @@ -513,6 +520,44 @@ const patchTestCases: Record<string, PatchTestCase> = {
},
}

const expected403Response = {
status: 403,
body: {
title: 'Forbidden',
status: 403,
detail: 'User does not have access to update admin controlled fields',
type: 'https://httpstatuses.org/403',
},
}

SUPER_USER_DELEGATION_TYPES.map((delegationType) => {
const delegationTypeName = AuthDelegationType[delegationType]

patchTestCases[
`should return a forbidden exception when adding super user delegation type: ${delegationTypeName}`
] = {
user: currentUser,
tenantId: TENANT_ID,
scopeName: mockedPatchApiScope.name,
input: {
addedDelegationTypes: [delegationType],
},
expected: expected403Response,
}

patchTestCases[
`should return a forbidden exception when removing super user delegation type: ${delegationTypeName}`
] = {
user: currentUser,
tenantId: TENANT_ID,
scopeName: mockedPatchApiScope.name,
input: {
removedDelegationTypes: [delegationType],
},
expected: expected403Response,
}
})

describe('MeScopesController', () => {
describe('with auth', () => {
// GET: /v2/me/tenants/:tenantId/scopes
Expand Down Expand Up @@ -761,7 +806,7 @@ describe('MeScopesController', () => {
})
})

describe('PATCH: /v2/me/tenants/:tenantId/scopes/:scopeName', () => {
describe('PATCH: /v2/me/tenants/:tenantId/scopes/:scopeName as super user', () => {
let app: TestApp
let server: request.SuperTest<request.Test>
let apiScopeDelegationTypeModel: typeof ApiScopeDelegationType
Expand Down Expand Up @@ -831,6 +876,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
expected: {
Expand All @@ -845,6 +891,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
})
Expand All @@ -858,6 +905,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
expected: {
Expand All @@ -872,6 +920,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
})
Expand All @@ -883,6 +932,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
expected: {
Expand Down Expand Up @@ -949,7 +999,7 @@ describe('MeScopesController', () => {
})
})

describe('POST: /v2/me/tenants/:tenantId/scopes', () => {
describe('POST: /v2/me/tenants/:tenantId/scopes as super user', () => {
let app: TestApp
let server: request.SuperTest<request.Test>
let apiScopeDelegationTypeModel: typeof ApiScopeDelegationType
Expand Down Expand Up @@ -1035,7 +1085,7 @@ describe('MeScopesController', () => {
})
})

describe('POST: /v2/me/tenants/:tenantId/scopes', () => {
describe('POST: /v2/me/tenants/:tenantId/scopes as normal user', () => {
let app: TestApp
let server: request.SuperTest<request.Test>
let apiScopeDelegationTypeModel: typeof ApiScopeDelegationType
Expand Down
1 change: 1 addition & 0 deletions libs/auth-api-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export * from './lib/resources/admin/dto/admin-create-scope.dto'
export * from './lib/resources/admin/dto/admin-patch-scope.dto'
export * from './lib/resources/resource-translation.service'
export * from './lib/resources/scope.service'
export { SUPER_USER_DELEGATION_TYPES } from './lib/resources/utils/filters'

// Clients module
export * from './lib/clients/clients.module'
Expand Down
35 changes: 17 additions & 18 deletions libs/auth-api-lib/src/lib/clients/admin/admin-clients.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ import {
superUserFields,
} from './dto/admin-patch-client.dto'
import { ClientDelegationType } from '../models/client-delegation-type.model'
import { filterPersonalRepresentative } from '../../resources/utils/personalRepresentativeFilter'
import {
delegationTypeSuperUserFilter,
SUPER_USER_DELEGATION_TYPES,
} from '../../resources/utils/filters'

export const clientBaseAttributes: Partial<Client> = {
absoluteRefreshTokenLifetime: 8 * 60 * 60, // 8 hours
Expand Down Expand Up @@ -179,7 +182,7 @@ export class AdminClientsService {
// Remove defined super admin fields
...omit(clientDto, superUserFields),
// Remove personal representative from delegation types since it is not allowed for non-super admins
supportedDelegationTypes: filterPersonalRepresentative(
supportedDelegationTypes: delegationTypeSuperUserFilter(
clientDto.supportedDelegationTypes ?? [],
),
}
Expand Down Expand Up @@ -659,29 +662,25 @@ export class AdminClientsService {
) {
const isSuperUser = this.isSuperAdmin(user)

const updatedFields = Object.keys(input)
const superUserUpdatedFields = updatedFields.filter((field) =>
superUserFields.includes(field),
)

// Verify that the user is super admin, so they can update PersonalRepresentative in the delegation type
// Verify if superuser delegation types are being updated that user is super user
const allDelegationTypes = [
...(input.removedDelegationTypes ?? []),
...(input.addedDelegationTypes ?? []),
]

if (!isSuperUser && allDelegationTypes.length > 0) {
for (const delegationType of allDelegationTypes) {
if (
delegationType.startsWith(
`${AuthDelegationType.PersonalRepresentative}:`,
)
) {
return false
}
}
const hasSuperUserDelegationType = allDelegationTypes.some(
(delegationType) => SUPER_USER_DELEGATION_TYPES.includes(delegationType),
)

if (!isSuperUser && hasSuperUserDelegationType) {
return false
}

const updatedFields = Object.keys(input)
const superUserUpdatedFields = updatedFields.filter((field) =>
superUserFields.includes(field),
)

if (superUserUpdatedFields.length === 0) {
// There are no superuser fields to update
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
IsString,
} from 'class-validator'

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

import { ClientType } from '../../../types'
import { AdminPatchClientDto } from './admin-patch-client.dto'

Expand Down Expand Up @@ -50,9 +52,10 @@ export class AdminCreateClientDto extends OmitType(AdminPatchClientDto, [

@IsArray()
@IsOptional()
@IsEnum(AuthDelegationType, { each: true })
@ApiProperty({
example: ['Custom'],
type: [String],
})
supportedDelegationTypes?: string[]
supportedDelegationTypes?: AuthDelegationType[]
saevarma marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading