From 2db8f394841ec5de16ded504a7231e181af7ceaf Mon Sep 17 00:00:00 2001 From: "Cornelius A. Ludmann" Date: 2025年10月17日 10:42:46 +0000 Subject: [PATCH] [CLC-2032] Fix Classic PAYG sunset bypass via websocket API The original sunset implementation only added checks to the new gRPC API (WorkspaceServiceAPI) but missed the legacy websocket API (GitpodServerImpl). This allowed users to bypass the sunset blocking through: - Gitpod CLI/Local App (uses experimental/v1 API) - JetBrains Gateway (uses websocket API directly) - Public API with Personal Access Tokens - Dashboard when feature flag is disabled This fix adds the sunset check to both startWorkspace() and createWorkspace() methods in GitpodServerImpl, using the same isWorkspaceStartBlockedBySunset() function that's already used in WorkspaceServiceAPI. The check: - Blocks installation-owned users (no organizationId) - Blocks users in non-exempted organizations - Exempts dedicated installations - Exempts organizations in the exemptedOrganizations list Co-authored-by: Ona --- .../src/workspace/gitpod-server-impl.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index f0dfb1ff04b231..f505688cf9d0a5 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -139,6 +139,7 @@ import { getPrimaryEmail } from "@gitpod/public-api-common/lib/user-utils"; import { AnalyticsController } from "../analytics-controller"; import { ClientHeaderFields } from "../express-util"; import { filter } from "../util/objects"; +import { isWorkspaceStartBlockedBySunset } from "../util/featureflags"; // shortcut export const traceWI = (ctx: TraceContext, wi: Omit) => TraceContext.setOWI(ctx, wi); // userId is already taken care of in WebsocketConnectionManager @@ -581,6 +582,16 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { const { workspace, latestInstance: instance } = await this.workspaceService.getWorkspace(user.id, workspaceId); await this.guardAccess({ kind: "workspace", subject: workspace }, "get"); + // Check if user is blocked by Classic PAYG sunset + if ( + await isWorkspaceStartBlockedBySunset(user, workspace.organizationId, this.config.isDedicatedInstallation) + ) { + throw new ApplicationError( + ErrorCodes.PERMISSION_DENIED, + "Gitpod Classic PAYG has sunset. Please visit https://app.ona.com/login to continue.", + ); + } + // (gpl) We keep this check here for backwards compatibility, it should be superfluous in the future if (instance && instance.status.phase !== "stopped") { traceWI(ctx, { instanceId: instance.id }); @@ -850,6 +861,16 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { logContext = { userId: user.id }; + // Check if user is blocked by Classic PAYG sunset + if ( + await isWorkspaceStartBlockedBySunset(user, options.organizationId, this.config.isDedicatedInstallation) + ) { + throw new ApplicationError( + ErrorCodes.PERMISSION_DENIED, + "Gitpod Classic PAYG has sunset. Please visit https://app.ona.com/login to continue.", + ); + } + normalizedContextUrl = this.contextParser.normalizeContextURL(contextUrl); const { context, project } = await this.contextService.parseContext(user, contextUrl, {

AltStyle によって変換されたページ (->オリジナル) /