-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
user-deletion-service.ts
103 lines (92 loc) · 4.1 KB
/
user-deletion-service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/
import { injectable, inject } from "inversify";
import { WorkspaceDB, TeamDB } from "@gitpod/gitpod-db/lib";
import { User, Workspace } from "@gitpod/gitpod-protocol";
import { StorageClient } from "../storage/storage-client";
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
import { StopWorkspacePolicy } from "@gitpod/ws-manager/lib";
import { AuthProviderService } from "../auth/auth-provider-service";
import { WorkspaceService } from "../workspace/workspace-service";
import { UserService } from "./user-service";
import { TransactionalContext } from "@gitpod/gitpod-db/lib/typeorm/transactional-db-impl";
import { OrganizationService } from "../orgs/organization-service";
@injectable()
export class UserDeletionService {
constructor(
@inject(UserService) private readonly userService: UserService,
@inject(WorkspaceDB) private readonly workspaceDb: WorkspaceDB,
@inject(TeamDB) private readonly teamDb: TeamDB,
@inject(StorageClient) private readonly storageClient: StorageClient,
@inject(WorkspaceService) private readonly workspaceService: WorkspaceService,
@inject(AuthProviderService) private readonly authProviderService: AuthProviderService,
@inject(OrganizationService) private readonly organizationService: OrganizationService,
) {
this.userService.onDeleteUser(async (subjectId, user, ctx) => {
await this.contributeToDeleteUser(subjectId, user, ctx);
});
}
private async contributeToDeleteUser(userId: string, user: User, ctx: TransactionalContext): Promise<void> {
// Stop all workspaces
await this.workspaceService.stopRunningWorkspacesForUser(
{},
userId,
user.id,
"user deleted",
StopWorkspacePolicy.IMMEDIATELY,
);
// Auth Providers
const authProviders = await this.authProviderService.getAuthProvidersOfUser(user);
for (const provider of authProviders) {
try {
await this.authProviderService.deleteAuthProviderOfUser(user.id, provider.id);
} catch (error) {
log.error({ userId: user.id }, "Failed to delete user's auth provider.", error);
}
}
await Promise.all([
// Workspace
this.anonymizeAllWorkspaces(user.id),
// Bucket
this.deleteUserBucket(user.id),
// Teams owned only by this user
this.deleteSoleOwnedTeams(user.id),
// Team memberships
this.deleteTeamMemberships(user.id),
]);
}
private async anonymizeAllWorkspaces(userId: string) {
const workspaces = await this.workspaceDb.findWorkspacesByUser(userId);
await Promise.all(
workspaces.map(async (ws) => {
this.anonymizeWorkspace(ws);
await this.workspaceDb.store(ws);
}),
);
}
private async deleteUserBucket(userId: string) {
try {
await this.storageClient.deleteUserContent(userId);
} catch (error) {
log.error({ userId }, "Failed to delete user bucket.", error);
}
}
private async deleteTeamMemberships(userId: string) {
const teams = await this.teamDb.findTeamsByUser(userId);
await Promise.all(teams.map((t) => this.teamDb.removeMemberFromTeam(userId, t.id)));
}
private async deleteSoleOwnedTeams(userId: string) {
const ownedTeams = await this.teamDb.findTeamsByUserAsSoleOwner(userId);
await Promise.all(ownedTeams.map((t) => this.organizationService.deleteOrganization(userId, t.id)));
}
private anonymizeWorkspace(ws: Workspace) {
ws.context.title = "deleted-title";
ws.context.normalizedContextURL = "deleted-normalizedContextURL";
ws.contextURL = "deleted-contextURL";
ws.description = "deleted-description";
ws.context = {} as any;
}
}