Skip to content

Commit 8442491

Browse files
Merge branch 'main' into ft/additional-ws-gc-logging
2 parents 00af0fe + 2a5da84 commit 8442491

23 files changed

+174
-103
lines changed

components/dashboard/src/data/projects/create-project-mutation.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

components/gitpod-protocol/src/gitpod-service.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,6 @@ export interface GetDefaultWorkspaceImageResult {
311311

312312
export interface CreateProjectParams {
313313
name: string;
314-
/** @deprecated unused */
315-
slug: string;
316314
cloneUrl: string;
317315
teamId: string;
318316
appInstallationId: string;

components/gitpod-protocol/src/protocol.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,10 @@ export interface Repository {
12861286
* Optional date when the repository was last pushed to.
12871287
*/
12881288
pushedAt?: string;
1289+
/**
1290+
* Optional display name (e.g. for Bitbucket)
1291+
*/
1292+
displayName?: string;
12891293
}
12901294

12911295
export interface RepositoryInfo {

components/gitpod-protocol/src/util/parse-workspace-id.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,9 @@ export class ParseWorkspaceIdTest {
8181
const actual = matchesNewWorkspaceIdExactly("moccasin-ferret-15599b3");
8282
expect(actual).to.be.false;
8383
}
84+
@test public matchesWorkspaceIdExactly_new_negative_empty() {
85+
const actual = matchesNewWorkspaceIdExactly(undefined);
86+
expect(actual).to.be.false;
87+
}
8488
}
8589
module.exports = new ParseWorkspaceIdTest();

components/gitpod-protocol/src/util/parse-workspace-id.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const parseWorkspaceIdFromHostname = function (hostname: string) {
3333
}
3434
};
3535

36-
/** Equalls UUIDv4 (and REGEX_WORKSPACE_ID_LEGACY!) */
36+
/** Equals UUIDv4 (and REGEX_WORKSPACE_ID_LEGACY!) */
3737
const REGEX_INSTANCE_ID = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;
3838
const REGEX_INSTANCE_ID_EXACT = new RegExp(`^${REGEX_INSTANCE_ID.source}$`);
3939

@@ -49,6 +49,10 @@ export const matchesInstanceIdOrLegacyWorkspaceIdExactly = function (maybeId: st
4949
* @param maybeWorkspaceId
5050
* @returns
5151
*/
52-
export const matchesNewWorkspaceIdExactly = function (maybeWorkspaceId: string): boolean {
52+
export const matchesNewWorkspaceIdExactly = function (maybeWorkspaceId?: string): boolean {
53+
if (!maybeWorkspaceId) {
54+
return false;
55+
}
56+
5357
return REGEX_WORKSPACE_ID_EXACT.test(maybeWorkspaceId);
5458
};

components/server/src/api/configuration-service-api.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { PaginationToken, generatePaginationToken, parsePaginationToken } from "
2727
import { ctxUserId } from "../util/request-context";
2828
import { UserService } from "../user/user-service";
2929
import { SortOrder } from "@gitpod/public-api/lib/gitpod/v1/sorting_pb";
30-
import { Project } from "@gitpod/gitpod-protocol";
30+
import { CommitContext, Project } from "@gitpod/gitpod-protocol";
3131
import { DeepPartial } from "@gitpod/gitpod-protocol/lib/util/deep-partial";
3232
import { ContextService } from "../workspace/context-service";
3333

@@ -76,18 +76,20 @@ export class ConfigurationServiceAPI implements ServiceImpl<typeof Configuration
7676
throw new ApplicationError(ErrorCodes.NOT_FOUND, "user not found");
7777
}
7878

79-
const cloneUrl = await this.contextService.parseContextUrlAsCloneUrl(installer, req.cloneUrl);
80-
if (!cloneUrl) {
79+
const context = await this.contextService.parseContextUrl(installer, req.cloneUrl);
80+
if (!CommitContext.is(context)) {
8181
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "clone_url is not valid");
8282
}
8383

84+
// The dashboard, for example, does not provide an explicit name, so we infer it
85+
const name = req.name || (context.repository.displayName ?? context.repository.name);
86+
8487
const project = await this.projectService.createProject(
8588
{
8689
teamId: req.organizationId,
87-
name: req.name,
88-
cloneUrl,
90+
name,
91+
cloneUrl: context.repository.cloneUrl,
8992
appInstallationId: "",
90-
slug: "",
9193
},
9294
installer,
9395
);

components/server/src/api/workspace-service-api.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messag
5858
import { ContextService } from "../workspace/context-service";
5959
import { UserService } from "../user/user-service";
6060
import { ContextParser } from "../workspace/context-parser-service";
61+
import { matchesNewWorkspaceIdExactly as isWorkspaceId } from "@gitpod/gitpod-protocol/lib/util/parse-workspace-id";
6162

6263
@injectable()
6364
export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceInterface> {
@@ -68,8 +69,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
6869
@inject(ContextParser) private contextParser: ContextParser;
6970

7071
async getWorkspace(req: GetWorkspaceRequest, _: HandlerContext): Promise<GetWorkspaceResponse> {
71-
if (!req.workspaceId) {
72-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
72+
if (!isWorkspaceId(req.workspaceId)) {
73+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
7374
}
7475
const info = await this.workspaceService.getWorkspace(ctxUserId(), req.workspaceId);
7576
const response = new GetWorkspaceResponse();
@@ -198,8 +199,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
198199

199200
async startWorkspace(req: StartWorkspaceRequest): Promise<StartWorkspaceResponse> {
200201
// We rely on FGA to do the permission checking
201-
if (!req.workspaceId) {
202-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
202+
if (!isWorkspaceId(req.workspaceId)) {
203+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
203204
}
204205
const user = await this.userService.findUserById(ctxUserId(), ctxUserId());
205206
const { workspace, latestInstance: instance } = await this.workspaceService.getWorkspace(
@@ -227,8 +228,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
227228
req: GetWorkspaceDefaultImageRequest,
228229
_: HandlerContext,
229230
): Promise<GetWorkspaceDefaultImageResponse> {
230-
if (!req.workspaceId) {
231-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
231+
if (!isWorkspaceId(req.workspaceId)) {
232+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
232233
}
233234
const result = await this.workspaceService.getWorkspaceDefaultImage(ctxUserId(), req.workspaceId);
234235
const response = new GetWorkspaceDefaultImageResponse({
@@ -246,8 +247,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
246247
}
247248

248249
async sendHeartBeat(req: SendHeartBeatRequest, _: HandlerContext): Promise<SendHeartBeatResponse> {
249-
if (!req.workspaceId) {
250-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
250+
if (!isWorkspaceId(req.workspaceId)) {
251+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
251252
}
252253
const info = await this.workspaceService.getWorkspace(ctxUserId(), req.workspaceId);
253254
if (!info.latestInstance?.id || info.latestInstance.status.phase !== "running") {
@@ -265,8 +266,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
265266
req: GetWorkspaceOwnerTokenRequest,
266267
_: HandlerContext,
267268
): Promise<GetWorkspaceOwnerTokenResponse> {
268-
if (!req.workspaceId) {
269-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
269+
if (!isWorkspaceId(req.workspaceId)) {
270+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
270271
}
271272
const ownerToken = await this.workspaceService.getOwnerToken(ctxUserId(), req.workspaceId);
272273
const response = new GetWorkspaceOwnerTokenResponse();
@@ -278,8 +279,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
278279
req: GetWorkspaceEditorCredentialsRequest,
279280
_: HandlerContext,
280281
): Promise<GetWorkspaceEditorCredentialsResponse> {
281-
if (!req.workspaceId) {
282-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
282+
if (!isWorkspaceId(req.workspaceId)) {
283+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
283284
}
284285
const credentials = await this.workspaceService.getIDECredentials(ctxUserId(), req.workspaceId);
285286
const response = new GetWorkspaceEditorCredentialsResponse();
@@ -288,8 +289,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
288289
}
289290

290291
async updateWorkspace(req: UpdateWorkspaceRequest): Promise<UpdateWorkspaceResponse> {
291-
if (!req.workspaceId) {
292-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
292+
if (!isWorkspaceId(req.workspaceId)) {
293+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
293294
}
294295
if (req.spec?.timeout?.inactivity?.seconds || (req.spec?.sshPublicKeys && req.spec?.sshPublicKeys.length > 0)) {
295296
throw new ApplicationError(ErrorCodes.UNIMPLEMENTED, "not implemented");
@@ -363,17 +364,17 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
363364
}
364365

365366
async stopWorkspace(req: StopWorkspaceRequest): Promise<StopWorkspaceResponse> {
366-
if (!req.workspaceId) {
367-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
367+
if (!isWorkspaceId(req.workspaceId)) {
368+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
368369
}
369370
await this.workspaceService.stopWorkspace(ctxUserId(), req.workspaceId, "stopped via API");
370371
const response = new StopWorkspaceResponse();
371372
return response;
372373
}
373374

374375
async deleteWorkspace(req: DeleteWorkspaceRequest): Promise<DeleteWorkspaceResponse> {
375-
if (!req.workspaceId) {
376-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
376+
if (!isWorkspaceId(req.workspaceId)) {
377+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
377378
}
378379
await this.workspaceService.deleteWorkspace(ctxUserId(), req.workspaceId, "user");
379380
const response = new DeleteWorkspaceResponse();
@@ -389,8 +390,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
389390
}
390391

391392
async createWorkspaceSnapshot(req: CreateWorkspaceSnapshotRequest): Promise<CreateWorkspaceSnapshotResponse> {
392-
if (!req.workspaceId) {
393-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
393+
if (!isWorkspaceId(req.workspaceId)) {
394+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
394395
}
395396
const snapshot = await this.workspaceService.takeSnapshot(ctxUserId(), {
396397
workspaceId: req.workspaceId,
@@ -410,8 +411,8 @@ export class WorkspaceServiceAPI implements ServiceImpl<typeof WorkspaceServiceI
410411
}
411412

412413
async updateWorkspacePort(req: UpdateWorkspacePortRequest): Promise<UpdateWorkspacePortResponse> {
413-
if (!req.workspaceId) {
414-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
414+
if (!isWorkspaceId(req.workspaceId)) {
415+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "a valid workspaceId is required");
415416
}
416417
if (!req.port) {
417418
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "port is required");

components/server/src/authorization/spicedb-authorizer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { base64decode } from "@jmondi/oauth2-server";
1515
import { DecodedZedToken } from "@gitpod/spicedb-impl/lib/impl/v1/impl.pb";
1616
import { ctxTryGetCache, ctxTrySetCache } from "../util/request-context";
1717
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
18+
import { isGrpcError } from "@gitpod/gitpod-protocol/lib/util/grpc";
1819

1920
async function tryThree<T>(errMessage: string, code: (attempt: number) => Promise<T>): Promise<T> {
2021
let attempt = 0;
@@ -104,6 +105,10 @@ export class SpiceDBAuthorizer {
104105
const permitted = response.permissionship === v1.CheckPermissionResponse_Permissionship.HAS_PERMISSION;
105106
return { permitted, checkedAt: response.checkedAt?.token };
106107
} catch (err) {
108+
// we should not consider users supplying invalid requests as internal server errors
109+
if (isGrpcError(err) && err.code === grpc.status.INVALID_ARGUMENT) {
110+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, `Invalid request for permission check: ${err}`);
111+
}
107112
error = err;
108113
log.error("[spicedb] Failed to perform authorization check.", err, {
109114
request: new TrustedValue(req),

components/server/src/bitbucket-server/bitbucket-server-context-parser.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class TestBitbucketServerContextParser {
9696
host: "bitbucket.gitpod-dev.com",
9797
owner: "GIT",
9898
name: "gitpod-test-repo",
99+
displayName: "Gitpod Test Repo",
99100
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
100101
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
101102
defaultBranch: "master",
@@ -123,6 +124,7 @@ class TestBitbucketServerContextParser {
123124
host: "bitbucket.gitpod-dev.com",
124125
owner: "GIT",
125126
name: "gitpod-test-repo",
127+
displayName: "Gitpod Test Repo",
126128
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
127129
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
128130
defaultBranch: "master",
@@ -150,6 +152,7 @@ class TestBitbucketServerContextParser {
150152
host: "bitbucket.gitpod-dev.com",
151153
owner: "geropl",
152154
name: "test-user-repo",
155+
displayName: "test-user-repo",
153156
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/~geropl/test-user-repo.git",
154157
webUrl: "https://bitbucket.gitpod-dev.com/users/geropl/repos/test-user-repo",
155158
defaultBranch: "main",
@@ -177,6 +180,7 @@ class TestBitbucketServerContextParser {
177180
defaultBranch: "main",
178181
host: "bitbucket.gitpod-dev.com",
179182
name: "test-user-repo",
183+
displayName: "test-user-repo",
180184
owner: "geropl",
181185
private: true,
182186
repoKind: "users",
@@ -203,6 +207,7 @@ class TestBitbucketServerContextParser {
203207
defaultBranch: "master",
204208
host: "bitbucket.gitpod-dev.com",
205209
name: "gitpod-test-repo",
210+
displayName: "Gitpod Test Repo",
206211
owner: "GIT",
207212
private: true,
208213
repoKind: "projects",
@@ -229,6 +234,7 @@ class TestBitbucketServerContextParser {
229234
defaultBranch: "master",
230235
host: "bitbucket.gitpod-dev.com",
231236
name: "gitpod-test-repo",
237+
displayName: "Gitpod Test Repo",
232238
owner: "GIT",
233239
private: true,
234240
repoKind: "projects",
@@ -256,6 +262,7 @@ class TestBitbucketServerContextParser {
256262
defaultBranch: "main",
257263
host: "bitbucket.gitpod-dev.com",
258264
name: "browser-extension-test",
265+
displayName: "browser-extension-test",
259266
owner: "svenefftinge",
260267
repoKind: "users",
261268
private: false,
@@ -284,6 +291,7 @@ class TestBitbucketServerContextParser {
284291
defaultBranch: "main",
285292
host: "bitbucket.gitpod-dev.com",
286293
name: "browser-extension-test",
294+
displayName: "browser-extension-test",
287295
owner: "svenefftinge",
288296
repoKind: "users",
289297
private: false,
@@ -310,6 +318,7 @@ class TestBitbucketServerContextParser {
310318
host: "bitbucket.gitpod-dev.com",
311319
owner: "GIT",
312320
name: "gitpod-test-repo",
321+
displayName: "Gitpod Test Repo",
313322
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
314323
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
315324
defaultBranch: "master",
@@ -323,6 +332,7 @@ class TestBitbucketServerContextParser {
323332
host: "bitbucket.gitpod-dev.com",
324333
owner: "GIT",
325334
name: "gitpod-test-repo",
335+
displayName: "Gitpod Test Repo",
326336
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
327337
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
328338
defaultBranch: "master",
@@ -350,6 +360,7 @@ class TestBitbucketServerContextParser {
350360
host: "bitbucket.gitpod-dev.com",
351361
owner: "GIT",
352362
name: "gitpod-test-repo",
363+
displayName: "Gitpod Test Repo",
353364
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
354365
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
355366
defaultBranch: "master",
@@ -363,6 +374,7 @@ class TestBitbucketServerContextParser {
363374
host: "bitbucket.gitpod-dev.com",
364375
owner: "GIT",
365376
name: "gitpod-test-repo",
377+
displayName: "Gitpod Test Repo",
366378
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
367379
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
368380
defaultBranch: "master",
@@ -389,6 +401,7 @@ class TestBitbucketServerContextParser {
389401
host: "bitbucket.gitpod-dev.com",
390402
owner: "GIT",
391403
name: "gitpod-test-repo",
404+
displayName: "Gitpod Test Repo",
392405
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
393406
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
394407
defaultBranch: "master",
@@ -415,6 +428,7 @@ class TestBitbucketServerContextParser {
415428
host: "bitbucket.gitpod-dev.com",
416429
owner: "GIT",
417430
name: "gitpod-test-repo",
431+
displayName: "Gitpod Test Repo",
418432
cloneUrl: "https://bitbucket.gitpod-dev.com/scm/git/gitpod-test-repo.git",
419433
webUrl: "https://bitbucket.gitpod-dev.com/projects/GIT/repos/gitpod-test-repo",
420434
defaultBranch: "master",

0 commit comments

Comments
 (0)