diff --git a/apps/opencase/src/infrastructure/keycloak/KeycloakTenantProvisioner.ts b/apps/opencase/src/infrastructure/keycloak/KeycloakTenantProvisioner.ts index 7aecb89..8ce7973 100644 --- a/apps/opencase/src/infrastructure/keycloak/KeycloakTenantProvisioner.ts +++ b/apps/opencase/src/infrastructure/keycloak/KeycloakTenantProvisioner.ts @@ -37,50 +37,12 @@ export class KeycloakTenantProvisioner { }) const tenantId = 'system' - const clientId = OidcJwtVerifier.computeTenantClientId(this.cfg.clientIdPrefix, tenantId) - const { id: clientUuid } = await this.admin.ensureClient({ - clientId, - publicClient: true, - standardFlowEnabled: true, - redirectUris: this.cfg.spaRedirectUris, - webOrigins: this.cfg.spaWebOrigins - }) - - await this.admin.ensureClientRole(clientUuid, 'case.admin') - - await this.admin.ensureProtocolMapper(clientUuid, { - name: 'tenantId', - protocol: 'openid-connect', - protocolMapper: 'oidc-hardcoded-claim-mapper', - config: { - 'claim.name': 'tenantId', - 'claim.value': tenantId, - 'jsonType.label': 'String', - 'id.token.claim': 'true', - 'access.token.claim': 'true', - 'userinfo.token.claim': 'true' - } - }) - - await this.admin.ensureProtocolMapper(clientUuid, { - name: 'scope', - protocol: 'openid-connect', - protocolMapper: 'oidc-usermodel-client-role-mapper', - config: { - 'clientId': clientId, - 'rolePrefix': '', - 'claim.name': 'scope', - 'jsonType.label': 'String', - 'multivalued': 'true', - 'access.token.claim': 'true', - 'id.token.claim': 'true', - 'userinfo.token.claim': 'true' - } - }) + const roles = ['case.read', 'case.write', 'case.owner', 'case.admin'] + const { clientId, clientUuid } = await this.ensureTenantClient(tenantId, roles) const { id: userId } = await this.admin.ensureUser({ username: email, email, enabled: true }) await this.admin.setUserPassword(userId, password, false) - await this.admin.assignClientRoles(userId, clientUuid, ['case.admin']) + await this.admin.assignClientRoles(userId, clientUuid, roles) logger.info({ clientId, email }, 'Bootstrapped system admin user in Keycloak') } @@ -88,6 +50,20 @@ export class KeycloakTenantProvisioner { async provisionTenant (tenantId: string): Promise<{ adminEmail: string, adminPassword: string }> { await this.admin.ensureRealmExists() + const roles = ['case.read', 'case.write', 'case.owner'] + const { clientId, clientUuid } = await this.ensureTenantClient(tenantId, roles) + + const adminEmail = `admin@${tenantId}.local` + const { id: userId } = await this.admin.ensureUser({ username: adminEmail, email: adminEmail, enabled: true }) + const adminPassword = randomBytes(18).toString('base64url') + await this.admin.setUserPassword(userId, adminPassword, true) + await this.admin.assignClientRoles(userId, clientUuid, roles) + + logger.info({ tenantId, clientId, adminEmail }, 'Provisioned tenant client and admin user in Keycloak') + return { adminEmail, adminPassword } + } + + private async ensureTenantClient (tenantId: string, roles: string[]): Promise<{ clientId: string, clientUuid: string }> { const clientId = OidcJwtVerifier.computeTenantClientId(this.cfg.clientIdPrefix, tenantId) const { id: clientUuid } = await this.admin.ensureClient({ clientId, @@ -97,7 +73,6 @@ export class KeycloakTenantProvisioner { webOrigins: this.cfg.spaWebOrigins }) - const roles = ['case.read', 'case.write', 'case.owner'] for (const role of roles) { await this.admin.ensureClientRole(clientUuid, role) } @@ -132,14 +107,7 @@ export class KeycloakTenantProvisioner { } }) - const adminEmail = `admin@${tenantId}.local` - const { id: userId } = await this.admin.ensureUser({ username: adminEmail, email: adminEmail, enabled: true }) - const adminPassword = randomBytes(18).toString('base64url') - await this.admin.setUserPassword(userId, adminPassword, true) - await this.admin.assignClientRoles(userId, clientUuid, roles) - - logger.info({ tenantId, clientId, adminEmail }, 'Provisioned tenant client and admin user in Keycloak') - return { adminEmail, adminPassword } + return { clientId, clientUuid } } }