Skip to content

Commit

Permalink
Merge pull request #139 from anatawa12/delete-user-log
Browse files Browse the repository at this point in the history
Delete user log
  • Loading branch information
anatawa12 authored Feb 15, 2024
2 parents 323eb4e + c42e1df commit 0f50d0a
Show file tree
Hide file tree
Showing 19 changed files with 379 additions and 14 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
-->

## 2023.12.2-kinel.2

### Server
- スパムが行動後即時にアカウントを削除するような場合への対策として、アカウント削除時にアカウントデータが保持されるようになりました。
- 個人情報の保護に関する法律に基づく正当な要求があった場合には削除されます。
- また、より良い対応方法が追加された際にこのデータは削除されます。

## 2023.12.2-kinel.1

2023.12.2と全く同一です
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: anatawa12
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class NirilaDeleteUserLogRepository1708005334196 {
constructor() {
this.name = 'NirilaDeleteUserLogRepository1708005334196'
}

async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "nirila_delete_user_log" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "username" character varying(128) NOT NULL, "email" character varying(128), "info" jsonb NOT NULL, CONSTRAINT "PK_34be175a3f407ea9f0dd42da572" PRIMARY KEY ("id")); COMMENT ON COLUMN "nirila_delete_user_log"."username" IS 'The username of the deleted User.'; COMMENT ON COLUMN "nirila_delete_user_log"."email" IS 'The email adddress of the deleted User.'`);
await queryRunner.query(`CREATE INDEX "IDX_343c0efea0f002a2bf34960bd1" ON "nirila_delete_user_log" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_940ffb8ea315bef184208780f8" ON "nirila_delete_user_log" ("username") `);
await queryRunner.query(`CREATE INDEX "IDX_da5751593e765f39d09303f768" ON "nirila_delete_user_log" ("email") `);
}

async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_da5751593e765f39d09303f768"`);
await queryRunner.query(`DROP INDEX "public"."IDX_940ffb8ea315bef184208780f8"`);
await queryRunner.query(`DROP INDEX "public"."IDX_343c0efea0f002a2bf34960bd1"`);
await queryRunner.query(`DROP TABLE "nirila_delete_user_log"`);
}
}
4 changes: 3 additions & 1 deletion packages/backend/src/core/entities/UserEntityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,18 +310,20 @@ export class UserEntityService implements OnModuleInit {
detail?: D,
includeSecrets?: boolean,
userProfile?: MiUserProfile,
asModerator?: boolean,
},
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
const opts = Object.assign({
detail: false,
includeSecrets: false,
asModerator: undefined as boolean | undefined,
}, options);

const user = typeof src === 'object' ? src : await this.usersRepository.findOneByOrFail({ id: src });

const meId = me ? me.id : null;
const isMe = meId === user.id;
const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false;
const iAmModerator = opts.asModerator ?? (me ? await this.roleService.isModerator(me as MiUser) : false);

const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null;
const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin')
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/di-symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const DI = {
redisForTimelines: Symbol('redisForTimelines'),

//#region Repositories
nirilaDeleteUserLogRepository: Symbol('nirilaDeleteUserLogRepository'),
usersRepository: Symbol('usersRepository'),
notesRepository: Symbol('notesRepository'),
announcementsRepository: Symbol('announcementsRepository'),
Expand Down
38 changes: 38 additions & 0 deletions packages/backend/src/models/NirilaDeleteUserLog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: anatawa12
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { PrimaryColumn, Entity, Index, Column } from 'typeorm';
import { MiUserProfile } from '@/models/UserProfile.js';
import { id } from './util/id.js';
import { MiUser } from './User.js';

@Entity('nirila_delete_user_log')
export class NirilaDeleteUserLog {
// delete user log id
@PrimaryColumn(id())
public id: string;

@Index()
@Column(id())
public userId: MiUser['id'];

@Index()
@Column('varchar', {
length: 128,
comment: 'The username of the deleted User.',
})
public username: MiUser['username'];

@Index()
@Column('varchar', {
length: 128, nullable: true,
comment: 'The email adddress of the deleted User.',
})
public email: MiUserProfile['email'];

// user profile info
@Column('jsonb')
public info: Record<string, any>;
}
10 changes: 9 additions & 1 deletion packages/backend/src/models/RepositoryModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@

import { Module } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js';
import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, NirilaDeleteUserLog } from './_.js';
import type { DataSource } from 'typeorm';
import type { Provider } from '@nestjs/common';

const $nirilaDeleteUserLogRepository: Provider = {
provide: DI.nirilaDeleteUserLogRepository,
useFactory: (db: DataSource) => db.getRepository(NirilaDeleteUserLog),
inject: [DI.db],
};

const $usersRepository: Provider = {
provide: DI.usersRepository,
useFactory: (db: DataSource) => db.getRepository(MiUser),
Expand Down Expand Up @@ -403,6 +409,7 @@ const $userMemosRepository: Provider = {
imports: [
],
providers: [
$nirilaDeleteUserLogRepository,
$usersRepository,
$notesRepository,
$announcementsRepository,
Expand Down Expand Up @@ -470,6 +477,7 @@ const $userMemosRepository: Provider = {
$userMemosRepository,
],
exports: [
$nirilaDeleteUserLogRepository,
$usersRepository,
$notesRepository,
$announcementsRepository,
Expand Down
4 changes: 4 additions & 0 deletions packages/backend/src/models/_.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { NirilaDeleteUserLog } from '@/models/NirilaDeleteUserLog.js';
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
import { MiAccessToken } from '@/models/AccessToken.js';
import { MiAd } from '@/models/Ad.js';
Expand Down Expand Up @@ -71,6 +72,8 @@ import { MiUserListFavorite } from '@/models/UserListFavorite.js';
import type { Repository } from 'typeorm';

export {
NirilaDeleteUserLog,

MiAbuseUserReport,
MiAccessToken,
MiAd,
Expand Down Expand Up @@ -138,6 +141,7 @@ export {
MiUserMemo,
};

export type NirilaDeleteUserLogRepository = Repository<NirilaDeleteUserLog>;
export type AbuseUserReportsRepository = Repository<MiAbuseUserReport>;
export type AccessTokensRepository = Repository<MiAccessToken>;
export type AdsRepository = Repository<MiAd>;
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DataSource, Logger } from 'typeorm';
import * as highlight from 'cli-highlight';
import { entities as charts } from '@/core/chart/entities.js';

import { NirilaDeleteUserLog } from '@/models/NirilaDeleteUserLog.js';
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
import { MiAccessToken } from '@/models/AccessToken.js';
import { MiAd } from '@/models/Ad.js';
Expand Down Expand Up @@ -125,6 +126,7 @@ class MyCustomLogger implements Logger {
}

export const entities = [
NirilaDeleteUserLog,
MiAnnouncement,
MiAnnouncementRead,
MiMeta,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
import { Inject, Injectable } from '@nestjs/common';
import { MoreThan } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import type { DriveFilesRepository, MiUser, NotesRepository, UserProfilesRepository, UsersRepository, NirilaDeleteUserLogRepository, SigninsRepository } from '@/models/_.js';
import type Logger from '@/logger.js';
import { DriveService } from '@/core/DriveService.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import type { MiNote } from '@/models/Note.js';
import { EmailService } from '@/core/EmailService.js';
import { bindThis } from '@/decorators.js';
import { SearchService } from '@/core/SearchService.js';
import { RoleService } from '@/core/RoleService.js';
import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { DbUserDeleteJobData } from '../types.js';
Expand All @@ -35,6 +39,17 @@ export class DeleteAccountProcessorService {
@Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository,

@Inject(DI.nirilaDeleteUserLogRepository)
private nirilaDeleteUserLogRepository: NirilaDeleteUserLogRepository,

@Inject(DI.signinsRepository)
private signinsRepository: SigninsRepository,

private roleService: RoleService,
private roleEntityService: RoleEntityService,
private idService: IdService,
private userEntityService: UserEntityService,

private driveService: DriveService,
private emailService: EmailService,
private queueLoggerService: QueueLoggerService,
Expand All @@ -43,6 +58,70 @@ export class DeleteAccountProcessorService {
this.logger = this.queueLoggerService.logger.createSubLogger('delete-account');
}

@bindThis
async logDelete(user: MiUser) {
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });

// data from src/server/api/endpoints/users/show.ts
const detailedUser = await this.userEntityService.pack(user, null, {
detail: true,
asModerator: true,
});

// data from src/server/api/endpoints/admin/show-user.ts

const isModerator = await this.roleService.isModerator(user);
const isSilenced = !(await this.roleService.getUserPolicies(user.id)).canPublicNote;

const signins = await this.signinsRepository.findBy({ userId: user.id });

const roleAssigns = await this.roleService.getUserAssigns(user.id);
const roles = await this.roleService.getUserRoles(user.id);

const adminInfo = {
email: profile.email,
emailVerified: profile.emailVerified,
autoAcceptFollowed: profile.autoAcceptFollowed,
noCrawle: profile.noCrawle,
preventAiLearning: profile.preventAiLearning,
alwaysMarkNsfw: profile.alwaysMarkNsfw,
autoSensitive: profile.autoSensitive,
carefulBot: profile.carefulBot,
injectFeaturedNote: profile.injectFeaturedNote,
receiveAnnouncementEmail: profile.receiveAnnouncementEmail,
mutedWords: profile.mutedWords,
mutedInstances: profile.mutedInstances,
notificationRecieveConfig: profile.notificationRecieveConfig,
isModerator: isModerator,
isSilenced: isSilenced,
isSuspended: user.isSuspended,
isHibernated: user.isHibernated,
lastActiveDate: user.lastActiveDate,
moderationNote: profile.moderationNote ?? '',
signins,
policies: await this.roleService.getUserPolicies(user.id),
roles: await this.roleEntityService.packMany(roles, { id: user.id }), // note: me is unused param
roleAssigns: roleAssigns.map(a => ({
createdAt: this.idService.parse(a.id).date.toISOString(),
expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null,
roleId: a.roleId,
})),
};

const info: Record<string, any> = {
user: detailedUser,
adminInfo,
};

await this.nirilaDeleteUserLogRepository.insert({
id: this.idService.gen(),
userId: user.id,
username: user.username,
email: profile.email,
info,
});
}

@bindThis
public async process(job: Bull.Job<DbUserDeleteJobData>): Promise<string | void> {
this.logger.info(`Deleting account of ${job.data.user.id} ...`);
Expand All @@ -52,6 +131,12 @@ export class DeleteAccountProcessorService {
return;
}

try {
this.logDelete(user);
} catch (e) {
this.logger.error(`Failed to log delete: ${e}`);
}

{ // Delete notes
let cursor: MiNote['id'] | null = null;

Expand Down
4 changes: 4 additions & 0 deletions packages/backend/src/server/api/EndpointsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { Module } from '@nestjs/common';

import { CoreModule } from '@/core/CoreModule.js';
import * as ep___admin_nirilaDeleteUserLogAccess from './endpoints/admin/nirila-delete-user-log-access.js';
import * as ep___admin_meta from './endpoints/admin/meta.js';
import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
Expand Down Expand Up @@ -368,6 +369,7 @@ import { GetterService } from './GetterService.js';
import { ApiLoggerService } from './ApiLoggerService.js';
import type { Provider } from '@nestjs/common';

const $admin_nirilaDeleteUserLogAccess: Provider = { provide: 'ep:admin/nirila-delete-user-log-access', useClass: ep___admin_nirilaDeleteUserLogAccess.default };
const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_meta.default };
const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default };
const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default };
Expand Down Expand Up @@ -734,6 +736,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
providers: [
GetterService,
ApiLoggerService,
$admin_nirilaDeleteUserLogAccess,
$admin_meta,
$admin_abuseUserReports,
$admin_accounts_create,
Expand Down Expand Up @@ -1094,6 +1097,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$retention,
],
exports: [
$admin_nirilaDeleteUserLogAccess,
$admin_meta,
$admin_abuseUserReports,
$admin_accounts_create,
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/server/api/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Schema } from '@/misc/json-schema.js';
import { permissions } from 'misskey-js';
import { RolePolicies } from '@/core/RoleService.js';

import * as ep___admin_nirilaDeleteUserLogAccess from './endpoints/admin/nirila-delete-user-log-access.js';
import * as ep___admin_meta from './endpoints/admin/meta.js';
import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
Expand Down Expand Up @@ -367,6 +368,7 @@ import * as ep___fetchExternalResources from './endpoints/fetch-external-resourc
import * as ep___retention from './endpoints/retention.js';

const eps = [
['admin/nirila-delete-user-log-access', ep___admin_nirilaDeleteUserLogAccess],
['admin/meta', ep___admin_meta],
['admin/abuse-user-reports', ep___admin_abuseUserReports],
['admin/accounts/create', ep___admin_accounts_create],
Expand Down
Loading

0 comments on commit 0f50d0a

Please sign in to comment.