UNPKG

@speckle/shared

Version:

Shared code between various Speckle JS packages

194 lines 7.72 kB
import { err, ok } from 'true-myth/result'; import { ProjectNoAccessError, ProjectNotEnoughPermissionsError, ProjectNotFoundError, WorkspaceNoAccessError } from '../domain/authErrors.js'; import { Roles } from '../../core/constants.js'; import { isMinimumProjectRole } from '../domain/logic/roles.js'; import { hasMinimumProjectRole, isPubliclyReadableProject } from '../checks/projects.js'; import { ensureCanUseWorkspacePlanFeatureFragment, ensureWorkspaceRoleAndSessionFragment } from './workspaces.js'; import { checkIfAdminOverrideEnabledFragment, ensureMinimumServerRoleFragment } from './server.js'; import { ProjectVisibility } from '../domain/projects/types.js'; const workspaceRoleImplicitProjectRoleMap = (projectVisibility) => { const isFullyPrivate = projectVisibility === ProjectVisibility.Private; return { [Roles.Workspace.Admin]: Roles.Stream.Owner, [Roles.Workspace.Member]: isFullyPrivate ? null : Roles.Stream.Reviewer, [Roles.Workspace.Guest]: null }; }; /** * Ensure user has a minimum explicit or implicit project role */ export const ensureMinimumProjectRoleFragment = (loaders) => async ({ userId, projectId, role, explicit }) => { const requiredProjectRole = role || Roles.Stream.Reviewer; const isTestingForMinimumAccess = requiredProjectRole === Roles.Stream.Reviewer; const env = await loaders.getEnv(); const project = await loaders.getProject({ projectId }); if (!project) return err(new ProjectNotFoundError()); // Check for explicit project role first const hasExplicitProjectRole = await hasMinimumProjectRole(loaders)({ userId, projectId, role: requiredProjectRole }); if (hasExplicitProjectRole) return ok(); // Now check if there's an implicit one const { workspaceId } = project; if (env.FF_WORKSPACES_MODULE_ENABLED && !!workspaceId) { // Check for implicit workspace project role const userWorkspaceRole = await loaders.getWorkspaceRole({ userId, workspaceId }); if (userWorkspaceRole) { const implicitProjectRole = explicit ? null : workspaceRoleImplicitProjectRoleMap(project.visibility)[userWorkspaceRole]; if (implicitProjectRole) { // Does it fit minimum? if (isMinimumProjectRole(implicitProjectRole, requiredProjectRole)) { return ok(); } else { // Have some permissions, but not enough return err(new ProjectNotEnoughPermissionsError()); } } } } // Do we have any role at all? const anyRoleFound = await loaders.getProjectRole({ userId, projectId }); return err(isTestingForMinimumAccess || !anyRoleFound ? new ProjectNoAccessError() : new ProjectNotEnoughPermissionsError()); }; /** * Ensure user has access to the project's workspace (has role & SSO session, if any), if it has one */ export const ensureProjectWorkspaceAccessFragment = (loaders) => async ({ userId, projectId }) => { const env = await loaders.getEnv(); const project = await loaders.getProject({ projectId }); if (!project) return err(new ProjectNotFoundError()); const { workspaceId } = project; if (!workspaceId || !env.FF_WORKSPACES_MODULE_ENABLED) return ok(); const memberWithSsoSession = await ensureWorkspaceRoleAndSessionFragment(loaders)({ userId, workspaceId }); if (memberWithSsoSession.isErr) { switch (memberWithSsoSession.error.code) { case WorkspaceNoAccessError.code: return err(new WorkspaceNoAccessError("You do not have access to this project's workspace")); default: return err(memberWithSsoSession.error); } } return memberWithSsoSession; }; /** * Check if project is publicly readable or not */ export const checkIfPubliclyReadableProjectFragment = (loaders) => async ({ projectId }) => { const project = await loaders.getProject({ projectId }); if (!project) return err(new ProjectNotFoundError()); return ok(await isPubliclyReadableProject(loaders)({ projectId })); }; /** * Ensure user has implicit/explicit project membership and read access */ export const ensureImplicitProjectMemberWithReadAccessFragment = (loaders) => async ({ userId, projectId, role }) => { // Ensure user is authed const ensuredServerRole = await ensureMinimumServerRoleFragment(loaders)({ userId, role: Roles.Server.Guest }); if (ensuredServerRole.isErr) { return err(ensuredServerRole.error); } // Check if user has admin override enabled const isAdminOverrideEnabled = await checkIfAdminOverrideEnabledFragment(loaders)({ userId }); if (isAdminOverrideEnabled.isErr) { return err(isAdminOverrideEnabled.error); } if (isAdminOverrideEnabled.value) return ok(); // And ensure (implicit/explicit) project role const ensuredProjectRole = await ensureMinimumProjectRoleFragment(loaders)({ userId: userId, projectId, role }); if (ensuredProjectRole.isErr) { return err(ensuredProjectRole.error); } // No god mode, ensure workspace access const ensuredWorkspaceAccess = await ensureProjectWorkspaceAccessFragment(loaders)({ userId: userId, projectId }); if (ensuredWorkspaceAccess.isErr) { return err(ensuredWorkspaceAccess.error); } return ok(); }; /** * Ensure user has implicit/explicit project membership and write access */ export const ensureImplicitProjectMemberWithWriteAccessFragment = (loaders) => async ({ userId, projectId, role }) => { const requiredProjectRole = role || Roles.Stream.Contributor; const requiredServerRole = requiredProjectRole === Roles.Stream.Owner ? Roles.Server.User : Roles.Server.Guest; // Ensure user is authed const ensuredServerRole = await ensureMinimumServerRoleFragment(loaders)({ userId, role: requiredServerRole }); if (ensuredServerRole.isErr) { return err(ensuredServerRole.error); } // And ensure (implicit/explicit) project role const ensuredProjectRole = await ensureMinimumProjectRoleFragment(loaders)({ userId: userId, projectId, role: requiredProjectRole }); if (ensuredProjectRole.isErr) { return err(ensuredProjectRole.error); } // Ensure workspace access const ensuredWorkspaceAccess = await ensureProjectWorkspaceAccessFragment(loaders)({ userId: userId, projectId }); if (ensuredWorkspaceAccess.isErr) { return err(ensuredWorkspaceAccess.error); } return ok(); }; /** * Ensure project is workspaced and has access to a specific plan feature */ export const ensureCanUseProjectWorkspacePlanFeatureFragment = (loaders) => async ({ projectId, feature, allowUnworkspaced = false }) => { const project = await loaders.getProject({ projectId }); if (!project) return err(new ProjectNotFoundError()); const workspaceId = project.workspaceId; if (!workspaceId) { if (allowUnworkspaced) return ok(); return err(new WorkspaceNoAccessError({ message: 'The project must be in a workspace' })); } const canUseFeature = await ensureCanUseWorkspacePlanFeatureFragment(loaders)({ workspaceId, feature }); if (canUseFeature.isErr) return err(canUseFeature.error); return ok(); }; //# sourceMappingURL=projects.js.map