@speckle/shared
Version:
Shared code between various Speckle JS packages
239 lines • 11.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureCanUseWorkspacePlanFeatureFragment = exports.ensureUserIsWorkspaceAdminFragment = exports.ensureModelCanBeCreatedFragment = exports.ensureWorkspaceProjectCanBeCreatedFragment = exports.ensureWorkspaceNotReadOnlyFragment = exports.ensureWorkspacesEnabledFragment = exports.ensureWorkspaceRoleAndSessionFragment = void 0;
const result_1 = require("true-myth/result");
const workspaceRole_js_1 = require("../checks/workspaceRole.js");
const authErrors_js_1 = require("../domain/authErrors.js");
const constants_js_1 = require("../../core/constants.js");
const plans_js_1 = require("../../workspaces/helpers/plans.js");
const workspaceSeat_js_1 = require("../checks/workspaceSeat.js");
const server_js_1 = require("./server.js");
const features_js_1 = require("../../workspaces/helpers/features.js");
/**
* Ensure user has a workspace role, and a valid SSO session (if SSO is configured)
*/
const ensureWorkspaceRoleAndSessionFragment = (loaders) => async ({ userId, workspaceId, role }) => {
const testedRole = role ?? constants_js_1.Roles.Workspace.Guest;
const testingForMinimumRole = testedRole === constants_js_1.Roles.Workspace.Guest;
// Get workspace, so we can resolve its slug for error scenarios
const workspace = await loaders.getWorkspace({ workspaceId });
// hides the fact, that the workspace does not exist
if (!workspace)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
const hasMinimumRole = await (0, workspaceRole_js_1.hasMinimumWorkspaceRole)(loaders)({
userId,
workspaceId,
role: testedRole
});
if (!hasMinimumRole)
return (0, result_1.err)(testingForMinimumRole
? new authErrors_js_1.WorkspaceNoAccessError()
: new authErrors_js_1.WorkspaceNotEnoughPermissionsError());
const hasMinimumMemberRole = await (0, workspaceRole_js_1.hasMinimumWorkspaceRole)(loaders)({
userId,
workspaceId,
role: 'workspace:member'
});
// only members and above need to use sso
if (!hasMinimumMemberRole)
return (0, result_1.ok)();
const workspaceSsoProvider = await loaders.getWorkspaceSsoProvider({
workspaceId
});
if (!workspaceSsoProvider)
return (0, result_1.ok)();
const workspaceSsoSession = await loaders.getWorkspaceSsoSession({
userId,
workspaceId
});
if (!workspaceSsoSession)
return (0, result_1.err)(new authErrors_js_1.WorkspaceSsoSessionNoAccessError({
payload: { workspaceSlug: workspace.slug }
}));
const isExpiredSession = new Date().getTime() > workspaceSsoSession.validUntil.getTime();
if (isExpiredSession)
return (0, result_1.err)(new authErrors_js_1.WorkspaceSsoSessionNoAccessError({
payload: { workspaceSlug: workspace.slug }
}));
return (0, result_1.ok)();
};
exports.ensureWorkspaceRoleAndSessionFragment = ensureWorkspaceRoleAndSessionFragment;
/**
* Ensure the workspaces module is enabled
*/
const ensureWorkspacesEnabledFragment = (loaders) => async () => {
const env = await loaders.getEnv();
if (!env.FF_WORKSPACES_MODULE_ENABLED)
return (0, result_1.err)(new authErrors_js_1.WorkspacesNotEnabledError());
return (0, result_1.ok)();
};
exports.ensureWorkspacesEnabledFragment = ensureWorkspacesEnabledFragment;
/**
* Ensure workspace is not read-only
*/
const ensureWorkspaceNotReadOnlyFragment = (loaders) => async ({ workspaceId }) => {
const workspacePlan = await loaders.getWorkspacePlan({ workspaceId });
if (!workspacePlan)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
if ((0, plans_js_1.isWorkspacePlanStatusReadOnly)(workspacePlan.status))
return (0, result_1.err)(new authErrors_js_1.WorkspaceReadOnlyError());
return (0, result_1.ok)();
};
exports.ensureWorkspaceNotReadOnlyFragment = ensureWorkspaceNotReadOnlyFragment;
/**
* Ensure workspace can accept new project (not read-only, limits not reached).
* If userId is specified, will also check for user role & seat
*/
const ensureWorkspaceProjectCanBeCreatedFragment = (loaders) => async ({ workspaceId, userId }) => {
// First check user even has access
if (userId) {
// Is Member+
const isNotGuest = await (0, workspaceRole_js_1.hasMinimumWorkspaceRole)(loaders)({
userId,
workspaceId,
role: constants_js_1.Roles.Workspace.Member
});
if (!isNotGuest) {
return (0, result_1.err)(new authErrors_js_1.WorkspaceNotEnoughPermissionsError('Guests cannot create projects in the workspace'));
}
}
const ensuredNotReadOnly = await (0, exports.ensureWorkspaceNotReadOnlyFragment)(loaders)({
workspaceId
});
if (ensuredNotReadOnly.isErr)
return (0, result_1.err)(ensuredNotReadOnly.error);
const workspacePlan = await loaders.getWorkspacePlan({ workspaceId });
if (!workspacePlan)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
// Now check editor seat
if (userId) {
const isEditor = await (0, workspaceSeat_js_1.hasEditorSeat)(loaders)({
userId,
workspaceId
});
if (!isEditor)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoEditorSeatError());
}
const workspaceLimits = await loaders.getWorkspaceLimits({ workspaceId });
if (!workspaceLimits)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
// no limits imposed
if (workspaceLimits.projectCount === null)
return (0, result_1.ok)();
const currentProjectCount = await loaders.getWorkspaceProjectCount({
workspaceId
});
// this will not happen in practice
if (currentProjectCount === null)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
return currentProjectCount < workspaceLimits.projectCount
? (0, result_1.ok)()
: (0, result_1.err)(new authErrors_js_1.WorkspaceLimitsReachedError({
message: 'You have reached the maximum number of projects for your plan. Upgrade to increase it.',
payload: { limit: 'projectCount' }
}));
};
exports.ensureWorkspaceProjectCanBeCreatedFragment = ensureWorkspaceProjectCanBeCreatedFragment;
/**
* Ensure model can be created (workspace not read-only, limits not reached).
* If userId is specified, will also check for appropriate user role & seat
*/
const ensureModelCanBeCreatedFragment = (loaders) => async ({ projectId, userId, addedModelCount, workspaceId }) => {
addedModelCount = addedModelCount ?? 1;
const { FF_WORKSPACES_MODULE_ENABLED, FF_PERSONAL_PROJECTS_LIMITS_ENABLED } = await loaders.getEnv();
const project = await loaders.getProject({ projectId });
if (!project)
return (0, result_1.err)(new authErrors_js_1.ProjectNotFoundError());
// Project may not be attached to a workspace yet, then we use the specified workspaceId
workspaceId = workspaceId || project.workspaceId || undefined;
// If workspace
if (workspaceId && FF_WORKSPACES_MODULE_ENABLED) {
if (userId) {
// Has workspace role
const isInWorkspace = await (0, workspaceRole_js_1.hasAnyWorkspaceRole)(loaders)({
userId,
workspaceId
});
if (!isInWorkspace) {
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
}
}
const ensuredNotReadOnly = await (0, exports.ensureWorkspaceNotReadOnlyFragment)(loaders)({
workspaceId
});
if (ensuredNotReadOnly.isErr)
return (0, result_1.err)(ensuredNotReadOnly.error);
const workspacePlan = await loaders.getWorkspacePlan({ workspaceId });
if (!workspacePlan)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
const workspaceLimits = await loaders.getWorkspaceLimits({ workspaceId });
if (!workspaceLimits)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
if (workspaceLimits.modelCount === null)
return (0, result_1.ok)();
const currentModelCount = await loaders.getWorkspaceModelCount({ workspaceId });
if (currentModelCount === null)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
return currentModelCount + addedModelCount <= workspaceLimits.modelCount
? (0, result_1.ok)()
: (0, result_1.err)(new authErrors_js_1.WorkspaceLimitsReachedError({
message: 'You have reached the maximum number of models for your plan. Upgrade to increase it.',
payload: {
limit: 'modelCount'
}
}));
}
else {
// If not - check personal project limits
if (FF_PERSONAL_PROJECTS_LIMITS_ENABLED) {
return (0, result_1.err)(new authErrors_js_1.PersonalProjectsLimitedError('No new models can be added to personal projects'));
}
return (0, result_1.ok)();
}
};
exports.ensureModelCanBeCreatedFragment = ensureModelCanBeCreatedFragment;
const ensureUserIsWorkspaceAdminFragment = (loaders) => async ({ userId, workspaceId }) => {
const ensuredWorkspacesEnabled = await (0, exports.ensureWorkspacesEnabledFragment)(loaders)({});
if (ensuredWorkspacesEnabled.isErr)
return (0, result_1.err)(ensuredWorkspacesEnabled.error);
const ensuredServerRole = await (0, server_js_1.ensureMinimumServerRoleFragment)(loaders)({
userId,
role: constants_js_1.Roles.Server.User
});
if (ensuredServerRole.isErr)
return (0, result_1.err)(ensuredServerRole.error);
const ensuredWorkspaceAccess = await (0, exports.ensureWorkspaceRoleAndSessionFragment)(loaders)({
userId: userId,
workspaceId,
role: constants_js_1.Roles.Workspace.Admin
});
if (ensuredWorkspaceAccess.isErr)
return (0, result_1.err)(ensuredWorkspaceAccess.error);
return (0, result_1.ok)();
};
exports.ensureUserIsWorkspaceAdminFragment = ensureUserIsWorkspaceAdminFragment;
/**
* Check if workspace has access to a specific feature
*/
const ensureCanUseWorkspacePlanFeatureFragment = (loaders) => async ({ workspaceId, feature }) => {
const ensuredWorkspacesEnabled = await (0, exports.ensureWorkspacesEnabledFragment)(loaders)({});
if (ensuredWorkspacesEnabled.isErr)
return (0, result_1.err)(ensuredWorkspacesEnabled.error);
const ensuredNotReadOnly = await (0, exports.ensureWorkspaceNotReadOnlyFragment)(loaders)({
workspaceId
});
if (ensuredNotReadOnly.isErr)
return (0, result_1.err)(ensuredNotReadOnly.error);
const workspacePlan = await loaders.getWorkspacePlan({ workspaceId });
if (!workspacePlan)
return (0, result_1.err)(new authErrors_js_1.WorkspaceNoAccessError());
const featureFlags = await loaders.getEnv();
const canUseFeature = (0, features_js_1.workspacePlanHasAccessToFeature)({
plan: workspacePlan.name,
feature,
featureFlags
});
return canUseFeature ? (0, result_1.ok)() : (0, result_1.err)(new authErrors_js_1.WorkspacePlanNoFeatureAccessError());
};
exports.ensureCanUseWorkspacePlanFeatureFragment = ensureCanUseWorkspacePlanFeatureFragment;
//# sourceMappingURL=workspaces.js.map