Skip to content
Merged
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
6 changes: 5 additions & 1 deletion apps/server/src/utils/hook-wrapper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,11 @@ describe('transformToHookProject', () => {
expect(result.users).toEqual([project.owner])

// Assert sur la transformation des rôles
expect(result.roles).toEqual([{ userId: project.owner.id, role: 'owner' }])
expect(result.roles).toEqual([{
name: 'owner',
position: 0,
users: [project.owner],
}])

// Assert sur la transformation des clusters
expect(result.clusters).toEqual([associatedCluster, nonAssociatedCluster].map(({ kubeconfig, ...cluster }) => ({
Expand Down
56 changes: 42 additions & 14 deletions apps/server/src/utils/hook-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,21 @@ const user = {
const projectMember = {
upsert: async (projectId: Project['id'], userId: ProjectMembers['userId']) => {
const project = await getHookProjectInfos(projectId)
const projectStore = dbToObj(await getProjectStore(project.id))
const hookProject = transformToHookProject(project, projectStore)
const store = dbToObj(await getAdminPlugin())

const member = project.members.find(m => m.userId === userId)
if (!member) throw new Error('Member not found')

const memberRoles = project.roles
.filter(role => member.roleIds.includes(role.id))
.map(role => ({ ...role, permissions: role.permissions.toString(), oidcGroup: role.oidcGroup ?? undefined }))
.map(role => ({
...role,
permissions: role.permissions.toString(),
oidcGroup: role.oidcGroup ?? undefined,
project: hookProject,
}))

const payload = {
userId: member.userId,
Expand All @@ -163,24 +170,28 @@ const projectMember = {
lastLogin: member.user.lastLogin?.toISOString(),
projectId: project.id,
roles: memberRoles,
project: {
id: project.id,
slug: project.slug,
},
project: hookProject,
}

return hooks.upsertProjectMember.execute(payload, store)
},
delete: async (projectId: Project['id'], userId: ProjectMembers['userId']) => {
const project = await getHookProjectInfos(projectId)
const projectStore = dbToObj(await getProjectStore(project.id))
const hookProject = transformToHookProject(project, projectStore)
const store = dbToObj(await getAdminPlugin())

const member = project.members.find(m => m.userId === userId)
if (!member) throw new Error('Member not found')

const memberRoles = project.roles
.filter(role => member.roleIds.includes(role.id))
.map(role => ({ ...role, permissions: role.permissions.toString(), oidcGroup: role.oidcGroup ?? undefined }))
.map(role => ({
...role,
permissions: role.permissions.toString(),
oidcGroup: role.oidcGroup ?? undefined,
project: hookProject,
}))

const payload = {
userId: member.userId,
Expand All @@ -194,10 +205,7 @@ const projectMember = {
lastLogin: member.user.lastLogin?.toISOString(),
projectId: project.id,
roles: memberRoles,
project: {
id: project.id,
slug: project.slug,
},
project: hookProject,
}

return hooks.deleteProjectMember.execute(payload, store)
Expand All @@ -209,9 +217,14 @@ const projectRole = {
const role = await getRole(roleId)
if (!role) throw new Error('Role not found')

const project = await getHookProjectInfos(role.projectId)
const projectStore = dbToObj(await getProjectStore(role.projectId))
const hookProject = transformToHookProject(project, projectStore)

const rolePayload = {
...role,
permissions: role.permissions.toString(),
project: hookProject,
}
const store = dbToObj(await getAdminPlugin())
return hooks.upsertProjectRole.execute(rolePayload, store)
Expand All @@ -220,9 +233,14 @@ const projectRole = {
const role = await getRole(roleId)
if (!role) throw new Error('Role not found')

const project = await getHookProjectInfos(role.projectId)
const projectStore = dbToObj(await getProjectStore(role.projectId))
const hookProject = transformToHookProject(project, projectStore)

const rolePayload = {
...role,
permissions: role.permissions.toString(),
project: hookProject,
}
const store = dbToObj(await getAdminPlugin())
return hooks.deleteProjectRole.execute(rolePayload, store)
Expand Down Expand Up @@ -331,10 +349,20 @@ export function transformToHookProject(project: ProjectInfos, store: Store, repo
store,
users: [project.owner, ...project.members.map(({ user }) => user)],
roles: [
{ userId: project.ownerId, role: 'owner' },
...project.members.map(member => ({
userId: member.userId,
role: 'user' as const,
{
name: 'owner',
position: 0,
users: [project.owner],
},
...project.roles.map(role => ({
name: role.name,
permissions: role.permissions.toString(),
position: role.position,
type: role.type,
oidcGroup: role.oidcGroup,
users: project.members
.filter(member => member.roleIds.includes(role.id))
.map(member => member.user),
})),
],
})
Expand Down
18 changes: 10 additions & 8 deletions packages/hooks/src/hooks/hook-project-member.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type { ProjectMember, ProjectRole } from '@cpn-console/shared'
import type { ProjectRole } from './hook-project-role.js'
import type { Project } from './hook-project.js'
import type { Hook } from './hook.js'
import { createHook } from './hook.js'

export type ProjectMemberPayload = ProjectMember & {
export interface ProjectMember {
userId: string
email: string
firstName: string
lastName: string
roles: ProjectRole[]
project: {
id: string
slug: string
}
project: Project
}

export const upsertProjectMember: Hook<ProjectMemberPayload> = createHook()
export const deleteProjectMember: Hook<ProjectMemberPayload> = createHook()
export const upsertProjectMember: Hook<ProjectMember> = createHook()
export const deleteProjectMember: Hook<ProjectMember> = createHook()
13 changes: 12 additions & 1 deletion packages/hooks/src/hooks/hook-project-role.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import type { ProjectRole } from '@cpn-console/shared'
import type { Project } from './hook-project.js'
import type { Hook } from './hook.js'
import { createHook } from './hook.js'

export interface ProjectRole {
id: string
name: string
permissions: string
projectId: string
position: number
type?: string
oidcGroup?: string
project: Project
}

export const upsertProjectRole: Hook<ProjectRole> = createHook()
export const deleteProjectRole: Hook<ProjectRole> = createHook()
8 changes: 6 additions & 2 deletions packages/hooks/src/hooks/hook-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ export interface RepoCreds {
}

export interface Role {
userId: string
role: 'owner' | 'user'
name: string
permissions?: string
position: number
type?: string
oidcGroup?: string
users: UserObject[]
}

export interface EnvironmentApis {
Expand Down
11 changes: 8 additions & 3 deletions plugins/gitlab/src/class.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createHash } from 'node:crypto'
import { PluginApi, type Project, type UniqueRepo } from '@cpn-console/hooks'
import { PluginApi, type Project, type UniqueRepo, type ProjectMember } from '@cpn-console/hooks'
import type { AccessTokenScopes, CommitAction, GroupSchema, MemberSchema, ProjectVariableSchema, VariableSchema, AllRepositoryTreesOptions, CondensedProjectSchema, Gitlab, ProjectSchema, RepositoryFileExpandedSchema } from '@gitbeaker/core'
import { AccessLevel } from '@gitbeaker/core'
import type { VaultProjectApi } from '@cpn-console/vault-plugin/types/vault-project-api.js'
Expand Down Expand Up @@ -234,12 +234,12 @@ export class GitlabZoneApi extends GitlabApi {
}

export class GitlabProjectApi extends GitlabApi {
private project: Project | UniqueRepo
private project: Project | UniqueRepo | ProjectMember['project']
private gitlabGroup: GroupSchema | undefined
private specialRepositories: string[] = [infraAppsRepoName, internalMirrorRepoName]
private zoneApi: GitlabZoneApi

constructor(project: Project | UniqueRepo) {
constructor(project: Project | UniqueRepo | ProjectMember['project']) {
super()
this.project = project
this.api = getApi()
Expand Down Expand Up @@ -434,6 +434,11 @@ export class GitlabProjectApi extends GitlabApi {
return this.api.GroupMembers.add(group.id, userId, accessLevel)
}

public async editGroupMember(userId: number, accessLevel: AccessLevelAllowed = AccessLevel.DEVELOPER): Promise<MemberSchema> {
const group = await this.getOrCreateProjectGroup()
return this.api.GroupMembers.edit(group.id, userId, accessLevel)
}

public async removeGroupMember(userId: number) {
const group = await this.getOrCreateProjectGroup()
return this.api.GroupMembers.remove(group.id, userId)
Expand Down
Loading