merge: Log IP addresses used during registration (!1163)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1163 Closes #836 Approved-by: Hazelnoot <acomputerdog@gmail.com> Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
commit
1f26659995
3 changed files with 60 additions and 1 deletions
14
packages/backend/migration/1752377661219-UserPending-ip.js
Normal file
14
packages/backend/migration/1752377661219-UserPending-ip.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: наб and other Sharkey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class UserPendingIp1752377661219 {
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_pending" ADD "requestOriginIp" varchar(128)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_pending" DROP COLUMN "requestOriginIp"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,4 +37,10 @@ export class MiUserPending {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public reason: string;
|
public reason: string;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 128,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public requestOriginIp: string | null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as argon2 from 'argon2';
|
import * as argon2 from 'argon2';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, MiRegistrationTicket, MiMeta } from '@/models/_.js';
|
import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, MiRegistrationTicket, MiMeta, UserIpsRepository } from '@/models/_.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { CaptchaService } from '@/core/CaptchaService.js';
|
import { CaptchaService } from '@/core/CaptchaService.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
|
@ -19,11 +19,14 @@ import { FastifyReplyError } from '@/misc/fastify-reply-error.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
|
import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
import Logger from '@/logger.js';
|
||||||
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import { SigninService } from './SigninService.js';
|
import { SigninService } from './SigninService.js';
|
||||||
import type { FastifyRequest, FastifyReply } from 'fastify';
|
import type { FastifyRequest, FastifyReply } from 'fastify';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SignupApiService {
|
export class SignupApiService {
|
||||||
|
private logger: Logger;
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
private config: Config,
|
private config: Config,
|
||||||
|
|
@ -46,6 +49,9 @@ export class SignupApiService {
|
||||||
@Inject(DI.registrationTicketsRepository)
|
@Inject(DI.registrationTicketsRepository)
|
||||||
private registrationTicketsRepository: RegistrationTicketsRepository,
|
private registrationTicketsRepository: RegistrationTicketsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userIpsRepository)
|
||||||
|
private userIpsRepository: UserIpsRepository,
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private captchaService: CaptchaService,
|
private captchaService: CaptchaService,
|
||||||
|
|
@ -53,7 +59,9 @@ export class SignupApiService {
|
||||||
private signinService: SigninService,
|
private signinService: SigninService,
|
||||||
private emailService: EmailService,
|
private emailService: EmailService,
|
||||||
private roleService: RoleService,
|
private roleService: RoleService,
|
||||||
|
private loggerService: LoggerService,
|
||||||
) {
|
) {
|
||||||
|
this.logger = this.loggerService.getLogger('Signup');
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
@ -213,6 +221,7 @@ export class SignupApiService {
|
||||||
username: username,
|
username: username,
|
||||||
password: hash,
|
password: hash,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
|
requestOriginIp: this.meta.enableIpLogging ? request.ip : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const link = `${this.config.url}/signup-complete/${code}`;
|
const link = `${this.config.url}/signup-complete/${code}`;
|
||||||
|
|
@ -249,6 +258,10 @@ export class SignupApiService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.meta.enableIpLogging) {
|
||||||
|
this.logIp(request.ip, null, account.id);
|
||||||
|
}
|
||||||
|
|
||||||
const moderators = await this.roleService.getModerators();
|
const moderators = await this.roleService.getModerators();
|
||||||
|
|
||||||
for (const moderator of moderators) {
|
for (const moderator of moderators) {
|
||||||
|
|
@ -282,6 +295,10 @@ export class SignupApiService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.meta.enableIpLogging) {
|
||||||
|
this.logIp(request.ip, null, account.id);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...res,
|
...res,
|
||||||
token: secret,
|
token: secret,
|
||||||
|
|
@ -332,6 +349,15 @@ export class SignupApiService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pendingUser.requestOriginIp) {
|
||||||
|
this.logIp(pendingUser.requestOriginIp, this.idService.parse(pendingUser.id).date, account.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sign-up request and the confirmation may've come from different addresses: log both
|
||||||
|
if (this.meta.enableIpLogging) {
|
||||||
|
this.logIp(request.ip, null, account.id);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.meta.approvalRequiredForSignup) {
|
if (this.meta.approvalRequiredForSignup) {
|
||||||
if (pendingUser.email) {
|
if (pendingUser.email) {
|
||||||
this.emailService.sendEmail(pendingUser.email, 'Approval pending',
|
this.emailService.sendEmail(pendingUser.email, 'Approval pending',
|
||||||
|
|
@ -359,4 +385,17 @@ export class SignupApiService {
|
||||||
throw new FastifyReplyError(400, String(err), err);
|
throw new FastifyReplyError(400, String(err), err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private logIp(ip: string, ipDate: Date | null, userId: MiLocalUser['id']) {
|
||||||
|
try {
|
||||||
|
this.userIpsRepository.createQueryBuilder().insert().values({
|
||||||
|
createdAt: ipDate ?? new Date(),
|
||||||
|
userId,
|
||||||
|
ip,
|
||||||
|
}).orIgnore(true).execute();
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error(err as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue