Skip to content

Commit

Permalink
Merge pull request #317 from incubateur-ademe/feat/modify-user-role
Browse files Browse the repository at this point in the history
feat: modify user role
  • Loading branch information
rtaieb authored Dec 26, 2024
2 parents f243042 + a203dfe commit bd56a03
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/actions/userProjet/delete-user-from-projet-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const deleteUserFromProjetAction = async (
if (!session) {
return { type: "error", message: "UNAUTHENTICATED", updatedProjet: null };
}
const canUpdateUserRole = await new PermissionManager(session).canUpdateUserRole(userId, projectId);
const canUpdateUserRole = await new PermissionManager(session).canModifiyUserRole(userId, projectId);

if (!canUpdateUserRole) {
return { type: "error", message: "UNAUTHORIZED", updatedProjet: null };
Expand Down
31 changes: 17 additions & 14 deletions src/actions/users/update-user-role-project-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,41 @@ import { auth } from "@/src/lib/next-auth/auth";
import { ResponseAction } from "../actions-types";
import { customCaptureException } from "@/src/lib/sentry/sentryCustomMessage";
import { RoleProjet } from "@prisma/client";
import { UserProjetWithUser } from "@/src/lib/prisma/prismaCustomTypes";
import { ProjetWithRelations } from "@/src/lib/prisma/prismaCustomTypes";
import { updateUserRoleProject } from "@/src/lib/prisma/prisma-user-projet-queries";
import { PermissionManager } from "@/src/helpers/permission-manager";

export const updateUserRoleProjectAction = async (
userId: string,
projectId: number,
role: RoleProjet,
): Promise<ResponseAction<{ member: UserProjetWithUser | null }>> => {
): Promise<ResponseAction<{ projet: ProjetWithRelations | null }>> => {
const session = await auth();

// On garde ce code au cas où on autorise un jour la modification d'un rôle d'un utilisateur.
if (!session || session) {
return { type: "error", message: "UNAUTHENTICATED", member: null };
if (!session) {
return { type: "error", message: "UNAUTHENTICATED", projet: null };
}

// const permission = new PermissionManager(session);
// const canUpdateUserRole = await permission.canUpdateUserRole(session?.user.id, userId, projectId);
//
// if (!canUpdateUserRole) {
// return { type: "error", message: "UNAUTHORIZED", member: null };
// }
const canUpdateUserRole = await new PermissionManager(session).canModifiyUserRole(userId, projectId);

if (!canUpdateUserRole) {
return { type: "error", message: "UNAUTHORIZED", projet: null };
}

if (role === RoleProjet.ADMIN) {
return { type: "error", message: "ADMIN_ROLE_NOT_ALLOWED", projet: null };
}

try {
const member = await updateUserRoleProject(userId, projectId, role);
const projet = await updateUserRoleProject(userId, projectId, role);

return {
type: "success",
message: "ROLE_UPDATED",
member,
projet,
};
} catch (e) {
customCaptureException("Error in updating user role DB call", e);
return { type: "error", message: "TECHNICAL_ERROR", member: null };
return { type: "error", message: "TECHNICAL_ERROR", projet: null };
}
};
2 changes: 2 additions & 0 deletions src/components/modal/modal-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { AvailableProjetsForCollectiviteModal } from "../liste-projets/available
import { ViewerModeModal } from "../tableau-de-bord/viewer-mode-modal";
// eslint-disable-next-line max-len
import { SourcingRexContentSeeProjetModal } from "@/src/components/sourcing/side-panel/sourcing-rex-content-see-projet-modal";
import { PartageMemberModificationRoleModale } from "../partage/partage-member-modification-role-modale";

export default function ModalProvider() {
return (
<>
<PartageMemberModificationRoleModale />
<EstimationMateriauModalContainer />
<AideFicheModal />
<PartageOverviewDeleteOrQuitModale />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const modal = createModal({
export const PartageMemberModificationRoleModale = () => {
const userId = useUserStore((state) => state.userInfos?.id);
const projetId = useProjetsStore((state) => state.getCurrentProjet()?.id);
const addOrUpdateProjet = useProjetsStore((state) => state.addOrUpdateProjet);
const currentUserModification = useModalStore((state) => state.currentUserModification);
const setCurrentUserModification = useModalStore((state) => state.setCurrentUserModification);
const form = useForm<PartageUserModificationData>({
Expand Down Expand Up @@ -57,6 +58,9 @@ export const PartageMemberModificationRoleModale = () => {
data.role as RoleProjet,
);
notifications(result.type, result.message);
if (result.type === "success" && result.projet) {
addOrUpdateProjet(result.projet);
}
}
};

Expand Down Expand Up @@ -96,9 +100,8 @@ export const PartageMemberModificationRoleModale = () => {
path="role"
label=""
options={[
{ name: "Admin", value: RoleProjet.ADMIN },
{ name: "Editeur", value: RoleProjet.EDITEUR },
{ name: "Lecteur", value: RoleProjet.LECTEUR },
{ name: "Editeur", value: RoleProjet.EDITEUR },
]}
/>
<div className="flex justify-between">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { notifications } from "../common/notifications";
import { PopupMenu } from "../common/popup-menu";
import { useProjetsStore } from "@/src/stores/projets/provider";
import { useCanEditProjet } from "@/src/hooks/use-can-edit-projet";
import { RoleProjet } from "@prisma/client";

export type PartageOverviewMemberStatusAdminProps = {
member: UserProjetWithUser;
Expand All @@ -17,9 +18,16 @@ export const PartageOverviewMemberStatusAcceptedAdmin = (props: PartageOverviewM
const currentProjet = useProjetsStore((state) => state.getCurrentProjet());
const setCurrentDeleteOrQuitModal = useModalStore((state) => state.setCurrentDeleteOrQuitModal);
const addOrUpdateProjet = useProjetsStore((state) => state.addOrUpdateProjet);
const setCurrentUserModification = useModalStore((state) => state.setCurrentUserModification);
const canEditProjet = useCanEditProjet(currentProjet?.id);

const links = [
{
label: "Modifier les accès",
iconId: "ri-pencil-fill",
className: "text-dsfr-text-label-blue-france",
onClick: () => setCurrentUserModification(props),
},
{
label: "Supprimer le membre",
iconId: "ri-delete-bin-fill",
Expand Down Expand Up @@ -53,7 +61,7 @@ export const PartageOverviewMemberStatusAcceptedAdmin = (props: PartageOverviewM
<i className="ri-checkbox-circle-fill mr-2 size-6 text-dsfr-background-action-high-success-hover"></i>
activé
</div>
{!props.isCurrentUser && canEditProjet && <PopupMenu links={links} />}
{!props.isCurrentUser && props.member.role !== RoleProjet.ADMIN && canEditProjet && <PopupMenu links={links} />}
</div>
);
};
1 change: 1 addition & 0 deletions src/helpers/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ export const error = {
INVITATION_NOT_FOUND: "Ce lien d'invitation a déjà été utilisé ou n'est pas valide.",
INVITATION_NOT_FOR_EMAIL: "Cette invitation ne correspond pas à votre email.",
ALREADY_SUBSCRIBED_NEWSLETTER: "Cette adresse email est déjà abonnée à notre newsletter.",
ADMIN_ROLE_NOT_ALLOWED: "Vous ne pouvez pas ajouter le rôle administrateur.",
};
13 changes: 10 additions & 3 deletions src/helpers/permission-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,21 @@ export class PermissionManager {
return this.authenticatedUserId === userId;
}

async canUpdateUserRole(targetUserId: string, projectId: number) {
async canModifiyUserRole(targetUserId: string, projectId: number) {
if (!this.authenticatedUserId) {
return false;
}

if (!(await this.isAdmin(projectId))) {
if (!(await this.canEditProject(projectId))) {
return false;
} else if (this.authenticatedUserId !== targetUserId) {
}

const targetUserRole = await getUserProjet(targetUserId, projectId);
if (targetUserRole?.role === RoleProjet.ADMIN) {
return false;
}

if (this.authenticatedUserId !== targetUserId) {
return true;
} else {
return await this.checkOtherAdminsExist(projectId, targetUserId);
Expand Down
10 changes: 5 additions & 5 deletions src/lib/prisma/prisma-user-projet-queries.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { InvitationStatus, RoleProjet, User, user_projet } from "@prisma/client";
import { prismaClient } from "@/src/lib/prisma/prismaClient";
import { UserProjetWithRelations, UserProjetWithUser } from "@/src/lib/prisma/prismaCustomTypes";
import { projetPublicSelect, projetUpdated } from "@/src/lib/prisma/prismaProjetQueries";
import { ProjetWithRelations, UserProjetWithRelations, UserProjetWithUser } from "@/src/lib/prisma/prismaCustomTypes";
import { projetIncludes, projetPublicSelect, projetUpdated } from "@/src/lib/prisma/prismaProjetQueries";

export const getUserProjet = async (userId: string, projectId: number): Promise<user_projet | null> => {
return prismaClient.user_projet.findUnique({
Expand Down Expand Up @@ -111,7 +111,7 @@ export const updateUserRoleProject = async (
userId: string,
projectId: number,
newRole: RoleProjet,
): Promise<UserProjetWithUser | null> => {
): Promise<ProjetWithRelations | null> => {
const response = await prismaClient.user_projet.update({
where: {
user_id_projet_id: {
Expand All @@ -124,13 +124,13 @@ export const updateUserRoleProject = async (
role: newRole,
},
include: {
user: true,
projet: { include: projetIncludes },
},
});

await projetUpdated(projectId);

return response;
return response.projet;
};

export const deleteUserFromProject = async (
Expand Down

0 comments on commit bd56a03

Please sign in to comment.