Skip to content

Commit

Permalink
feat(auth-api): Adding legal representative (#16161)
Browse files Browse the repository at this point in the history
* feat(ids-api): Use syslumenn api to verify delegation. (#16029)

* Use syslumenn api to verify delegation.

* Fix api error case.

* Fix config.

* chore: nx format:write update dirty files

* Handle error in api.

* Fix typo in name.

* Refactor mock.

* Update infra.

* Update host in infra.

* Add syslumenn infra to other auth apis.

* Also return empty array if error.

* Use post.

* Fix tests.

* Single delegation type.

* Remove infra config from pr public.

* Fix type.

* Refactor error handling in check for scopes.

* Openapi fix.

* Refactor verification error handling.

* Fix status code.

* Decrease syslumenn api timeout to 3s.

---------

Co-authored-by: andes-it <[email protected]>
Co-authored-by: Valur Einarsson <[email protected]>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* feat(auth-api): Filter delegation indexing by provider (#16141)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* feat(portals-admin-ids-admin): Add LegalRepresentative delegation type (#16069)

* Add missing translation for legal representation delegation type in IDS Admin

* Refactor delegation type filtering from client and scope creation to account for LegalRepresentative as superuser only.

* Update patch super user delegation type check.
Add tests.

* Handle legal representative in scope patch.
Add tests.

* Hide it from the UI.

* Fix duplicated message id

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* chore: nx format:write update dirty files

---------

Co-authored-by: andes-it <[email protected]>
Co-authored-by: Valur Einarsson <[email protected]>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Sep 26, 2024
1 parent 3d4afbb commit d6f7986
Show file tree
Hide file tree
Showing 39 changed files with 943 additions and 172 deletions.
8 changes: 8 additions & 0 deletions apps/services/auth/admin-api/infra/auth-admin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-admin-api'> => {
prod: 'IS/GOV/5402696029/Skatturinn/ft-v1',
},
COMPANY_REGISTRY_REDIS_NODES: REDIS_NODE_CONFIG,
SYSLUMENN_HOST: {
dev: 'https://api.syslumenn.is/staging',
staging: 'https://api.syslumenn.is/staging',
prod: 'https://api.syslumenn.is',
},
SYSLUMENN_TIMEOUT: '3000',
})
.secrets({
CLIENT_SECRET_ENCRYPTION_KEY:
Expand All @@ -67,6 +73,8 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-admin-api'> => {
'/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET',
NATIONAL_REGISTRY_IDS_CLIENT_SECRET:
'/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET',
SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME',
SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD',
})
.xroad(Base, Client, RskProcuring)
.ingress({
Expand Down
18 changes: 10 additions & 8 deletions apps/services/auth/admin-api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
SequelizeConfigService,
} from '@island.is/auth-api-lib'
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 { CompanyRegistryConfig } from '@island.is/clients/rsk/company-registry'
import { SyslumennClientConfig } from '@island.is/clients/syslumenn'
import { AuditModule } from '@island.is/nest/audit'
import { IdsClientConfig, XRoadConfig } from '@island.is/nest/config'
import { FeatureFlagConfig } from '@island.is/nest/feature-flags'
import { ProblemModule } from '@island.is/nest/problem'

import { environment } from '../environments'
Expand All @@ -21,16 +27,11 @@ import { ResourcesModule } from './modules/resources/resources.module'
import { TranslationModule } from './modules/translation/translation.module'
import { UsersModule } from './modules/users/users.module'
import { ClientsModule as ClientsV2Module } from './v2/clients/clients.module'
import { DelegationAdminModule } from './v2/delegations/delegation-admin.module'
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 { ScopesModule } from './v2/scopes/scopes.module'
import { ProvidersModule } from './v2/providers/providers.module'
import { DelegationAdminModule } from './v2/delegations/delegation-admin.module'
import { RskRelationshipsClientConfig } from '@island.is/clients-rsk-relationships'
import { FeatureFlagConfig } from '@island.is/nest/feature-flags'
import { IdsClientConfig, XRoadConfig } from '@island.is/nest/config'
import { NationalRegistryClientConfig } from '@island.is/clients/national-registry-v2'
import { CompanyRegistryConfig } from '@island.is/clients/rsk/company-registry'

@Module({
imports: [
Expand Down Expand Up @@ -64,6 +65,7 @@ import { CompanyRegistryConfig } from '@island.is/clients/rsk/company-registry'
FeatureFlagConfig,
XRoadConfig,
IdsClientConfig,
SyslumennClientConfig,
],
envFilePath: ['.env', '.env.secret'],
}),
Expand Down
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 @@ -50,6 +52,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 @@ -619,6 +625,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalGuardian,
AuthDelegationType.LegalRepresentative,
],
}

Expand All @@ -635,6 +642,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalGuardian,
AuthDelegationType.LegalRepresentative,
]),
)
})
Expand Down Expand Up @@ -985,6 +993,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
}

Expand All @@ -997,6 +1006,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
supportsCustomDelegation: true,
supportsLegalGuardians: true,
Expand Down Expand Up @@ -1025,6 +1035,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
supportsCustomDelegation: true,
supportsLegalGuardians: true,
Expand All @@ -1036,6 +1047,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
supportsCustomDelegation: true,
supportsLegalGuardians: true,
Expand All @@ -1049,6 +1061,7 @@ describe('MeClientsController with auth', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.LegalRepresentative,
],
}

Expand All @@ -1063,6 +1076,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 @@ -126,6 +127,10 @@ const createTestData = async ({
AuthDelegationType.LegalGuardian,
AuthDelegationProvider.NationalRegistry,
],
[
AuthDelegationType.LegalRepresentative,
AuthDelegationProvider.DistrictCommissionersRegistry,
],
].map(async ([delegationType, provider]) =>
fixtureFactory.createDelegationType({
id: delegationType,
Expand Down Expand Up @@ -373,6 +378,8 @@ interface PatchTestCase {
allowExplicitDelegationGrant?: boolean
grantToPersonalRepresentatives?: boolean
isAccessControlled?: boolean
addedDelegationTypes?: AuthDelegationType[]
removedDelegationTypes?: AuthDelegationType[]
}
expected: {
status: number
Expand Down Expand Up @@ -512,6 +519,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 @@ -760,7 +805,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 @@ -830,6 +875,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
expected: {
Expand All @@ -842,6 +888,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
})
Expand All @@ -855,6 +902,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
expected: {
Expand All @@ -867,6 +915,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
})
Expand All @@ -878,6 +927,7 @@ describe('MeScopesController', () => {
AuthDelegationType.LegalGuardian,
AuthDelegationType.ProcurationHolder,
AuthDelegationType.PersonalRepresentative,
AuthDelegationType.LegalRepresentative,
],
},
expected: {
Expand Down Expand Up @@ -944,7 +994,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 @@ -1028,7 +1078,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
10 changes: 9 additions & 1 deletion apps/services/auth/delegation-api/infra/delegation-api.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
json,
ref,
service,
ServiceBuilder,
ref,
} from '../../../../../infra/src/dsl/dsl'
import { Base, Client, RskProcuring } from '../../../../../infra/src/dsl/xroad'

Expand Down Expand Up @@ -54,12 +54,20 @@ export const serviceSetup = (services: {
prod: 'IS/GOV/5402696029/Skatturinn/ft-v1',
},
COMPANY_REGISTRY_REDIS_NODES: REDIS_NODE_CONFIG,
SYSLUMENN_HOST: {
dev: 'https://api.syslumenn.is/staging',
staging: 'https://api.syslumenn.is/staging',
prod: 'https://api.syslumenn.is',
},
SYSLUMENN_TIMEOUT: '3000',
})
.secrets({
IDENTITY_SERVER_CLIENT_SECRET:
'/k8s/services-auth/IDENTITY_SERVER_CLIENT_SECRET',
NATIONAL_REGISTRY_IDS_CLIENT_SECRET:
'/k8s/xroad/client/NATIONAL-REGISTRY/IDENTITYSERVER_SECRET',
SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME',
SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD',
})
.xroad(Base, Client, RskProcuring)
.readiness('/health/check')
Expand Down
Loading

0 comments on commit d6f7986

Please sign in to comment.