Skip to content

Commit e87346d

Browse files
committed
[server] FGA checks for all admin*Workspace methods
1 parent eaf66ba commit e87346d

File tree

3 files changed

+33
-10
lines changed

3 files changed

+33
-10
lines changed

components/server/src/authorization/definitions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export type WorkspaceResourceType = "workspace";
9191

9292
export type WorkspaceRelation = "org" | "owner" | "shared";
9393

94-
export type WorkspacePermission = "access" | "start" | "stop" | "delete" | "read_info";
94+
export type WorkspacePermission = "access" | "start" | "stop" | "delete" | "read_info" | "admin_control";
9595

9696
export const rel = {
9797
user(id: string) {

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

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,9 +2771,9 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
27712771
): Promise<AdminGetListResult<WorkspaceAndInstance>> {
27722772
traceAPIParams(ctx, { req });
27732773

2774-
await this.guardAdminAccess("adminGetWorkspaces", { req }, Permission.ADMIN_WORKSPACES);
2774+
const admin = await this.guardAdminAccess("adminGetWorkspaces", { req }, Permission.ADMIN_WORKSPACES);
27752775

2776-
return await this.workspaceDb
2776+
const wss = await this.workspaceDb
27772777
.trace(ctx)
27782778
.findAllWorkspaceAndInstances(
27792779
req.offset,
@@ -2782,12 +2782,27 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
27822782
req.orderDir === "asc" ? "ASC" : "DESC",
27832783
req,
27842784
);
2785+
2786+
await Promise.all(
2787+
wss.rows.map(async (row) => {
2788+
if (!(await this.auth.hasPermissionOnWorkspace(admin.id, "access", row.workspaceId))) {
2789+
wss.total--;
2790+
wss.rows = wss.rows.filter((ws) => ws.workspaceId !== row.workspaceId);
2791+
}
2792+
}),
2793+
);
2794+
return wss;
27852795
}
27862796

27872797
async adminGetWorkspace(ctx: TraceContext, workspaceId: string): Promise<WorkspaceAndInstance> {
27882798
traceAPIParams(ctx, { workspaceId });
27892799

2790-
await this.guardAdminAccess("adminGetWorkspace", { id: workspaceId }, Permission.ADMIN_WORKSPACES);
2800+
const admin = await this.guardAdminAccess(
2801+
"adminGetWorkspace",
2802+
{ id: workspaceId },
2803+
Permission.ADMIN_WORKSPACES,
2804+
);
2805+
await this.auth.checkPermissionOnWorkspace(admin.id, "access", workspaceId);
27912806

27922807
const result = await this.workspaceDb.trace(ctx).findWorkspaceAndInstance(workspaceId);
27932808
if (!result) {
@@ -2799,7 +2814,12 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
27992814
async adminGetWorkspaceInstances(ctx: TraceContext, workspaceId: string): Promise<WorkspaceInstance[]> {
28002815
traceAPIParams(ctx, { workspaceId });
28012816

2802-
await this.guardAdminAccess("adminGetWorkspaceInstances", { id: workspaceId }, Permission.ADMIN_WORKSPACES);
2817+
const admin = await this.guardAdminAccess(
2818+
"adminGetWorkspaceInstances",
2819+
{ id: workspaceId },
2820+
Permission.ADMIN_WORKSPACES,
2821+
);
2822+
await this.auth.checkPermissionOnWorkspace(admin.id, "access", workspaceId);
28032823

28042824
const result = await this.workspaceDb.trace(ctx).findInstances(workspaceId);
28052825
return result || [];
@@ -2813,6 +2833,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
28132833
{ id: workspaceId },
28142834
Permission.ADMIN_WORKSPACES,
28152835
);
2836+
await this.auth.checkPermissionOnWorkspace(admin.id, "admin_control", workspaceId);
28162837

28172838
const workspace = await this.workspaceDb.trace(ctx).findById(workspaceId);
28182839
if (workspace) {
@@ -2830,11 +2851,12 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
28302851
async adminRestoreSoftDeletedWorkspace(ctx: TraceContext, workspaceId: string): Promise<void> {
28312852
traceAPIParams(ctx, { workspaceId });
28322853

2833-
await this.guardAdminAccess(
2854+
const admin = await this.guardAdminAccess(
28342855
"adminRestoreSoftDeletedWorkspace",
28352856
{ id: workspaceId },
28362857
Permission.ADMIN_WORKSPACES,
28372858
);
2859+
await this.auth.checkPermissionOnWorkspace(admin.id, "admin_control", workspaceId);
28382860

28392861
await this.workspaceDb.trace(ctx).transaction(async (db) => {
28402862
const ws = await db.findById(workspaceId);

components/spicedb/schema/schema.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,19 @@ schema: |-
109109
relation shared: user:*
110110
111111
// Whether a user can access a workspace (with an IDE)
112-
//+ (hasAccessToRepository && isPrebuild)
113-
permission access = owner + shared
112+
permission access = owner + shared + admin_control
114113
115114
// Note: All of this is modelled after current behavior.
116115
// There are a lot of improvements we can make here in the light of Organizations, but we explicitly do that as a separate step
117116
permission start = owner
118-
permission stop = owner + org->installation_admin
117+
permission stop = owner + admin_control
119118
permission delete = owner
120119
121120
// Whether a user can read basic info/metadata of a workspace
122-
//+ (hasAccessToRepository && isPrebuild)
123121
permission read_info = owner + shared + org->member
122+
123+
// Whether someone is allowed to do administrative tasks on a workspace, e.g. un-delete etc.
124+
permission admin_control = org->installation_admin
124125
}
125126
# relationships to be used for assertions & validation
126127
relationships: |-

0 commit comments

Comments
 (0)