From f0235daf8640209f8b35f06792ce864c494ce4ed Mon Sep 17 00:00:00 2001 From: ArnaudTa <33383276+ArnaudTA@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:52:43 +0200 Subject: [PATCH] feat: :sparkles: introduce fine grained perms and and roles --- .../components/specs/permission-form.ct.ts | 150 ---- apps/client/src/App.vue | 7 + apps/client/src/api/xhr-client.ts | 2 +- apps/client/src/components/AdminRoleForm.vue | 223 +++++ apps/client/src/components/ClusterForm.vue | 5 +- .../client/src/components/EnvironmentForm.vue | 22 +- apps/client/src/components/PermissionForm.vue | 178 ---- .../client/src/components/ProjectRoleForm.vue | 149 ++++ apps/client/src/components/QuotaForm.vue | 6 +- apps/client/src/components/RangeInput.vue | 5 +- apps/client/src/components/RepoForm.vue | 25 +- apps/client/src/components/SideMenu.vue | 79 +- apps/client/src/components/StageForm.vue | 9 +- .../client/src/components/SuggestionInput.vue | 21 +- apps/client/src/components/TeamCt.vue | 174 ++-- apps/client/src/icons.ts | 1 + apps/client/src/router/index.ts | 29 +- apps/client/src/stores/admin-role.ts | 20 + apps/client/src/stores/ci-files.ts | 12 - apps/client/src/stores/project-user.ts | 28 +- apps/client/src/stores/project.ts | 14 +- apps/client/src/stores/user.ts | 28 +- apps/client/src/views/UserProfile.vue | 62 ++ apps/client/src/views/admin/AdminRoles.vue | 117 +++ apps/client/src/views/admin/ListProjects.vue | 33 +- apps/client/src/views/admin/ListUser.vue | 45 +- .../src/views/projects/DsoDashboard.vue | 36 +- apps/client/src/views/projects/DsoRepos.vue | 19 +- apps/client/src/views/projects/DsoRoles.vue | 162 ++++ apps/client/src/views/projects/DsoTeam.vue | 21 +- .../src/views/projects/ManageEnvironments.vue | 14 +- apps/server/Dockerfile | 1 + apps/server/package.json | 2 +- apps/server/src/app.ts | 22 +- .../src/generate-files/controllers.spec.ts | 86 -- apps/server/src/generate-files/router.ts | 61 -- .../src/generate-files/templates/docker.yml | 25 - .../generate-files/templates/gitlab-ci.yml | 106 --- .../src/generate-files/templates/java.yml | 30 - .../src/generate-files/templates/node.yml | 25 - .../src/generate-files/templates/python.yml | 11 - .../src/generate-files/templates/rules.yml | 6 - .../src/generate-files/templates/vault.yml | 24 - apps/server/src/init/db/index.ts | 19 +- apps/server/src/prepare-app.ts | 1 + .../20240723135420_dso/migration.sql | 120 +++ .../20240725162050_dso/migration.sql | 5 + .../20240726210139_dso/migration.sql | 14 + .../20240826143230_dso/migration.sql | 3 + apps/server/src/prisma/schema.prisma | 257 +++--- .../src/resources/admin-role/business.ts | 72 ++ .../src/resources/admin-role/queries.ts | 29 + .../server/src/resources/admin-role/router.ts | 75 ++ apps/server/src/resources/cluster/business.ts | 268 +++--- apps/server/src/resources/cluster/queries.ts | 22 +- apps/server/src/resources/cluster/router.ts | 67 +- .../src/resources/environment/business.ts | 318 +++----- .../src/resources/environment/queries.ts | 49 +- .../src/resources/environment/router.ts | 98 ++- apps/server/src/resources/index.ts | 39 +- apps/server/src/resources/log/queries.ts | 2 - apps/server/src/resources/log/router.ts | 27 +- .../src/resources/organization/business.ts | 21 +- .../src/resources/organization/queries.ts | 2 - .../src/resources/organization/router.ts | 59 +- .../src/resources/permission/business.ts | 104 --- .../resources/permission/controllers.spec.ts | 201 ----- .../src/resources/permission/queries.ts | 61 -- .../server/src/resources/permission/router.ts | 79 -- .../src/resources/project-member/business.ts | 56 ++ .../src/resources/project-member/queries.ts | 30 + .../src/resources/project-member/router.ts | 77 ++ .../src/resources/project-role/business.ts | 73 ++ .../src/resources/project-role/queries.ts | 51 ++ .../src/resources/project-role/router.ts | 91 +++ .../src/resources/project-service/business.ts | 34 +- .../src/resources/project-service/router.ts | 31 +- apps/server/src/resources/project/business.ts | 315 +++---- apps/server/src/resources/project/queries.ts | 192 +---- apps/server/src/resources/project/router.ts | 166 ++-- apps/server/src/resources/queries-index.ts | 7 +- apps/server/src/resources/quota/business.ts | 142 ++-- apps/server/src/resources/quota/queries.ts | 12 +- apps/server/src/resources/quota/router.ts | 91 +-- .../src/resources/repository/business.ts | 176 ++-- .../src/resources/repository/queries.ts | 3 - .../server/src/resources/repository/router.ts | 104 ++- .../src/resources/service-monitor/router.ts | 8 +- apps/server/src/resources/stage/business.ts | 157 ++-- apps/server/src/resources/stage/queries.ts | 10 +- apps/server/src/resources/stage/router.ts | 91 +-- .../src/resources/system/config/business.ts | 16 +- .../src/resources/system/config/router.ts | 39 +- .../src/resources/system/db/business.ts | 3 - .../src/resources/system/db/controllers.ts | 19 - .../server/src/resources/system/db/queries.ts | 45 - apps/server/src/resources/user/business.ts | 207 ++--- apps/server/src/resources/user/queries.ts | 25 +- .../server/src/resources/user/role-queries.ts | 80 -- apps/server/src/resources/user/router.ts | 174 +--- apps/server/src/resources/zone/business.ts | 11 +- apps/server/src/resources/zone/queries.ts | 2 - apps/server/src/resources/zone/router.ts | 40 +- apps/server/src/utils/business.ts | 4 +- apps/server/src/utils/controller.ts | 186 +++-- apps/server/src/utils/errors.ts | 54 -- apps/server/src/utils/hook-wrapper.ts | 32 +- apps/server/src/utils/logger.ts | 7 +- apps/server/tsconfig.json | 3 +- packages/hooks/src/hooks/hook-project.ts | 6 +- packages/shared/src/api-client.ts | 4 +- packages/shared/src/contracts/admin-role.ts | 68 ++ packages/shared/src/contracts/files.ts | 33 - packages/shared/src/contracts/index.ts | 4 +- .../shared/src/contracts/project-member.ts | 63 ++ packages/shared/src/contracts/project-role.ts | 75 ++ packages/shared/src/contracts/project.ts | 1 + packages/shared/src/contracts/user.ts | 99 +-- packages/shared/src/schemas/environment.ts | 1 + packages/shared/src/schemas/index.ts | 1 + packages/shared/src/schemas/project.ts | 11 +- packages/shared/src/schemas/role.ts | 21 + packages/shared/src/schemas/user.ts | 68 +- packages/shared/src/schemas/utils.ts | 14 +- packages/shared/src/utils/functions.ts | 17 + packages/shared/src/utils/index.ts | 1 + packages/shared/src/utils/permissions.ts | 177 ++++ packages/shared/src/utils/schemas.spec.ts | 34 +- packages/test-utils/src/imports/data.ts | 770 +++++++----------- plugins/gitlab/src/repositories.ts | 3 +- plugins/gitlab/src/utils.ts | 17 - plugins/kubernetes/src/class.ts | 8 +- plugins/nexus/src/project.ts | 3 +- 133 files changed, 4002 insertions(+), 4468 deletions(-) delete mode 100644 apps/client/cypress/components/specs/permission-form.ct.ts create mode 100644 apps/client/src/components/AdminRoleForm.vue delete mode 100644 apps/client/src/components/PermissionForm.vue create mode 100644 apps/client/src/components/ProjectRoleForm.vue create mode 100644 apps/client/src/stores/admin-role.ts delete mode 100644 apps/client/src/stores/ci-files.ts create mode 100644 apps/client/src/views/UserProfile.vue create mode 100644 apps/client/src/views/admin/AdminRoles.vue create mode 100644 apps/client/src/views/projects/DsoRoles.vue delete mode 100644 apps/server/src/generate-files/controllers.spec.ts delete mode 100644 apps/server/src/generate-files/router.ts delete mode 100644 apps/server/src/generate-files/templates/docker.yml delete mode 100644 apps/server/src/generate-files/templates/gitlab-ci.yml delete mode 100644 apps/server/src/generate-files/templates/java.yml delete mode 100644 apps/server/src/generate-files/templates/node.yml delete mode 100644 apps/server/src/generate-files/templates/python.yml delete mode 100644 apps/server/src/generate-files/templates/rules.yml delete mode 100644 apps/server/src/generate-files/templates/vault.yml create mode 100644 apps/server/src/prisma/migrations/20240723135420_dso/migration.sql create mode 100644 apps/server/src/prisma/migrations/20240725162050_dso/migration.sql create mode 100644 apps/server/src/prisma/migrations/20240726210139_dso/migration.sql create mode 100644 apps/server/src/prisma/migrations/20240826143230_dso/migration.sql create mode 100644 apps/server/src/resources/admin-role/business.ts create mode 100644 apps/server/src/resources/admin-role/queries.ts create mode 100644 apps/server/src/resources/admin-role/router.ts delete mode 100644 apps/server/src/resources/permission/business.ts delete mode 100644 apps/server/src/resources/permission/controllers.spec.ts delete mode 100644 apps/server/src/resources/permission/queries.ts delete mode 100644 apps/server/src/resources/permission/router.ts create mode 100644 apps/server/src/resources/project-member/business.ts create mode 100644 apps/server/src/resources/project-member/queries.ts create mode 100644 apps/server/src/resources/project-member/router.ts create mode 100644 apps/server/src/resources/project-role/business.ts create mode 100644 apps/server/src/resources/project-role/queries.ts create mode 100644 apps/server/src/resources/project-role/router.ts delete mode 100644 apps/server/src/resources/system/db/business.ts delete mode 100644 apps/server/src/resources/system/db/controllers.ts delete mode 100644 apps/server/src/resources/system/db/queries.ts delete mode 100644 apps/server/src/resources/user/role-queries.ts create mode 100644 packages/shared/src/contracts/admin-role.ts delete mode 100644 packages/shared/src/contracts/files.ts create mode 100644 packages/shared/src/contracts/project-member.ts create mode 100644 packages/shared/src/contracts/project-role.ts create mode 100644 packages/shared/src/schemas/role.ts create mode 100644 packages/shared/src/utils/permissions.ts diff --git a/apps/client/cypress/components/specs/permission-form.ct.ts b/apps/client/cypress/components/specs/permission-form.ct.ts deleted file mode 100644 index 9996290bd..000000000 --- a/apps/client/cypress/components/specs/permission-form.ct.ts +++ /dev/null @@ -1,150 +0,0 @@ -import '@gouvminint/vue-dsfr/styles' -import '@gouvfr/dsfr/dist/dsfr.min.css' -import '@gouvfr/dsfr/dist/utility/icons/icons.min.css' -import '@gouvfr/dsfr/dist/utility/utility.main.min.css' -import '@/main.css' -import PermissionForm from '@/components/PermissionForm.vue' -import { createRandomDbSetup, getRandomUser, getRandomMember } from '@cpn-console/test-utils' -import { useProjectStore } from '@/stores/project.js' -import { useUserStore } from '@/stores/user.js' -import { Pinia, createPinia, setActivePinia } from 'pinia' -import { useUsersStore } from '@/stores/users.js' - -describe('PermissionForm.vue', () => { - let pinia: Pinia - - beforeEach(() => { - pinia = createPinia() - - setActivePinia(pinia) - }) - - it('Should mount a PermissionForm with users to licence', () => { - const randomDbSetup = createRandomDbSetup({ nbUsers: 3, envs: ['dev'] }) - const projectStore = useProjectStore() - const userStore = useUserStore() - const usersStore = useUsersStore() - - let userToLicence = getRandomUser() - userToLicence = { ...getRandomMember(userToLicence.id), ...userToLicence } - randomDbSetup.project.members = [...randomDbSetup.project.members, userToLicence] - usersStore.users = randomDbSetup.users.reduce((acc, curr) => { - return { ...acc, [curr.id]: curr } - }, {}) - usersStore.users[userToLicence.userId] = userToLicence - - projectStore.selectedProject = randomDbSetup.project - const owner = randomDbSetup.project.roles?.find(role => role.role === 'owner')?.user - userStore.userProfile = randomDbSetup.users[1] - - const environment = projectStore.selectedProject?.environments[0] - const ownerPermission = environment.permissions.find(permission => permission.user.email === owner.email) - const userPermission = environment.permissions.find(permission => permission.user.email !== owner.email) - - const props = { - environment, - } - - cy.mount(PermissionForm, { props }) - - cy.getByDataTestid('permissionsFieldset') - .should('contain', `Droits des utilisateurs sur l'environnement ${props.environment.name}`) - cy.get('li') - .should('have.length', props.environment.permissions.length) - - cy.getByDataTestid(`userPermissionLi-${ownerPermission.user.email}`) - .within(() => { - cy.getByDataTestid('userEmail') - .should('contain', ownerPermission.user.email) - .get('input#range') - .should('have.value', ownerPermission.level) - .and('be.disabled') - .getByDataTestid('deletePermissionBtn') - .should('have.attr', 'title', 'Les droits du owner ne peuvent être supprimés') - .and('be.disabled') - .get('[data-testid$=UpsertPermissionBtn]') - .should('be.disabled') - .and('have.attr', 'title', 'Les droits du owner ne peuvent être inférieurs à rwd') - }) - - cy.getByDataTestid(`userPermissionLi-${userPermission.user.email}`) - .within(() => { - cy.getByDataTestid('userEmail') - .should('contain', userPermission.user.email) - .get('input#range') - .should('have.value', userPermission.level) - .and('be.enabled') - .getByDataTestid('deletePermissionBtn') - .should('have.attr', 'title', `Supprimer les droits de ${userPermission.user.email}`) - .and('be.enabled') - .get('[data-testid$=UpsertPermissionBtn]') - .should('have.attr', 'title', `Modifier les droits de ${userPermission.user.email}`) - }) - cy.getByDataTestid('newPermissionFieldset') - .should('contain', 'Accréditer un membre du projet') - .within(() => { - cy.get('label') - .should('contain', `E-mail de l'utilisateur à accréditer sur l'environnement ${props.environment.name}`) - cy.get('.fr-hint-text') - .should('contain', `Entrez l'e-mail d'un membre du projet ${projectStore.selectedProject.name}. Ex : ${userToLicence.email}`) - cy.get('datalist#suggestionList') - .find('option') - .should('have.length', projectStore.selectedProject.members.length - props.environment.permissions.length) - .should('have.value', userToLicence.email) - }) - }) - it('Should mount a PermissionForm with no user to licence', () => { - const randomDbSetup = createRandomDbSetup({ nbUsers: 3, envs: ['dev'] }) - const projectStore = useProjectStore() - const userStore = useUserStore() - const usersStore = useUsersStore() - - usersStore.users = randomDbSetup.users.reduce((acc, curr) => { - return { ...acc, [curr.id]: curr } - }, {}) - projectStore.selectedProject = randomDbSetup.project - userStore.userProfile = randomDbSetup.users[1] - - const environment = projectStore.selectedProject?.environments[0] - - const props = { - environment, - } - - cy.mount(PermissionForm, { props }) - - cy.getByDataTestid('newPermissionFieldset') - .should('contain', 'Accréditer un membre du projet') - .within(() => { - cy.get('label') - .should('contain', `E-mail de l'utilisateur à accréditer sur l'environnement ${props.environment.name}`) - cy.get('.fr-hint-text') - .should('contain', `Tous les membres du projet ${projectStore.selectedProject.name} sont déjà accrédités.`) - }) - }) - - it('Should mount a PermissionForm without permission for current user', () => { - const randomDbSetup = createRandomDbSetup({ nbUsers: 3, envs: ['dev'] }) - const projectStore = useProjectStore() - const userStore = useUserStore() - const usersStore = useUsersStore() - - usersStore.users = randomDbSetup.users.reduce((acc, curr) => { - return { ...acc, [curr.id]: curr } - }, {}) - - projectStore.selectedProject = randomDbSetup.project - userStore.userProfile = getRandomUser() - - const environment = projectStore.selectedProject?.environments[0] - - const props = { - environment, - } - - cy.mount(PermissionForm, { props }) - - cy.getByDataTestid('notPermittedAlert') - .should('contain', `Vous n'avez aucun droit sur l'environnement ${props.environment.name}. Un membre possédant des droits sur cet environnement peut vous accréditer.`) - }) -}) diff --git a/apps/client/src/App.vue b/apps/client/src/App.vue index 80d5ad09c..ff11458f1 100644 --- a/apps/client/src/App.vue +++ b/apps/client/src/App.vue @@ -3,6 +3,7 @@ import { apiPrefix } from '@cpn-console/shared' import { getKeycloak } from './utils/keycloak/keycloak.js' import { useUserStore } from './stores/user.js' import { useSnackbarStore } from './stores/snackbar.js' +import router from './router/index.js' const keycloak = getKeycloak() const userStore = useUserStore() @@ -44,6 +45,12 @@ watch(label, (label: string) => { quickLinks.value[0].label = label }) +userStore.$subscribe(() => { + if (router.currentRoute.value.fullPath.startsWith('/admin') && userStore.adminPerms === 0n) { + window.location.pathname = '/' + } +}) +