Skip to content

Commit b0eefca

Browse files
authored
[server] Move some admin rpcs to non EE (#17341)
* [server] Move snapshot service out of EE * Fix * retest * [server] Move some admin rpcs to non EE * fix * Fix
1 parent bfd7d07 commit b0eefca

File tree

2 files changed

+261
-373
lines changed

2 files changed

+261
-373
lines changed

components/server/ee/src/workspace/gitpod-server-impl.ts

Lines changed: 1 addition & 339 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,13 @@
55
*/
66

77
import { injectable, inject } from "inversify";
8-
import { GitpodServerImpl, traceAPIParams, traceWI, censor } from "../../../src/workspace/gitpod-server-impl";
8+
import { GitpodServerImpl, traceAPIParams, traceWI } from "../../../src/workspace/gitpod-server-impl";
99
import { TraceContext, TraceContextWithSpan } from "@gitpod/gitpod-protocol/lib/util/tracing";
1010
import {
1111
GitpodClient,
12-
AdminGetListRequest,
1312
User,
1413
Team,
15-
TeamMemberInfo,
16-
AdminGetListResult,
1714
Permission,
18-
AdminBlockUserRequest,
19-
AdminModifyRoleOrPermissionRequest,
20-
RoleOrPermission,
21-
AdminModifyPermanentWorkspaceFeatureFlagRequest,
22-
UserFeatureSettings,
23-
AdminGetWorkspacesRequest,
24-
WorkspaceAndInstance,
2515
GetWorkspaceTimeoutResult,
2616
WorkspaceTimeoutDuration,
2717
SetWorkspaceTimeoutResult,
@@ -38,7 +28,6 @@ import {
3828
StartPrebuildResult,
3929
ClientHeaderFields,
4030
FindPrebuildsParams,
41-
TeamMemberRole,
4231
WORKSPACE_TIMEOUT_DEFAULT_SHORT,
4332
PrebuildEvent,
4433
OpenPrebuildContext,
@@ -47,7 +36,6 @@ import { ResponseError } from "vscode-jsonrpc";
4736
import {
4837
AdmissionLevel,
4938
ControlAdmissionRequest,
50-
StopWorkspacePolicy,
5139
DescribeWorkspaceRequest,
5240
SetTimeoutRequest,
5341
} from "@gitpod/ws-manager/lib";
@@ -56,7 +44,6 @@ import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
5644
import { LicenseValidationResult } from "@gitpod/gitpod-protocol/lib/license-protocol";
5745
import { PrebuildManager } from "../prebuilds/prebuild-manager";
5846
import { GuardedCostCenter, ResourceAccessGuard, ResourceAccessOp } from "../../../src/auth/resource-access";
59-
import { BlockedRepository } from "@gitpod/gitpod-protocol/lib/blocked-repositories-protocol";
6047
import { CostCenterJSON, ListUsageRequest, ListUsageResponse } from "@gitpod/gitpod-protocol/lib/usage";
6148
import {
6249
CostCenter,
@@ -367,331 +354,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
367354
});
368355
}
369356

370-
async adminGetUsers(ctx: TraceContext, req: AdminGetListRequest<User>): Promise<AdminGetListResult<User>> {
371-
traceAPIParams(ctx, { req: censor(req, "searchTerm") }); // searchTerm may contain PII
372-
373-
await this.guardAdminAccess("adminGetUsers", { req }, Permission.ADMIN_USERS);
374-
375-
try {
376-
const res = await this.userDB.findAllUsers(
377-
req.offset,
378-
req.limit,
379-
req.orderBy,
380-
req.orderDir === "asc" ? "ASC" : "DESC",
381-
req.searchTerm,
382-
);
383-
res.rows = res.rows.map(this.censorUser);
384-
return res;
385-
} catch (e) {
386-
throw new ResponseError(ErrorCodes.INTERNAL_SERVER_ERROR, e.toString());
387-
}
388-
}
389-
390-
async adminGetUser(ctx: TraceContext, userId: string): Promise<User> {
391-
traceAPIParams(ctx, { userId });
392-
393-
await this.guardAdminAccess("adminGetUser", { id: userId }, Permission.ADMIN_USERS);
394-
395-
let result: User | undefined;
396-
try {
397-
result = await this.userDB.findUserById(userId);
398-
} catch (e) {
399-
throw new ResponseError(ErrorCodes.INTERNAL_SERVER_ERROR, e.toString());
400-
}
401-
402-
if (!result) {
403-
throw new ResponseError(ErrorCodes.NOT_FOUND, "not found");
404-
}
405-
return this.censorUser(result);
406-
}
407-
408-
async adminBlockUser(ctx: TraceContext, req: AdminBlockUserRequest): Promise<User> {
409-
traceAPIParams(ctx, { req });
410-
411-
await this.guardAdminAccess("adminBlockUser", { req }, Permission.ADMIN_USERS);
412-
413-
const targetUser = await this.userService.blockUser(req.id, req.blocked);
414-
415-
const stoppedWorkspaces = await this.workspaceStarter.stopRunningWorkspacesForUser(
416-
ctx,
417-
req.id,
418-
"user blocked by admin",
419-
StopWorkspacePolicy.IMMEDIATELY,
420-
);
421-
422-
log.info(`Stopped ${stoppedWorkspaces.length} workspaces in response to admin initiated block.`, {
423-
userId: targetUser.id,
424-
workspaceIds: stoppedWorkspaces.map((w) => w.id),
425-
});
426-
427-
// For some reason, returning the result of `this.userDB.storeUser(target)` does not work. The response never arrives the caller.
428-
// Returning `target` instead (which should be equivalent).
429-
return this.censorUser(targetUser);
430-
}
431-
432-
async adminVerifyUser(ctx: TraceContext, userId: string): Promise<User> {
433-
await this.guardAdminAccess("adminVerifyUser", { id: userId }, Permission.ADMIN_USERS);
434-
try {
435-
const user = await this.userDB.findUserById(userId);
436-
if (!user) {
437-
throw new ResponseError(ErrorCodes.NOT_FOUND, `No user with id ${userId} found.`);
438-
}
439-
this.verificationService.markVerified(user);
440-
await this.userDB.updateUserPartial(user);
441-
return user;
442-
} catch (e) {
443-
throw new ResponseError(ErrorCodes.INTERNAL_SERVER_ERROR, e.toString());
444-
}
445-
}
446-
447-
async adminDeleteUser(ctx: TraceContext, userId: string): Promise<void> {
448-
traceAPIParams(ctx, { userId });
449-
450-
await this.guardAdminAccess("adminDeleteUser", { id: userId }, Permission.ADMIN_USERS);
451-
452-
try {
453-
await this.userDeletionService.deleteUser(userId);
454-
} catch (e) {
455-
throw new ResponseError(ErrorCodes.INTERNAL_SERVER_ERROR, e.toString());
456-
}
457-
}
458-
459-
async adminGetBlockedRepositories(
460-
ctx: TraceContext,
461-
req: AdminGetListRequest<BlockedRepository>,
462-
): Promise<AdminGetListResult<BlockedRepository>> {
463-
traceAPIParams(ctx, { req: censor(req, "searchTerm") }); // searchTerm may contain PII
464-
465-
await this.guardAdminAccess("adminGetBlockedRepositories", { req }, Permission.ADMIN_USERS);
466-
467-
try {
468-
const res = await this.blockedRepostoryDB.findAllBlockedRepositories(
469-
req.offset,
470-
req.limit,
471-
req.orderBy,
472-
req.orderDir === "asc" ? "ASC" : "DESC",
473-
req.searchTerm,
474-
);
475-
return res;
476-
} catch (e) {
477-
throw new ResponseError(ErrorCodes.INTERNAL_SERVER_ERROR, e.toString());
478-
}
479-
}
480-
481-
async adminCreateBlockedRepository(
482-
ctx: TraceContext,
483-
urlRegexp: string,
484-
blockUser: boolean,
485-
): Promise<BlockedRepository> {
486-
traceAPIParams(ctx, { urlRegexp, blockUser });
487-
488-
await this.guardAdminAccess("adminCreateBlockedRepository", { urlRegexp, blockUser }, Permission.ADMIN_USERS);
489-
490-
return await this.blockedRepostoryDB.createBlockedRepository(urlRegexp, blockUser);
491-
}
492-
493-
async adminDeleteBlockedRepository(ctx: TraceContext, id: number): Promise<void> {
494-
traceAPIParams(ctx, { id });
495-
496-
await this.guardAdminAccess("adminDeleteBlockedRepository", { id }, Permission.ADMIN_USERS);
497-
498-
await this.blockedRepostoryDB.deleteBlockedRepository(id);
499-
}
500-
501-
async adminModifyRoleOrPermission(ctx: TraceContext, req: AdminModifyRoleOrPermissionRequest): Promise<User> {
502-
traceAPIParams(ctx, { req });
503-
504-
await this.guardAdminAccess("adminModifyRoleOrPermission", { req }, Permission.ADMIN_USERS);
505-
506-
const target = await this.userDB.findUserById(req.id);
507-
if (!target) {
508-
throw new ResponseError(ErrorCodes.NOT_FOUND, "not found");
509-
}
510-
511-
const rolesOrPermissions = new Set((target.rolesOrPermissions || []) as string[]);
512-
req.rpp.forEach((e) => {
513-
if (e.add) {
514-
rolesOrPermissions.add(e.r as string);
515-
} else {
516-
rolesOrPermissions.delete(e.r as string);
517-
}
518-
});
519-
target.rolesOrPermissions = Array.from(rolesOrPermissions.values()) as RoleOrPermission[];
520-
521-
await this.userDB.storeUser(target);
522-
// For some reason, neither returning the result of `this.userDB.storeUser(target)` nor returning `target` work.
523-
// The response never arrives the caller.
524-
// Returning the following works at the cost of an additional DB query:
525-
return this.censorUser((await this.userDB.findUserById(req.id))!);
526-
}
527-
528-
async adminModifyPermanentWorkspaceFeatureFlag(
529-
ctx: TraceContext,
530-
req: AdminModifyPermanentWorkspaceFeatureFlagRequest,
531-
): Promise<User> {
532-
traceAPIParams(ctx, { req });
533-
534-
await this.guardAdminAccess("adminModifyPermanentWorkspaceFeatureFlag", { req }, Permission.ADMIN_USERS);
535-
const target = await this.userDB.findUserById(req.id);
536-
if (!target) {
537-
throw new ResponseError(ErrorCodes.NOT_FOUND, "not found");
538-
}
539-
540-
const featureSettings: UserFeatureSettings = target.featureFlags || {};
541-
const featureFlags = new Set(featureSettings.permanentWSFeatureFlags || []);
542-
543-
req.changes.forEach((e) => {
544-
if (e.add) {
545-
featureFlags.add(e.featureFlag);
546-
} else {
547-
featureFlags.delete(e.featureFlag);
548-
}
549-
});
550-
featureSettings.permanentWSFeatureFlags = Array.from(featureFlags);
551-
target.featureFlags = featureSettings;
552-
553-
await this.userDB.storeUser(target);
554-
// For some reason, returning the result of `this.userDB.storeUser(target)` does not work. The response never arrives the caller.
555-
// Returning `target` instead (which should be equivalent).
556-
return this.censorUser(target);
557-
}
558-
559-
async adminGetTeamMembers(ctx: TraceContext, teamId: string): Promise<TeamMemberInfo[]> {
560-
await this.guardAdminAccess("adminGetTeamMembers", { teamId }, Permission.ADMIN_WORKSPACES);
561-
562-
const team = await this.teamDB.findTeamById(teamId);
563-
if (!team) {
564-
throw new ResponseError(ErrorCodes.NOT_FOUND, "Team not found");
565-
}
566-
const members = await this.teamDB.findMembersByTeam(team.id);
567-
return members;
568-
}
569-
570-
async adminGetTeams(ctx: TraceContext, req: AdminGetListRequest<Team>): Promise<AdminGetListResult<Team>> {
571-
await this.guardAdminAccess("adminGetTeams", { req }, Permission.ADMIN_WORKSPACES);
572-
573-
return await this.teamDB.findTeams(
574-
req.offset,
575-
req.limit,
576-
req.orderBy,
577-
req.orderDir === "asc" ? "ASC" : "DESC",
578-
req.searchTerm as string,
579-
);
580-
}
581-
582-
async adminGetTeamById(ctx: TraceContext, id: string): Promise<Team | undefined> {
583-
await this.guardAdminAccess("adminGetTeamById", { id }, Permission.ADMIN_WORKSPACES);
584-
return await this.teamDB.findTeamById(id);
585-
}
586-
587-
async adminSetTeamMemberRole(
588-
ctx: TraceContext,
589-
teamId: string,
590-
userId: string,
591-
role: TeamMemberRole,
592-
): Promise<void> {
593-
await this.guardAdminAccess("adminSetTeamMemberRole", { teamId, userId, role }, Permission.ADMIN_WORKSPACES);
594-
return this.teamDB.setTeamMemberRole(userId, teamId, role);
595-
}
596-
597-
async adminGetWorkspaces(
598-
ctx: TraceContext,
599-
req: AdminGetWorkspacesRequest,
600-
): Promise<AdminGetListResult<WorkspaceAndInstance>> {
601-
traceAPIParams(ctx, { req });
602-
603-
await this.guardAdminAccess("adminGetWorkspaces", { req }, Permission.ADMIN_WORKSPACES);
604-
605-
return await this.workspaceDb
606-
.trace(ctx)
607-
.findAllWorkspaceAndInstances(
608-
req.offset,
609-
req.limit,
610-
req.orderBy,
611-
req.orderDir === "asc" ? "ASC" : "DESC",
612-
req,
613-
);
614-
}
615-
616-
async adminGetWorkspace(ctx: TraceContext, workspaceId: string): Promise<WorkspaceAndInstance> {
617-
traceAPIParams(ctx, { workspaceId });
618-
619-
await this.guardAdminAccess("adminGetWorkspace", { id: workspaceId }, Permission.ADMIN_WORKSPACES);
620-
621-
const result = await this.workspaceDb.trace(ctx).findWorkspaceAndInstance(workspaceId);
622-
if (!result) {
623-
throw new ResponseError(ErrorCodes.NOT_FOUND, "not found");
624-
}
625-
return result;
626-
}
627-
628-
async adminGetWorkspaceInstances(ctx: TraceContext, workspaceId: string): Promise<WorkspaceInstance[]> {
629-
traceAPIParams(ctx, { workspaceId });
630-
631-
await this.guardAdminAccess("adminGetWorkspaceInstances", { id: workspaceId }, Permission.ADMIN_WORKSPACES);
632-
633-
const result = await this.workspaceDb.trace(ctx).findInstances(workspaceId);
634-
return result || [];
635-
}
636-
637-
async adminForceStopWorkspace(ctx: TraceContext, workspaceId: string): Promise<void> {
638-
traceAPIParams(ctx, { workspaceId });
639-
640-
await this.guardAdminAccess("adminForceStopWorkspace", { id: workspaceId }, Permission.ADMIN_WORKSPACES);
641-
642-
const workspace = await this.workspaceDb.trace(ctx).findById(workspaceId);
643-
if (workspace) {
644-
await this.internalStopWorkspace(ctx, workspace, "stopped by admin", StopWorkspacePolicy.IMMEDIATELY, true);
645-
}
646-
}
647-
648-
async adminRestoreSoftDeletedWorkspace(ctx: TraceContext, workspaceId: string): Promise<void> {
649-
traceAPIParams(ctx, { workspaceId });
650-
651-
await this.guardAdminAccess(
652-
"adminRestoreSoftDeletedWorkspace",
653-
{ id: workspaceId },
654-
Permission.ADMIN_WORKSPACES,
655-
);
656-
657-
await this.workspaceDb.trace(ctx).transaction(async (db) => {
658-
const ws = await db.findById(workspaceId);
659-
if (!ws) {
660-
throw new ResponseError(ErrorCodes.NOT_FOUND, `No workspace with id '${workspaceId}' found.`);
661-
}
662-
if (!ws.softDeleted) {
663-
return;
664-
}
665-
if (!!ws.contentDeletedTime) {
666-
throw new ResponseError(ErrorCodes.NOT_FOUND, "The workspace content was already garbage-collected.");
667-
}
668-
// @ts-ignore
669-
ws.softDeleted = null;
670-
ws.softDeletedTime = "";
671-
ws.pinned = true;
672-
await db.store(ws);
673-
});
674-
}
675-
676-
async adminGetProjectsBySearchTerm(
677-
ctx: TraceContext,
678-
req: AdminGetListRequest<Project>,
679-
): Promise<AdminGetListResult<Project>> {
680-
await this.guardAdminAccess("adminGetProjectsBySearchTerm", { req }, Permission.ADMIN_PROJECTS);
681-
return await this.projectDB.findProjectsBySearchTerm(
682-
req.offset,
683-
req.limit,
684-
req.orderBy,
685-
req.orderDir === "asc" ? "ASC" : "DESC",
686-
req.searchTerm as string,
687-
);
688-
}
689-
690-
async adminGetProjectById(ctx: TraceContext, id: string): Promise<Project | undefined> {
691-
await this.guardAdminAccess("adminGetProjectById", { id }, Permission.ADMIN_PROJECTS);
692-
return await this.projectDB.findProjectById(id);
693-
}
694-
695357
protected async findPrebuiltWorkspace(
696358
parentCtx: TraceContext,
697359
user: User,

0 commit comments

Comments
 (0)