Prevent username reusing
This commit is contained in:
parent
3432d6e615
commit
85008303f5
7 changed files with 59 additions and 3 deletions
|
@ -1,6 +1,11 @@
|
||||||
ChangeLog
|
ChangeLog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
unreleased
|
||||||
|
--------------------
|
||||||
|
### 🐛Fixes
|
||||||
|
* すでに使われたことのあるユーザー名を再度使えないように
|
||||||
|
|
||||||
11.26.1 (2019/07/21)
|
11.26.1 (2019/07/21)
|
||||||
--------------------
|
--------------------
|
||||||
### 🐛Fixes
|
### 🐛Fixes
|
||||||
|
|
13
migration/1563757595828-UsedUsername.ts
Normal file
13
migration/1563757595828-UsedUsername.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class UsedUsername1563757595828 implements MigrationInterface {
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "used_username" ("username" character varying(128) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_78fd79d2d24c6ac2f4cc9a31a5d" PRIMARY KEY ("username"))`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.query(`DROP TABLE "used_username"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -48,6 +48,7 @@ import { AttestationChallenge } from '../models/entities/attestation-challenge';
|
||||||
import { Page } from '../models/entities/page';
|
import { Page } from '../models/entities/page';
|
||||||
import { PageLike } from '../models/entities/page-like';
|
import { PageLike } from '../models/entities/page-like';
|
||||||
import { ModerationLog } from '../models/entities/moderation-log';
|
import { ModerationLog } from '../models/entities/moderation-log';
|
||||||
|
import { UsedUsername } from '../models/entities/used-username';
|
||||||
|
|
||||||
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
|
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
|
||||||
|
|
||||||
|
@ -100,6 +101,7 @@ export const entities = [
|
||||||
UserGroupInvite,
|
UserGroupInvite,
|
||||||
UserNotePining,
|
UserNotePining,
|
||||||
UserSecurityKey,
|
UserSecurityKey,
|
||||||
|
UsedUsername,
|
||||||
AttestationChallenge,
|
AttestationChallenge,
|
||||||
Following,
|
Following,
|
||||||
FollowRequest,
|
FollowRequest,
|
||||||
|
|
20
src/models/entities/used-username.ts
Normal file
20
src/models/entities/used-username.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { PrimaryColumn, Entity, Column } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class UsedUsername {
|
||||||
|
@PrimaryColumn('varchar', {
|
||||||
|
length: 128,
|
||||||
|
})
|
||||||
|
public username: string;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone')
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
constructor(data: Partial<UsedUsername>) {
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(data)) {
|
||||||
|
(this as any)[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ import { HashtagRepository } from './repositories/hashtag';
|
||||||
import { PageRepository } from './repositories/page';
|
import { PageRepository } from './repositories/page';
|
||||||
import { PageLikeRepository } from './repositories/page-like';
|
import { PageLikeRepository } from './repositories/page-like';
|
||||||
import { ModerationLogRepository } from './repositories/moderation-logs';
|
import { ModerationLogRepository } from './repositories/moderation-logs';
|
||||||
|
import { UsedUsername } from './entities/used-username';
|
||||||
|
|
||||||
export const Apps = getCustomRepository(AppRepository);
|
export const Apps = getCustomRepository(AppRepository);
|
||||||
export const Notes = getCustomRepository(NoteRepository);
|
export const Notes = getCustomRepository(NoteRepository);
|
||||||
|
@ -64,6 +65,7 @@ export const UserGroups = getCustomRepository(UserGroupRepository);
|
||||||
export const UserGroupJoinings = getRepository(UserGroupJoining);
|
export const UserGroupJoinings = getRepository(UserGroupJoining);
|
||||||
export const UserGroupInvites = getCustomRepository(UserGroupInviteRepository);
|
export const UserGroupInvites = getCustomRepository(UserGroupInviteRepository);
|
||||||
export const UserNotePinings = getRepository(UserNotePining);
|
export const UserNotePinings = getRepository(UserNotePining);
|
||||||
|
export const UsedUsernames = getRepository(UsedUsername);
|
||||||
export const Followings = getCustomRepository(FollowingRepository);
|
export const Followings = getCustomRepository(FollowingRepository);
|
||||||
export const FollowRequests = getCustomRepository(FollowRequestRepository);
|
export const FollowRequests = getCustomRepository(FollowRequestRepository);
|
||||||
export const Instances = getRepository(Instance);
|
export const Instances = getRepository(Instance);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { Users } from '../../../../models';
|
import { Users, UsedUsernames } from '../../../../models';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['users'],
|
tags: ['users'],
|
||||||
|
@ -21,7 +21,9 @@ export default define(meta, async (ps) => {
|
||||||
usernameLower: ps.username.toLowerCase()
|
usernameLower: ps.username.toLowerCase()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const exist2 = await UsedUsernames.count({ username: ps.username.toLowerCase() });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
available: exist === 0
|
available: exist === 0 && exist2 === 0
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import generateUserToken from '../common/generate-native-user-token';
|
||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
import { fetchMeta } from '../../../misc/fetch-meta';
|
import { fetchMeta } from '../../../misc/fetch-meta';
|
||||||
import * as recaptcha from 'recaptcha-promise';
|
import * as recaptcha from 'recaptcha-promise';
|
||||||
import { Users, Signins, RegistrationTickets } from '../../../models';
|
import { Users, Signins, RegistrationTickets, UsedUsernames } from '../../../models';
|
||||||
import { genId } from '../../../misc/gen-id';
|
import { genId } from '../../../misc/gen-id';
|
||||||
import { usersChart } from '../../../services/chart';
|
import { usersChart } from '../../../services/chart';
|
||||||
import { User } from '../../../models/entities/user';
|
import { User } from '../../../models/entities/user';
|
||||||
|
@ -13,6 +13,7 @@ import { UserKeypair } from '../../../models/entities/user-keypair';
|
||||||
import { toPunyNullable } from '../../../misc/convert-host';
|
import { toPunyNullable } from '../../../misc/convert-host';
|
||||||
import { UserProfile } from '../../../models/entities/user-profile';
|
import { UserProfile } from '../../../models/entities/user-profile';
|
||||||
import { getConnection } from 'typeorm';
|
import { getConnection } from 'typeorm';
|
||||||
|
import { UsedUsername } from '../../../models/entities/used-username';
|
||||||
|
|
||||||
export default async (ctx: Koa.BaseContext) => {
|
export default async (ctx: Koa.BaseContext) => {
|
||||||
const body = ctx.request.body as any;
|
const body = ctx.request.body as any;
|
||||||
|
@ -78,11 +79,18 @@ export default async (ctx: Koa.BaseContext) => {
|
||||||
// Generate secret
|
// Generate secret
|
||||||
const secret = generateUserToken();
|
const secret = generateUserToken();
|
||||||
|
|
||||||
|
// Check username duplication
|
||||||
if (await Users.findOne({ usernameLower: username.toLowerCase(), host: null })) {
|
if (await Users.findOne({ usernameLower: username.toLowerCase(), host: null })) {
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check deleted username duplication
|
||||||
|
if (await UsedUsernames.findOne({ username: username.toLowerCase() })) {
|
||||||
|
ctx.status = 400;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const keyPair = await new Promise<string[]>((s, j) =>
|
const keyPair = await new Promise<string[]>((s, j) =>
|
||||||
generateKeyPair('rsa', {
|
generateKeyPair('rsa', {
|
||||||
modulusLength: 4096,
|
modulusLength: 4096,
|
||||||
|
@ -133,6 +141,10 @@ export default async (ctx: Koa.BaseContext) => {
|
||||||
autoWatch: false,
|
autoWatch: false,
|
||||||
password: hash,
|
password: hash,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
await transactionalEntityManager.save(new UsedUsername({
|
||||||
|
username: username.toLowerCase(),
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
usersChart.update(account, true);
|
usersChart.update(account, true);
|
||||||
|
|
Loading…
Reference in a new issue