From ccaa96bc58789957801364afeccd3271f89820bc Mon Sep 17 00:00:00 2001 From: Weiko Date: Wed, 22 Nov 2023 17:12:46 +0100 Subject: [PATCH] Fix workspace/user deletion (#2648) * Fix workspace/user deletion * remove logs * add defaultWorkspace check --- server/src/core/auth/services/auth.service.ts | 8 ++++++++ .../refresh-token/refresh-token.entity.ts | 4 +++- server/src/core/user/services/user.service.ts | 14 ++++++------- server/src/core/user/user.entity.ts | 8 ++++++-- server/src/core/user/user.resolver.ts | 9 ++------- .../workspace/services/workspace.service.ts | 1 + ...0856-addCascadeDeleteOnRefreshTokenUser.ts | 16 +++++++++++++++ ...-addWorkspaceDeleteCascadeSetNullInUser.ts | 16 +++++++++++++++ ...538754-addCascadeDeleteOnRelationObject.ts | 20 +++++++++++++++++++ .../object-metadata/object-metadata.entity.ts | 6 ++++++ .../relation-metadata.entity.ts | 6 ++++++ .../workspace-manager.service.ts | 1 - 12 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 server/src/database/typeorm/core/migrations/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts create mode 100644 server/src/database/typeorm/core/migrations/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts create mode 100644 server/src/database/typeorm/metadata/migrations/1700661538754-addCascadeDeleteOnRelationObject.ts diff --git a/server/src/core/auth/services/auth.service.ts b/server/src/core/auth/services/auth.service.ts index 6400e326d..1de649cfc 100644 --- a/server/src/core/auth/services/auth.service.ts +++ b/server/src/core/auth/services/auth.service.ts @@ -159,10 +159,18 @@ export class AuthService { assert(user, "This user doesn't exist", NotFoundException); + assert( + user.defaultWorkspace, + 'User has no default workspace', + NotFoundException, + ); + // passwordHash is hidden for security reasons user.passwordHash = ''; user.workspaceMember = await this.userService.loadWorkspaceMember(user); + console.log(user.workspaceMember); + const accessToken = await this.tokenService.generateAccessToken(user.id); const refreshToken = await this.tokenService.generateRefreshToken(user.id); diff --git a/server/src/core/refresh-token/refresh-token.entity.ts b/server/src/core/refresh-token/refresh-token.entity.ts index ff0de0e44..4ed595798 100644 --- a/server/src/core/refresh-token/refresh-token.entity.ts +++ b/server/src/core/refresh-token/refresh-token.entity.ts @@ -23,7 +23,9 @@ export class RefreshToken { @PrimaryGeneratedColumn('uuid') id: string; - @ManyToOne(() => User, (user) => user.refreshTokens) + @ManyToOne(() => User, (user) => user.refreshTokens, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'userId' }) user: User; diff --git a/server/src/core/user/services/user.service.ts b/server/src/core/user/services/user.service.ts index f7c791c0d..438af946a 100644 --- a/server/src/core/user/services/user.service.ts +++ b/server/src/core/user/services/user.service.ts @@ -70,16 +70,14 @@ export class UserService extends TypeOrmQueryService { ); } - async deleteUser({ - workspaceId: _workspaceId, - userId, - }: { - workspaceId: string; - userId: string; - }) { - const user = await this.userRepository.findBy({ id: userId }); + async deleteUser(userId: string): Promise { + const user = await this.userRepository.findOneBy({ + id: userId, + }); assert(user, 'User not found'); + await this.userRepository.delete(user.id); + return user; } } diff --git a/server/src/core/user/user.entity.ts b/server/src/core/user/user.entity.ts index 646ad0da8..602521bc3 100644 --- a/server/src/core/user/user.entity.ts +++ b/server/src/core/user/user.entity.ts @@ -63,10 +63,14 @@ export class User { deletedAt: Date; @Field(() => Workspace, { nullable: false }) - @ManyToOne(() => Workspace, (workspace) => workspace.users) + @ManyToOne(() => Workspace, (workspace) => workspace.users, { + onDelete: 'SET NULL', + }) defaultWorkspace: Workspace; - @OneToMany(() => RefreshToken, (refreshToken) => refreshToken.user) + @OneToMany(() => RefreshToken, (refreshToken) => refreshToken.user, { + cascade: true, + }) refreshTokens: RefreshToken[]; @Field(() => UserWorkspaceMember, { nullable: false }) diff --git a/server/src/core/user/user.resolver.ts b/server/src/core/user/user.resolver.ts index 2a0fba704..37ccaad11 100644 --- a/server/src/core/user/user.resolver.ts +++ b/server/src/core/user/user.resolver.ts @@ -19,11 +19,9 @@ import { AuthUser } from 'src/decorators/auth-user.decorator'; import { EnvironmentService } from 'src/integrations/environment/environment.service'; import { streamToBuffer } from 'src/utils/stream-to-buffer'; import { FileUploadService } from 'src/core/file/services/file-upload.service'; -import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator'; import { assert } from 'src/utils/assert'; import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; import { User } from 'src/core/user/user.entity'; -import { Workspace } from 'src/core/workspace/workspace.entity'; import { UserWorkspaceMember } from 'src/core/user/dtos/workspace-member.dto'; import { UserService } from './services/user.service'; @@ -96,10 +94,7 @@ export class UserResolver { } @Mutation(() => User) - async deleteUser( - @AuthUser() { id: userId }: User, - @AuthWorkspace() { id: workspaceId }: Workspace, - ) { - return this.userService.deleteUser({ userId, workspaceId }); + async deleteUser(@AuthUser() { id: userId }: User) { + return this.userService.deleteUser(userId); } } diff --git a/server/src/core/workspace/services/workspace.service.ts b/server/src/core/workspace/services/workspace.service.ts index c1e5d04b4..87c73fe9c 100644 --- a/server/src/core/workspace/services/workspace.service.ts +++ b/server/src/core/workspace/services/workspace.service.ts @@ -22,6 +22,7 @@ export class WorkspaceService extends TypeOrmQueryService { assert(workspace, 'Workspace not found'); await this.workspaceManagerService.delete(id); + await this.workspaceRepository.delete(id); return workspace; } diff --git a/server/src/database/typeorm/core/migrations/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts b/server/src/database/typeorm/core/migrations/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts new file mode 100644 index 000000000..cebae100c --- /dev/null +++ b/server/src/database/typeorm/core/migrations/1700661180856-addCascadeDeleteOnRefreshTokenUser.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddCascadeDeleteOnRefreshTokenUser1700661180856 implements MigrationInterface { + name = 'AddCascadeDeleteOnRefreshTokenUser1700661180856' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "core"."refreshToken" DROP CONSTRAINT "FK_7008a2b0fb083127f60b5f4448e"`); + await queryRunner.query(`ALTER TABLE "core"."refreshToken" ADD CONSTRAINT "FK_7008a2b0fb083127f60b5f4448e" FOREIGN KEY ("userId") REFERENCES "core"."user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "core"."refreshToken" DROP CONSTRAINT "FK_7008a2b0fb083127f60b5f4448e"`); + await queryRunner.query(`ALTER TABLE "core"."refreshToken" ADD CONSTRAINT "FK_7008a2b0fb083127f60b5f4448e" FOREIGN KEY ("userId") REFERENCES "core"."user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + +} diff --git a/server/src/database/typeorm/core/migrations/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts b/server/src/database/typeorm/core/migrations/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts new file mode 100644 index 000000000..f1f7ddece --- /dev/null +++ b/server/src/database/typeorm/core/migrations/1700663611659-addWorkspaceDeleteCascadeSetNullInUser.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddWorkspaceDeleteCascadeSetNullInUser1700663611659 implements MigrationInterface { + name = 'AddWorkspaceDeleteCascadeSetNullInUser1700663611659' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "core"."user" DROP CONSTRAINT "FK_2ec910029395fa7655621c88908"`); + await queryRunner.query(`ALTER TABLE "core"."user" ADD CONSTRAINT "FK_2ec910029395fa7655621c88908" FOREIGN KEY ("defaultWorkspaceId") REFERENCES "core"."workspace"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "core"."user" DROP CONSTRAINT "FK_2ec910029395fa7655621c88908"`); + await queryRunner.query(`ALTER TABLE "core"."user" ADD CONSTRAINT "FK_2ec910029395fa7655621c88908" FOREIGN KEY ("defaultWorkspaceId") REFERENCES "core"."workspace"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + +} diff --git a/server/src/database/typeorm/metadata/migrations/1700661538754-addCascadeDeleteOnRelationObject.ts b/server/src/database/typeorm/metadata/migrations/1700661538754-addCascadeDeleteOnRelationObject.ts new file mode 100644 index 000000000..93d356ac3 --- /dev/null +++ b/server/src/database/typeorm/metadata/migrations/1700661538754-addCascadeDeleteOnRelationObject.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddCascadeDeleteOnRelationObject1700661538754 implements MigrationInterface { + name = 'AddCascadeDeleteOnRelationObject1700661538754' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824"`); + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d"`); + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d" FOREIGN KEY ("fromObjectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824" FOREIGN KEY ("toObjectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824"`); + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d"`); + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d" FOREIGN KEY ("fromObjectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824" FOREIGN KEY ("toObjectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + +} diff --git a/server/src/metadata/object-metadata/object-metadata.entity.ts b/server/src/metadata/object-metadata/object-metadata.entity.ts index 65ce5d496..8e5999722 100644 --- a/server/src/metadata/object-metadata/object-metadata.entity.ts +++ b/server/src/metadata/object-metadata/object-metadata.entity.ts @@ -75,12 +75,18 @@ export class ObjectMetadataEntity implements ObjectMetadataInterface { @OneToMany( () => RelationMetadataEntity, (relation: RelationMetadataEntity) => relation.fromObjectMetadata, + { + cascade: true, + }, ) fromRelations: RelationMetadataEntity[]; @OneToMany( () => RelationMetadataEntity, (relation: RelationMetadataEntity) => relation.toObjectMetadata, + { + cascade: true, + }, ) toRelations: RelationMetadataEntity[]; diff --git a/server/src/metadata/relation-metadata/relation-metadata.entity.ts b/server/src/metadata/relation-metadata/relation-metadata.entity.ts index fbf2fbc92..6e46b8280 100644 --- a/server/src/metadata/relation-metadata/relation-metadata.entity.ts +++ b/server/src/metadata/relation-metadata/relation-metadata.entity.ts @@ -46,12 +46,18 @@ export class RelationMetadataEntity implements RelationMetadataInterface { @ManyToOne( () => ObjectMetadataEntity, (object: ObjectMetadataEntity) => object.fromRelations, + { + onDelete: 'CASCADE', + }, ) fromObjectMetadata: ObjectMetadataEntity; @ManyToOne( () => ObjectMetadataEntity, (object: ObjectMetadataEntity) => object.toRelations, + { + onDelete: 'CASCADE', + }, ) toObjectMetadata: ObjectMetadataEntity; diff --git a/server/src/workspace/workspace-manager/workspace-manager.service.ts b/server/src/workspace/workspace-manager/workspace-manager.service.ts index f0fd7bf43..71af9bf38 100644 --- a/server/src/workspace/workspace-manager/workspace-manager.service.ts +++ b/server/src/workspace/workspace-manager/workspace-manager.service.ts @@ -255,7 +255,6 @@ export class WorkspaceManagerService { */ public async delete(workspaceId: string): Promise { // Delete data from metadata tables - await this.fieldMetadataService.deleteFieldsMetadata(workspaceId); await this.objectMetadataService.deleteObjectsMetadata(workspaceId); await this.workspaceMigrationService.delete(workspaceId); await this.dataSourceService.delete(workspaceId);