diff --git a/components/gitpod-protocol/src/gitpod-service.ts b/components/gitpod-protocol/src/gitpod-service.ts index 244a48c42c0bd4..b02a41cc8d54df 100644 --- a/components/gitpod-protocol/src/gitpod-service.ts +++ b/components/gitpod-protocol/src/gitpod-service.ts @@ -63,6 +63,7 @@ import { InstallationAdminSettings, TelemetryData } from "./installation-admin-p import { Currency } from "./plans"; import { BillableSession, BillableSessionRequest } from "./usage"; import { SupportedWorkspaceClass } from "./workspace-class"; +import { BillingMode } from "./billing-mode"; export interface GitpodClient { onInstanceUpdate(instance: WorkspaceInstance): void; @@ -298,6 +299,9 @@ export interface GitpodServer extends JsonRpcServer, AdminServer, setUsageAttribution(usageAttribution: string): Promise; + getBillingModeForUser(): Promise; + getBillingModeForTeam(teamId: string): Promise; + /** * Analytics */ diff --git a/components/server/ee/src/workspace/gitpod-server-impl.ts b/components/server/ee/src/workspace/gitpod-server-impl.ts index ffd77cd2be3a18..0223b1e3686935 100644 --- a/components/server/ee/src/workspace/gitpod-server-impl.ts +++ b/components/server/ee/src/workspace/gitpod-server-impl.ts @@ -6,7 +6,7 @@ import { injectable, inject } from "inversify"; import { GitpodServerImpl, traceAPIParams, traceWI, censor } from "../../../src/workspace/gitpod-server-impl"; -import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; +import { TraceContext, TraceContextWithSpan } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { GitpodServer, GitpodClient, @@ -106,12 +106,14 @@ import { ClientMetadata, traceClientMetadata } from "../../../src/websocket/webs import { BitbucketAppSupport } from "../bitbucket/bitbucket-app-support"; import { URL } from "url"; import { UserCounter } from "../user/user-counter"; -import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server"; import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution"; import { CachingUsageServiceClientProvider } from "@gitpod/usage-api/lib/usage/v1/sugar"; import * as usage from "@gitpod/usage-api/lib/usage/v1/usage_pb"; import { Timestamp } from "google-protobuf/google/protobuf/timestamp_pb"; import { EntitlementService } from "../../../src/billing/entitlement-service"; +import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode"; +import { BillingModes } from "../billing/billing-mode"; +import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server"; @injectable() export class GitpodServerEEImpl extends GitpodServerImpl { @@ -157,6 +159,8 @@ export class GitpodServerEEImpl extends GitpodServerImpl { @inject(CostCenterDB) protected readonly costCenterDB: CostCenterDB; @inject(EntitlementService) protected readonly entitlementService: EntitlementService; + @inject(BillingModes) protected readonly billingModes: BillingModes; + initialize( client: GitpodClient | undefined, user: User | undefined, @@ -2190,6 +2194,22 @@ export class GitpodServerEEImpl extends GitpodServerImpl { }; } + async getBillingModeForUser(ctx: TraceContextWithSpan): Promise { + traceAPIParams(ctx, {}); + + const user = this.checkUser("getBillingModeForUser"); + return this.billingModes.getBillingModeForUser(user, new Date()); + } + + async getBillingModeForTeam(ctx: TraceContextWithSpan, teamId: string): Promise { + traceAPIParams(ctx, { teamId }); + + this.checkAndBlockUser("getBillingModeForTeam"); + const team = await this.guardTeamOperation(teamId, "get"); + + return this.billingModes.getBillingModeForTeam(team, new Date()); + } + // (SaaS) – admin async adminGetAccountStatement(ctx: TraceContext, userId: string): Promise { traceAPIParams(ctx, { userId }); diff --git a/components/server/src/auth/rate-limiter.ts b/components/server/src/auth/rate-limiter.ts index b4bb8e3bff1391..43fa7b8059ca1d 100644 --- a/components/server/src/auth/rate-limiter.ts +++ b/components/server/src/auth/rate-limiter.ts @@ -211,6 +211,9 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig { subscribeTeamToStripe: { group: "default", points: 1 }, getStripePortalUrlForTeam: { group: "default", points: 1 }, listBilledUsage: { group: "default", points: 1 }, + getBillingModeForTeam: { group: "default", points: 1 }, + getBillingModeForUser: { group: "default", points: 1 }, + trackEvent: { group: "default", points: 1 }, trackLocation: { group: "default", points: 1 }, identifyUser: { group: "default", points: 1 }, diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index ccfdef991b3390..f57d84eb766e29 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -108,7 +108,11 @@ import { } from "@gitpod/gitpod-protocol/lib/team-subscription-protocol"; import { Cancelable } from "@gitpod/gitpod-protocol/lib/util/cancelable"; import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { InterfaceWithTraceContext, TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; +import { + InterfaceWithTraceContext, + TraceContext, + TraceContextWithSpan, +} from "@gitpod/gitpod-protocol/lib/util/tracing"; import { IdentifyMessage, RemoteIdentifyMessage, @@ -171,6 +175,7 @@ import { Currency } from "@gitpod/gitpod-protocol/lib/plans"; import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server"; import { BillableSession, BillableSessionRequest } from "@gitpod/gitpod-protocol/lib/usage"; import { WorkspaceClusterImagebuilderClientProvider } from "./workspace-cluster-imagebuilder-client-provider"; +import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode"; // shortcut export const traceWI = (ctx: TraceContext, wi: Omit) => TraceContext.setOWI(ctx, wi); // userId is already taken care of in WebsocketConnectionManager @@ -2056,13 +2061,14 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { return await this.projectsService.getProjectEnvironmentVariables(projectId); } - protected async guardTeamOperation(teamId: string | undefined, op: ResourceAccessOp): Promise { + protected async guardTeamOperation(teamId: string | undefined, op: ResourceAccessOp): Promise { const team = await this.teamDB.findTeamById(teamId || ""); if (!team) { throw new ResponseError(ErrorCodes.NOT_FOUND, "Team not found"); } const members = await this.teamDB.findMembersByTeam(team.id); await this.guardAccess({ kind: "team", subject: team, members }, op); + return team; } public async getTeams(ctx: TraceContext): Promise { @@ -3227,6 +3233,18 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { } } + async getBillingModeForUser(ctx: TraceContextWithSpan): Promise { + traceAPIParams(ctx, {}); + + return BillingMode.NONE; + } + + async getBillingModeForTeam(ctx: TraceContextWithSpan, teamId: string): Promise { + traceAPIParams(ctx, { teamId }); + + return BillingMode.NONE; + } + // //#endregion