Skip to content

Commit 834f6ec

Browse files
committed
[server] Fix permission issue for collaborators in listenForPrebuildUpdates
Incl. tests that exercise OrganizationService.listOrganizations()
1 parent 83a98be commit 834f6ec

File tree

2 files changed

+110
-3
lines changed

2 files changed

+110
-3
lines changed

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

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { TeamDB, testContainer } from "@gitpod/gitpod-db/lib";
7+
import { resetDB, TeamDB, testContainer, TypeORM } from "@gitpod/gitpod-db/lib";
88
import * as chai from "chai";
99
import { Container } from "inversify";
1010
import "mocha";
1111
import { OrganizationService } from "./organization-service";
1212
import { InstallationService } from "../auth/installation-service";
1313
import { ProjectsService } from "../projects/projects-service";
14-
import { Authorizer } from "../authorization/authorizer";
14+
import { Authorizer, SYSTEM_USER } from "../authorization/authorizer";
1515
import { IAnalyticsWriter } from "@gitpod/gitpod-protocol/lib/analytics";
1616
import { DefaultWorkspaceImageValidator } from "./default-workspace-image-validator";
1717
import { UserService } from "../user/user-service";
@@ -24,6 +24,9 @@ import { EntitlementService, EntitlementServiceImpl } from "../billing/entitleme
2424
import { EntitlementServiceUBP, LazyOrganizationService } from "../billing/entitlement-service-ubp";
2525
import { BillingModes } from "../billing/billing-mode";
2626
import { VerificationService } from "../auth/verification-service";
27+
import { Organization, User } from "@gitpod/gitpod-protocol";
28+
import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
29+
import { createTestContainer, withTestCtx } from "../test/service-testing-container-module";
2730

2831
const expect = chai.expect;
2932

@@ -197,4 +200,104 @@ describe("OrganizationService", async () => {
197200
expect(settings.allowedWorkspaceClasses).to.eql(["unknown"]);
198201
});
199202
});
203+
204+
describe("listOrganizations", async () => {
205+
let container: Container;
206+
let os: OrganizationService;
207+
let userService: UserService;
208+
let owner: User;
209+
let member: User;
210+
let collaborator: User;
211+
let stranger: User;
212+
let org1: Organization;
213+
let org2: Organization;
214+
215+
beforeEach(async () => {
216+
container = createTestContainer();
217+
Experiments.configureTestingClient({});
218+
219+
userService = container.get<UserService>(UserService);
220+
os = container.get<OrganizationService>(OrganizationService);
221+
222+
// Create test users
223+
owner = await userService.createUser({
224+
identity: {
225+
authId: "github|owner",
226+
authName: "owner",
227+
authProviderId: "github",
228+
},
229+
});
230+
231+
member = await userService.createUser({
232+
identity: {
233+
authId: "github|member",
234+
authName: "member",
235+
authProviderId: "github",
236+
},
237+
});
238+
239+
collaborator = await userService.createUser({
240+
identity: {
241+
authId: "github|collaborator",
242+
authName: "collaborator",
243+
authProviderId: "github",
244+
},
245+
});
246+
247+
stranger = await userService.createUser({
248+
identity: {
249+
authId: "github|stranger",
250+
authName: "stranger",
251+
authProviderId: "github",
252+
},
253+
});
254+
255+
// Create organizations
256+
org1 = await os.createOrganization(owner.id, "First Org");
257+
org2 = await os.createOrganization(owner.id, "Second Org");
258+
259+
// Add members with different roles
260+
const invite1 = await os.getOrCreateInvite(owner.id, org1.id);
261+
await withTestCtx(SYSTEM_USER, () => os.joinOrganization(member.id, invite1.id));
262+
await os.addOrUpdateMember(owner.id, org1.id, collaborator.id, "collaborator");
263+
264+
// Create an org-owned user
265+
await os.createOrgOwnedUser({
266+
organizationId: org1.id,
267+
identity: {
268+
authId: "github|orgowned",
269+
authName: "orgowned",
270+
authProviderId: "github",
271+
},
272+
});
273+
});
274+
275+
afterEach(async () => {
276+
// Clean up DB
277+
await resetDB(container.get(TypeORM));
278+
// Deactivate all services
279+
await container.unbindAllAsync();
280+
});
281+
282+
it("should list only organizations the user is a member of", async () => {
283+
// Owner is member of both orgs
284+
const ownerResult = await os.listOrganizations(owner.id, {}, "member");
285+
expect(ownerResult.rows.map((o) => o.id)).to.include(org1.id);
286+
expect(ownerResult.rows.map((o) => o.id)).to.include(org2.id);
287+
288+
// Member is only in org1
289+
const memberResult = await os.listOrganizations(member.id, {}, "member");
290+
expect(memberResult.rows.map((o) => o.id)).to.include(org1.id);
291+
expect(memberResult.rows.map((o) => o.id)).to.not.include(org2.id);
292+
293+
// Collaborator is only in org1
294+
const collaboratorResults = await os.listOrganizations(collaborator.id, {}, "member");
295+
expect(collaboratorResults.rows.map((o) => o.id)).to.include(org1.id);
296+
expect(collaboratorResults.rows.map((o) => o.id)).to.not.include(org2.id);
297+
298+
// Stranger is in no orgs
299+
const strangerResult = await os.listOrganizations(stranger.id, {}, "member");
300+
expect(strangerResult.total).to.equal(0);
301+
});
302+
});
200303
});

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,11 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
268268
subjectId: SubjectId.fromUserId(userId),
269269
},
270270
async () => {
271-
const organizations = await this.getTeams(ctx ?? {});
271+
const { rows: organizations } = await this.organizationService.listOrganizations(
272+
userId,
273+
{ limit: 10 },
274+
"member",
275+
);
272276
for (const organization of organizations) {
273277
const hasPermission = await this.auth.hasPermissionOnOrganization(
274278
userId,

0 commit comments

Comments
 (0)