Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 80 additions & 2 deletions plugins/gitlab/src/functions.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -232,3 +232,81 @@ export const commitFiles: StepCall<UniqueRepo | Project | ClusterObject | ZoneOb
return returnResult
}
}

export const upsertAdminRole: StepCall<AdminRole> = 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<AdminRole> = 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',
},
}
}
}
6 changes: 6 additions & 0 deletions plugins/gitlab/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
deleteZone,
getDsoProjectSecrets,
syncRepository,
upsertAdminRole,
upsertDsoProject,
upsertZone,
} from './functions.js'
Expand Down Expand Up @@ -74,6 +75,11 @@ export const plugin: Plugin = {
main: deleteZone,
},
},
upsertAdminRole: {
steps: {
main: upsertAdminRole,
},
},
},
monitor,
start,
Expand Down
22 changes: 22 additions & 0 deletions plugins/gitlab/src/infos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
},
Expand Down
5 changes: 3 additions & 2 deletions plugins/gitlab/src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SimpleUserSchema> {
export async function upsertUser(user: UserObject, isAdmin = false, isAuditor = false): Promise<SimpleUserSchema> {
const api = getApi()
const username = createUsername(user.email)
const existingUser = await getUser({ ...user, username })
Expand All @@ -38,6 +38,8 @@ export async function upsertUser(user: UserObject): Promise<SimpleUserSchema> {
// sso options
externUid: user.id,
provider: 'openid_connect',
admin: isAdmin,
auditor: isAuditor,
}

if (existingUser) {
Expand All @@ -64,7 +66,6 @@ export async function upsertUser(user: UserObject): Promise<SimpleUserSchema> {

return api.Users.create({
...userDefinitionBase,
admin: false,
canCreateGroup: false,
forceRandomPassword: true,
projectsLimit: 0,
Expand Down
16 changes: 8 additions & 8 deletions plugins/keycloak/src/functions.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -65,15 +65,15 @@ export const upsertProject: StepCall<Project> = 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 })

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,
Expand Down Expand Up @@ -231,7 +231,7 @@ export const deleteZone: StepCall<ZoneObject> = async ({ args: zone }) => {
export const upsertAdminRole: StepCall<AdminRole> = 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)
Expand All @@ -240,7 +240,7 @@ export const upsertAdminRole: StepCall<AdminRole> = 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!,
Expand Down Expand Up @@ -388,7 +388,7 @@ export const deleteProjectRole: StepCall<ProjectRole> = async ({ args: role }) =

export const upsertProjectMember: StepCall<ProjectMemberPayload> = 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()

Expand All @@ -410,7 +410,7 @@ export const upsertProjectMember: StepCall<ProjectMemberPayload> = 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`)
Expand Down
15 changes: 1 addition & 14 deletions plugins/keycloak/src/infos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
},
}

Expand Down
Loading