Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions components/server/src/iam/iam-session-app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import request from "supertest";

import * as chai from "chai";
import { OIDCCreateSessionPayload } from "./iam-oidc-create-session-payload";
import { TeamMemberInfo, TeamMemberRole, User } from "@gitpod/gitpod-protocol";
import { TeamMemberInfo, User } from "@gitpod/gitpod-protocol";
import { OrganizationService } from "../orgs/organization-service";
import { UserService } from "../user/user-service";
import { TeamDB, UserDB } from "@gitpod/gitpod-db/lib";
Expand All @@ -40,9 +40,6 @@ class TestIamSessionApp {
};

protected userServiceMock: Partial<UserService> = {
createUser: (params) => {
return { id: "id-new-user" } as any;
},
updateUser: (userId, update) => {
return {} as any;
},
Expand All @@ -67,8 +64,10 @@ class TestIamSessionApp {
listMembers: async (teamId: string): Promise<TeamMemberInfo[]> => {
return [];
},
async addOrUpdateMember(userId: string, teamId: string, memberId: string, role: TeamMemberRole): Promise<void> {
this.memberships.add(memberId);
async createOrgOwnedUser(params): Promise<User> {
const user = { id: "id-new-user" } as any as User;
this.memberships.add(user.id);
return user;
},
};

Expand Down
35 changes: 9 additions & 26 deletions components/server/src/iam/iam-session-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { reportJWTCookieIssued } from "../prometheus-metrics";
import { ApplicationError } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { OrganizationService } from "../orgs/organization-service";
import { UserService } from "../user/user-service";
import { UserDB } from "@gitpod/gitpod-db/lib";
import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer";
import { runWithSubjectId, runWithRequestContext } from "../util/request-context";

Expand All @@ -29,7 +28,6 @@ export class IamSessionApp {
@inject(UserService) private readonly userService: UserService,
@inject(OrganizationService) private readonly orgService: OrganizationService,
@inject(SessionHandler) private readonly session: SessionHandler,
@inject(UserDB) private readonly userDb: UserDB,
) {}

public getMiddlewares() {
Expand Down Expand Up @@ -167,30 +165,15 @@ export class IamSessionApp {
private async createNewOIDCUser(payload: OIDCCreateSessionPayload): Promise<User> {
const { claims, organizationId } = payload;

return this.userDb.transaction(async (_, ctx) => {
// Until we support SKIM (or any other means to sync accounts) we create new users here as a side-effect of the login
const user = await this.userService.createUser(
{
organizationId,
identity: { ...this.mapOIDCProfileToIdentity(payload), lastSigninTime: new Date().toISOString() },
userUpdate: (user) => {
user.fullName = claims.name;
user.name = claims.name;
user.avatarUrl = claims.picture;
},
},
ctx,
);

await this.orgService.addOrUpdateMember(
SYSTEM_USER_ID,
organizationId,
user.id,
"member",
{ flexibleRole: true },
ctx,
);
return user;
// Until we support SKIM (or any other means to sync accounts) we create new users here as a side-effect of the login
return this.orgService.createOrgOwnedUser({
organizationId,
identity: { ...this.mapOIDCProfileToIdentity(payload), lastSigninTime: new Date().toISOString() },
userUpdate: (user) => {
user.fullName = claims.name;
user.name = claims.name;
user.avatarUrl = claims.picture;
},
});
}
}
52 changes: 51 additions & 1 deletion components/server/src/orgs/organization-service.spec.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* See License.AGPL.txt in the project root for license information.
*/

import { BUILTIN_INSTLLATION_ADMIN_USER_ID, TypeORM } from "@gitpod/gitpod-db/lib";
import { BUILTIN_INSTLLATION_ADMIN_USER_ID, TypeORM, UserDB } from "@gitpod/gitpod-db/lib";
import { Organization, OrganizationSettings, TeamMemberRole, User } from "@gitpod/gitpod-protocol";
import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
Expand Down Expand Up @@ -473,4 +473,54 @@ describe("OrganizationService", async () => {
);
await assertUpdateSettings("should enable workspace sharing", { workspaceSharingDisabled: false }, {});
});

it("org-owned users can't create new organizations", async () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@filiptronicek These are the core tests added

const userDB = container.get<UserDB>(UserDB);
const os = container.get(OrganizationService);

// create the owner (installation-level)
const owner = await userDB.newUser();

// create an org
const orgService = container.get(OrganizationService);
const myOrg = await orgService.createOrganization(owner.id, "my-org");

// create org-owned user
const member = await createOrgOwnedUser(os, myOrg.id);

await expectError(ErrorCodes.PERMISSION_DENIED, () => os.createOrganization(member.id, "member's crew"));
});

it("org-owned users can't join another org", async () => {
const userDB = container.get<UserDB>(UserDB);
const os = container.get(OrganizationService);

// create the owner (installation-level)
const owner = await userDB.newUser();

// create the orgs
const orgService = container.get(OrganizationService);
const myOrg = await orgService.createOrganization(owner.id, "my-org");
const anotherOrg = await orgService.createOrganization(owner.id, "another-org");

// create org-owned user
const member = await createOrgOwnedUser(os, myOrg.id);

const failingInvite = await orgService.getOrCreateInvite(owner.id, anotherOrg.id);
await expectError(ErrorCodes.PERMISSION_DENIED, () => os.joinOrganization(member.id, failingInvite.id));
});
});

async function createOrgOwnedUser(os: OrganizationService, organizationId: string) {
// create org-owned member
return os.createOrgOwnedUser({
organizationId,
identity: {
authId: "123",
authProviderId: "https://accounts.google.com",
authName: "member",
lastSigninTime: new Date().toISOString(),
},
userUpdate: (user) => {},
});
}
2 changes: 1 addition & 1 deletion components/server/src/orgs/organization-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe("OrganizationService", async () => {
} as any as InstallationService);
container.bind(StripeService).toConstantValue({} as any as StripeService);
container.bind(UsageService).toConstantValue({} as any as UsageService);
container.bind(UserAuthentication).toSelf().inSingletonScope();
container.bind(UserAuthentication).toConstantValue({} as any as UserAuthentication);
os = container.get(OrganizationService);
});

Expand Down
23 changes: 22 additions & 1 deletion components/server/src/orgs/organization-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
TeamMembershipInvite,
WorkspaceTimeoutDuration,
OrgMemberRole,
User,
} from "@gitpod/gitpod-protocol";
import { IAnalyticsWriter } from "@gitpod/gitpod-protocol/lib/analytics";
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
Expand All @@ -33,7 +34,7 @@ import { StripeService } from "../billing/stripe-service";
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
import { UsageService } from "./usage-service";
import { CostCenter_BillingStrategy } from "@gitpod/gitpod-protocol/lib/usage";
import { UserAuthentication } from "../user/user-authentication";
import { CreateUserParams, UserAuthentication } from "../user/user-authentication";

@injectable()
export class OrganizationService {
Expand Down Expand Up @@ -324,6 +325,26 @@ export class OrganizationService {
return invite.teamId;
}

/**
* Convenience method, analogue to UserService.createUser()
``
*/
public async createOrgOwnedUser(params: CreateUserParams & { organizationId: string }): Promise<User> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the method that before was buried in IamSessionApp.

return this.userDB.transaction(async (_, ctx) => {
const user = await this.userService.createUser(params, ctx);

await this.addOrUpdateMember(
SYSTEM_USER_ID,
params.organizationId,
user.id,
"member",
{ flexibleRole: true },
ctx,
);
return user;
});
}

/**
* Add or update member to an organization, if there's no `owner` in the organization, target role will be owner
*
Expand Down
7 changes: 2 additions & 5 deletions components/server/src/user/env-var-service.spec.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-s
import * as chai from "chai";
import { Container } from "inversify";
import "mocha";
import { createTestContainer, withTestCtx } from "../test/service-testing-container-module";
import { createTestContainer } from "../test/service-testing-container-module";
import { resetDB } from "@gitpod/gitpod-db/lib/test/reset-db";
import { OrganizationService } from "../orgs/organization-service";
import { UserService } from "./user-service";
import { expectError } from "../test/expect-utils";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { EnvVarService } from "./env-var-service";
import { ProjectsService } from "../projects/projects-service";
import { SYSTEM_USER } from "../authorization/authorizer";

const expect = chai.expect;

Expand Down Expand Up @@ -98,9 +97,8 @@ describe("EnvVarService", async () => {

const orgService = container.get<OrganizationService>(OrganizationService);
org = await orgService.createOrganization(BUILTIN_INSTLLATION_ADMIN_USER_ID, "myOrg");
const invite = await orgService.getOrCreateInvite(BUILTIN_INSTLLATION_ADMIN_USER_ID, org.id);

member = await userService.createUser({
member = await orgService.createOrgOwnedUser({
organizationId: org.id,
identity: {
authId: "foo",
Expand All @@ -109,7 +107,6 @@ describe("EnvVarService", async () => {
primaryEmail: "[email protected]",
},
});
await withTestCtx(SYSTEM_USER, () => orgService.joinOrganization(member.id, invite.id));
stranger = await userService.createUser({
identity: {
authId: "foo2",
Expand Down
7 changes: 2 additions & 5 deletions components/server/src/user/gitpod-token-service.spec.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-s
import * as chai from "chai";
import { Container } from "inversify";
import "mocha";
import { createTestContainer, withTestCtx } from "../test/service-testing-container-module";
import { createTestContainer } from "../test/service-testing-container-module";
import { resetDB } from "@gitpod/gitpod-db/lib/test/reset-db";
import { OrganizationService } from "../orgs/organization-service";
import { UserService } from "./user-service";
import { expectError } from "../test/expect-utils";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { GitpodTokenService } from "./gitpod-token-service";
import { SYSTEM_USER } from "../authorization/authorizer";

const expect = chai.expect;

Expand All @@ -35,10 +34,9 @@ describe("GitpodTokenService", async () => {

const orgService = container.get<OrganizationService>(OrganizationService);
org = await orgService.createOrganization(BUILTIN_INSTLLATION_ADMIN_USER_ID, "myOrg");
const invite = await orgService.getOrCreateInvite(BUILTIN_INSTLLATION_ADMIN_USER_ID, org.id);

const userService = container.get<UserService>(UserService);
member = await userService.createUser({
member = await orgService.createOrgOwnedUser({
organizationId: org.id,
identity: {
authId: "foo",
Expand All @@ -47,7 +45,6 @@ describe("GitpodTokenService", async () => {
primaryEmail: "[email protected]",
},
});
await withTestCtx(SYSTEM_USER, () => orgService.joinOrganization(member.id, invite.id));
stranger = await userService.createUser({
identity: {
authId: "foo2",
Expand Down
7 changes: 2 additions & 5 deletions components/server/src/user/sshkey-service.spec.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-s
import * as chai from "chai";
import { Container } from "inversify";
import "mocha";
import { createTestContainer, withTestCtx } from "../test/service-testing-container-module";
import { createTestContainer } from "../test/service-testing-container-module";
import { resetDB } from "@gitpod/gitpod-db/lib/test/reset-db";
import { SSHKeyService } from "./sshkey-service";
import { OrganizationService } from "../orgs/organization-service";
import { UserService } from "./user-service";
import { expectError } from "../test/expect-utils";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { SYSTEM_USER } from "../authorization/authorizer";

const expect = chai.expect;

Expand Down Expand Up @@ -46,10 +45,9 @@ describe("SSHKeyService", async () => {

const orgService = container.get<OrganizationService>(OrganizationService);
org = await orgService.createOrganization(BUILTIN_INSTLLATION_ADMIN_USER_ID, "myOrg");
const invite = await orgService.getOrCreateInvite(BUILTIN_INSTLLATION_ADMIN_USER_ID, org.id);

const userService = container.get<UserService>(UserService);
member = await userService.createUser({
member = await orgService.createOrgOwnedUser({
organizationId: org.id,
identity: {
authId: "foo",
Expand All @@ -58,7 +56,6 @@ describe("SSHKeyService", async () => {
primaryEmail: "[email protected]",
},
});
await withTestCtx(SYSTEM_USER, () => orgService.joinOrganization(member.id, invite.id));
stranger = await userService.createUser({
identity: {
authId: "foo2",
Expand Down
22 changes: 2 additions & 20 deletions components/server/src/user/token-service.spec.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-s
import * as chai from "chai";
import "mocha";
import { Container } from "inversify";
import { createTestContainer, withTestCtx } from "../test/service-testing-container-module";
import { createTestContainer } from "../test/service-testing-container-module";
import { BUILTIN_INSTLLATION_ADMIN_USER_ID, TypeORM, UserDB } from "@gitpod/gitpod-db/lib";
import { resetDB } from "@gitpod/gitpod-db/lib/test/reset-db";
import { OrganizationService } from "../orgs/organization-service";
import { SYSTEM_USER } from "../authorization/authorizer";
import { UserService } from "./user-service";
import { Organization, Token, User } from "@gitpod/gitpod-protocol";
import { TokenService } from "./token-service";
import { TokenProvider } from "./token-provider";
Expand All @@ -33,11 +31,9 @@ describe("TokenService", async () => {

let container: Container;
let tokenService: TokenService;
let userService: UserService;
let userDB: UserDB;
let orgService: OrganizationService;
let org: Organization;
let owner: User;
let user: User;

let token: Token;
Expand Down Expand Up @@ -127,23 +123,10 @@ describe("TokenService", async () => {

tokenService = container.get<TokenService>(TokenService);
userDB = container.get<UserDB>(UserDB);
userService = container.get<UserService>(UserService);
orgService = container.get<OrganizationService>(OrganizationService);
org = await orgService.createOrganization(BUILTIN_INSTLLATION_ADMIN_USER_ID, "myOrg");
const invite = await orgService.getOrCreateInvite(BUILTIN_INSTLLATION_ADMIN_USER_ID, org.id);
// first not builtin user join an org will be an owner
owner = await userService.createUser({
organizationId: org.id,
identity: {
authId: "foo",
authName: "bar",
authProviderId: "github",
primaryEmail: "[email protected]",
},
});
await withTestCtx(SYSTEM_USER, () => orgService.joinOrganization(owner.id, invite.id));

user = await userService.createUser({
user = await orgService.createOrgOwnedUser({
organizationId: org.id,
identity: {
authId: githubUserAuthId,
Expand All @@ -159,7 +142,6 @@ describe("TokenService", async () => {
primaryEmail: "[email protected]",
});
await userDB.storeUser(user);
await withTestCtx(SYSTEM_USER, () => orgService.joinOrganization(user.id, invite.id));

// test data
token = <Token>{
Expand Down
Loading
Loading