Skip to content

Commit

Permalink
[server] more org service (#18174)
Browse files Browse the repository at this point in the history
  • Loading branch information
svenefftinge authored Jul 6, 2023
1 parent b523545 commit 3e215bc
Show file tree
Hide file tree
Showing 15 changed files with 438 additions and 357 deletions.
2 changes: 0 additions & 2 deletions components/gitpod-protocol/src/util/timeutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@ export const yearsLater = (fromDate: string, years: number): string =>
return d.toISOString();
});

// tslint:disable-next-line:no-shadowed-variable
export const addMillis = (d1: string, millis: number) =>
liftDate1(d1, (d1) => new Date(d1.getTime() + millis).toISOString());
export const durationInHours = (d1: string, d2: string) =>
liftDate(d1, d2, (d1, d2) => millisecondsToHours(d1.getTime() - d2.getTime()));
export const durationInMillis = (d1: string, d2: string) => liftDate(d1, d2, (d1, d2) => d1.getTime() - d2.getTime());
// tslint:disable-next-line:no-shadowed-variable
export const isDateGreaterOrEqual = (d1: string, d2: string): boolean =>
liftDate(d1, d2, (d1, d2) => d1.getTime() >= d2.getTime());
export const isDateSmallerOrEqual = (d1: string, d2: string | undefined) => !d2 || d1 <= d2;
Expand Down
2 changes: 2 additions & 0 deletions components/server/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
},
"rules": {
"no-var": "error",
"no-void": "error",
"prefer-const": "error",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-non-null-asserted-optional-chain": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-misused-promises": [
Expand Down
1 change: 0 additions & 1 deletion components/server/BUILD.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ packages:
- .eslintrc
- package.json
- mocha.opts
- tslint.yaml
deps:
- components/content-service-api/typescript:lib
- components/gitpod-db:lib
Expand Down
197 changes: 95 additions & 102 deletions components/server/src/orgs/organization-service.spec.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,42 @@
import { DBUser, TypeORM, UserDB, testContainer } from "@gitpod/gitpod-db/lib";
import { DBTeam } from "@gitpod/gitpod-db/lib/typeorm/entity/db-team";
import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { User } from "@gitpod/ide-service-api/lib/ide.pb";
import { fail } from "assert";
import * as chai from "chai";
import { Container } from "inversify";
import "mocha";
import { serviceTestingContainerModule } from "../test/service-testing-container-module";
import { OrganizationService } from "./organization-service";
import { expectError } from "../projects/projects-service.spec.db";
import { Organization } from "@gitpod/gitpod-protocol";

const expect = chai.expect;

describe("OrganizationService", async () => {
let container: Container;
let os: OrganizationService;

let owner: User;
let member: User;
let stranger: User;
let org: Organization;

beforeEach(async () => {
container = testContainer.createChild();
container.load(serviceTestingContainerModule);
Experiments.configureTestingClient({
centralizedPermissions: true,
});
os = container.get(OrganizationService);
const userDB = container.get<UserDB>(UserDB);
owner = await userDB.newUser();
org = await os.createOrganization(owner.id, "myorg");
const invite = await os.getOrCreateInvite(owner.id, org.id);

member = await userDB.newUser();
await os.joinOrganization(member.id, invite.id);

stranger = await userDB.newUser();
});

Expand All @@ -47,108 +57,58 @@ describe("OrganizationService", async () => {
await repo.delete(stranger.id);
});

it("should allow only owners to an org", async () => {
const os = container.get(OrganizationService);
const org = await os.createOrganization(owner.id, "myorg");
expect(org.name).to.equal("myorg");

const invite = await os.getOrCreateInvite(owner.id, org.id);
expect(invite).to.not.be.undefined;
await os.joinOrganization(member.id, invite.id);

try {
await os.deleteOrganization(member.id, org.id);
fail("should not be allowed");
} catch (err) {
expect(err).instanceOf(ApplicationError);
expect((err as ApplicationError).code).to.equal(ErrorCodes.PERMISSION_DENIED);
}

try {
await os.deleteOrganization(stranger.id, org.id);
fail("should not be allowed");
} catch (err) {
expect(err).instanceOf(ApplicationError);
expect((err as ApplicationError).code).to.equal(ErrorCodes.NOT_FOUND);
}
it("should deleteOrganization", async () => {
await expectError(ErrorCodes.PERMISSION_DENIED, () => os.deleteOrganization(member.id, org.id));
await expectError(ErrorCodes.NOT_FOUND, () => os.deleteOrganization(stranger.id, org.id));

await os.deleteOrganization(owner.id, org.id);
});

it("should allow owners to get an invite", async () => {
const os = container.get(OrganizationService);
const org = await os.createOrganization(owner.id, "myorg");
it("should getOrCreateInvite and resetInvite", async () => {
expect(org.name).to.equal("myorg");

const invite = await os.getOrCreateInvite(owner.id, org.id);
expect(invite).to.not.be.undefined;

const invite2 = await os.getOrCreateInvite(owner.id, org.id);
const invite2 = await os.getOrCreateInvite(member.id, org.id);
expect(invite2.id).to.equal(invite.id);

const invite3 = await os.resetInvite(owner.id, org.id);
expect(invite3.id).to.not.equal(invite.id);
});

it("check strangers cannot do much", async () => {
const os = container.get(OrganizationService);
const org = await os.createOrganization(owner.id, "myorg");
expect(org.name).to.equal("myorg");
const invite4 = await os.resetInvite(member.id, org.id);
expect(invite4.id).to.not.equal(invite3.id);

try {
await os.getOrCreateInvite(stranger.id, org.id);
fail("should have thrown");
} catch (e) {
expect(e.message).to.contain("not found");
}
await expectError(ErrorCodes.NOT_FOUND, () => os.getOrCreateInvite(stranger.id, org.id));
await expectError(ErrorCodes.NOT_FOUND, () => os.resetInvite(stranger.id, org.id));
});

// let's make sure an invite is created by the owner
const invite = await os.getOrCreateInvite(owner.id, org.id);
expect(invite).to.not.be.undefined;
it("should listMembers", async () => {
let members = await os.listMembers(owner.id, org.id);
expect(members.length).to.eq(2);
expect(members.some((m) => m.userId === owner.id)).to.be.true;
expect(members.some((m) => m.userId === member.id)).to.be.true;

members = await os.listMembers(member.id, org.id);
expect(members.length).to.eq(2);
expect(members.some((m) => m.userId === owner.id)).to.be.true;
expect(members.some((m) => m.userId === member.id)).to.be.true;

// still the invite should not be accessible to strangers
try {
await os.getOrCreateInvite(stranger.id, org.id);
fail("should have thrown");
} catch (e) {
expect(e.message).to.contain("not found");
}
await expectError(ErrorCodes.NOT_FOUND, () => os.listMembers(stranger.id, org.id));
});

it("check change and remove members", async () => {
const os = container.get(OrganizationService);
const org = await os.createOrganization(owner.id, "myorg");
expect(org.name).to.equal("myorg");
it("should setOrganizationMemberRole and removeOrganizationMember", async () => {
await expectError(ErrorCodes.PERMISSION_DENIED, () =>
os.setOrganizationMemberRole(member.id, org.id, owner.id, "member"),
);

const invite = await os.getOrCreateInvite(owner.id, org.id);
expect(invite).to.not.be.undefined;
// try upgrade the member to owner
await expectError(ErrorCodes.PERMISSION_DENIED, () =>
os.setOrganizationMemberRole(member.id, org.id, member.id, "owner"),
);

const result = await os.joinOrganization(member.id, invite.id);
expect(result.added).to.be.true;

try {
// try downgrade the owner to member
await os.setOrganizationMemberRole(member.id, org.id, owner.id, "member");
expect.fail("should have thrown");
} catch (e) {
expect(ApplicationError.hasErrorCode(e) && e.code).to.equal(ErrorCodes.PERMISSION_DENIED);
}

try {
// try upgrade the member to owner
await os.setOrganizationMemberRole(member.id, org.id, member.id, "owner");
expect.fail("should have thrown");
} catch (e) {
expect(ApplicationError.hasErrorCode(e) && e.code).to.equal(ErrorCodes.PERMISSION_DENIED);
}

try {
// try removing the owner
await os.removeOrganizationMember(member.id, org.id, owner.id);
expect.fail("should have thrown");
} catch (e) {
expect(ApplicationError.hasErrorCode(e) && e.code).to.equal(ErrorCodes.PERMISSION_DENIED);
}
// try removing the owner
await expectError(ErrorCodes.PERMISSION_DENIED, () => os.removeOrganizationMember(member.id, org.id, owner.id));

// owners can upgrade members
await os.setOrganizationMemberRole(owner.id, org.id, member.id, "owner");
Expand All @@ -162,30 +122,63 @@ describe("OrganizationService", async () => {
owner = previouslyMember;

// owner can downgrade themselves only if they are not the last owner
try {
await os.setOrganizationMemberRole(owner.id, org.id, owner.id, "member");
expect.fail("should have thrown");
} catch (error) {
expect(ApplicationError.hasErrorCode(error) && error.code, error.message).to.equal(ErrorCodes.CONFLICT);
}
await expectError(ErrorCodes.CONFLICT, () =>
os.setOrganizationMemberRole(owner.id, org.id, owner.id, "member"),
);

// owner can delete themselves only if they are not the last owner
try {
await os.setOrganizationMemberRole(owner.id, org.id, owner.id, "member");
expect.fail("should have thrown");
} catch (error) {
expect(ApplicationError.hasErrorCode(error) && error.code).to.equal(ErrorCodes.CONFLICT);
}
await expectError(ErrorCodes.CONFLICT, () => os.removeOrganizationMember(owner.id, org.id, owner.id));

// members can remove themselves
await os.removeOrganizationMember(member.id, org.id, member.id);

try {
// try remove the member again
await os.removeOrganizationMember(member.id, org.id, member.id);
expect.fail("should have thrown");
} catch (e) {
expect(ApplicationError.hasErrorCode(e) && e.code).to.equal(ErrorCodes.NOT_FOUND);
}
// try remove the member again
await expectError(ErrorCodes.NOT_FOUND, () => os.removeOrganizationMember(member.id, org.id, member.id));
});

it("should listOrganizationsByMember", async () => {
await os.createOrganization(owner.id, "org1");
await os.createOrganization(owner.id, "org2");
let orgs = await os.listOrganizationsByMember(owner.id, owner.id);
expect(orgs.length).to.eq(3);
orgs = await os.listOrganizationsByMember(member.id, owner.id);
expect(orgs.length).to.eq(1);
orgs = await os.listOrganizationsByMember(stranger.id, owner.id);
expect(orgs.length).to.eq(0);
});

it("should getOrganization", async () => {
const foundOrg = await os.getOrganization(owner.id, org.id);
expect(foundOrg.name).to.equal(org.name);

const foundByMember = await os.getOrganization(member.id, org.id);
expect(foundByMember.name).to.equal(org.name);

await expectError(ErrorCodes.NOT_FOUND, () => os.getOrganization(stranger.id, org.id));
});

it("should updateOrganization", async () => {
org.name = "newName";
await os.updateOrganization(owner.id, org.id, org);
const updated = await os.getOrganization(owner.id, org.id);
expect(updated.name).to.equal(org.name);

await expectError(ErrorCodes.PERMISSION_DENIED, () => os.updateOrganization(member.id, org.id, org));
await expectError(ErrorCodes.NOT_FOUND, () => os.updateOrganization(stranger.id, org.id, org));
});

it("should getSettings and updateSettings", async () => {
const settings = await os.getSettings(owner.id, org.id);
expect(settings).to.not.be.undefined;
expect(settings).to.not.be.null;

settings.workspaceSharingDisabled = true;

await os.updateSettings(owner.id, org.id, settings);
const updated = await os.getSettings(owner.id, org.id);
expect(updated.workspaceSharingDisabled).to.be.true;

await expectError(ErrorCodes.PERMISSION_DENIED, () => os.updateSettings(member.id, org.id, settings));
await expectError(ErrorCodes.NOT_FOUND, () => os.updateSettings(stranger.id, org.id, settings));
});
});
Loading

0 comments on commit 3e215bc

Please sign in to comment.