Skip to content

Commit f8fb77d

Browse files
committed
feat(keycloack): add configurable purge at upsert
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
1 parent 0b4383f commit f8fb77d

File tree

2 files changed

+66
-20
lines changed

2 files changed

+66
-20
lines changed

plugins/keycloak/src/functions.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { AdminRole, Project, StepCall, UserEmail, ZoneObject, ProjectMemberPayload } from '@cpn-console/hooks'
2-
import type { ProjectRole } from '@cpn-console/shared'
2+
import { ENABLED, type ProjectRole } from '@cpn-console/shared'
33
import { generateRandomPassword, parseError, PluginResultBuilder } from '@cpn-console/hooks'
44
import type GroupRepresentation from '@keycloak/keycloak-admin-client/lib/defs/groupRepresentation.js'
55
import type ClientRepresentation from '@keycloak/keycloak-admin-client/lib/defs/clientRepresentation.js'
@@ -60,27 +60,32 @@ export const deleteProject: StepCall<Project> = async ({ args: project }) => {
6060
}
6161
}
6262

63-
export const upsertProject: StepCall<Project> = async ({ args: project }) => {
63+
export const upsertProject: StepCall<Project> = async ({ args: project, config }) => {
6464
const pluginResult = new PluginResultBuilder('Up-to-date')
6565
try {
6666
const kcClient = await getkcClient()
6767
const projectName = project.slug
68+
const purgeEnabled = config.keycloak?.purge === ENABLED
6869
const projectGroup = await getOrCreateProjectGroup(kcClient, projectName)
6970

7071
const groupMembers = await kcClient.groups.listMembers({ id: projectGroup.id })
7172

7273
await Promise.all([
7374
...groupMembers.map((member) => {
7475
if (!project.users.some(({ id }) => id === member.id)) {
75-
return kcClient.users.delFromGroup({
76-
// @ts-ignore id is present on user, bad typing in lib
77-
id: member.id,
78-
groupId: projectGroup.id,
79-
})
80-
.catch((err) => {
81-
pluginResult.addKoMessage(`Can't remove ${member.email} from keycloak project group`)
82-
pluginResult.addExtra(`remove-${member.id}`, err)
76+
if (purgeEnabled) {
77+
return kcClient.users.delFromGroup({
78+
// @ts-ignore id is present on user, bad typing in lib
79+
id: member.id,
80+
groupId: projectGroup.id,
8381
})
82+
.catch((err) => {
83+
pluginResult.addKoMessage(`Can't remove ${member.email} from keycloak project group`)
84+
pluginResult.addExtra(`remove-${member.id}`, err)
85+
})
86+
} else {
87+
console.warn(`User ${member.email} is not in project ${projectName} anymore, but purge is disabled`)
88+
}
8489
}
8590
return undefined
8691
}),
@@ -223,9 +228,10 @@ export const deleteZone: StepCall<ZoneObject> = async ({ args: zone }) => {
223228
}
224229
}
225230

226-
export const upsertAdminRole: StepCall<AdminRole> = async ({ args: role }) => {
231+
export const upsertAdminRole: StepCall<AdminRole> = async ({ args: role, config }) => {
227232
if (!role.oidcGroup) return { status: { result: 'OK', message: 'No OIDC Group defined' } }
228233
const pluginResult = new PluginResultBuilder('Up-to-date')
234+
const purgeEnabled = config.keycloak?.purge === ENABLED
229235
try {
230236
const kcClient = await getkcClient()
231237
const group = await getOrCreateGroupByPath(kcClient, role.oidcGroup)
@@ -234,14 +240,18 @@ export const upsertAdminRole: StepCall<AdminRole> = async ({ args: role }) => {
234240
await Promise.all([
235241
...groupMembers.map((member) => {
236242
if (member.id && !role.members.some(({ id }) => id === member.id)) {
237-
return kcClient.users.delFromGroup({
238-
id: member.id,
239-
groupId: group!.id!,
240-
})
241-
.catch((err) => {
242-
pluginResult.addKoMessage(`Can't remove ${member.email} from keycloak admin group`)
243-
pluginResult.addExtra(`remove-${member.id}`, err)
243+
if (purgeEnabled) {
244+
return kcClient.users.delFromGroup({
245+
id: member.id,
246+
groupId: group!.id!,
244247
})
248+
.catch((err) => {
249+
pluginResult.addKoMessage(`Can't remove ${member.email} from keycloak admin group`)
250+
pluginResult.addExtra(`remove-${member.id}`, err)
251+
})
252+
} else {
253+
console.warn(`User ${member.email} is not in admin role ${role.oidcGroup} anymore, but purge is disabled`)
254+
}
245255
}
246256
return undefined
247257
}),
@@ -376,8 +386,9 @@ export const deleteProjectRole: StepCall<ProjectRole> = async ({ args: role }) =
376386
}
377387
}
378388

379-
export const upsertProjectMember: StepCall<ProjectMemberPayload> = async ({ args: member }) => {
389+
export const upsertProjectMember: StepCall<ProjectMemberPayload> = async ({ args: member, config }) => {
380390
const pluginResult = new PluginResultBuilder('Synced')
391+
const purgeEnabled = config.keycloak?.purge === ENABLED
381392
try {
382393
const kcClient = await getkcClient()
383394

@@ -399,7 +410,11 @@ export const upsertProjectMember: StepCall<ProjectMemberPayload> = async ({ args
399410
if (shouldBeMember && !isMember) {
400411
await kcClient.users.addToGroup({ id: member.userId, groupId: roleGroup.id })
401412
} else if (!shouldBeMember && isMember) {
402-
await kcClient.users.delFromGroup({ id: member.userId, groupId: roleGroup.id })
413+
if (purgeEnabled) {
414+
await kcClient.users.delFromGroup({ id: member.userId, groupId: roleGroup.id })
415+
} else {
416+
console.warn(`User ${member.email} is not in project ${member.project.slug} anymore, but purge is disabled`)
417+
}
403418
}
404419
}
405420

plugins/keycloak/src/infos.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,39 @@
11
import type { ServiceInfos } from '@cpn-console/hooks'
2+
import { DISABLED } from '@cpn-console/shared'
23

34
const infos: ServiceInfos = {
45
name: 'keycloak',
56
title: 'Keycloak',
7+
config: {
8+
global: [
9+
{
10+
kind: 'switch',
11+
key: 'purge',
12+
initialValue: DISABLED,
13+
permissions: {
14+
admin: { read: true, write: true },
15+
user: { read: false, write: false },
16+
},
17+
title: 'Purger les utilisateurs non synchronisés',
18+
value: DISABLED,
19+
description: 'Purger les utilisateurs non synchronisés de Keycloak lors de la synchronisation',
20+
},
21+
],
22+
project: [
23+
{
24+
kind: 'switch',
25+
key: 'purge',
26+
initialValue: DISABLED,
27+
permissions: {
28+
admin: { read: true, write: true },
29+
user: { read: false, write: false },
30+
},
31+
title: 'Purger les utilisateurs non synchronisés',
32+
value: DISABLED,
33+
description: 'Purger les utilisateurs non synchronisés de Keycloak lors de la synchronisation',
34+
},
35+
],
36+
},
637
}
738

839
export default infos

0 commit comments

Comments
 (0)