mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-29 16:17:39 +09:00
Closes https://github.com/webstudio-is/webstudio/issues/3991 Adds HTTP Basic Authentication support for published pages and project-level `.wsauth` rules. This introduces a new `@webstudio-is/wsauth` package that owns the generic authentication format logic: parsing, validation, serialization, route matching, resource generation, and request authentication. The current supported method is `basic`, with the package structured so additional auth methods can be added later. Builder changes include: - Page Settings now has an `Authentication` section for per-page Basic Auth credentials. - Project Settings now has an `Authentication` section for editing `.wsauth` route rules. - Page Settings was split into section-specific files with focused tests. - Auth UI shows Pro badges/info tooltips but remains editable. - Publishing to custom domains is blocked on plans without `allowAuth` when either page auth or project auth is configured. - ProBadge now renders as a real pricing link and uses improved text alignment. Build/publish changes include: - CLI prebuild generates and merges `.wsauth` entries. - Published app templates enforce auth for HTML/XML routes. - Generated auth resources are shared between build output and runtime.
79 lines
3.2 KiB
TypeScript
79 lines
3.2 KiB
TypeScript
import type { Role } from "@webstudio-is/project";
|
|
import type { AuthPermit } from "@webstudio-is/trpc-interface/index.server";
|
|
import type { PlanFeatures } from "@webstudio-is/plans";
|
|
|
|
/**
|
|
* Human-readable descriptions of what each workspace role can do.
|
|
* Mirrors the role-based permission logic in getPermissions().
|
|
*/
|
|
export const roleDescriptions: Record<Role, string> = {
|
|
viewers: "Can view, copy instances, and clone the project.",
|
|
editors: "Can edit content only — text, images, and predefined components.",
|
|
builders: "Can make any design changes and publish to staging only.",
|
|
administrators: "Can make any design changes and publish to custom domains.",
|
|
};
|
|
|
|
/**
|
|
* Single source of truth for all user permissions. Combines workspace
|
|
* role-based access with subscription plan features.
|
|
*
|
|
* Role-based permission mapping (mirrors backend checks):
|
|
* own → all actions
|
|
* administrators → create, duplicate, rename, tags, transfer
|
|
* builders → create, duplicate, rename, tags
|
|
* editors → rename, tags
|
|
* viewers → open in safe mode only (handled by caller)
|
|
*/
|
|
export const getPermissions = ({
|
|
role,
|
|
planFeatures,
|
|
authPermit,
|
|
workspaces,
|
|
}: {
|
|
role: Role | "own";
|
|
planFeatures: PlanFeatures;
|
|
authPermit?: AuthPermit;
|
|
workspaces: Array<{ role: Role | "own" }>;
|
|
}) => {
|
|
const isOwn = role === "own";
|
|
const isAdmin = role === "administrators";
|
|
const isBuilder = role === "builders" || isAdmin;
|
|
const isEditor = role === "editors" || isBuilder;
|
|
// Content mode is always available in shared projects (non-owner access)
|
|
// because content mode is a safe/restricted editing mode.
|
|
// authPermit is server-validated, so we trust it here.
|
|
const isSharedProject = authPermit !== undefined && authPermit !== "own";
|
|
|
|
return {
|
|
// Workspace role permissions
|
|
canCreateProject: isOwn || isBuilder,
|
|
canDuplicate: isOwn || isBuilder,
|
|
canRename: isOwn || isEditor,
|
|
canShare: isOwn,
|
|
canDelete: isOwn,
|
|
canTransfer: isOwn || isAdmin,
|
|
canEditTags: isOwn || isEditor,
|
|
canOpenSettings: isOwn,
|
|
// Builders (but not admins/owners) can publish to staging only.
|
|
// Share-link users with "build" permit have the same restriction.
|
|
canPublishToStagingOnly:
|
|
(isBuilder && !isAdmin && !isOwn) || authPermit === "build",
|
|
// Plan feature permissions
|
|
canDownloadAssets: planFeatures.canDownloadAssets,
|
|
canRestoreBackups: planFeatures.canRestoreBackups,
|
|
allowDynamicData: planFeatures.allowDynamicData,
|
|
allowAuth: planFeatures.allowAuth,
|
|
allowContentMode: isSharedProject || planFeatures.allowContentMode,
|
|
allowStagingPublish: planFeatures.allowStagingPublish,
|
|
allowAdditionalPermissions: planFeatures.allowAdditionalPermissions,
|
|
maxContactEmailsPerProject: planFeatures.maxContactEmailsPerProject,
|
|
maxDomainsAllowedPerUser: planFeatures.maxDomainsAllowedPerUser,
|
|
maxDailyPublishesPerUser: planFeatures.maxDailyPublishesPerUser,
|
|
maxWorkspaces: planFeatures.maxWorkspaces,
|
|
canInviteMembers: planFeatures.maxWorkspaces > 1,
|
|
canCreateWorkspace:
|
|
workspaces.filter((w) => w.role === "own").length <
|
|
planFeatures.maxWorkspaces,
|
|
};
|
|
};
|