@gitpod/gitpod-protocol 0.1.5-pavel-prebuilds.0 → 0.1.5-pavel-git-status-fix.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/gitpod-schema.json +16 -0
- package/lib/admin-protocol.d.ts +4 -0
- package/lib/admin-protocol.d.ts.map +1 -1
- package/lib/admin-protocol.js.map +1 -1
- package/lib/attribution.d.ts +20 -0
- package/lib/attribution.d.ts.map +1 -0
- package/lib/attribution.js +40 -0
- package/lib/attribution.js.map +1 -0
- package/lib/blocked-repositories-protocol.d.ts +13 -0
- package/lib/blocked-repositories-protocol.d.ts.map +1 -0
- package/lib/blocked-repositories-protocol.js +8 -0
- package/lib/blocked-repositories-protocol.js.map +1 -0
- package/lib/context-url.js +1 -1
- package/lib/context-url.js.map +1 -1
- package/lib/experiments/always-default.d.ts +8 -0
- package/lib/experiments/always-default.d.ts.map +1 -0
- package/lib/experiments/always-default.js +20 -0
- package/lib/experiments/always-default.js.map +1 -0
- package/lib/experiments/configcat-server.d.ts +8 -0
- package/lib/experiments/configcat-server.d.ts.map +1 -0
- package/lib/experiments/configcat-server.js +35 -0
- package/lib/experiments/configcat-server.js.map +1 -0
- package/lib/experiments/configcat.d.ts +22 -0
- package/lib/experiments/configcat.d.ts.map +1 -0
- package/lib/experiments/configcat.js +53 -0
- package/lib/experiments/configcat.js.map +1 -0
- package/lib/experiments/types.d.ts +23 -0
- package/lib/experiments/types.d.ts.map +1 -0
- package/lib/experiments/types.js +10 -0
- package/lib/experiments/types.js.map +1 -0
- package/lib/gitpod-service.d.ts +16 -5
- package/lib/gitpod-service.d.ts.map +1 -1
- package/lib/gitpod-service.js.map +1 -1
- package/lib/installation-admin-protocol.d.ts +3 -0
- package/lib/installation-admin-protocol.d.ts.map +1 -1
- package/lib/installation-admin-protocol.js +1 -0
- package/lib/installation-admin-protocol.js.map +1 -1
- package/lib/messaging/error.d.ts +1 -0
- package/lib/messaging/error.d.ts.map +1 -1
- package/lib/messaging/error.js +2 -0
- package/lib/messaging/error.js.map +1 -1
- package/lib/protocol.d.ts +50 -1
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +90 -2
- package/lib/protocol.js.map +1 -1
- package/lib/protocol.spec.d.ts +7 -0
- package/lib/protocol.spec.d.ts.map +1 -0
- package/lib/protocol.spec.js +127 -0
- package/lib/protocol.spec.js.map +1 -0
- package/lib/teams-projects-protocol.d.ts +1 -0
- package/lib/teams-projects-protocol.d.ts.map +1 -1
- package/lib/teams-projects-protocol.js.map +1 -1
- package/lib/usage.d.ts +22 -0
- package/lib/usage.d.ts.map +1 -0
- package/lib/usage.js +115 -0
- package/lib/usage.js.map +1 -0
- package/lib/util/debug-app.d.ts +25 -0
- package/lib/util/debug-app.d.ts.map +1 -0
- package/lib/util/debug-app.js +100 -0
- package/lib/util/debug-app.js.map +1 -0
- package/lib/util/logging.d.ts.map +1 -1
- package/lib/util/logging.js +8 -1
- package/lib/util/logging.js.map +1 -1
- package/lib/util/timeutil.d.ts +1 -0
- package/lib/util/timeutil.d.ts.map +1 -1
- package/lib/util/timeutil.js +7 -1
- package/lib/util/timeutil.js.map +1 -1
- package/lib/workspace-cluster.d.ts +6 -1
- package/lib/workspace-cluster.d.ts.map +1 -1
- package/lib/workspace-cluster.js.map +1 -1
- package/lib/workspace-instance.d.ts +6 -0
- package/lib/workspace-instance.d.ts.map +1 -1
- package/package.json +9 -6
- package/pkg-yarn.lock +7 -5
- package/provenance-bundle.jsonl +1 -1
- package/src/admin-protocol.ts +7 -0
- package/src/attribution.ts +48 -0
- package/src/blocked-repositories-protocol.ts +13 -0
- package/src/context-url.ts +1 -1
- package/src/experiments/always-default.ts +24 -0
- package/src/experiments/configcat-server.ts +38 -0
- package/src/experiments/configcat.ts +58 -0
- package/src/experiments/types.ts +35 -0
- package/src/gitpod-service.ts +21 -4
- package/src/installation-admin-protocol.ts +3 -0
- package/src/messaging/error.ts +3 -0
- package/src/protocol.spec.ts +97 -0
- package/src/protocol.ts +135 -3
- package/src/teams-projects-protocol.ts +1 -0
- package/src/usage.ts +146 -0
- package/src/util/debug-app.ts +81 -0
- package/src/util/logging.ts +11 -2
- package/src/util/timeutil.ts +6 -0
- package/src/workspace-cluster.ts +3 -1
- package/src/workspace-instance.ts +11 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Client } from "./types";
|
|
8
|
+
import * as configcat from "configcat-node";
|
|
9
|
+
import { LogLevel } from "configcat-common";
|
|
10
|
+
import { ConfigCatClient } from "./configcat";
|
|
11
|
+
import { newAlwaysReturningDefaultValueClient } from "./always-default";
|
|
12
|
+
|
|
13
|
+
let client: Client | undefined;
|
|
14
|
+
|
|
15
|
+
export function getExperimentsClientForBackend(): Client {
|
|
16
|
+
// We have already instantiated a client, we can just re-use it.
|
|
17
|
+
if (client !== undefined) {
|
|
18
|
+
return client;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Retrieve SDK key from ENV Variable
|
|
22
|
+
const sdkKey = process.env.CONFIGCAT_SDK_KEY;
|
|
23
|
+
|
|
24
|
+
// Self-hosted installations do not set the ConfigCat SDK key, so always use a client which returns the default value.
|
|
25
|
+
if (sdkKey === undefined || sdkKey === "") {
|
|
26
|
+
client = newAlwaysReturningDefaultValueClient();
|
|
27
|
+
return client;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const configCatClient = configcat.createClient(sdkKey, {
|
|
31
|
+
pollIntervalSeconds: 3 * 60, // 3 minutes
|
|
32
|
+
logger: configcat.createConsoleLogger(LogLevel.Error),
|
|
33
|
+
maxInitWaitTimeSeconds: 0,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
client = new ConfigCatClient(configCatClient);
|
|
37
|
+
return client;
|
|
38
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Attributes, Client } from "./types";
|
|
8
|
+
import { User as ConfigCatUser } from "configcat-common/lib/RolloutEvaluator";
|
|
9
|
+
import { IConfigCatClient } from "configcat-common/lib/ConfigCatClient";
|
|
10
|
+
import { User } from "../protocol";
|
|
11
|
+
|
|
12
|
+
export const USER_ID_ATTRIBUTE = "user_id";
|
|
13
|
+
export const PROJECT_ID_ATTRIBUTE = "project_id";
|
|
14
|
+
export const TEAM_ID_ATTRIBUTE = "team_id";
|
|
15
|
+
export const TEAM_IDS_ATTRIBUTE = "team_ids";
|
|
16
|
+
export const TEAM_NAME_ATTRIBUTE = "team_name";
|
|
17
|
+
export const TEAM_NAMES_ATTRIBUTE = "team_names";
|
|
18
|
+
|
|
19
|
+
export class ConfigCatClient implements Client {
|
|
20
|
+
private client: IConfigCatClient;
|
|
21
|
+
|
|
22
|
+
constructor(cc: IConfigCatClient) {
|
|
23
|
+
this.client = cc;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getValueAsync<T>(experimentName: string, defaultValue: T, attributes: Attributes): Promise<T> {
|
|
27
|
+
return this.client.getValueAsync(experimentName, defaultValue, attributesToUser(attributes));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
dispose(): void {
|
|
31
|
+
return this.client.dispose();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function attributesToUser(attributes: Attributes): ConfigCatUser {
|
|
36
|
+
const userId = attributes.user?.id || "";
|
|
37
|
+
const email = User.is(attributes.user) ? User.getPrimaryEmail(attributes.user) : attributes.user?.email || "";
|
|
38
|
+
|
|
39
|
+
const custom: { [key: string]: string } = {};
|
|
40
|
+
if (userId) {
|
|
41
|
+
custom[USER_ID_ATTRIBUTE] = userId;
|
|
42
|
+
}
|
|
43
|
+
if (attributes.projectId) {
|
|
44
|
+
custom[PROJECT_ID_ATTRIBUTE] = attributes.projectId;
|
|
45
|
+
}
|
|
46
|
+
if (attributes.teamId) {
|
|
47
|
+
custom[TEAM_ID_ATTRIBUTE] = attributes.teamId;
|
|
48
|
+
}
|
|
49
|
+
if (attributes.teamName) {
|
|
50
|
+
custom[TEAM_NAME_ATTRIBUTE] = attributes.teamName;
|
|
51
|
+
}
|
|
52
|
+
if (attributes.teams) {
|
|
53
|
+
custom[TEAM_NAMES_ATTRIBUTE] = attributes.teams.map((t) => t.name).join(",");
|
|
54
|
+
custom[TEAM_IDS_ATTRIBUTE] = attributes.teams.map((t) => t.id).join(",");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return new ConfigCatUser(userId, email, "", custom);
|
|
58
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { User } from "../protocol";
|
|
8
|
+
import { Team } from "../teams-projects-protocol";
|
|
9
|
+
|
|
10
|
+
export const Client = Symbol("Client");
|
|
11
|
+
|
|
12
|
+
// Attributes define attributes which can be used to segment audiences.
|
|
13
|
+
// Set the attributes which you want to use to group audiences into.
|
|
14
|
+
export interface Attributes {
|
|
15
|
+
// user.id is mapped to ConfigCat's "identifier" + "custom.user_id"
|
|
16
|
+
user?: User | { id: string; email?: string };
|
|
17
|
+
|
|
18
|
+
// Currently selected Gitpod Project ID (mapped to "custom.project_id")
|
|
19
|
+
projectId?: string;
|
|
20
|
+
|
|
21
|
+
// Currently selected Gitpod Team ID (mapped to "custom.team_id")
|
|
22
|
+
teamId?: string;
|
|
23
|
+
// Currently selected Gitpod Team Name (mapped to "custom.team_name")
|
|
24
|
+
teamName?: string;
|
|
25
|
+
|
|
26
|
+
// All the Gitpod Teams that the user is a member (or owner) of (mapped to "custom.team_names" and "custom.team_ids")
|
|
27
|
+
teams?: Team[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Client {
|
|
31
|
+
getValueAsync<T>(experimentName: string, defaultValue: T, attributes: Attributes): Promise<T>;
|
|
32
|
+
|
|
33
|
+
// dispose will dispose of the client, no longer retrieving flags
|
|
34
|
+
dispose(): void;
|
|
35
|
+
}
|
package/src/gitpod-service.ts
CHANGED
|
@@ -24,6 +24,9 @@ import {
|
|
|
24
24
|
GuessGitTokenScopesParams,
|
|
25
25
|
GuessedGitTokenScopes,
|
|
26
26
|
ProjectEnvVar,
|
|
27
|
+
PrebuiltWorkspace,
|
|
28
|
+
UserSSHPublicKeyValue,
|
|
29
|
+
SSHPublicKeyValue,
|
|
27
30
|
} from "./protocol";
|
|
28
31
|
import {
|
|
29
32
|
Team,
|
|
@@ -56,6 +59,8 @@ import {
|
|
|
56
59
|
import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from "./analytics";
|
|
57
60
|
import { IDEServer } from "./ide-protocol";
|
|
58
61
|
import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol";
|
|
62
|
+
import { Currency } from "./plans";
|
|
63
|
+
import { BillableSession } from "./usage";
|
|
59
64
|
|
|
60
65
|
export interface GitpodClient {
|
|
61
66
|
onInstanceUpdate(instance: WorkspaceInstance): void;
|
|
@@ -147,6 +152,12 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
147
152
|
setEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
148
153
|
deleteEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
149
154
|
|
|
155
|
+
// User SSH Keys
|
|
156
|
+
hasSSHPublicKey(): Promise<boolean>;
|
|
157
|
+
getSSHPublicKeys(): Promise<UserSSHPublicKeyValue[]>;
|
|
158
|
+
addSSHPublicKey(value: SSHPublicKeyValue): Promise<UserSSHPublicKeyValue>;
|
|
159
|
+
deleteSSHPublicKey(id: string): Promise<void>;
|
|
160
|
+
|
|
150
161
|
// Teams
|
|
151
162
|
getTeams(): Promise<Team[]>;
|
|
152
163
|
getTeamMembers(teamId: string): Promise<TeamMemberInfo[]>;
|
|
@@ -171,6 +182,8 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
171
182
|
getUserProjects(): Promise<Project[]>;
|
|
172
183
|
getProjectOverview(projectId: string): Promise<Project.Overview | undefined>;
|
|
173
184
|
findPrebuilds(params: FindPrebuildsParams): Promise<PrebuildWithStatus[]>;
|
|
185
|
+
findPrebuildByWorkspaceID(workspaceId: string): Promise<PrebuiltWorkspace | undefined>;
|
|
186
|
+
getPrebuild(prebuildId: string): Promise<PrebuildWithStatus | undefined>;
|
|
174
187
|
triggerPrebuild(projectId: string, branchName: string | null): Promise<StartPrebuildResult>;
|
|
175
188
|
cancelPrebuild(projectId: string, prebuildId: string): Promise<void>;
|
|
176
189
|
fetchProjectRepositoryConfiguration(projectId: string): Promise<string | undefined>;
|
|
@@ -270,10 +283,13 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
270
283
|
|
|
271
284
|
getGithubUpgradeUrls(): Promise<GithubUpgradeURL[]>;
|
|
272
285
|
|
|
273
|
-
getStripePublishableKey(): Promise<string
|
|
274
|
-
getStripeSetupIntentClientSecret(): Promise<string
|
|
275
|
-
|
|
276
|
-
subscribeTeamToStripe(teamId: string, setupIntentId: string): Promise<void>;
|
|
286
|
+
getStripePublishableKey(): Promise<string>;
|
|
287
|
+
getStripeSetupIntentClientSecret(): Promise<string>;
|
|
288
|
+
findStripeSubscriptionIdForTeam(teamId: string): Promise<string | undefined>;
|
|
289
|
+
subscribeTeamToStripe(teamId: string, setupIntentId: string, currency: Currency): Promise<void>;
|
|
290
|
+
getStripePortalUrlForTeam(teamId: string): Promise<string>;
|
|
291
|
+
|
|
292
|
+
getBilledUsage(attributionId: string): Promise<BillableSession[]>;
|
|
277
293
|
|
|
278
294
|
/**
|
|
279
295
|
* Analytics
|
|
@@ -378,6 +394,7 @@ export interface SetWorkspaceTimeoutResult {
|
|
|
378
394
|
|
|
379
395
|
export interface GetWorkspaceTimeoutResult {
|
|
380
396
|
duration: WorkspaceTimeoutDuration;
|
|
397
|
+
durationRaw: string;
|
|
381
398
|
canChange: boolean;
|
|
382
399
|
}
|
|
383
400
|
|
|
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
8
8
|
|
|
9
9
|
const InstallationAdminSettingsPrototype = {
|
|
10
10
|
sendTelemetry: true,
|
|
11
|
+
sendCustomerID: true,
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export type InstallationAdminSettings = typeof InstallationAdminSettingsPrototype;
|
|
@@ -28,6 +29,8 @@ export interface TelemetryData {
|
|
|
28
29
|
totalUsers: number;
|
|
29
30
|
totalWorkspaces: number;
|
|
30
31
|
totalInstances: number;
|
|
32
|
+
licenseType: string;
|
|
33
|
+
customerID?: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export namespace InstallationAdmin {
|
package/src/messaging/error.ts
CHANGED
|
@@ -62,6 +62,9 @@ export namespace ErrorCodes {
|
|
|
62
62
|
// 490 Too Many Running Workspace
|
|
63
63
|
export const TOO_MANY_RUNNING_WORKSPACES = 490;
|
|
64
64
|
|
|
65
|
+
// 500 Internal Server Error
|
|
66
|
+
export const INTERNAL_SERVER_ERROR = 500;
|
|
67
|
+
|
|
65
68
|
// 501 EE Feature
|
|
66
69
|
export const EE_FEATURE = 501;
|
|
67
70
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { suite, test } from "mocha-typescript";
|
|
8
|
+
import * as chai from "chai";
|
|
9
|
+
import { SSHPublicKeyValue } from ".";
|
|
10
|
+
|
|
11
|
+
const expect = chai.expect;
|
|
12
|
+
|
|
13
|
+
@suite
|
|
14
|
+
class TestSSHPublicKeyValue {
|
|
15
|
+
private key =
|
|
16
|
+
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCnrN9UdK1bNGPmZfenTWXLuYYDjlYvZE8S+WOfP08WpR1GETzX5ZvgYOEZGwEE8KUPHC9cge4Hvo/ydIS9aqbZ5MiVGJ8cAIq1Ic89SjlDWU6fl8TwIqOPCi2imAASlEDP4q8vMLK1N6UOW1EVbxyL3uybGd10ysC1t1FxFPveIGNsYE/MOQeuEWS16AplpXYXIfVRSlgAskeBft2w8Ud3B4gNe8ECLA/FXu96UpvZkdtOarA3JZ9Z27GveNJg9Mtmmw0+US0KXiO9x9NyH7G8+mqVDwDY+nNvaFA5gtQxkkl/uY2oz9k/B4Rjlj3jOiUXe5uQs3XUm5m8g9a9fh62DabLpA2fEvtfg+a/VqNe52dNa5YjupwvBd6Inb5uMW/TYjNl6bNHPlXFKw/nwLOVzukpkjxMZUKS6+4BGkpoasj6y2rTU/wkpbdD8J7yjI1p6J9aKkC6KksIWgN7xGmHkv2PCGDqMHTNbnQyowtNKMgA/667vAYJ0qW7HAHBFXJRs6uRi/DI3+c1QV2s4wPCpEHDIYApovQ0fbON4WDPoGMyHd7kPh9xB/bX7Dj0uMXImu1pdTd62fQ/1XXX64+vjAAXS/P9RSCD0RCRt/K3LPKl2m7GPI3y1niaE52XhxZw+ms9ays6NasNVMw/ZC+f02Ti+L5FBEVf8230RVVRQ== notfound@gitpod.io";
|
|
17
|
+
|
|
18
|
+
@test public testValidate() {
|
|
19
|
+
const key = this.key;
|
|
20
|
+
const [t, k, e] = key.split(" ");
|
|
21
|
+
expect(
|
|
22
|
+
SSHPublicKeyValue.getData({
|
|
23
|
+
key,
|
|
24
|
+
name: "NiceName",
|
|
25
|
+
}),
|
|
26
|
+
).to.deep.equal({ type: t, key: k, email: e });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test public testValidateWithDiffType() {
|
|
30
|
+
const key = this.key;
|
|
31
|
+
const [_, k, e] = key.split(" ");
|
|
32
|
+
expect(
|
|
33
|
+
SSHPublicKeyValue.getData({
|
|
34
|
+
key: key.replace("ssh-rsa", "sk-ecdsa-sha2-nistp256@openssh.com"),
|
|
35
|
+
name: "NiceName",
|
|
36
|
+
}),
|
|
37
|
+
).to.deep.equal({ type: "sk-ecdsa-sha2-nistp256@openssh.com", key: k, email: e });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@test public testValidateWithoutEmail() {
|
|
41
|
+
const key = this.key;
|
|
42
|
+
const [t, k, _] = key.split(" ");
|
|
43
|
+
expect(
|
|
44
|
+
SSHPublicKeyValue.getData({
|
|
45
|
+
key: key.replace(" notfound@gitpod.io", ""),
|
|
46
|
+
name: "NiceName",
|
|
47
|
+
}),
|
|
48
|
+
).to.deep.equal({ type: t, key: k, email: undefined });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test public testValidateWithoutEmailButEndsWithSpaces() {
|
|
52
|
+
const key = this.key;
|
|
53
|
+
const [t, k, _] = key.split(" ");
|
|
54
|
+
expect(
|
|
55
|
+
SSHPublicKeyValue.getData({
|
|
56
|
+
key: key.replace("notfound@gitpod.io", " "),
|
|
57
|
+
name: "NiceName",
|
|
58
|
+
}),
|
|
59
|
+
).to.deep.equal({ type: t, key: k, email: undefined });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@test public testValidateWithError() {
|
|
63
|
+
expect(() =>
|
|
64
|
+
SSHPublicKeyValue.getData({
|
|
65
|
+
key: "Hello World",
|
|
66
|
+
name: "NiceName",
|
|
67
|
+
}),
|
|
68
|
+
).throw("Key is invalid");
|
|
69
|
+
|
|
70
|
+
expect(() =>
|
|
71
|
+
SSHPublicKeyValue.getData({
|
|
72
|
+
key: "",
|
|
73
|
+
name: "NiceName",
|
|
74
|
+
}),
|
|
75
|
+
).throw("Key is invalid");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@test public testGetFingerprint() {
|
|
79
|
+
const key = this.key;
|
|
80
|
+
expect(
|
|
81
|
+
SSHPublicKeyValue.getFingerprint({
|
|
82
|
+
key,
|
|
83
|
+
name: "NiceName",
|
|
84
|
+
}),
|
|
85
|
+
).to.equal("ykjP/b5aqoa3envmXzWpPMCGgEFMu3QvubfSTNrJCMA=");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@test public testGetFingerprintWithIncorrectPublicKey() {
|
|
89
|
+
expect(() =>
|
|
90
|
+
SSHPublicKeyValue.getFingerprint({
|
|
91
|
+
key: "Hello World",
|
|
92
|
+
name: "NiceName",
|
|
93
|
+
}),
|
|
94
|
+
).to.throw("Key is invalid");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
module.exports = new TestSSHPublicKeyValue(); // Only to circumvent no usage warning :-/
|
package/src/protocol.ts
CHANGED
|
@@ -70,11 +70,14 @@ export namespace User {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Returns the stored email or if it doesn't exist returns the primaryEmail of the first identity this user signed up with.
|
|
74
74
|
* @param user
|
|
75
75
|
* @returns A primaryEmail, or undefined if there is none.
|
|
76
76
|
*/
|
|
77
77
|
export function getPrimaryEmail(user: User): string | undefined {
|
|
78
|
+
if (user.additionalData?.profile?.emailAddress) {
|
|
79
|
+
return user.additionalData?.profile?.emailAddress;
|
|
80
|
+
}
|
|
78
81
|
const identities = user.identities.filter((i) => !!i.primaryEmail);
|
|
79
82
|
if (identities.length <= 0) {
|
|
80
83
|
return undefined;
|
|
@@ -136,6 +139,50 @@ export namespace User {
|
|
|
136
139
|
}
|
|
137
140
|
user.additionalData.ideSettings = newIDESettings;
|
|
138
141
|
}
|
|
142
|
+
|
|
143
|
+
export function getProfile(user: User): Profile {
|
|
144
|
+
return {
|
|
145
|
+
name: User.getName(user!) || "",
|
|
146
|
+
email: User.getPrimaryEmail(user!) || "",
|
|
147
|
+
company: user?.additionalData?.profile?.companyName,
|
|
148
|
+
avatarURL: user?.avatarUrl,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function setProfile(user: User, profile: Profile): User {
|
|
153
|
+
user.fullName = profile.name;
|
|
154
|
+
user.avatarUrl = profile.avatarURL;
|
|
155
|
+
|
|
156
|
+
if (!user.additionalData) {
|
|
157
|
+
user.additionalData = {};
|
|
158
|
+
}
|
|
159
|
+
if (!user.additionalData.profile) {
|
|
160
|
+
user.additionalData.profile = {};
|
|
161
|
+
}
|
|
162
|
+
user.additionalData.profile.emailAddress = profile.email;
|
|
163
|
+
user.additionalData.profile.companyName = profile.company;
|
|
164
|
+
user.additionalData.profile.lastUpdatedDetailsNudge = new Date().toISOString();
|
|
165
|
+
|
|
166
|
+
return user;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// The actual Profile of a User
|
|
170
|
+
export interface Profile {
|
|
171
|
+
name: string;
|
|
172
|
+
email: string;
|
|
173
|
+
company?: string;
|
|
174
|
+
avatarURL?: string;
|
|
175
|
+
}
|
|
176
|
+
export namespace Profile {
|
|
177
|
+
export function hasChanges(before: Profile, after: Profile) {
|
|
178
|
+
return (
|
|
179
|
+
before.name !== after.name ||
|
|
180
|
+
before.email !== after.email ||
|
|
181
|
+
before.company !== after.company ||
|
|
182
|
+
before.avatarURL !== after.avatarURL
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
139
186
|
}
|
|
140
187
|
|
|
141
188
|
export interface AdditionalUserData {
|
|
@@ -150,9 +197,24 @@ export interface AdditionalUserData {
|
|
|
150
197
|
oauthClientsApproved?: { [key: string]: string };
|
|
151
198
|
// to remember GH Orgs the user installed/updated the GH App for
|
|
152
199
|
knownGitHubOrgs?: string[];
|
|
153
|
-
|
|
154
200
|
// Git clone URL pointing to the user's dotfile repo
|
|
155
201
|
dotfileRepo?: string;
|
|
202
|
+
// Identifies an explicit team or user ID to which all the user's workspace usage should be attributed to (e.g. for billing purposes)
|
|
203
|
+
usageAttributionId?: string;
|
|
204
|
+
// preferred workspace classes
|
|
205
|
+
workspaceClasses?: WorkspaceClasses;
|
|
206
|
+
// additional user profile data
|
|
207
|
+
profile?: ProfileDetails;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// The format in which we store User Profiles in
|
|
211
|
+
export interface ProfileDetails {
|
|
212
|
+
// when was the last time the user updated their profile information or has been nudged to do so.
|
|
213
|
+
lastUpdatedDetailsNudge?: string;
|
|
214
|
+
// the user's company name
|
|
215
|
+
companyName?: string;
|
|
216
|
+
// the user's email
|
|
217
|
+
emailAddress?: string;
|
|
156
218
|
}
|
|
157
219
|
|
|
158
220
|
export interface EmailNotificationSettings {
|
|
@@ -171,6 +233,11 @@ export type IDESettings = {
|
|
|
171
233
|
useLatestVersion?: boolean;
|
|
172
234
|
};
|
|
173
235
|
|
|
236
|
+
export interface WorkspaceClasses {
|
|
237
|
+
regular: string;
|
|
238
|
+
prebuild: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
174
241
|
export interface UserPlatform {
|
|
175
242
|
uid: string;
|
|
176
243
|
userAgent: string;
|
|
@@ -205,6 +272,7 @@ export const WorkspaceFeatureFlags = {
|
|
|
205
272
|
full_workspace_backup: undefined,
|
|
206
273
|
fixed_resources: undefined,
|
|
207
274
|
persistent_volume_claim: undefined,
|
|
275
|
+
protected_secrets: undefined,
|
|
208
276
|
};
|
|
209
277
|
export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags;
|
|
210
278
|
|
|
@@ -356,6 +424,68 @@ export namespace UserEnvVar {
|
|
|
356
424
|
}
|
|
357
425
|
}
|
|
358
426
|
|
|
427
|
+
export interface SSHPublicKeyValue {
|
|
428
|
+
name: string;
|
|
429
|
+
key: string;
|
|
430
|
+
}
|
|
431
|
+
export interface UserSSHPublicKey extends SSHPublicKeyValue {
|
|
432
|
+
id: string;
|
|
433
|
+
key: string;
|
|
434
|
+
userId: string;
|
|
435
|
+
fingerprint: string;
|
|
436
|
+
creationTime: string;
|
|
437
|
+
lastUsedTime?: string;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export type UserSSHPublicKeyValue = Omit<UserSSHPublicKey, "key" | "userId">;
|
|
441
|
+
|
|
442
|
+
export namespace SSHPublicKeyValue {
|
|
443
|
+
export function validate(value: SSHPublicKeyValue): string | undefined {
|
|
444
|
+
if (value.name.length === 0) {
|
|
445
|
+
return "Title must not be empty.";
|
|
446
|
+
}
|
|
447
|
+
if (value.name.length > 255) {
|
|
448
|
+
return "Title too long. Maximum value length is 255 characters.";
|
|
449
|
+
}
|
|
450
|
+
if (value.key.length === 0) {
|
|
451
|
+
return "Key must not be empty.";
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
getData(value);
|
|
455
|
+
} catch (e) {
|
|
456
|
+
return "Key is invalid. You must supply a key in OpenSSH public key format.";
|
|
457
|
+
}
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function getData(value: SSHPublicKeyValue) {
|
|
462
|
+
// Begins with 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com'.
|
|
463
|
+
const regex =
|
|
464
|
+
/^(?<type>ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519|sk-ecdsa-sha2-nistp256@openssh\.com|sk-ssh-ed25519@openssh\.com) (?<key>.*?)( (?<email>.*?))?$/;
|
|
465
|
+
const resultGroup = regex.exec(value.key.trim());
|
|
466
|
+
if (!resultGroup) {
|
|
467
|
+
throw new Error("Key is invalid.");
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
type: resultGroup.groups?.["type"] as string,
|
|
471
|
+
key: resultGroup.groups?.["key"] as string,
|
|
472
|
+
email: resultGroup.groups?.["email"] || undefined,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export function getFingerprint(value: SSHPublicKeyValue) {
|
|
477
|
+
const data = getData(value);
|
|
478
|
+
let buf = Buffer.from(data.key, "base64");
|
|
479
|
+
// gitlab style
|
|
480
|
+
// const hash = createHash("md5").update(buf).digest("hex");
|
|
481
|
+
// github style
|
|
482
|
+
const hash = createHash("sha256").update(buf).digest("base64");
|
|
483
|
+
return hash;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export const MAXIMUM_KEY_LENGTH = 5;
|
|
487
|
+
}
|
|
488
|
+
|
|
359
489
|
export interface GitpodToken {
|
|
360
490
|
/** Hash value (SHA256) of the token (primary key). */
|
|
361
491
|
tokenHash: string;
|
|
@@ -490,6 +620,7 @@ export interface Snapshot {
|
|
|
490
620
|
|
|
491
621
|
export interface VolumeSnapshot {
|
|
492
622
|
id: string;
|
|
623
|
+
workspaceId: string;
|
|
493
624
|
creationTime: string;
|
|
494
625
|
volumeHandle: string;
|
|
495
626
|
}
|
|
@@ -563,7 +694,7 @@ export interface Workspace {
|
|
|
563
694
|
|
|
564
695
|
export type WorkspaceSoftDeletion = "user" | "gc";
|
|
565
696
|
|
|
566
|
-
export type WorkspaceType = "regular" | "prebuild" | "probe";
|
|
697
|
+
export type WorkspaceType = "regular" | "prebuild" | "probe"; // TODO(gpl) Removed prove here
|
|
567
698
|
|
|
568
699
|
export namespace Workspace {
|
|
569
700
|
export function getFullRepositoryName(ws: Workspace): string | undefined {
|
|
@@ -639,6 +770,7 @@ export interface JetBrainsConfig {
|
|
|
639
770
|
}
|
|
640
771
|
export interface JetBrainsProductConfig {
|
|
641
772
|
prebuilds?: JetBrainsPrebuilds;
|
|
773
|
+
vmoptions?: string;
|
|
642
774
|
}
|
|
643
775
|
export interface JetBrainsPrebuilds {
|
|
644
776
|
version?: "stable" | "latest" | "both";
|
package/src/usage.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { WorkspaceType } from "./protocol";
|
|
8
|
+
|
|
9
|
+
export interface BillableSession {
|
|
10
|
+
// The id of the one paying the bill
|
|
11
|
+
attributionId: string;
|
|
12
|
+
|
|
13
|
+
// Relevant for workspace type. When prebuild, shows "prebuild"
|
|
14
|
+
userId?: string;
|
|
15
|
+
teamId?: string;
|
|
16
|
+
|
|
17
|
+
instanceId: string;
|
|
18
|
+
|
|
19
|
+
workspaceId: string;
|
|
20
|
+
|
|
21
|
+
workspaceType: BillableWorkspaceType;
|
|
22
|
+
|
|
23
|
+
// "standard" or "XL"
|
|
24
|
+
workspaceClass: string;
|
|
25
|
+
|
|
26
|
+
// When the workspace started
|
|
27
|
+
startTime: string;
|
|
28
|
+
|
|
29
|
+
// When the workspace ended
|
|
30
|
+
endTime: string;
|
|
31
|
+
|
|
32
|
+
// The credits used for this session
|
|
33
|
+
credits: number;
|
|
34
|
+
|
|
35
|
+
// TODO - maybe
|
|
36
|
+
projectId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type BillableWorkspaceType = WorkspaceType;
|
|
40
|
+
|
|
41
|
+
export const billableSessionDummyData: BillableSession[] = [
|
|
42
|
+
{
|
|
43
|
+
attributionId: "some-attribution-id",
|
|
44
|
+
userId: "prebuild",
|
|
45
|
+
teamId: "prebuild",
|
|
46
|
+
instanceId: "some-instance-id",
|
|
47
|
+
workspaceId: "some-workspace-id",
|
|
48
|
+
workspaceType: "prebuild",
|
|
49
|
+
workspaceClass: "XL",
|
|
50
|
+
startTime: new Date(Date.now() + -3 * 24 * 3600 * 1000).toISOString(), // 3 days ago
|
|
51
|
+
endTime: new Date().toISOString(),
|
|
52
|
+
credits: 320,
|
|
53
|
+
projectId: "project-123",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
attributionId: "some-attribution-id2",
|
|
57
|
+
userId: "some-user",
|
|
58
|
+
teamId: "some-team",
|
|
59
|
+
instanceId: "some-instance-id2",
|
|
60
|
+
workspaceId: "some-workspace-id2",
|
|
61
|
+
workspaceType: "regular",
|
|
62
|
+
workspaceClass: "standard",
|
|
63
|
+
startTime: new Date(Date.now() + -5 * 24 * 3600 * 1000).toISOString(),
|
|
64
|
+
endTime: new Date().toISOString(),
|
|
65
|
+
credits: 130,
|
|
66
|
+
projectId: "project-123",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
attributionId: "some-attribution-id3",
|
|
70
|
+
userId: "some-other-user",
|
|
71
|
+
teamId: "some-other-team",
|
|
72
|
+
instanceId: "some-instance-id3",
|
|
73
|
+
workspaceId: "some-workspace-id3",
|
|
74
|
+
workspaceType: "regular",
|
|
75
|
+
workspaceClass: "XL",
|
|
76
|
+
startTime: new Date(Date.now() + -5 * 24 * 3600 * 1000).toISOString(),
|
|
77
|
+
endTime: new Date(Date.now() + -4 * 24 * 3600 * 1000).toISOString(),
|
|
78
|
+
credits: 150,
|
|
79
|
+
projectId: "project-134",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
attributionId: "some-attribution-id4",
|
|
83
|
+
userId: "some-other-user2",
|
|
84
|
+
teamId: "some-other-team2",
|
|
85
|
+
instanceId: "some-instance-id4",
|
|
86
|
+
workspaceId: "some-workspace-id4",
|
|
87
|
+
workspaceType: "regular",
|
|
88
|
+
workspaceClass: "standard",
|
|
89
|
+
startTime: new Date(Date.now() + -10 * 24 * 3600 * 1000).toISOString(),
|
|
90
|
+
endTime: new Date(Date.now() + -9 * 24 * 3600 * 1000).toISOString(),
|
|
91
|
+
credits: 330,
|
|
92
|
+
projectId: "project-137",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
attributionId: "some-attribution-id5",
|
|
96
|
+
userId: "some-other-user3",
|
|
97
|
+
teamId: "some-other-team3",
|
|
98
|
+
instanceId: "some-instance-id5",
|
|
99
|
+
workspaceId: "some-workspace-id5",
|
|
100
|
+
workspaceType: "regular",
|
|
101
|
+
workspaceClass: "XL",
|
|
102
|
+
startTime: new Date(Date.now() + -2 * 24 * 3600 * 1000).toISOString(),
|
|
103
|
+
endTime: new Date().toISOString(),
|
|
104
|
+
credits: 222,
|
|
105
|
+
projectId: "project-138",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
attributionId: "some-attribution-id6",
|
|
109
|
+
userId: "some-other-user4",
|
|
110
|
+
teamId: "some-other-team4",
|
|
111
|
+
instanceId: "some-instance-id6",
|
|
112
|
+
workspaceId: "some-workspace-id3",
|
|
113
|
+
workspaceType: "regular",
|
|
114
|
+
workspaceClass: "XL",
|
|
115
|
+
startTime: new Date(Date.now() + -7 * 24 * 3600 * 1000).toISOString(),
|
|
116
|
+
endTime: new Date(Date.now() + -6 * 24 * 3600 * 1000).toISOString(),
|
|
117
|
+
credits: 300,
|
|
118
|
+
projectId: "project-134",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
attributionId: "some-attribution-id8",
|
|
122
|
+
userId: "some-other-user5",
|
|
123
|
+
teamId: "some-other-team5",
|
|
124
|
+
instanceId: "some-instance-id8",
|
|
125
|
+
workspaceId: "some-workspace-id3",
|
|
126
|
+
workspaceType: "regular",
|
|
127
|
+
workspaceClass: "standard",
|
|
128
|
+
startTime: new Date(Date.now() + -1 * 24 * 3600 * 1000).toISOString(),
|
|
129
|
+
endTime: new Date().toISOString(),
|
|
130
|
+
credits: 100,
|
|
131
|
+
projectId: "project-567",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
attributionId: "some-attribution-id7",
|
|
135
|
+
userId: "prebuild",
|
|
136
|
+
teamId: "some-other-team7",
|
|
137
|
+
instanceId: "some-instance-id7",
|
|
138
|
+
workspaceId: "some-workspace-id7",
|
|
139
|
+
workspaceType: "prebuild",
|
|
140
|
+
workspaceClass: "XL",
|
|
141
|
+
startTime: new Date(Date.now() + -1 * 24 * 3600 * 1000).toISOString(),
|
|
142
|
+
endTime: new Date().toISOString(),
|
|
143
|
+
credits: 200,
|
|
144
|
+
projectId: "project-345",
|
|
145
|
+
},
|
|
146
|
+
];
|