Skip to content

Commit

Permalink
[server] blocklist repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexTugarev committed May 4, 2022
1 parent 612041c commit fc47cb7
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 8 deletions.
16 changes: 9 additions & 7 deletions components/server/ee/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,10 @@ export class GitpodServerEEImpl extends GitpodServerImpl {

const result = await this.eligibilityService.mayStartWorkspace(user, new Date(), runningInstances);
if (!result.enoughCredits) {
throw new ResponseError(ErrorCodes.NOT_ENOUGH_CREDIT, `Not enough monthly workspace hours. Please upgrade your account to get more hours for your workspaces.`);
throw new ResponseError(
ErrorCodes.NOT_ENOUGH_CREDIT,
`Not enough monthly workspace hours. Please upgrade your account to get more hours for your workspaces.`,
);
}
if (!!result.hitParallelWorkspaceLimit) {
throw new ResponseError(
Expand Down Expand Up @@ -565,14 +568,13 @@ export class GitpodServerEEImpl extends GitpodServerImpl {

await this.guardAdminAccess("adminBlockUser", { req }, Permission.ADMIN_USERS);

const target = await this.userDB.findUserById(req.id);
if (!target) {
let targetUser;
try {
targetUser = await this.userService.blockUser(req.id, req.blocked);
} catch (error) {
throw new ResponseError(ErrorCodes.NOT_FOUND, "not found");
}

target.blocked = !!req.blocked;
await this.userDB.storeUser(target);

const workspaceDb = this.workspaceDb.trace(ctx);
const workspaces = await workspaceDb.findWorkspacesByUser(req.id);
const isDefined = <T>(x: T | undefined): x is T => x !== undefined;
Expand All @@ -584,7 +586,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {

// For some reason, returning the result of `this.userDB.storeUser(target)` does not work. The response never arrives the caller.
// Returning `target` instead (which should be equivalent).
return this.censorUser(target);
return this.censorUser(targetUser);
}

async adminDeleteUser(ctx: TraceContext, userId: string): Promise<void> {
Expand Down
22 changes: 21 additions & 1 deletion components/server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
import { filePathTelepresenceAware } from "@gitpod/gitpod-protocol/lib/env";

export const Config = Symbol("Config");
export type Config = Omit<ConfigSerialized, "hostUrl" | "chargebeeProviderOptionsFile" | "licenseFile"> & {
export type Config = Omit<
ConfigSerialized,
"blockedRepositories" | "hostUrl" | "chargebeeProviderOptionsFile" | "licenseFile"
> & {
hostUrl: GitpodHostUrl;
workspaceDefaults: WorkspaceDefaults;
chargebeeProviderOptions?: ChargebeeProviderOptions;
builtinAuthProvidersConfigured: boolean;
blockedRepositories: { urlRegExp: RegExp; blockUser: boolean }[];
};

export interface WorkspaceDefaults {
Expand Down Expand Up @@ -152,6 +156,12 @@ export interface ConfigSerialized {
* Key '*' specifies the default rate limit for a cloneURL, unless overriden by a specific cloneURL.
*/
prebuildLimiter: { [cloneURL: string]: number } & { "*": number };

/**
* List of repositories not allowed to be used for workspace starts.
* `blockUser` attribute to control handling of the user's account.
*/
blockedRepositories?: { urlRegExp: string; blockUser: boolean }[];
}

export namespace ConfigFile {
Expand Down Expand Up @@ -201,6 +211,15 @@ export namespace ConfigFile {
if (licenseFile) {
license = fs.readFileSync(filePathTelepresenceAware(licenseFile), "utf-8");
}
const blockedRepositories: { urlRegExp: RegExp; blockUser: boolean }[] = [];
if (config.blockedRepositories) {
for (const { blockUser, urlRegExp } of config.blockedRepositories) {
blockedRepositories.push({
blockUser,
urlRegExp: new RegExp(urlRegExp),
});
}
}
return {
...config,
hostUrl,
Expand All @@ -214,6 +233,7 @@ export namespace ConfigFile {
? new Date(config.workspaceGarbageCollection.startDate).getTime()
: Date.now(),
},
blockedRepositories,
};
}
}
10 changes: 10 additions & 0 deletions components/server/src/user/user-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,16 @@ export class UserService {
return false;
}

async blockUser(targetUserId: string, block: boolean): Promise<User> {
const target = await this.userDb.findUserById(targetUserId);
if (!target) {
throw new Error("Not found.");
}

target.blocked = !!block;
return await this.userDb.storeUser(target);
}

async findUserForLogin(params: { candidate: Identity }) {
let user = await this.userDb.findUserByIdentity(params.candidate);
return user;
Expand Down
18 changes: 18 additions & 0 deletions components/server/src/workspace/workspace-starter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ export class WorkspaceStarter {

options = options || {};
try {
await this.checkBlockedRepository(user, workspace.contextURL);

// Some workspaces do not have an image source.
// Workspaces without image source are not only legacy, but also happened due to what looks like a bug.
// Whenever a such a workspace is re-started we'll give it an image source now. This is in line with how this thing used to work.
Expand Down Expand Up @@ -332,6 +334,22 @@ export class WorkspaceStarter {
}
}

protected async checkBlockedRepository(user: User, contextURL: string) {
const hit = this.config.blockedRepositories.find((r) => !!contextURL && r.urlRegExp.test(contextURL));
if (!hit) {
return;
}
if (hit.blockUser) {
try {
await this.userService.blockUser(user.id, true);
log.info({ userId: user.id }, "Blocked user.", { contextURL });
} catch (error) {
log.error({ userId: user.id }, "Failed to block user.", error, { contextURL });
}
}
throw new Error(`${contextURL} is blocklisted on Gitpod.`);
}

// Note: this function does not expect to be awaited for by its caller. This means that it takes care of error handling itself.
protected async actuallyStartWorkspace(
ctx: TraceContext,
Expand Down

0 comments on commit fc47cb7

Please sign in to comment.