Skip to content

Commit

Permalink
feat(ids-api): Use syslumenn api to verify delegation. (#16029)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
4 people authored Sep 24, 2024
1 parent 9c26661 commit 52fc588
Show file tree
Hide file tree
Showing 24 changed files with 698 additions and 115 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
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
4 changes: 3 additions & 1 deletion apps/services/auth/delegation-api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ 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 { RskRelationshipsClientConfig } from '@island.is/clients-rsk-relationships'
import { SyslumennClientConfig } from '@island.is/clients/syslumenn'
import { AuditModule } from '@island.is/nest/audit'
import {
ConfigModule,
Expand Down Expand Up @@ -50,6 +51,7 @@ import { ScopesModule } from './scopes/scopes.module'
CompanyRegistryConfig,
XRoadConfig,
DelegationApiUserSystemNotificationConfig,
SyslumennClientConfig,
],
}),
],
Expand Down
8 changes: 8 additions & 0 deletions apps/services/auth/ids-api/infra/ids-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-ids-api'> => {
// Origin for Android prod app
'android:apk-key-hash:EsLTUu5kaY7XPmMl2f7nbq4amu-PNzdYu3FecNf90wU',
]),
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:
Expand All @@ -92,6 +98,8 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-ids-api'> => {
NOVA_PASSWORD: '/k8s/services-auth/NOVA_PASSWORD',
NATIONAL_REGISTRY_B2C_CLIENT_SECRET:
'/k8s/services-auth/NATIONAL_REGISTRY_B2C_CLIENT_SECRET',
SYSLUMENN_USERNAME: '/k8s/services-auth/SYSLUMENN_USERNAME',
SYSLUMENN_PASSWORD: '/k8s/services-auth/SYSLUMENN_PASSWORD',
})
.xroad(Base, Client, RskProcuring, NationalRegistryAuthB2C)
.readiness('/health/check')
Expand Down
4 changes: 3 additions & 1 deletion apps/services/auth/ids-api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RskRelationshipsClientConfig } from '@island.is/clients-rsk-relationshi
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 { UserProfileClientConfig } from '@island.is/clients/user-profile'
import { AuditModule } from '@island.is/nest/audit'
import {
Expand All @@ -28,12 +29,12 @@ import { DelegationsModule } from './delegations/delegations.module'
import { GrantsModule } from './grants/grants.module'
import { LoginRestrictionsModule } from './login-restrictions/login-restrictions.module'
import { NotificationsModule } from './notifications/notifications.module'
import { PasskeysModule } from './passkeys/passkeys.module'
import { PermissionsModule } from './permissions/permissions.module'
import { ResourcesModule } from './resources/resources.module'
import { TranslationModule } from './translation/translation.module'
import { UserProfileModule } from './user-profile/user-profile.module'
import { UsersModule } from './users/users.module'
import { PasskeysModule } from './passkeys/passkeys.module'

@Module({
imports: [
Expand Down Expand Up @@ -68,6 +69,7 @@ import { PasskeysModule } from './passkeys/passkeys.module'
PasskeysCoreConfig,
NationalRegistryV3ClientConfig,
smsModuleConfig,
SyslumennClientConfig,
],
}),
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger'
import { IsBoolean } from 'class-validator'

export class DelegationVerificationResult {
@IsBoolean()
@ApiProperty()
verified!: boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ApiProperty } from '@nestjs/swagger'
import { IsArray, IsEnum, IsString } from 'class-validator'

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

export class DelegationVerification {
@IsString()
@ApiProperty()
fromNationalId!: string

@IsArray()
@IsEnum(AuthDelegationType, { each: true })
@ApiProperty({
enum: AuthDelegationType,
enumName: 'AuthDelegationType',
isArray: true,
})
delegationTypes!: AuthDelegationType[]
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
Body,
Controller,
Get,
Inject,
ParseArrayPipe,
Post,
Query,
UseGuards,
Version,
Expand All @@ -25,11 +27,14 @@ import {
ScopesGuard,
} from '@island.is/auth-nest-tools'
import { LOGGER_PROVIDER } from '@island.is/logging'
import { Documentation } from '@island.is/nest/swagger'
import { AuthDelegationType } from '@island.is/shared/types'

import { DelegationVerificationResult } from './delegation-verification-result.dto'
import { DelegationVerification } from './delegation-verification.dto'

import type { Logger } from '@island.is/logging'
import type { User } from '@island.is/auth-nest-tools'

@UseGuards(IdsUserGuard, ScopesGuard)
@ApiTags('delegations')
@Controller({
Expand Down Expand Up @@ -110,4 +115,26 @@ export class DelegationsController {
delegationType,
)
}

@Scopes('@identityserver.api/authentication')
@Post('verify')
@Documentation({
description: 'Verifies a delegation at the source.',
response: { status: 200, type: DelegationVerificationResult },
})
@ApiOkResponse({ type: DelegationVerificationResult })
async verify(
@CurrentUser() user: User,
@Body()
request: DelegationVerification,
): Promise<DelegationVerificationResult> {
const verified =
await this.delegationsIncomingService.verifyDelegationAtProvider(
user,
request.fromNationalId,
request.delegationTypes,
)

return { verified }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
import { createNationalRegistryUser } from '@island.is/testing/fixtures'
import { TestApp, truncate } from '@island.is/testing/nest'

import { setupWithAuth } from '../../../../test/setup'
import {
nonExistingLegalRepresentativeNationalId,
setupWithAuth,
} from '../../../../test/setup'
import { testCases } from './delegations-filters-test-cases'
import { user } from './delegations-filters-types'

Expand Down Expand Up @@ -128,4 +131,58 @@ describe('DelegationsController', () => {
})
},
)

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)),
)

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 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)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ const legalGuardianScopes = ['lg1', 'lg2']
const procurationHolderScopes = ['ph1', 'ph2']
const customScopes1 = ['cu1', 'cu2']
const customScopes2 = ['cu3', 'cu4']
const legalRepresentativeScopes = ['lr1', 'lr2']

const apiScopes = [
...legalGuardianScopes,
...procurationHolderScopes,
...customScopes1,
...customScopes2,
...legalRepresentativeScopes,
]

const fromCustom = [
Expand All @@ -48,6 +50,9 @@ const supportedDelegationTypes = (scopeName: string): AuthDelegationType[] => {
if (customScopes1.includes(scopeName) || customScopes2.includes(scopeName)) {
result.push(AuthDelegationType.Custom)
}
if (legalRepresentativeScopes.includes(scopeName)) {
result.push(AuthDelegationType.LegalRepresentative)
}
return result
}

Expand Down Expand Up @@ -98,6 +103,11 @@ const testCases: Record<string, TestCase> = {
],
expected: [...legalGuardianScopes, ...identityResources],
},
'7': {
fromNationalId: createNationalId('person'),
delegationType: [AuthDelegationType.LegalRepresentative],
expected: [...legalRepresentativeScopes, ...identityResources],
},
}

const user = createCurrentUser({
Expand Down
Loading

0 comments on commit 52fc588

Please sign in to comment.