diff --git a/components/gitpod-db/src/team-db.spec.db.ts b/components/gitpod-db/src/team-db.spec.db.ts index 682cee95ee383e..704dcbee89f395 100644 --- a/components/gitpod-db/src/team-db.spec.db.ts +++ b/components/gitpod-db/src/team-db.spec.db.ts @@ -15,6 +15,7 @@ import { TypeORM } from './typeorm/typeorm'; import { DBTeam } from './typeorm/entity/db-team'; import { DBTeamMembership } from './typeorm/entity/db-team-membership'; import { DBUser } from './typeorm/entity/db-user'; +import { DBIdentity } from './typeorm/entity/db-identity'; @suite class TeamDBSpec { @@ -35,6 +36,7 @@ import { DBUser } from './typeorm/entity/db-user'; await manager.getRepository(DBTeam).delete({}); await manager.getRepository(DBTeamMembership).delete({}); await manager.getRepository(DBUser).delete({}); + await manager.getRepository(DBIdentity).delete({}); } @test(timeout(10000)) @@ -48,5 +50,18 @@ import { DBUser } from './typeorm/entity/db-user'; expect(dbResult[0].name).to.eq('Ground Control'); } + @test(timeout(10000)) + public async findTeamMembers() { + const user = await this.userDb.newUser(); + user.identities.push({ authProviderId: 'GitHub', authId: '1234', authName: 'Major Tom', primaryEmail: 'tom@example.com' }); + await this.userDb.storeUser(user); + const team = await this.db.createTeam(user.id, 'Flight Crew'); + const members = await this.db.findMembersByTeam(team.id); + expect(members.length).to.eq(1); + expect(members[0].userId).to.eq(user.id); + expect(members[0].primaryEmail).to.eq('tom@example.com'); + } + } + module.exports = new TeamDBSpec() diff --git a/components/gitpod-db/src/team-db.ts b/components/gitpod-db/src/team-db.ts index 5ccae08a259be3..5119c705877a0b 100644 --- a/components/gitpod-db/src/team-db.ts +++ b/components/gitpod-db/src/team-db.ts @@ -4,13 +4,12 @@ * See License.enterprise.txt in the project root folder. */ -import { Team } from "@gitpod/gitpod-protocol"; -import { DBTeamMembership } from "./typeorm/entity/db-team-membership"; +import { Team, TeamMemberInfo } from "@gitpod/gitpod-protocol"; export const TeamDB = Symbol('TeamDB'); export interface TeamDB { findTeamById(teamId: string): Promise; - findMembershipsByTeam(teamId: string): Promise; + findMembersByTeam(teamId: string): Promise; findTeamsByUser(userId: string): Promise; createTeam(userId: string, name: string): Promise; } \ No newline at end of file diff --git a/components/gitpod-db/src/typeorm/team-db-impl.ts b/components/gitpod-db/src/typeorm/team-db-impl.ts index 414c951d61bd0b..ece476d52b29e7 100644 --- a/components/gitpod-db/src/typeorm/team-db-impl.ts +++ b/components/gitpod-db/src/typeorm/team-db-impl.ts @@ -4,6 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ +import { Team, TeamMemberInfo, User } from "@gitpod/gitpod-protocol"; import { inject, injectable } from "inversify"; import { TypeORM } from "./typeorm"; import { Repository } from "typeorm"; @@ -11,7 +12,7 @@ import * as uuidv4 from 'uuid/v4'; import { TeamDB } from "../team-db"; import { DBTeam } from "./entity/db-team"; import { DBTeamMembership } from "./entity/db-team-membership"; -import { Team } from "@gitpod/gitpod-protocol"; +import { DBUser } from "./entity/db-user"; @injectable() export class TeamDBImpl implements TeamDB { @@ -29,14 +30,27 @@ export class TeamDBImpl implements TeamDB { return (await this.getEntityManager()).getRepository(DBTeamMembership); } + protected async getUserRepo(): Promise> { + return (await this.getEntityManager()).getRepository(DBUser); + } + public async findTeamById(teamId: string): Promise { const teamRepo = await this.getTeamRepo(); return teamRepo.findOne({ id: teamId }); } - public async findMembershipsByTeam(teamId: string): Promise { + public async findMembersByTeam(teamId: string): Promise { const membershipRepo = await this.getMembershipRepo(); - return membershipRepo.find({ teamId }); + const userRepo = await this.getUserRepo(); + const memberships = await membershipRepo.find({ teamId }); + const users = await userRepo.findByIds(memberships.map(m => m.userId)); + return users.map(u => ({ + userId: u.id, + fullName: u.fullName || u.name, + primaryEmail: User.getPrimaryEmail(u), + avatarUrl: u.avatarUrl, + memberSince: u.creationDate, + })); } public async findTeamsByUser(userId: string): Promise { diff --git a/components/gitpod-db/src/typeorm/user-db-impl.ts b/components/gitpod-db/src/typeorm/user-db-impl.ts index 2fe6e7a035a5d1..357ea2f76eeb8a 100644 --- a/components/gitpod-db/src/typeorm/user-db-impl.ts +++ b/components/gitpod-db/src/typeorm/user-db-impl.ts @@ -205,8 +205,8 @@ export class TypeORMUserDBImpl implements UserDB { } public async findIdentitiesByName(identity: Identity): Promise { - const userRepo = await this.getIdentitiesRepo(); - const qBuilder = userRepo.createQueryBuilder('identity') + const repo = await this.getIdentitiesRepo(); + const qBuilder = repo.createQueryBuilder('identity') .where(`identity.authProviderId = :authProviderId`, { authProviderId: identity.authProviderId }) .andWhere(`identity.deleted != true`) .andWhere(`identity.authName = :authName`, { authName: identity.authName }); diff --git a/components/gitpod-db/src/user-db.spec.db.ts b/components/gitpod-db/src/user-db.spec.db.ts index ee9e251602542d..b57f239e67efa3 100644 --- a/components/gitpod-db/src/user-db.spec.db.ts +++ b/components/gitpod-db/src/user-db.spec.db.ts @@ -36,6 +36,7 @@ import { DBWorkspaceInstance } from './typeorm/entity/db-workspace-instance'; const typeorm = testContainer.get(TypeORM); const mnr = await typeorm.getConnection(); await mnr.getRepository(DBUser).delete({}); + await mnr.getRepository(DBIdentity).delete({}); await mnr.getRepository(DBWorkspace).delete({}); await mnr.getRepository(DBWorkspaceInstance).delete({}); } diff --git a/components/server/src/auth/resource-access.ts b/components/server/src/auth/resource-access.ts index 97cae0cb233978..5ea899d327ceb7 100644 --- a/components/server/src/auth/resource-access.ts +++ b/components/server/src/auth/resource-access.ts @@ -4,8 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ -import { GitpodToken, Snapshot, Team, Token, User, UserEnvVar, Workspace, WorkspaceInstance } from "@gitpod/gitpod-protocol"; -import { DBTeamMembership } from '@gitpod/gitpod-db/lib/typeorm/entity/db-team-membership'; +import { GitpodToken, Snapshot, Team, TeamMemberInfo, Token, User, UserEnvVar, Workspace, WorkspaceInstance } from "@gitpod/gitpod-protocol"; declare var resourceInstance: GuardedResource; export type GuardedResourceKind = typeof resourceInstance.kind; @@ -83,7 +82,7 @@ export interface GuardEnvVar { export interface GuardedTeam { kind: "team"; subject: Team; - memberships: DBTeamMembership[]; + members: TeamMemberInfo[]; } export interface GuardedGitpodToken { @@ -157,7 +156,7 @@ export class OwnerResourceGuard implements ResourceAccessGuard { case "envVar": return resource.subject.userId === this.userId; case "team": - return resource.memberships.some(membership => membership.userId === this.userId); + return resource.members.some(m => m.userId === this.userId); } } diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index b06488f9de5a3a..08523b0d3aa29e 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -46,7 +46,6 @@ import { MessageBusIntegration } from './messagebus-integration'; import { WorkspaceDeletionService } from './workspace-deletion-service'; import { WorkspaceFactory } from './workspace-factory'; import { WorkspaceStarter } from './workspace-starter'; -import { DBTeamMembership } from "@gitpod/gitpod-db/lib/typeorm/entity/db-team-membership"; @injectable() @@ -1396,18 +1395,9 @@ export class GitpodServerImpl => { - const member = await this.userDB.findUserById(m.userId); - return { - userId: m.userId, - fullName: member?.fullName || member?.name, - primaryEmail: !!member ? User.getPrimaryEmail(member) : undefined, - avatarUrl: member?.avatarUrl, - memberSince: m.creationTime, - }; - })); + const members = await this.teamDB.findMembersByTeam(team.id); + await this.guardAccess({ kind: "team", subject: team, members }, "get"); + return members; } public async createTeam(name: string): Promise {