Skip to content

Commit 22c2a55

Browse files
authored
[server] feature flag all spicedb calls (#18428)
1 parent 73a98e7 commit 22c2a55

File tree

5 files changed

+64
-22
lines changed

5 files changed

+64
-22
lines changed

components/server/src/authorization/authorizer.spec.db.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ describe("Authorizer", async () => {
9999
const p1 = v4();
100100
const p2 = v4();
101101
await authorizer.addOrganization(
102+
"",
102103
orgId,
103104
[
104105
{ userId: u1, role: "member" },
@@ -115,7 +116,7 @@ describe("Authorizer", async () => {
115116
await expected(rel.project(p2).org.organization(orgId));
116117

117118
// add org again with different members and projects
118-
await authorizer.addOrganization(orgId, [{ userId: u2, role: "member" }], [p2]);
119+
await authorizer.addOrganization("", orgId, [{ userId: u2, role: "member" }], [p2]);
119120
await expected(rel.organization(orgId).installation.installation);
120121
await notExpected(rel.organization(orgId).member.user(u1));
121122
await expected(rel.organization(orgId).member.user(u2));

components/server/src/authorization/authorizer.ts

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
rel,
2020
} from "./definitions";
2121
import { SpiceDBAuthorizer } from "./spicedb-authorizer";
22+
import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
2223

2324
export function createInitializingAuthorizer(spiceDbAuthorizer: SpiceDBAuthorizer): Authorizer {
2425
const target = new Authorizer(spiceDbAuthorizer);
@@ -127,8 +128,10 @@ export class Authorizer {
127128
}
128129

129130
// write operations below
130-
131-
public async removeAllRelationships(type: ResourceType, id: string) {
131+
public async removeAllRelationships(userId: string, type: ResourceType, id: string) {
132+
if (await this.isDisabled(userId)) {
133+
return;
134+
}
132135
await this.authorizer.deleteRelationships(
133136
v1.DeleteRelationshipsRequest.create({
134137
relationshipFilter: {
@@ -156,7 +159,18 @@ export class Authorizer {
156159
}
157160
}
158161

162+
private async isDisabled(userId: string): Promise<boolean> {
163+
return !(await getExperimentsClientForBackend().getValueAsync("centralizedPermissions", false, {
164+
user: {
165+
id: userId,
166+
},
167+
}));
168+
}
169+
159170
async addUser(userId: string, owningOrgId?: string) {
171+
if (await this.isDisabled(userId)) {
172+
return;
173+
}
160174
const oldOrgs = await this.findAll(rel.user(userId).organization.organization(""));
161175
const updates = [set(rel.user(userId).self.user(userId))];
162176
updates.push(
@@ -184,10 +198,16 @@ export class Authorizer {
184198
}
185199

186200
async removeUser(userId: string) {
187-
await this.removeAllRelationships("user", userId);
201+
if (await this.isDisabled(userId)) {
202+
return;
203+
}
204+
await this.removeAllRelationships(userId, "user", userId);
188205
}
189206

190207
async addOrganizationRole(orgID: string, userID: string, role: TeamMemberRole): Promise<void> {
208+
if (await this.isDisabled(userID)) {
209+
return;
210+
}
191211
const updates = [set(rel.organization(orgID).member.user(userID))];
192212
if (role === "owner") {
193213
updates.push(set(rel.organization(orgID).owner.user(userID)));
@@ -198,48 +218,61 @@ export class Authorizer {
198218
}
199219

200220
async removeOrganizationRole(orgID: string, userID: string, role: TeamMemberRole): Promise<void> {
221+
if (await this.isDisabled(userID)) {
222+
return;
223+
}
201224
const updates = [remove(rel.organization(orgID).owner.user(userID))];
202225
if (role === "member") {
203226
updates.push(remove(rel.organization(orgID).member.user(userID)));
204227
}
205228
await this.authorizer.writeRelationships(...updates);
206229
}
207230

208-
async addProjectToOrg(orgID: string, projectID: string): Promise<void> {
231+
async addProjectToOrg(userId: string, orgID: string, projectID: string): Promise<void> {
232+
if (await this.isDisabled(userId)) {
233+
return;
234+
}
209235
await this.authorizer.writeRelationships(
210236
set(rel.project(projectID).org.organization(orgID)), //
211237
);
212238
}
213239

214-
async removeProjectFromOrg(orgID: string, projectID: string): Promise<void> {
240+
async removeProjectFromOrg(userId: string, orgID: string, projectID: string): Promise<void> {
241+
if (await this.isDisabled(userId)) {
242+
return;
243+
}
215244
await this.authorizer.writeRelationships(
216245
remove(rel.project(projectID).org.organization(orgID)), //
217246
);
218247
}
219248

220249
async addOrganization(
250+
userId: string,
221251
orgId: string,
222252
members: { userId: string; role: TeamMemberRole }[],
223253
projectIds: string[],
224254
): Promise<void> {
255+
if (await this.isDisabled(userId)) {
256+
return;
257+
}
225258
await this.addOrganizationMembers(orgId, members);
226259

227-
await this.addOrganizationProjects(orgId, projectIds);
260+
await this.addOrganizationProjects(userId, orgId, projectIds);
228261

229262
await this.authorizer.writeRelationships(
230263
set(rel.organization(orgId).installation.installation), //
231264
);
232265
}
233266

234-
private async addOrganizationProjects(orgID: string, projectIds: string[]): Promise<void> {
267+
private async addOrganizationProjects(userId: string, orgID: string, projectIds: string[]): Promise<void> {
235268
const existing = await this.findAll(rel.project("").org.organization(orgID));
236269
const toBeRemoved = asSet(existing.map((r) => r.resource?.objectId));
237270
for (const projectId of projectIds) {
238-
await this.addProjectToOrg(orgID, projectId);
271+
await this.addProjectToOrg(userId, orgID, projectId);
239272
toBeRemoved.delete(projectId);
240273
}
241274
for (const projectId of toBeRemoved) {
242-
await this.removeProjectFromOrg(orgID, projectId);
275+
await this.removeProjectFromOrg(userId, orgID, projectId);
243276
}
244277
}
245278

@@ -258,15 +291,21 @@ export class Authorizer {
258291
}
259292
}
260293

261-
async addInstallationAdminRole(userID: string) {
294+
async addInstallationAdminRole(userId: string) {
295+
if (await this.isDisabled(userId)) {
296+
return;
297+
}
262298
await this.authorizer.writeRelationships(
263-
set(rel.installation.admin.user(userID)), //
299+
set(rel.installation.admin.user(userId)), //
264300
);
265301
}
266302

267-
async removeInstallationAdminRole(userID: string) {
303+
async removeInstallationAdminRole(userId: string) {
304+
if (await this.isDisabled(userId)) {
305+
return;
306+
}
268307
await this.authorizer.writeRelationships(
269-
remove(rel.installation.admin.user(userID)), //
308+
remove(rel.installation.admin.user(userId)), //
270309
);
271310
}
272311

components/server/src/authorization/relationship-updater.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class RelationshipUpdater {
7474
// Add relationships
7575
await this.updateUser(user);
7676
for (const org of orgs) {
77-
await this.updateOrganization(org);
77+
await this.updateOrganization(user.id, org);
7878
}
7979
AdditionalUserData.set(user, {
8080
fgaRelationshipsVersion: this.version,
@@ -136,10 +136,11 @@ export class RelationshipUpdater {
136136
}
137137
}
138138

139-
private async updateOrganization(org: Organization): Promise<void> {
139+
private async updateOrganization(userId: string, org: Organization): Promise<void> {
140140
const members = await this.orgDB.findMembersByTeam(org.id);
141141
const projects = await this.projectDB.findProjects(org.id);
142142
await this.authorizer.addOrganization(
143+
userId,
143144
org.id,
144145
members,
145146
projects.map((p) => p.id),

components/server/src/orgs/organization-service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class OrganizationService {
6666
result = await this.teamDB.transaction(async (db) => {
6767
result = await db.createTeam(userId, name);
6868
const members = await db.findMembersByTeam(result.id);
69-
await this.auth.addOrganization(result.id, members, []);
69+
await this.auth.addOrganization(userId, result.id, members, []);
7070
return result;
7171
});
7272
} catch (err) {
@@ -110,7 +110,7 @@ export class OrganizationService {
110110

111111
await db.deleteTeam(orgId);
112112

113-
await this.auth.removeAllRelationships("organization", orgId);
113+
await this.auth.removeAllRelationships(userId, "organization", orgId);
114114
});
115115
return this.analytics.track({
116116
userId: userId,
@@ -121,6 +121,7 @@ export class OrganizationService {
121121
});
122122
} catch (err) {
123123
await this.auth.addOrganization(
124+
userId,
124125
orgId,
125126
members,
126127
projects.map((p) => p.id),

components/server/src/projects/projects-service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,10 @@ export class ProjectsService {
239239
await this.projectDB.transaction(async (db) => {
240240
await db.storeProject(project);
241241

242-
await this.auth.addProjectToOrg(teamId, project.id);
242+
await this.auth.addProjectToOrg(installer.id, teamId, project.id);
243243
});
244244
} catch (err) {
245-
await this.auth.removeProjectFromOrg(teamId, project.id);
245+
await this.auth.removeProjectFromOrg(installer.id, teamId, project.id);
246246
throw err;
247247
}
248248
await this.onDidCreateProject(project, installer);
@@ -309,7 +309,7 @@ export class ProjectsService {
309309
orgId = project.teamId;
310310
await db.markDeleted(projectId);
311311

312-
await this.auth.removeProjectFromOrg(orgId, projectId);
312+
await this.auth.removeProjectFromOrg(userId, orgId, projectId);
313313
});
314314
this.analytics.track({
315315
userId,
@@ -320,7 +320,7 @@ export class ProjectsService {
320320
});
321321
} catch (err) {
322322
if (orgId) {
323-
await this.auth.addProjectToOrg(orgId, projectId);
323+
await this.auth.addProjectToOrg(userId, orgId, projectId);
324324
}
325325
throw err;
326326
}

0 commit comments

Comments
 (0)