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
12 changes: 10 additions & 2 deletions apps/client/src/components/AdminRoleForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ const props = withDefaults(defineProps<{
permissions: bigint
name: string
oidcGroup: string
type: string
}>(), {
name: 'Nouveau rôle',
oidcGroup: '',
type: 'custom',
})
const emits = defineEmits<{
delete: []
save: [{ name: string, permissions: string, oidcGroup: string }]
save: [{ name: string, permissions: string, oidcGroup: string, type: string }]
cancel: []
}>()
const usersStore = useUsersStore()
Expand All @@ -33,6 +35,8 @@ const isUpdated = computed(() => {
return !shallowEqual(props, role.value)
})

const isSystem = computed(() => props.type === 'system')

const errorSchema = computed<SharedZodError | undefined>(() => {
const schemaValidation = RoleSchema.partial().safeParse(role.value)
return schemaValidation.success ? undefined : schemaValidation.error
Expand Down Expand Up @@ -139,6 +143,7 @@ function closeModal() {
label-visible
hint="Ne doit pas dépasser 30 caractères."
class="mb-5"
:disabled="isSystem"
/>
<p
class="fr-h6"
Expand All @@ -163,7 +168,7 @@ function closeModal() {
:label="perm.label"
:hint="perm?.hint"
:name="perm.key"
:disabled="role.permissions & ADMIN_PERMS.MANAGE && perm.key !== 'MANAGE'"
:disabled="isSystem || (role.permissions & ADMIN_PERMS.MANAGE && perm.key !== 'MANAGE')"
@update:model-value="(checked: boolean) => updateChecked(checked, perm.key)"
/>
</div>
Expand All @@ -174,8 +179,10 @@ function closeModal() {
label-visible
placeholder="/admin"
class="mb-5"
:disabled="isSystem"
/>
<DsfrButton
v-if="!isSystem"
data-testid="saveBtn"
label="Enregistrer"
secondary
Expand All @@ -184,6 +191,7 @@ function closeModal() {
@click="$emit('save', { ...role, permissions: role.permissions.toString() })"
/>
<DsfrButton
v-if="!isSystem"
data-testid="deleteBtn"
label="Supprimer"
secondary
Expand Down
25 changes: 20 additions & 5 deletions apps/client/src/components/ProjectRoleForm.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
import type { Member, ProjectV2, RoleBigint } from '@cpn-console/shared'
import type { Member, ProjectRoleBigint, ProjectV2 } from '@cpn-console/shared'
import { PROJECT_PERMS, projectPermsDetails, shallowEqual } from '@cpn-console/shared'

const props = defineProps<{
Expand All @@ -10,25 +10,32 @@ const props = defineProps<{
allMembers: Member[]
projectId: ProjectV2['id']
isEveryone: boolean
oidcGroup?: string
type?: string
}>()

defineEmits<{
delete: []
updateMemberRoles: [checked: boolean, userId: Member['userId']]
save: [value: Omit<RoleBigint, 'position'>]
save: [value: Omit<ProjectRoleBigint, 'position' | 'projectId'>]
cancel: []
}>()
const router = useRouter()
const role = ref({
...props,
permissions: props.permissions ?? 0n,
allMembers: props.allMembers ?? [],
oidcGroup: props.oidcGroup ?? '',
type: props.type ?? 'custom',
})

const isUpdated = computed(() => {
if (role.value.isEveryone) return props.permissions !== role.value.permissions
return !shallowEqual(props, role.value)
})

const isSystem = computed(() => props.type === 'system')

const tabListName = 'Liste d’onglet'
const tabTitles = [
{ title: 'Général', icon: 'ri:checkbox-circle-line', tabId: 'general' },
Expand Down Expand Up @@ -66,7 +73,15 @@ function updateChecked(checked: boolean, value: bigint) {
data-testid="roleNameInput"
label-visible
class="mb-5"
:disabled="role.isEveryone"
:disabled="role.isEveryone || isSystem"
/>
<h6>Groupe OIDC</h6>
<DsfrInput
v-model="role.oidcGroup"
data-testid="roleOidcGroupInput"
label-visible
class="mb-5"
:disabled="role.isEveryone || isSystem"
/>
<h6>Permissions</h6>
<div
Expand All @@ -87,7 +102,7 @@ function updateChecked(checked: boolean, value: bigint) {
:label="perm?.label"
:hint="perm?.hint"
:name="perm.key"
:disabled="role.permissions & PROJECT_PERMS.MANAGE && perm.key !== 'MANAGE'"
:disabled="(role.permissions & PROJECT_PERMS.MANAGE && perm.key !== 'MANAGE') || role.type === 'system'"
@update:model-value="(checked: boolean) => updateChecked(checked, PROJECT_PERMS[perm.key])"
/>
</div>
Expand All @@ -100,7 +115,7 @@ function updateChecked(checked: boolean, value: bigint) {
@click="$emit('save', role)"
/>
<DsfrButton
v-if="!role.isEveryone"
v-if="!role.isEveryone && role.type !== 'system'"
data-testid="deleteBtn"
label="Supprimer"
secondary
Expand Down
10 changes: 7 additions & 3 deletions apps/client/src/components/ProjectRoles.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import type { Member, Role, RoleBigint } from '@cpn-console/shared'
import type { Member, ProjectRole, ProjectRoleBigint, Role, RoleBigint } from '@cpn-console/shared'

Check notice on line 2 in apps/client/src/components/ProjectRoles.vue

View check run for this annotation

cloud-pi-native-sonarqube / SonarQube Code Analysis

apps/client/src/components/ProjectRoles.vue#L2

Remove this unused import of 'RoleBigint'.
import { useSnackbarStore } from '@/stores/snackbar.js'
import type { Project } from '@/utils/project-utils.js'

Expand All @@ -11,7 +11,7 @@

const selectedId = ref<string>()

type RoleItem = Omit<Role, 'permissions'> & { permissions: bigint, memberCounts: number, isEveryone: boolean }
type RoleItem = Omit<ProjectRole, 'permissions'> & { permissions: bigint, memberCounts: number, isEveryone: boolean }

const roleList = ref<RoleItem[]>([])

Expand Down Expand Up @@ -56,7 +56,7 @@
snackbarStore.setMessage('Rôle mis à jour', 'success')
}

async function saveRole(role: Omit<RoleBigint, 'position'>) {
async function saveRole(role: Omit<ProjectRoleBigint, 'position' | 'projectId'>) {
if (role.id === 'everyone') {
await saveEveryoneRole(role)
snackbarStore.setMessage('Rôle mis à jour', 'success')
Expand All @@ -67,6 +67,7 @@
id: selectedRole.value.id,
permissions: role.permissions.toString(),
name: role.name,
oidcGroup: role.oidcGroup,
}])
reload()
snackbarStore.setMessage('Rôle mis à jour', 'success')
Expand All @@ -86,6 +87,7 @@
permissions: BigInt(props.project.everyonePerms),
position: 1000,
isEveryone: true,
projectId: props.project.id,
})
roleList.value = roles
}
Expand Down Expand Up @@ -142,6 +144,8 @@
:permissions="BigInt(selectedRole.permissions)"
:project-id="project.id"
:is-everyone="selectedRole.isEveryone"
:oidc-group="selectedRole.oidcGroup"
:type="selectedRole.type"
:all-members="project.members"
@delete="deleteRole(selectedRole.id)"
@update-member-roles="(checked: boolean, userId: Member['userId']) => updateMember(checked, userId)"
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/utils/project-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class Project implements ProjectV2 {
locked: boolean
owner: Omit<User, 'adminRoleIds'>
ownerId: string
roles: { id: string, name: string, permissions: string, position: number }[]
roles: { id: string, name: string, permissions: string, position: number, projectId: string, oidcGroup?: string, type?: string }[]
members: ({ userId: string, firstName: string, lastName: string, email: string, roleIds: string[] } | { updatedAt: string, createdAt: string, firstName: string, lastName: string, email: string, userId: string, roleIds: string[] })[]
createdAt: string
updatedAt: string
Expand Down
6 changes: 4 additions & 2 deletions apps/client/src/views/admin/AdminRoles.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ async function deleteRole(roleId: Role['id']) {
selectedId.value = undefined
}

async function saveRole(role: Pick<AdminRole, 'name' | 'oidcGroup' | 'permissions'>) {
async function saveRole(role: Pick<AdminRole, 'name' | 'oidcGroup' | 'permissions' | 'type'>) {
if (!selectedRole.value) return
await adminRoleStore.patchRoles(
[{
id: selectedRole.value.id,
permissions: role.permissions.toString(),
name: role.name,
oidcGroup: role.oidcGroup,
type: role.type,
}],
)
snackbarStore.setMessage('Rôle mis à jour', 'success')
Expand Down Expand Up @@ -117,8 +118,9 @@ onBeforeMount(async () => {
:name="selectedRole.name"
:permissions="BigInt(selectedRole.permissions)"
:oidc-group="selectedRole.oidcGroup"
:type="selectedRole.type"
@delete="deleteRole(selectedRole.id)"
@save="(role: Pick<AdminRole, 'name' | 'oidcGroup' | 'permissions'>) => saveRole(role)"
@save="(role: Pick<AdminRole, 'name' | 'oidcGroup' | 'permissions' | 'type'>) => saveRole(role)"
@cancel="() => cancel()"
/>
</div>
Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/__mocks__/utils/hook-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const hook = {
delete: vi.fn(),
getSecrets: vi.fn(),
},
projectRole: {
upsert: vi.fn(),
delete: vi.fn(),
},
user: {
retrieveUserByEmail: vi.fn(),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "ProjectRole" ADD COLUMN "oidcGroup" TEXT NOT NULL DEFAULT '';
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- AlterTable
ALTER TABLE "AdminRole" ADD COLUMN "type" TEXT NOT NULL DEFAULT 'custom';

-- AlterTable
ALTER TABLE "ProjectRole" ADD COLUMN "type" TEXT NOT NULL DEFAULT 'custom';

-- Update AdminRole system roles
UPDATE "AdminRole" SET "type" = 'system' WHERE "name" IN ('Admin', 'Admin Locaux');

-- Update ProjectRole system roles
UPDATE "ProjectRole" SET "type" = 'system' WHERE "name" IN ('Administrateur', 'DevOps', 'Développeur', 'Lecture seule');
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- Update existing Admin role to be system role 'Administrateur Plateforme'
UPDATE "AdminRole"
SET
"name" = 'Administrateur Plateforme',
"type" = 'system',
"permissions" = 3, -- Assuming 3n means bit 0 and 1 (1 | 2 = 3)
"oidcGroup" = '/admin',
"position" = 0
WHERE "id" = '76229c96-4716-45bc-99da-00498ec9018c'::uuid;

-- Insert 'Lecture Seule Plateforme' system role if it doesn't exist
INSERT INTO "AdminRole" ("id", "name", "permissions", "position", "oidcGroup", "type")
VALUES (
'35848aa2-e881-4770-9844-0c5c3693e506'::uuid,
'Lecture Seule Plateforme',
1, -- Assuming 1n means bit 0
2,
'/readonly',
'system'
)
ON CONFLICT ("id") DO UPDATE
SET
"name" = 'Lecture Seule Plateforme',
"type" = 'system',
"permissions" = 1,
"oidcGroup" = '/readonly';
2 changes: 1 addition & 1 deletion apps/server/src/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
provider = "postgresql"
1 change: 1 addition & 0 deletions apps/server/src/prisma/schema/admin.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ model AdminRole {
permissions BigInt
position Int @db.SmallInt
oidcGroup String @default("")
type String @default("custom")
}

model SystemSetting {
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/prisma/schema/project.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ model ProjectRole {
permissions BigInt
projectId String @db.Uuid
position Int @db.SmallInt
oidcGroup String @default("")
type String @default("custom")
project Project @relation(fields: [projectId], references: [id])
}

Expand Down
Loading
Loading