feat: 登録を拒否するメールアドレスのドメインを手動で設定できるように (#12740)
* feat: 使い捨てアドレスのドメインを手動で設定できるように * Update CHANGELOG.md * disposableEmailDomains -> bannedEmailDomains * isBlockedHostを使うように
This commit is contained in:
parent
b3c4f7eddc
commit
5b5a537f56
10 changed files with 68 additions and 3 deletions
|
@ -94,6 +94,7 @@
|
||||||
- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
|
- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
- Feat: 使い捨てメールのドメインを手動で設定できるように
|
||||||
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
|
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
|
||||||
- Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
|
- Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
|
||||||
- Enhance: カスタム絵文字のインポート時の動作を改善
|
- Enhance: カスタム絵文字のインポート時の動作を改善
|
||||||
|
|
1
locales/index.d.ts
vendored
1
locales/index.d.ts
vendored
|
@ -1745,6 +1745,7 @@ export interface Locale {
|
||||||
"disposable": string;
|
"disposable": string;
|
||||||
"mx": string;
|
"mx": string;
|
||||||
"smtp": string;
|
"smtp": string;
|
||||||
|
"banned": string;
|
||||||
};
|
};
|
||||||
"_ffVisibility": {
|
"_ffVisibility": {
|
||||||
"public": string;
|
"public": string;
|
||||||
|
|
|
@ -1652,6 +1652,7 @@ _emailUnavailable:
|
||||||
disposable: "恒久的に使用可能なアドレスではありません"
|
disposable: "恒久的に使用可能なアドレスではありません"
|
||||||
mx: "正しいメールサーバーではありません"
|
mx: "正しいメールサーバーではありません"
|
||||||
smtp: "メールサーバーが応答しません"
|
smtp: "メールサーバーが応答しません"
|
||||||
|
banned: "このメールアドレスでは登録できません"
|
||||||
|
|
||||||
_ffVisibility:
|
_ffVisibility:
|
||||||
public: "公開"
|
public: "公開"
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class bannedEmailDomains1703209889304 {
|
||||||
|
constructor() {
|
||||||
|
this.name = 'bannedEmailDomains1703209889304';
|
||||||
|
}
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "bannedEmailDomains" character varying(1024) array NOT NULL DEFAULT '{}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "bannedEmailDomains"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { validate as validateEmail } from 'deep-email-validator';
|
import { validate as validateEmail } from 'deep-email-validator';
|
||||||
import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
|
import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
|
@ -30,6 +31,7 @@ export class EmailService {
|
||||||
|
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
private loggerService: LoggerService,
|
private loggerService: LoggerService,
|
||||||
|
private utilityService: UtilityService,
|
||||||
private httpRequestService: HttpRequestService,
|
private httpRequestService: HttpRequestService,
|
||||||
) {
|
) {
|
||||||
this.logger = this.loggerService.getLogger('email');
|
this.logger = this.loggerService.getLogger('email');
|
||||||
|
@ -155,7 +157,7 @@ export class EmailService {
|
||||||
@bindThis
|
@bindThis
|
||||||
public async validateEmailForAccount(emailAddress: string): Promise<{
|
public async validateEmailForAccount(emailAddress: string): Promise<{
|
||||||
available: boolean;
|
available: boolean;
|
||||||
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp';
|
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned';
|
||||||
}> {
|
}> {
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
|
|
||||||
|
@ -184,12 +186,16 @@ export class EmailService {
|
||||||
validated = { valid: true, reason: null };
|
validated = { valid: true, reason: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const available = exist === 0 && validated.valid;
|
const emailDomain: string = emailAddress.split('@')[1];
|
||||||
|
const isBanned = this.utilityService.isBlockedHost(meta.bannedEmailDomains, emailDomain);
|
||||||
|
|
||||||
|
const available = exist === 0 && validated.valid && !isBanned;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
available,
|
available,
|
||||||
reason: available ? null :
|
reason: available ? null :
|
||||||
exist !== 0 ? 'used' :
|
exist !== 0 ? 'used' :
|
||||||
|
isBanned ? 'banned' :
|
||||||
validated.reason === 'regex' ? 'format' :
|
validated.reason === 'regex' ? 'format' :
|
||||||
validated.reason === 'disposable' ? 'disposable' :
|
validated.reason === 'disposable' ? 'disposable' :
|
||||||
validated.reason === 'mx' ? 'mx' :
|
validated.reason === 'mx' ? 'mx' :
|
||||||
|
|
|
@ -495,6 +495,13 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public manifestJsonOverride: string;
|
public manifestJsonOverride: string;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 1024,
|
||||||
|
array: true,
|
||||||
|
default: '{}',
|
||||||
|
})
|
||||||
|
public bannedEmailDomains: string[];
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
|
length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
|
||||||
})
|
})
|
||||||
|
|
|
@ -145,6 +145,14 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
bannedEmailDomains: {
|
||||||
|
type: 'array',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
preservedUsernames: {
|
preservedUsernames: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -513,6 +521,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
|
enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
|
||||||
enableServerMachineStats: instance.enableServerMachineStats,
|
enableServerMachineStats: instance.enableServerMachineStats,
|
||||||
enableIdenticonGeneration: instance.enableIdenticonGeneration,
|
enableIdenticonGeneration: instance.enableIdenticonGeneration,
|
||||||
|
bannedEmailDomains: instance.bannedEmailDomains,
|
||||||
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
||||||
manifestJsonOverride: instance.manifestJsonOverride,
|
manifestJsonOverride: instance.manifestJsonOverride,
|
||||||
enableFanoutTimeline: instance.enableFanoutTimeline,
|
enableFanoutTimeline: instance.enableFanoutTimeline,
|
||||||
|
|
|
@ -122,6 +122,7 @@ export const paramDef = {
|
||||||
enableServerMachineStats: { type: 'boolean' },
|
enableServerMachineStats: { type: 'boolean' },
|
||||||
enableIdenticonGeneration: { type: 'boolean' },
|
enableIdenticonGeneration: { type: 'boolean' },
|
||||||
serverRules: { type: 'array', items: { type: 'string' } },
|
serverRules: { type: 'array', items: { type: 'string' } },
|
||||||
|
bannedEmailDomains: { type: 'array', items: { type: 'string' } },
|
||||||
preservedUsernames: { type: 'array', items: { type: 'string' } },
|
preservedUsernames: { type: 'array', items: { type: 'string' } },
|
||||||
manifestJsonOverride: { type: 'string' },
|
manifestJsonOverride: { type: 'string' },
|
||||||
enableFanoutTimeline: { type: 'boolean' },
|
enableFanoutTimeline: { type: 'boolean' },
|
||||||
|
@ -526,6 +527,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
set.notesPerOneAd = ps.notesPerOneAd;
|
set.notesPerOneAd = ps.notesPerOneAd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.bannedEmailDomains !== undefined) {
|
||||||
|
set.bannedEmailDomains = ps.bannedEmailDomains;
|
||||||
|
}
|
||||||
|
|
||||||
const before = await this.metaService.fetch(true);
|
const before = await this.metaService.fetch(true);
|
||||||
|
|
||||||
await this.metaService.update(set);
|
await this.metaService.update(set);
|
||||||
|
|
|
@ -38,6 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
|
<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
|
||||||
<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
|
<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
|
||||||
<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
|
<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
|
||||||
|
<span v-else-if="emailState === 'unavailable:banned'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.banned }}</span>
|
||||||
<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
|
<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
|
||||||
<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
|
<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
|
||||||
<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
|
<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
|
||||||
|
@ -110,7 +111,7 @@ const retypedPassword = ref<string>('');
|
||||||
const invitationCode = ref<string>('');
|
const invitationCode = ref<string>('');
|
||||||
const email = ref('');
|
const email = ref('');
|
||||||
const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null);
|
const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null);
|
||||||
const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
|
const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:banned' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
|
||||||
const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
|
const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
|
||||||
const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
|
const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
|
||||||
const submitting = ref<boolean>(false);
|
const submitting = ref<boolean>(false);
|
||||||
|
@ -209,6 +210,7 @@ function onChangeEmail(): void {
|
||||||
result.reason === 'used' ? 'unavailable:used' :
|
result.reason === 'used' ? 'unavailable:used' :
|
||||||
result.reason === 'format' ? 'unavailable:format' :
|
result.reason === 'format' ? 'unavailable:format' :
|
||||||
result.reason === 'disposable' ? 'unavailable:disposable' :
|
result.reason === 'disposable' ? 'unavailable:disposable' :
|
||||||
|
result.reason === 'banned' ? 'unavailable:banned' :
|
||||||
result.reason === 'mx' ? 'unavailable:mx' :
|
result.reason === 'mx' ? 'unavailable:mx' :
|
||||||
result.reason === 'smtp' ? 'unavailable:smtp' :
|
result.reason === 'smtp' ? 'unavailable:smtp' :
|
||||||
'unavailable';
|
'unavailable';
|
||||||
|
|
|
@ -83,6 +83,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder>
|
||||||
|
<template #label>Banned Email Domains</template>
|
||||||
|
|
||||||
|
<div class="_gaps_m">
|
||||||
|
<MkTextarea v-model="bannedEmailDomains">
|
||||||
|
<template #label>Banned Email Domains List</template>
|
||||||
|
</MkTextarea>
|
||||||
|
<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
<template #label>Log IP address</template>
|
<template #label>Log IP address</template>
|
||||||
<template v-if="enableIpLogging" #suffix>Enabled</template>
|
<template v-if="enableIpLogging" #suffix>Enabled</template>
|
||||||
|
@ -124,6 +135,7 @@ import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import MkRange from '@/components/MkRange.vue';
|
import MkRange from '@/components/MkRange.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { fetchInstance } from '@/instance.js';
|
import { fetchInstance } from '@/instance.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -141,6 +153,7 @@ const enableIpLogging = ref<boolean>(false);
|
||||||
const enableActiveEmailValidation = ref<boolean>(false);
|
const enableActiveEmailValidation = ref<boolean>(false);
|
||||||
const enableVerifymailApi = ref<boolean>(false);
|
const enableVerifymailApi = ref<boolean>(false);
|
||||||
const verifymailAuthKey = ref<string | null>(null);
|
const verifymailAuthKey = ref<string | null>(null);
|
||||||
|
const bannedEmailDomains = ref<string>('');
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api('admin/meta');
|
const meta = await os.api('admin/meta');
|
||||||
|
@ -161,6 +174,7 @@ async function init() {
|
||||||
enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
|
enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
|
||||||
enableVerifymailApi.value = meta.enableVerifymailApi;
|
enableVerifymailApi.value = meta.enableVerifymailApi;
|
||||||
verifymailAuthKey.value = meta.verifymailAuthKey;
|
verifymailAuthKey.value = meta.verifymailAuthKey;
|
||||||
|
bannedEmailDomains.value = meta.bannedEmailDomains.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
|
@ -180,6 +194,7 @@ function save() {
|
||||||
enableActiveEmailValidation: enableActiveEmailValidation.value,
|
enableActiveEmailValidation: enableActiveEmailValidation.value,
|
||||||
enableVerifymailApi: enableVerifymailApi.value,
|
enableVerifymailApi: enableVerifymailApi.value,
|
||||||
verifymailAuthKey: verifymailAuthKey.value,
|
verifymailAuthKey: verifymailAuthKey.value,
|
||||||
|
bannedEmailDomains: bannedEmailDomains.value.split('\n'),
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue