From a790fef2613015fac0bc4fb119abaa1f48de5841 Mon Sep 17 00:00:00 2001 From: Hazel K Date: Mon, 7 Oct 2024 10:02:49 -0400 Subject: [PATCH 1/6] prevent deletion or suspension of system accounts --- .../backend/src/core/DeleteAccountService.ts | 2 ++ .../backend/src/core/UserSuspendService.ts | 3 ++ .../src/core/entities/UserEntityService.ts | 2 ++ .../backend/src/misc/is-system-account.ts | 11 ++++++ .../backend/src/models/json-schema/user.ts | 5 +++ .../server/api/endpoints/admin/show-user.ts | 6 ++++ .../test/unit/misc/is-system-account.ts | 36 +++++++++++++++++++ packages/frontend/src/pages/admin-user.vue | 7 ++-- packages/misskey-js/src/autogen/types.ts | 3 ++ 9 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/misc/is-system-account.ts create mode 100644 packages/backend/test/unit/misc/is-system-account.ts diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index 7f1b8f3efb..8408e95863 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -13,6 +13,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { isSystemAccount } from '@/misc/is-system-account.js'; @Injectable() export class DeleteAccountService { @@ -38,6 +39,7 @@ export class DeleteAccountService { }, moderator?: MiUser): Promise { const _user = await this.usersRepository.findOneByOrFail({ id: user.id }); if (_user.isRoot) throw new Error('cannot delete a root account'); + if (isSystemAccount(_user)) throw new Error('cannot delete a system account'); if (moderator != null) { this.moderationLogService.log(moderator, 'deleteAccount', { diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 7920e58e36..30dcaa6f7d 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -15,6 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import { RelationshipJobData } from '@/queue/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { isSystemAccount } from '@/misc/is-system-account.js'; @Injectable() export class UserSuspendService { @@ -38,6 +39,8 @@ export class UserSuspendService { @bindThis public async suspend(user: MiUser, moderator: MiUser): Promise { + if (isSystemAccount(user)) throw new Error('cannot suspend a system account'); + await this.usersRepository.update(user.id, { isSuspended: true, }); diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 40830f86b4..d465e2cd4c 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -53,6 +53,7 @@ import type { OnModuleInit } from '@nestjs/common'; import type { NoteEntityService } from './NoteEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; import type { PageEntityService } from './PageEntityService.js'; +import { isSystemAccount } from '@/misc/is-system-account.js'; const Ajv = _Ajv.default; const ajv = new Ajv(); @@ -614,6 +615,7 @@ export class UserEntityService implements OnModuleInit { backgroundId: user.backgroundId, isModerator: isModerator, isAdmin: isAdmin, + isSystem: isSystemAccount(user), injectFeaturedNote: profile!.injectFeaturedNote, receiveAnnouncementEmail: profile!.receiveAnnouncementEmail, alwaysMarkNsfw: profile!.alwaysMarkNsfw, diff --git a/packages/backend/src/misc/is-system-account.ts b/packages/backend/src/misc/is-system-account.ts new file mode 100644 index 0000000000..0b699944b3 --- /dev/null +++ b/packages/backend/src/misc/is-system-account.ts @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * Checks if the given user represents a system account, such as instance.actor. + */ +export function isSystemAccount(user: { readonly username: string }): boolean { + return user.username.includes('.'); +} diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 249b9bba38..24b6c50e93 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -121,6 +121,11 @@ export const packedUserLiteSchema = { nullable: false, optional: true, default: false, }, + isSystem: { + type: 'boolean', + nullable: false, optional: true, + default: false, + }, isSilenced: { type: 'boolean', nullable: false, optional: false, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index a7ca7f9547..dda6a0e882 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -11,6 +11,7 @@ import { RoleService } from '@/core/RoleService.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; import { IdService } from '@/core/IdService.js'; import { notificationRecieveConfig } from '@/models/json-schema/user.js'; +import { isSystemAccount } from '@/misc/is-system-account.js'; export const meta = { tags: ['admin'], @@ -111,6 +112,10 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + isSystem: { + type: 'boolean', + optional: false, nullable: false, + }, isSilenced: { type: 'boolean', optional: false, nullable: false, @@ -240,6 +245,7 @@ export default class extends Endpoint { // eslint- mutedInstances: profile.mutedInstances, notificationRecieveConfig: profile.notificationRecieveConfig, isModerator: isModerator, + isSystem: isSystemAccount(user), isSilenced: isSilenced, isSuspended: user.isSuspended, isHibernated: user.isHibernated, diff --git a/packages/backend/test/unit/misc/is-system-account.ts b/packages/backend/test/unit/misc/is-system-account.ts new file mode 100644 index 0000000000..045fe04477 --- /dev/null +++ b/packages/backend/test/unit/misc/is-system-account.ts @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { isSystemAccount } from '@/misc/is-system-account.js'; + +describe(isSystemAccount, () => { + it('should return true for instance.actor', () => { + expect(isSystemAccount({ username: 'instance.actor' })).toBeTruthy(); + }); + + it('should return true for relay.actor', () => { + expect(isSystemAccount({ username: 'relay.actor' })).toBeTruthy(); + }); + + it('should return true for any username with a dot', () => { + expect(isSystemAccount({ username: 'some.user' })).toBeTruthy(); + expect(isSystemAccount({ username: 'some.' })).toBeTruthy(); + expect(isSystemAccount({ username: '.user' })).toBeTruthy(); + expect(isSystemAccount({ username: '.' })).toBeTruthy(); + }); + + it('should return true for usernames with multiple dots', () => { + expect(isSystemAccount({ username: 'some.user.account' })).toBeTruthy(); + expect(isSystemAccount({ username: '..' })).toBeTruthy(); + }); + + it('should return false for usernames without a dot', () => { + expect(isSystemAccount({ username: 'instance_actor' })).toBeFalsy(); + expect(isSystemAccount({ username: 'instanceactor' })).toBeFalsy(); + expect(isSystemAccount({ username: 'relay_actor' })).toBeFalsy(); + expect(isSystemAccount({ username: 'relayactor' })).toBeFalsy(); + expect(isSystemAccount({ username: '' })).toBeFalsy(); + }); +}); diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 187ec66b42..70fd2eb927 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only - {{ i18n.ts.isSystemAccount }} + {{ i18n.ts.isSystemAccount }} {{ i18n.ts.instanceInfo }} @@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.silence }} - {{ i18n.ts.suspend }} + {{ i18n.ts.suspend }} {{ i18n.ts.markAsNSFW }}
@@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.unsetUserBanner }} {{ i18n.ts.deleteAllFiles }}
- {{ i18n.ts.deleteAccount }} + {{ i18n.ts.deleteAccount }}
@@ -236,6 +236,7 @@ const approved = ref(false); const suspended = ref(false); const markedAsNSFW = ref(false); const moderationNote = ref(''); +const isSystem = computed(() => user.value?.isSystem ?? false); const filesPagination = { endpoint: 'admin/drive/files' as const, limit: 10, diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 79b4e4a038..adffa36950 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3820,6 +3820,8 @@ export type components = { isAdmin?: boolean; /** @default false */ isModerator?: boolean; + /** @default false */ + isSystem?: boolean; isSilenced: boolean; noindex: boolean; isBot?: boolean; @@ -9199,6 +9201,7 @@ export type operations = { }]>; }; isModerator: boolean; + isSystem: boolean; isSilenced: boolean; isSuspended: boolean; isHibernated: boolean; From 844dfaaf5d01f92fa25b80107b165d2d20849e36 Mon Sep 17 00:00:00 2001 From: Hazel K Date: Mon, 7 Oct 2024 10:40:44 -0400 Subject: [PATCH 2/6] model missing "approved" property in show-user response --- packages/backend/src/server/api/endpoints/admin/show-user.ts | 4 ++++ packages/misskey-js/src/autogen/types.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index dda6a0e882..2ba064b9dd 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -32,6 +32,10 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + approved: { + type: 'boolean', + optional: false, nullable: false, + }, autoAcceptFollowed: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index adffa36950..1e6b343850 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -9062,6 +9062,7 @@ export type operations = { 'application/json': { email: string | null; emailVerified: boolean; + approved: boolean; autoAcceptFollowed: boolean; noCrawle: boolean; preventAiLearning: boolean; From c77e90d98ade9575234327a92f5193ef010527b9 Mon Sep 17 00:00:00 2001 From: Hazel K Date: Mon, 7 Oct 2024 10:41:18 -0400 Subject: [PATCH 3/6] update autogen types --- packages/misskey-js/src/autogen/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 1e6b343850..2f252755e3 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -7957,6 +7957,7 @@ export type operations = { host: string; isSuspended?: boolean; isNSFW?: boolean; + rejectReports?: boolean; moderationNote?: string; }; }; From adc7b91e3b489424ab6d7187cec91844670fa8e8 Mon Sep 17 00:00:00 2001 From: Hazel K Date: Mon, 7 Oct 2024 10:41:41 -0400 Subject: [PATCH 4/6] fix UI hiding on admin-user.vue --- packages/frontend/src/pages/admin-user.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 70fd2eb927..6b663f9128 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -227,16 +227,16 @@ const tab = ref(props.initialTab); const chartSrc = ref('per-user-notes'); const user = ref(); const init = ref>(); -const info = ref(); +const info = ref(null); const ips = ref(null); -const ap = ref(null); +const ap = ref(null); const moderator = ref(false); const silenced = ref(false); const approved = ref(false); const suspended = ref(false); const markedAsNSFW = ref(false); const moderationNote = ref(''); -const isSystem = computed(() => user.value?.isSystem ?? false); +const isSystem = computed(() => info.value?.isSystem ?? false); const filesPagination = { endpoint: 'admin/drive/files' as const, limit: 10, From 39214431f4b694a098f6417309af1ed9c857a2f1 Mon Sep 17 00:00:00 2001 From: Hazel K Date: Mon, 7 Oct 2024 11:45:58 -0400 Subject: [PATCH 5/6] check for host --- .../backend/src/misc/is-system-account.ts | 9 ++++- .../test/unit/misc/is-system-account.ts | 37 ++++++++++++------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/packages/backend/src/misc/is-system-account.ts b/packages/backend/src/misc/is-system-account.ts index 0b699944b3..a34882f494 100644 --- a/packages/backend/src/misc/is-system-account.ts +++ b/packages/backend/src/misc/is-system-account.ts @@ -3,9 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +interface UserLike { + readonly username: string; + readonly host: string | null; +} + /** * Checks if the given user represents a system account, such as instance.actor. */ -export function isSystemAccount(user: { readonly username: string }): boolean { - return user.username.includes('.'); +export function isSystemAccount(user: UserLike): boolean { + return user.host == null && user.username.includes('.'); } diff --git a/packages/backend/test/unit/misc/is-system-account.ts b/packages/backend/test/unit/misc/is-system-account.ts index 045fe04477..d0ab152630 100644 --- a/packages/backend/test/unit/misc/is-system-account.ts +++ b/packages/backend/test/unit/misc/is-system-account.ts @@ -7,30 +7,41 @@ import { isSystemAccount } from '@/misc/is-system-account.js'; describe(isSystemAccount, () => { it('should return true for instance.actor', () => { - expect(isSystemAccount({ username: 'instance.actor' })).toBeTruthy(); + expect(isSystemAccount({ username: 'instance.actor', host: null })).toBeTruthy(); }); it('should return true for relay.actor', () => { - expect(isSystemAccount({ username: 'relay.actor' })).toBeTruthy(); + expect(isSystemAccount({ username: 'relay.actor', host: null })).toBeTruthy(); }); it('should return true for any username with a dot', () => { - expect(isSystemAccount({ username: 'some.user' })).toBeTruthy(); - expect(isSystemAccount({ username: 'some.' })).toBeTruthy(); - expect(isSystemAccount({ username: '.user' })).toBeTruthy(); - expect(isSystemAccount({ username: '.' })).toBeTruthy(); + expect(isSystemAccount({ username: 'some.user', host: null })).toBeTruthy(); + expect(isSystemAccount({ username: 'some.', host: null })).toBeTruthy(); + expect(isSystemAccount({ username: '.user', host: null })).toBeTruthy(); + expect(isSystemAccount({ username: '.', host: null })).toBeTruthy(); }); it('should return true for usernames with multiple dots', () => { - expect(isSystemAccount({ username: 'some.user.account' })).toBeTruthy(); - expect(isSystemAccount({ username: '..' })).toBeTruthy(); + expect(isSystemAccount({ username: 'some.user.account', host: null })).toBeTruthy(); + expect(isSystemAccount({ username: '..', host: null })).toBeTruthy(); }); it('should return false for usernames without a dot', () => { - expect(isSystemAccount({ username: 'instance_actor' })).toBeFalsy(); - expect(isSystemAccount({ username: 'instanceactor' })).toBeFalsy(); - expect(isSystemAccount({ username: 'relay_actor' })).toBeFalsy(); - expect(isSystemAccount({ username: 'relayactor' })).toBeFalsy(); - expect(isSystemAccount({ username: '' })).toBeFalsy(); + expect(isSystemAccount({ username: 'instance_actor', host: null })).toBeFalsy(); + expect(isSystemAccount({ username: 'instanceactor', host: null })).toBeFalsy(); + expect(isSystemAccount({ username: 'relay_actor', host: null })).toBeFalsy(); + expect(isSystemAccount({ username: 'relayactor', host: null })).toBeFalsy(); + expect(isSystemAccount({ username: '', host: null })).toBeFalsy(); + }); + + it('should return false for users from another instance', () => { + expect(isSystemAccount({ username: 'instance.actor', host: 'example.com' })).toBeFalsy(); + expect(isSystemAccount({ username: 'relay.actor', host: 'example.com' })).toBeFalsy(); + expect(isSystemAccount({ username: 'some.user', host: 'example.com' })).toBeFalsy(); + expect(isSystemAccount({ username: 'some.', host: 'example.com' })).toBeFalsy(); + expect(isSystemAccount({ username: '.user', host: 'example.com' })).toBeFalsy(); + expect(isSystemAccount({ username: '.', host: 'example.com' })).toBeFalsy(); + expect(isSystemAccount({ username: 'some.user.account', host: 'example.com' })).toBeFalsy(); + expect(isSystemAccount({ username: '..', host: 'example.com' })).toBeFalsy(); }); }); From cd9bce307218c864608a0e60cb5f74262bf10d0c Mon Sep 17 00:00:00 2001 From: Hazel K Date: Mon, 7 Oct 2024 14:58:39 -0400 Subject: [PATCH 6/6] prevent login and password reset for system accounts --- locales/en-US.yml | 2 ++ locales/index.d.ts | 8 ++++++++ locales/ja-JP.yml | 2 ++ .../backend/src/server/api/SigninApiService.ts | 7 +++++++ .../server/api/endpoints/admin/reset-password.ts | 5 +++++ packages/frontend/src/components/MkSignin.vue | 5 +++++ packages/frontend/src/pages/admin-user.vue | 2 +- .../src/scripts/show-system-account-dialog.ts | 15 +++++++++++++++ 8 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 packages/frontend/src/scripts/show-system-account-dialog.ts diff --git a/locales/en-US.yml b/locales/en-US.yml index 9160d12382..8163ca525b 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -638,6 +638,8 @@ userSuspended: "This user has been suspended." userSilenced: "This user is being silenced." yourAccountSuspendedTitle: "This account is suspended" yourAccountSuspendedDescription: "This account has been suspended due to breaking the server's terms of services or similar. Contact the administrator if you would like to know a more detailed reason. Please do not create a new account." +systemAccountTitle: "This is a system account" +systemAccountDescription: "This account is created and managed automatically by the system, and cannot be logged into." tokenRevoked: "Invalid token" tokenRevokedDescription: "This token has expired. Please log in again." accountDeleted: "Account deleted" diff --git a/locales/index.d.ts b/locales/index.d.ts index d8f914767f..fcd64071af 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2568,6 +2568,14 @@ export interface Locale extends ILocale { * このアカウントは、サーバーの利用規約に違反したなどの理由により、凍結されています。詳細については管理者までお問い合わせください。新しいアカウントを作らないでください。 */ "yourAccountSuspendedDescription": string; + /** + * This is a system account + */ + "systemAccountTitle": string; + /** + * This account is created and managed automatically by the system, and cannot be logged into. + */ + "systemAccountDescription": string; /** * トークンが無効です */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3e168f5087..7e39515c5c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -638,6 +638,8 @@ userSuspended: "このユーザーは凍結されています。" userSilenced: "このユーザーはサイレンスされています。" yourAccountSuspendedTitle: "アカウントが凍結されています" yourAccountSuspendedDescription: "このアカウントは、サーバーの利用規約に違反したなどの理由により、凍結されています。詳細については管理者までお問い合わせください。新しいアカウントを作らないでください。" +systemAccountTitle: "This is a system account" +systemAccountDescription: "This account is created and managed automatically by the system, and cannot be logged into." tokenRevoked: "トークンが無効です" tokenRevokedDescription: "ログイントークンが失効しています。ログインし直してください。" accountDeleted: "アカウントは削除されています" diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 6fbcacbc11..d212e3de79 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -26,6 +26,7 @@ import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; import type { FastifyReply, FastifyRequest } from 'fastify'; +import { isSystemAccount } from '@/misc/is-system-account.js'; @Injectable() export class SigninApiService { @@ -125,6 +126,12 @@ export class SigninApiService { }); } + if (isSystemAccount(user)) { + return error(403, { + id: 's8dhsj9s-a93j-493j-ja9k-kas9sj20aml2', + }); + } + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); if (!user.approved && instance.approvalRequiredForSignup) { diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index 828dbae712..e4bb545f5d 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -11,6 +11,7 @@ import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { isSystemAccount } from '@/misc/is-system-account.js'; export const meta = { tags: ['admin'], @@ -63,6 +64,10 @@ export default class extends Endpoint { // eslint- throw new Error('cannot reset password of root'); } + if (isSystemAccount(user)) { + throw new Error('cannot reset password of system account'); + } + const passwd = secureRndstr(8); // Generate hash of password diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index 42fa2bf4a7..9813774da3 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -77,6 +77,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js'; import { query, extractDomain } from '@/scripts/url.js'; import { login } from '@/account.js'; import { i18n } from '@/i18n.js'; +import { showSystemAccountDialog } from '@/scripts/show-system-account-dialog.js'; const signing = ref(false); const user = ref(null); @@ -204,6 +205,10 @@ function loginFailed(err: any): void { showSuspendedDialog(); break; } + case 's8dhsj9s-a93j-493j-ja9k-kas9sj20aml2': { + showSystemAccountDialog(); + break; + } case '22d05606-fbcf-421a-a2db-b32610dcfd1b': { os.alert({ type: 'error', diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 6b663f9128..16a2a7524b 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.markAsNSFW }}
- {{ i18n.ts.resetPassword }} + {{ i18n.ts.resetPassword }}
diff --git a/packages/frontend/src/scripts/show-system-account-dialog.ts b/packages/frontend/src/scripts/show-system-account-dialog.ts new file mode 100644 index 0000000000..3c28d901fc --- /dev/null +++ b/packages/frontend/src/scripts/show-system-account-dialog.ts @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; + +export function showSystemAccountDialog(): Promise { + return os.alert({ + type: 'error', + title: i18n.ts.systemAccountTitle, + text: i18n.ts.systemAccountDescription, + }); +}