From f62db8e44267d839b6662319bafa4a775b1c3f24 Mon Sep 17 00:00:00 2001 From: svenefftinge Date: Thu, 21 Sep 2023 06:21:13 +0000 Subject: [PATCH] [fga] trying to understand the sharing issue --- .../server/src/authorization/authorizer.ts | 23 ++++++------------- .../src/authorization/spicedb-authorizer.ts | 3 ++- .../src/workspace/gitpod-server-impl.ts | 21 ++++++++++++++++- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/components/server/src/authorization/authorizer.ts b/components/server/src/authorization/authorizer.ts index facf6fbb333f01..d57597cc28d60a 100644 --- a/components/server/src/authorization/authorizer.ts +++ b/components/server/src/authorization/authorizer.ts @@ -179,6 +179,7 @@ export class Authorizer { userId: string, permission: WorkspacePermission, workspaceId: string, + forceEnablement?: boolean, // temporary to find an issue with workspace sharing ): Promise { if (userId === SYSTEM_USER) { return true; @@ -191,7 +192,7 @@ export class Authorizer { consistency, }); - return this.authorizer.check(req, { userId }); + return this.authorizer.check(req, { userId }, forceEnablement); } async checkPermissionOnWorkspace(userId: string, permission: WorkspacePermission, workspaceId: string) { @@ -423,25 +424,15 @@ export class Authorizer { ): Promise { const rels: v1.RelationshipUpdate[] = []; for (const { orgID, userID, workspaceID, shared } of ids) { - this.internalAddWorkspaceToOrg(orgID, userID, workspaceID, shared, (u) => rels.push(u)); + rels.push(set(rel.workspace(workspaceID).org.organization(orgID))); + rels.push(set(rel.workspace(workspaceID).owner.user(userID))); + if (shared) { + rels.push(set(rel.workspace(workspaceID).shared.anyUser)); + } } await this.authorizer.writeRelationships(...rels); } - private internalAddWorkspaceToOrg( - orgID: string, - userID: string, - workspaceID: string, - shared: boolean, - acceptor: (update: v1.RelationshipUpdate) => void, - ): void { - acceptor(set(rel.workspace(workspaceID).org.organization(orgID))); - acceptor(set(rel.workspace(workspaceID).owner.user(userID))); - if (shared) { - acceptor(set(rel.workspace(workspaceID).shared.anyUser)); - } - } - async removeWorkspaceFromOrg(orgID: string, userID: string, workspaceID: string): Promise { if (!(await isFgaWritesEnabled(userID))) { return; diff --git a/components/server/src/authorization/spicedb-authorizer.ts b/components/server/src/authorization/spicedb-authorizer.ts index 584c03f048f90c..c445bdb64d457c 100644 --- a/components/server/src/authorization/spicedb-authorizer.ts +++ b/components/server/src/authorization/spicedb-authorizer.ts @@ -54,8 +54,9 @@ export class SpiceDBAuthorizer { experimentsFields: { userId: string; }, + forceEnablement?: boolean, ): Promise { - const featureEnabled = await isFgaChecksEnabled(experimentsFields.userId); + const featureEnabled = !!forceEnablement || (await isFgaChecksEnabled(experimentsFields.userId)); const timer = spicedbClientLatency.startTimer(); let error: Error | undefined; try { diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index b0eab46cb829e9..878ef3970eeaab 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -176,6 +176,8 @@ import { suggestionFromRecentWorkspace, suggestionFromUserRepo, } from "./suggested-repos-sorter"; +import { TrustedValue } from "@gitpod/gitpod-protocol/lib/util/scrubbing"; +import { rel } from "../authorization/definitions"; // shortcut export const traceWI = (ctx: TraceContext, wi: Omit) => TraceContext.setOWI(ctx, wi); // userId is already taken care of in WebsocketConnectionManager @@ -564,6 +566,23 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { `operation not permitted: missing ${op} permission on ${resource.kind}`, ); } + if (resource.kind === "workspace" && op === "get") { + // access to workspaces is granted. Let's verify that this is also thecase with FGA + const result = await this.auth.hasPermissionOnWorkspace( + this.userID!, + "read_info", + resource.subject.id, + true, + ); + if (!result) { + const isShared = await this.auth.find(rel.workspace(resource.subject.id).shared.anyUser); + log.error("user has access to workspace, but not through FGA", { + userId: this.userID, + workspace: new TrustedValue(resource.subject), + sharedRelationship: isShared && new TrustedValue(isShared), + }); + } + } } /** @@ -3794,7 +3813,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { if (!parsedAttributionId) { throw new ApplicationError(ErrorCodes.BAD_REQUEST, "Unable to parse attributionId"); } - await this.auth.checkPermissionOnOrganization(admin.id, "read_billing", attributionId); + await this.auth.checkPermissionOnOrganization(admin.id, "read_billing", parsedAttributionId.teamId); return this.billingModes.getBillingMode(admin.id, parsedAttributionId.teamId); }