feat: enhance session management by implementing edit permissions for team admins and updating session components to reflect new access controls

This commit is contained in:
Julien Froidefond
2026-02-17 14:20:40 +01:00
parent 5e9ae0936f
commit aad4b7f111
19 changed files with 333 additions and 90 deletions

View File

@@ -1,6 +1,6 @@
import { prisma } from '@/services/database';
import { resolveCollaborator } from '@/services/auth';
import { getTeamMemberIdsForAdminTeams } from '@/services/teams';
import { getTeamMemberIdsForAdminTeams, isAdminOfUser } from '@/services/teams';
import type { SwotCategory, ShareRole } from '@prisma/client';
// ============================================
@@ -111,6 +111,7 @@ export async function getTeamCollaboratorSessionsForAdmin(userId: string) {
isOwner: false as const,
role: 'VIEWER' as const,
isTeamCollab: true as const,
canEdit: true as const, // Admin has full rights on team member sessions
}));
return Promise.all(
@@ -122,8 +123,8 @@ export async function getTeamCollaboratorSessionsForAdmin(userId: string) {
}
export async function getSessionById(sessionId: string, userId: string) {
// Check if user owns the session OR has it shared
const session = await prisma.session.findFirst({
// Check if user owns the session, has it shared, or is team admin of owner
let session = await prisma.session.findFirst({
where: {
id: sessionId,
OR: [
@@ -154,13 +155,30 @@ export async function getSessionById(sessionId: string, userId: string) {
},
});
if (!session) return null;
if (!session) {
// Fallback: team admin viewing team member's session
const raw = await prisma.session.findUnique({
where: { id: sessionId },
include: {
user: { select: { id: true, name: true, email: true } },
items: { orderBy: { order: 'asc' } },
actions: {
include: { links: { include: { swotItem: true } } },
orderBy: { createdAt: 'asc' },
},
shares: { include: { user: { select: { id: true, name: true, email: true } } } },
},
});
if (!raw || !(await isAdminOfUser(raw.userId, userId))) return null;
session = raw;
}
// Determine user's role
const isOwner = session.userId === userId;
const share = session.shares.find((s) => s.userId === userId);
const isAdminOfOwner = !isOwner && !share && (await isAdminOfUser(session.userId, userId));
const role = isOwner ? ('OWNER' as const) : share?.role || ('VIEWER' as const);
const canEdit = isOwner || role === 'EDITOR';
const canEdit = isOwner || role === 'EDITOR' || isAdminOfOwner;
// Resolve collaborator to user if it's an email
const resolvedCollaborator = await resolveCollaborator(session.collaborator);
@@ -168,7 +186,7 @@ export async function getSessionById(sessionId: string, userId: string) {
return { ...session, isOwner, role, canEdit, resolvedCollaborator };
}
// Check if user can access session (owner or shared)
// Check if user can access session (owner, shared, or team admin of owner)
export async function canAccessSession(sessionId: string, userId: string) {
const count = await prisma.session.count({
where: {
@@ -176,10 +194,15 @@ export async function canAccessSession(sessionId: string, userId: string) {
OR: [{ userId }, { shares: { some: { userId } } }],
},
});
return count > 0;
if (count > 0) return true;
const session = await prisma.session.findUnique({
where: { id: sessionId },
select: { userId: true },
});
return session ? isAdminOfUser(session.userId, userId) : false;
}
// Check if user can edit session (owner or EDITOR role)
// Check if user can edit session (owner, EDITOR role, or team admin of owner)
export async function canEditSession(sessionId: string, userId: string) {
const count = await prisma.session.count({
where: {
@@ -187,7 +210,22 @@ export async function canEditSession(sessionId: string, userId: string) {
OR: [{ userId }, { shares: { some: { userId, role: 'EDITOR' } } }],
},
});
return count > 0;
if (count > 0) return true;
const session = await prisma.session.findUnique({
where: { id: sessionId },
select: { userId: true },
});
return session ? isAdminOfUser(session.userId, userId) : false;
}
// Check if user can delete session (owner or team admin only - NOT EDITOR)
export async function canDeleteSession(sessionId: string, userId: string) {
const session = await prisma.session.findUnique({
where: { id: sessionId },
select: { userId: true },
});
if (!session) return false;
return session.userId === userId || isAdminOfUser(session.userId, userId);
}
export async function createSession(userId: string, data: { title: string; collaborator: string }) {
@@ -204,15 +242,21 @@ export async function updateSession(
userId: string,
data: { title?: string; collaborator?: string }
) {
if (!(await canEditSession(sessionId, userId))) {
return { count: 0 };
}
return prisma.session.updateMany({
where: { id: sessionId, userId },
where: { id: sessionId },
data,
});
}
export async function deleteSession(sessionId: string, userId: string) {
if (!(await canDeleteSession(sessionId, userId))) {
return { count: 0 };
}
return prisma.session.deleteMany({
where: { id: sessionId, userId },
where: { id: sessionId },
});
}