fix(auth): keep workspace admin permissions on public projects

checkProjectAccess skipped the workspace membership lookup whenever access
was already granted another way — a PUBLIC project, or an existing project
membership — and only forced it for intents other than 'view'. The workspace
role does not just gate entry though; it feeds canEdit/isWorkspaceMember.

So a workspace ADMIN who is not the project owner lost canEdit the moment a
project was switched to public: the Add Version item on video cards, plus
canManageTags/canResolveComments/canRequestApproval/canShareVideo on the
video page, all disappeared, and the approvals endpoint returned 403. The
underlying POST routes use intent 'manage' and would still have accepted the
write, so the permission was there — only the UI was gone.

Resolve the workspace role for every signed-in non-owner. Owners already pass
every check on their own, so theirs is still only loaded when they mutate.
This commit is contained in:
yusufipk
2026-07-25 16:58:17 +07:00
parent a14eb9fb84
commit 60b2bc7369
+7 -3
View File
@@ -281,9 +281,13 @@ export async function checkProjectAccess(
const isProjectMember = !!projectMember;
const isProjectAdmin = projectMember?.role === ProjectMemberRole.ADMIN;
const needsWorkspaceForAccess = !!userId && !isOwner && !isProjectMember && !isPublic;
const needsWorkspaceForActions = !!userId && !isOwner && intent !== 'view';
const shouldLoadWorkspaceRole = needsWorkspaceForAccess || needsWorkspaceForActions;
// The workspace role decides `canEdit`/`isWorkspaceMember`, not just whether the viewer
// gets in at all, so it has to be resolved for every signed-in non-owner. Skipping it
// once access was already granted some other way (public project, or an existing project
// membership) silently downgraded workspace admins to read-only on `intent: 'view'` —
// the intent pages and GET routes use to decide which actions to render.
// Owners pass every check on their own; resolve their role only when they mutate.
const shouldLoadWorkspaceRole = !!userId && (!isOwner || intent !== 'view');
// Check workspace membership/role
let workspaceRole: WorkspaceMemberRole | 'OWNER' | null = null;