diff --git a/plugins/gitlab/src/functions.ts b/plugins/gitlab/src/functions.ts index 110ff01b8..7a06f7e13 100644 --- a/plugins/gitlab/src/functions.ts +++ b/plugins/gitlab/src/functions.ts @@ -1,8 +1,8 @@ import { okStatus, parseError, specificallyDisabled } from '@cpn-console/hooks' -import type { ClusterObject, PluginResult, Project, ProjectLite, StepCall, UniqueRepo, ZoneObject } from '@cpn-console/hooks' +import type { AdminRole, ClusterObject, PluginResult, Project, ProjectLite, StepCall, UniqueRepo, ZoneObject } from '@cpn-console/hooks' import { insert } from '@cpn-console/shared' import { deleteGroup } from './group.js' -import { createUsername, getUser } from './user.js' +import { createUsername, getUser, upsertUser } from './user.js' import { ensureMembers } from './members.js' import { ensureRepositories } from './repositories.js' import type { VaultSecrets } from './utils.js' @@ -232,3 +232,81 @@ export const commitFiles: StepCall = async (payload) => { + try { + const role = payload.args + const adminGroupPath = payload.config.gitlab?.adminGroupPath ?? DEFAULT_ADMIN_GROUP_PATH + const auditorGroupPath = payload.config.gitlab?.auditorGroupPath ?? DEFAULT_AUDITOR_GROUP_PATH + + const isAdmin = role.oidcGroup === adminGroupPath ? true : undefined + const isAuditor = role.oidcGroup === auditorGroupPath ? true : undefined + + if (isAdmin === undefined && isAuditor === undefined) { + return { + status: { + result: 'OK', + message: 'Not a managed role for GitLab plugin', + }, + } + } + + for (const member of role.members) { + await upsertUser(member, isAdmin, isAuditor) + } + + return { + status: { + result: 'OK', + message: 'Members synced', + }, + } + } catch (error) { + return { + error: parseError(cleanGitlabError(error)), + status: { + result: 'KO', + message: 'An error occured while syncing admin role', + }, + } + } +} + +export const deleteAdminRole: StepCall = async (payload) => { + try { + const role = payload.args + const adminGroupPath = payload.config.gitlab?.adminGroupPath ?? DEFAULT_ADMIN_GROUP_PATH + const auditorGroupPath = payload.config.gitlab?.auditorGroupPath ?? DEFAULT_AUDITOR_GROUP_PATH + + const isAdmin = role.oidcGroup === adminGroupPath ? false : undefined + const isAuditor = role.oidcGroup === auditorGroupPath ? false : undefined + + if (isAdmin === undefined && isAuditor === undefined) { + return { + status: { + result: 'OK', + message: 'Not a managed role for GitLab plugin', + }, + } + } + + for (const member of role.members) { + await upsertUser(member, isAdmin, isAuditor) + } + + return { + status: { + result: 'OK', + message: 'Admin role deleted and members synced', + }, + } + } catch (error) { + return { + error: parseError(cleanGitlabError(error)), + status: { + result: 'KO', + message: 'An error occured while deleting admin role', + }, + } + } +} diff --git a/plugins/gitlab/src/index.ts b/plugins/gitlab/src/index.ts index ef72f7dd5..44f857e18 100644 --- a/plugins/gitlab/src/index.ts +++ b/plugins/gitlab/src/index.ts @@ -6,6 +6,7 @@ import { deleteZone, getDsoProjectSecrets, syncRepository, + upsertAdminRole, upsertDsoProject, upsertZone, } from './functions.js' @@ -74,6 +75,11 @@ export const plugin: Plugin = { main: deleteZone, }, }, + upsertAdminRole: { + steps: { + main: upsertAdminRole, + }, + }, }, monitor, start, diff --git a/plugins/gitlab/src/infos.ts b/plugins/gitlab/src/infos.ts index af3b03869..98cfaa582 100644 --- a/plugins/gitlab/src/infos.ts +++ b/plugins/gitlab/src/infos.ts @@ -20,6 +20,28 @@ const infos = { title: 'Afficher l\'aide de trigger de pipeline', value: ENABLED, description: 'Afficher l\'aide de trigger de pipeline aux utilisateurs lorsqu\'ils souhaitent afficher les secrets du projet', + }, { + kind: 'text', + key: 'adminGroupPath', + permissions: { + admin: { read: true, write: true }, + user: { read: false, write: false }, + }, + title: 'Chemin du groupe OIDC Admin', + value: '/console/admin', + description: 'Le chemin du groupe OIDC qui donne les droits d\'administrateur GitLab', + placeholder: '/console/admin', + }, { + kind: 'text', + key: 'auditorGroupPath', + permissions: { + admin: { read: true, write: true }, + user: { read: false, write: false }, + }, + title: 'Chemin du groupe OIDC Auditeur', + value: '/console/readonly', + description: 'Le chemin du groupe OIDC qui donne les droits d\'auditeur GitLab', + placeholder: '/console/readonly', }], project: [], }, diff --git a/plugins/gitlab/src/user.ts b/plugins/gitlab/src/user.ts index cb984c036..2eed6599b 100644 --- a/plugins/gitlab/src/user.ts +++ b/plugins/gitlab/src/user.ts @@ -25,7 +25,7 @@ export async function getUser(user: { email: string, username: string, id: strin || allUsers.find(gitlabUser => gitlabUser.username === user.username) } -export async function upsertUser(user: UserObject): Promise { +export async function upsertUser(user: UserObject, isAdmin = false, isAuditor = false): Promise { const api = getApi() const username = createUsername(user.email) const existingUser = await getUser({ ...user, username }) @@ -38,6 +38,8 @@ export async function upsertUser(user: UserObject): Promise { // sso options externUid: user.id, provider: 'openid_connect', + admin: isAdmin, + auditor: isAuditor, } if (existingUser) { @@ -64,7 +66,6 @@ export async function upsertUser(user: UserObject): Promise { return api.Users.create({ ...userDefinitionBase, - admin: false, canCreateGroup: false, forceRandomPassword: true, projectsLimit: 0, diff --git a/plugins/keycloak/src/functions.ts b/plugins/keycloak/src/functions.ts index c828ed679..f5940d00b 100644 --- a/plugins/keycloak/src/functions.ts +++ b/plugins/keycloak/src/functions.ts @@ -1,6 +1,6 @@ import type { AdminRole, Project, StepCall, UserEmail, ZoneObject, ProjectMemberPayload } from '@cpn-console/hooks' -import { ENABLED, type ProjectRole } from '@cpn-console/shared' -import { generateRandomPassword, parseError, PluginResultBuilder } from '@cpn-console/hooks' +import type { ProjectRole } from '@cpn-console/shared' +import { generateRandomPassword, parseError, PluginResultBuilder, specificallyEnabled } from '@cpn-console/hooks' import type GroupRepresentation from '@keycloak/keycloak-admin-client/lib/defs/groupRepresentation.js' import type ClientRepresentation from '@keycloak/keycloak-admin-client/lib/defs/clientRepresentation.js' import type { CustomGroup } from './group.js' @@ -65,7 +65,7 @@ export const upsertProject: StepCall = async ({ args: project, config } try { const kcClient = await getkcClient() const projectName = project.slug - const purgeEnabled = config.keycloak?.purge === ENABLED + const purge = config.keycloak?.purge const projectGroup = await getOrCreateProjectGroup(kcClient, projectName) const groupMembers = await kcClient.groups.listMembers({ id: projectGroup.id }) @@ -73,7 +73,7 @@ export const upsertProject: StepCall = async ({ args: project, config } await Promise.all([ ...groupMembers.map((member) => { if (!project.users.some(({ id }) => id === member.id)) { - if (purgeEnabled) { + if (specificallyEnabled(purge)) { return kcClient.users.delFromGroup({ // @ts-ignore id is present on user, bad typing in lib id: member.id, @@ -231,7 +231,7 @@ export const deleteZone: StepCall = async ({ args: zone }) => { export const upsertAdminRole: StepCall = async ({ args: role, config }) => { if (!role.oidcGroup) return { status: { result: 'OK', message: 'No OIDC Group defined' } } const pluginResult = new PluginResultBuilder('Up-to-date') - const purgeEnabled = config.keycloak?.purge === ENABLED + const purge = config.keycloak?.purge try { const kcClient = await getkcClient() const group = await getOrCreateGroupByPath(kcClient, role.oidcGroup) @@ -240,7 +240,7 @@ export const upsertAdminRole: StepCall = async ({ args: role, config await Promise.all([ ...groupMembers.map((member) => { if (member.id && !role.members.some(({ id }) => id === member.id)) { - if (purgeEnabled) { + if (specificallyEnabled(purge)) { return kcClient.users.delFromGroup({ id: member.id, groupId: group!.id!, @@ -388,7 +388,7 @@ export const deleteProjectRole: StepCall = async ({ args: role }) = export const upsertProjectMember: StepCall = async ({ args: member, config }) => { const pluginResult = new PluginResultBuilder('Synced') - const purgeEnabled = config.keycloak?.purge === ENABLED + const purge = config.keycloak?.purge try { const kcClient = await getkcClient() @@ -410,7 +410,7 @@ export const upsertProjectMember: StepCall = async ({ args if (shouldBeMember && !isMember) { await kcClient.users.addToGroup({ id: member.userId, groupId: roleGroup.id }) } else if (!shouldBeMember && isMember) { - if (purgeEnabled) { + if (specificallyEnabled(purge)) { await kcClient.users.delFromGroup({ id: member.userId, groupId: roleGroup.id }) } else { console.warn(`User ${member.email} is not in project ${member.project.slug} anymore, but purge is disabled`) diff --git a/plugins/keycloak/src/infos.ts b/plugins/keycloak/src/infos.ts index f6fa873fa..6162c251d 100644 --- a/plugins/keycloak/src/infos.ts +++ b/plugins/keycloak/src/infos.ts @@ -19,20 +19,7 @@ const infos: ServiceInfos = { description: 'Purger les utilisateurs non synchronisés de Keycloak lors de la synchronisation', }, ], - project: [ - { - kind: 'switch', - key: 'purge', - initialValue: DISABLED, - permissions: { - admin: { read: true, write: true }, - user: { read: false, write: false }, - }, - title: 'Purger les utilisateurs non synchronisés', - value: DISABLED, - description: 'Purger les utilisateurs non synchronisés de Keycloak lors de la synchronisation', - }, - ], + project: [], }, }