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
34 changes: 18 additions & 16 deletions apps/server/src/resources/admin-role/business.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ describe('test admin-role business', () => {
prisma.adminRole.findFirst.mockResolvedValueOnce(dbRole)
prisma.adminRole.findMany.mockResolvedValueOnce([dbRole])
prisma.adminRole.create.mockResolvedValue(dbRole)
await createRole({ name: 'test' })
hook.adminRole.upsert.mockResolvedValue({ args: {}, failed: false, results: {} })
await createRole({ name: 'test' }, faker.string.uuid())

expect(prisma.adminRole.create).toHaveBeenCalledWith({ data: { name: 'test', permissions: 0n, position: 1 } })
})
Expand All @@ -60,7 +61,8 @@ describe('test admin-role business', () => {
prisma.adminRole.findFirst.mockResolvedValueOnce(dbRole)
prisma.adminRole.findMany.mockResolvedValueOnce([dbRole])
prisma.adminRole.create.mockResolvedValue(dbRole)
await createRole({ name: 'test' })
hook.adminRole.upsert.mockResolvedValue({ args: {}, failed: false, results: {} })
await createRole({ name: 'test' }, faker.string.uuid())

expect(prisma.adminRole.create).toHaveBeenCalledWith({ data: { name: 'test', permissions: 0n, position: 51 } })
})
Expand All @@ -78,7 +80,8 @@ describe('test admin-role business', () => {
prisma.adminRole.findFirst.mockResolvedValueOnce(null)
prisma.adminRole.findMany.mockResolvedValueOnce([dbRole])
prisma.adminRole.create.mockResolvedValue(dbRole)
await createRole({ name: 'test' })
hook.adminRole.upsert.mockResolvedValue({ args: {}, failed: false, results: {} })
await createRole({ name: 'test' }, faker.string.uuid())

expect(prisma.adminRole.create).toHaveBeenCalledWith({ data: { name: 'test', permissions: 0n, position: 0 } })
})
Expand Down Expand Up @@ -121,7 +124,8 @@ describe('test admin-role business', () => {
prisma.adminRole.findMany.mockResolvedValueOnce([])
prisma.adminRole.findUnique.mockResolvedValueOnce(dbRole)
prisma.adminRole.create.mockResolvedValue(dbRole)
await deleteRole(roleId)
hook.adminRole.delete.mockResolvedValue({ args: {}, failed: false, results: {} })
await deleteRole(roleId, faker.string.uuid())

expect(prisma.user.findMany).toHaveBeenCalledTimes(2)
expect(prisma.user.update).toHaveBeenNthCalledWith(1, { where: { id: users[0].id }, data: { adminRoleIds: [] } })
Expand All @@ -137,7 +141,7 @@ describe('test admin-role business', () => {
prisma.adminRole.findUnique.mockResolvedValue(systemRole as any)
prisma.user.findMany.mockResolvedValue([])

const response = await deleteRole(roleId)
const response = await deleteRole(roleId, faker.string.uuid())
expect(response).toBeInstanceOf(Forbidden403)
expect(prisma.adminRole.delete).not.toHaveBeenCalled()
})
Expand Down Expand Up @@ -223,15 +227,15 @@ describe('test admin-role business', () => {
name: 'New Admin Name',
}]

const result = await patchRoles(updateRoles)
const result = await patchRoles(updateRoles, faker.string.uuid())

await expect(result).toBeInstanceOf(Forbidden403)
expect(prisma.adminRole.update).toHaveBeenCalledTimes(0)
})

it('should do nothing', async () => {
prisma.adminRole.findMany.mockResolvedValue([])
await patchRoles([])
await patchRoles([], faker.string.uuid())
expect(prisma.adminRole.update).toHaveBeenCalledTimes(0)
})

Expand All @@ -242,7 +246,7 @@ describe('test admin-role business', () => {
]
prisma.adminRole.findMany.mockResolvedValue(dbRoles)

const response = await patchRoles(updateRoles)
const response = await patchRoles(updateRoles, faker.string.uuid())

expect(response).instanceOf(BadRequest400)
expect(prisma.adminRole.update).toHaveBeenCalledTimes(0)
Expand All @@ -253,7 +257,7 @@ describe('test admin-role business', () => {
]
prisma.adminRole.findMany.mockResolvedValue(dbRoles)

const response = await patchRoles(updateRoles)
const response = await patchRoles(updateRoles, faker.string.uuid())

expect(response).instanceOf(BadRequest400)
expect(prisma.adminRole.update).toHaveBeenCalledTimes(0)
Expand All @@ -264,19 +268,17 @@ describe('test admin-role business', () => {
{ id: dbRoles[1].id, position: 0 },
]
prisma.adminRole.findMany.mockResolvedValue(dbRoles)

await patchRoles(updateRoles)

hook.adminRole.upsert.mockResolvedValue({ args: {}, failed: false, results: {} })
await patchRoles(updateRoles, faker.string.uuid())
expect(prisma.adminRole.update).toHaveBeenCalledTimes(2)
})
it('should update permissions', async () => {
const updateRoles: (Pick<AdminRole, 'id'> & { permissions?: string })[] = [
{ id: dbRoles[1].id, permissions: '0' },
]
prisma.adminRole.findMany.mockResolvedValue(dbRoles)

await patchRoles(updateRoles)

hook.adminRole.upsert.mockResolvedValue({ args: {}, failed: false, results: {} })
await patchRoles(updateRoles, faker.string.uuid())
expect(prisma.adminRole.update).toHaveBeenCalledTimes(1)
expect(prisma.adminRole.update).toHaveBeenCalledWith({
data: {
Expand All @@ -302,7 +304,7 @@ describe('test admin-role business', () => {
}
prisma.adminRole.findMany.mockResolvedValue([systemRole as any])

const response = await patchRoles([{ id: systemRole.id, name: 'new name' }])
const response = await patchRoles([{ id: systemRole.id, name: 'new name' }], faker.string.uuid())
expect(response).toBeInstanceOf(Forbidden403)
expect(prisma.adminRole.update).not.toHaveBeenCalled()
})
Expand Down
29 changes: 19 additions & 10 deletions apps/server/src/resources/admin-role/business.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import type { Project, ProjectRole } from '@prisma/client'
import type { AdminRole, adminRoleContract } from '@cpn-console/shared'
import {
getAdminRoleById,
listAdminRoles,
} from '@/resources/queries-index.js'
import { addLogs, getAdminRoleById, listAdminRoles } from '@/resources/queries-index.js'
import type { ErrorResType } from '@/utils/errors.js'
import { BadRequest400, Forbidden403 } from '@/utils/errors.js'
import prisma from '@/prisma.js'
Expand All @@ -14,7 +11,10 @@ export async function listRoles() {
.then(roles => roles.map(role => ({ ...role, permissions: role.permissions.toString(), type: role.type ?? 'custom' })))
}

export async function patchRoles(roles: typeof adminRoleContract.patchAdminRoles.body._type): Promise<AdminRole[] | ErrorResType> {
export async function patchRoles(
roles: typeof adminRoleContract.patchAdminRoles.body._type,
requestId: string,
): Promise<AdminRole[] | ErrorResType> {
const dbRoles = await prisma.adminRole.findMany()
const positionsAvailable: number[] = []
const updatedRoles: (Omit<AdminRole, 'permissions'> & { permissions: bigint })[] = []
Expand Down Expand Up @@ -46,13 +46,17 @@ export async function patchRoles(roles: typeof adminRoleContract.patchAdminRoles
return new Forbidden403('Ce rôle système ne peut pas être renommé')
}
await prisma.adminRole.update({ where: { id }, data: role })
await hook.adminRole.upsert(id)
const hookReply = await hook.adminRole.upsert(id)
await addLogs({ action: 'Update Admin Role', data: hookReply, requestId })
}

return listRoles()
}

export async function createRole(role: typeof adminRoleContract.createAdminRole.body._type) {
export async function createRole(
role: typeof adminRoleContract.createAdminRole.body._type,
requestId: string,
) {
const dbMaxPosRole = (await prisma.adminRole.findFirst({
orderBy: { position: 'desc' },
select: { position: true },
Expand All @@ -66,7 +70,8 @@ export async function createRole(role: typeof adminRoleContract.createAdminRole.
},
})

await hook.adminRole.upsert(createdRole.id)
const hookReply = await hook.adminRole.upsert(createdRole.id)
await addLogs({ action: 'Create Admin Role', data: hookReply, requestId })

return listRoles()
}
Expand All @@ -87,11 +92,15 @@ export async function countRolesMembers() {
return rolesCounts
}

export async function deleteRole(roleId: Project['id']) {
export async function deleteRole(
roleId: Project['id'],
requestId: string,
) {
const role = await getAdminRoleById(roleId)
if (role) {
if (role.type === 'system') return new Forbidden403('Impossible de supprimer un rôle système')
await hook.adminRole.delete(role)
const hookReply = await hook.adminRole.delete(role)
await addLogs({ action: 'Delete Admin Role', data: hookReply, requestId })
}

const allUsers = await prisma.user.findMany({
Expand Down
8 changes: 4 additions & 4 deletions apps/server/src/resources/admin-role/router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('test adminRoleContract', () => {
.body(roleData)
.end()

expect(businessCreateRoleMock).toHaveBeenCalledWith(roleData)
expect(businessCreateRoleMock).toHaveBeenCalledWith(roleData, expect.any(String))
expect(response.json()).toEqual(newRole)
expect(response.statusCode).toEqual(201)
})
Expand Down Expand Up @@ -83,7 +83,7 @@ describe('test adminRoleContract', () => {
.body(rolesData)
.end()

expect(businessPatchRolesMock).toHaveBeenCalledWith(rolesData)
expect(businessPatchRolesMock).toHaveBeenCalledWith(rolesData, expect.any(String))
expect(response.json()).toEqual(updatedRoles)
expect(response.statusCode).toEqual(200)
})
Expand All @@ -99,7 +99,7 @@ describe('test adminRoleContract', () => {
.body(rolesData)
.end()

expect(businessPatchRolesMock).toHaveBeenCalledWith(rolesData)
expect(businessPatchRolesMock).toHaveBeenCalledWith(rolesData, expect.any(String))
expect(response.statusCode).toEqual(400)
})

Expand Down Expand Up @@ -161,7 +161,7 @@ describe('test adminRoleContract', () => {
.delete(adminRoleContract.deleteAdminRole.path.replace(':roleId', roleId))
.end()

expect(businessDeleteRoleMock).toHaveBeenCalledWith(roleId)
expect(businessDeleteRoleMock).toHaveBeenCalledWith(roleId, expect.any(String))
expect(response.statusCode).toEqual(204)
})

Expand Down
6 changes: 3 additions & 3 deletions apps/server/src/resources/admin-role/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function adminRoleRouter() {
const perms = await authUser(req)
if (!AdminAuthorized.isAdmin(perms.adminPermissions)) return new Forbidden403()

const resBody = await createRole(body)
const resBody = await createRole(body, req.id)

return {
status: 201,
Expand All @@ -38,7 +38,7 @@ export function adminRoleRouter() {
const perms = await authUser(req)
if (!AdminAuthorized.isAdmin(perms.adminPermissions)) return new Forbidden403()

const resBody = await patchRoles(body)
const resBody = await patchRoles(body, req.id)
if (resBody instanceof ErrorResType) return resBody

return {
Expand All @@ -63,7 +63,7 @@ export function adminRoleRouter() {
const perms = await authUser(req)
if (!AdminAuthorized.isAdmin(perms.adminPermissions)) return new Forbidden403()

const resBody = await deleteRole(params.roleId)
const resBody = await deleteRole(params.roleId, req.id)
if (resBody instanceof ErrorResType) return resBody

return {
Expand Down
3 changes: 0 additions & 3 deletions apps/server/src/resources/project-member/business.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,17 @@ export async function addMember(projectId: Project['id'], user: XOR<{ userId: st
}

await upsertMember({ projectId, userId: userInDb.id, roleIds: [] })
await hook.projectMember.upsert(projectId, userInDb.id)
return listMembers(projectId)
}

export async function patchMembers(projectId: Project['id'], members: typeof projectMemberContract.patchMembers.body._type) {
for (const member of members) {
await upsertMember({ projectId, userId: member.userId, roleIds: member.roles })
await hook.projectMember.upsert(projectId, member.userId)
}
return listMembers(projectId)
}

export async function removeMember(projectId: Project['id'], userId: User['id']) {
await hook.projectMember.delete(projectId, userId)
await deleteMember({ projectId, userId })
return listMembers(projectId)
}
10 changes: 3 additions & 7 deletions apps/server/src/resources/project-role/business.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
updateRole,
} from '@/resources/queries-index.js'
import { BadRequest400, Forbidden403, NotFound404 } from '@/utils/errors.js'
import { hook } from '@/utils/hook-wrapper.js'
import prisma from '@/prisma.js'

export async function listRoles(projectId: Project['id']) {
Expand Down Expand Up @@ -55,7 +54,6 @@ export async function patchRoles(projectId: Project['id'], roles: typeof project
if (positionsAvailable.length && positionsAvailable.length !== dbRoles.length) return new BadRequest400('Les numéros de position des rôles sont incohérentes')
for (const { id, ...role } of updatedRoles) {
await updateRole(id, role)
await hook.projectRole.upsert(id)
}

return listRoles(projectId)
Expand All @@ -78,7 +76,7 @@ export async function createRole(projectId: Project['id'], role: typeof projectR
throw new BadRequest400('oidcGroup doit commencer par /')
}

const createdRole = await prisma.projectRole.create({
await prisma.projectRole.create({
data: {
...role,
projectId,
Expand All @@ -88,8 +86,6 @@ export async function createRole(projectId: Project['id'], role: typeof projectR
},
})

await hook.projectRole.upsert(createdRole.id)

return listRoles(projectId)
}

Expand All @@ -107,10 +103,10 @@ export async function countRolesMembers(projectId: Project['id']) {

export async function deleteRole(roleId: Project['id']) {
const role = await prisma.projectRole.findUnique({ where: { id: roleId } })
if (role?.type === 'system') {
if (!role) throw new NotFound404()
if (role.type === 'system') {
return new Forbidden403('Ce rôle système ne peut pas être supprimé')
}
await hook.projectRole.delete(roleId)
await deleteRoleQuery(roleId)
return null
}
45 changes: 42 additions & 3 deletions 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 All @@ -218,7 +222,7 @@ describe('transformToHookProject', () => {
})))

// Assert sur la transformation des environnements
expect(result.environments).toEqual(project.environments.map(({ permissions: _, stage, quota, ...environment }) => ({
expect(result.environments).toEqual(project.environments.map(({ permissions: _, stage, quota, ...environment }: any) => ({
quota,
stage: stage.name,
permissions: [{ permissions: { rw: true, ro: true }, userId: project.ownerId }],
Expand All @@ -227,9 +231,44 @@ describe('transformToHookProject', () => {
})))

// Assert sur la transformation des repositories
expect(result.repositories).toEqual(project.repositories.map(repo => ({ ...repo, newCreds: mockReposCreds[repo.internalRepoName] })))
expect(result.repositories).toEqual(project.repositories.map((repo: any) => ({ ...repo, newCreds: mockReposCreds[repo.internalRepoName] })))

// Assert sur le store
expect(result.store).toEqual(mockStore)
})

it('handles members with roles correctly', () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion(non-blocker):

Suggested change
it('handles members with roles correctly', () => {
it('preserves project roles and members when creating hook', () => {

correctly n'a pas de sens intrinsèque.
Le test est justement là pour expliciter ce que correctly veut dire, dans ce contexte précis 😉

const roleDev = { id: 'role-dev', name: 'developer', permissions: 0n, position: 0 }
const memberDev = {
userId: 'user-dev',
roleIds: ['role-dev'],
user: { id: 'user-dev', firstName: 'Dev', lastName: 'User', email: 'dev@test.com', createdAt: new Date(), updatedAt: new Date(), adminRoleIds: [] },
id: 'member-dev',
projectId: project.id,
createdAt: new Date(),
updatedAt: new Date(),
}
const projectWithRoles = {
...project,
roles: [roleDev],
members: [memberDev],
}

// @ts-ignore - limited mock
const result = transformToHookProject(projectWithRoles, mockStore, mockReposCreds)

expect(result.roles).toContainEqual({
name: 'owner',
position: 0,
users: [project.owner],
})
expect(result.roles).toContainEqual({
name: 'developer',
permissions: '0',
position: 0,
type: undefined,
oidcGroup: undefined,
users: [memberDev.user],
})
})
})
Loading
Loading