From bb4583f0becae4303d321c7c167baf13011cf214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Mon, 12 Feb 2024 11:35:19 +0900 Subject: [PATCH] =?UTF-8?q?spec(OAuth2):=20=E3=82=AF=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=82=A2=E3=83=B3=E3=83=88=E6=83=85=E5=A0=B1=E3=81=AEDiscovery?= =?UTF-8?q?=E3=81=AE=E5=AF=BE=E5=BF=9C=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=AF=E3=83=A9=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=A7=E3=82=82=E8=AA=8D=E8=A8=BC=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(MisskeyIO#443)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../1707697398681-indie-auth-client.js | 18 ++ packages/backend/src/di-symbols.ts | 1 + .../backend/src/models/IndieAuthClient.ts | 30 ++ .../backend/src/models/RepositoryModule.ts | 80 +++++- packages/backend/src/models/_.ts | 3 + packages/backend/src/postgres.ts | 2 + .../backend/src/server/api/EndpointsModule.ts | 16 ++ packages/backend/src/server/api/endpoints.ts | 8 + .../api/endpoints/admin/indie-auth/create.ts | 90 ++++++ .../api/endpoints/admin/indie-auth/delete.ts | 58 ++++ .../api/endpoints/admin/indie-auth/list.ts | 75 +++++ .../api/endpoints/admin/indie-auth/update.ts | 69 +++++ .../src/server/oauth/OAuth2ProviderService.ts | 15 +- packages/backend/src/types.ts | 16 ++ .../frontend/src/pages/admin/security.vue | 111 +++++++- packages/misskey-js/etc/misskey-js.api.md | 37 ++- .../misskey-js/src/autogen/apiClientJSDoc.ts | 44 +++ packages/misskey-js/src/autogen/endpoint.ts | 10 + packages/misskey-js/src/autogen/entities.ts | 6 + packages/misskey-js/src/autogen/types.ts | 263 ++++++++++++++++++ packages/misskey-js/src/consts.ts | 18 ++ packages/misskey-js/src/entities.ts | 9 + 22 files changed, 969 insertions(+), 10 deletions(-) create mode 100644 packages/backend/migration/1707697398681-indie-auth-client.js create mode 100644 packages/backend/src/models/IndieAuthClient.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts diff --git a/packages/backend/migration/1707697398681-indie-auth-client.js b/packages/backend/migration/1707697398681-indie-auth-client.js new file mode 100644 index 0000000000..8211b668f7 --- /dev/null +++ b/packages/backend/migration/1707697398681-indie-auth-client.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class IndieAuthClient1707697398681 { + name = 'IndieAuthClient1707697398681' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "indie_auth_client" ("id" character varying(512) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(256), "redirectUris" character varying(512) array NOT NULL DEFAULT '{}', CONSTRAINT "PK_9a604c83d4dadfa1eb92ee03399" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_434fcbfbe82b58a90898e557b7" ON "indie_auth_client" ("createdAt") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_434fcbfbe82b58a90898e557b7"`); + await queryRunner.query(`DROP TABLE "indie_auth_client"`); + } +} diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 5be838511d..e20b428ab4 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -49,6 +49,7 @@ export const DI = { blockingsRepository: Symbol('blockingsRepository'), swSubscriptionsRepository: Symbol('swSubscriptionsRepository'), hashtagsRepository: Symbol('hashtagsRepository'), + indieAuthClientsRepository: Symbol('indieAuthClientsRepository'), abuseUserReportsRepository: Symbol('abuseUserReportsRepository'), registrationTicketsRepository: Symbol('registrationTicketsRepository'), authSessionsRepository: Symbol('authSessionsRepository'), diff --git a/packages/backend/src/models/IndieAuthClient.ts b/packages/backend/src/models/IndieAuthClient.ts new file mode 100644 index 0000000000..8498d7f3a6 --- /dev/null +++ b/packages/backend/src/models/IndieAuthClient.ts @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Column, Index } from 'typeorm'; + +@Entity('indie_auth_client') +export class MiIndieAuthClient { + @PrimaryColumn('varchar', { + length: 512, + }) + public id: string; + + @Index() + @Column('timestamp with time zone', { + default: () => 'CURRENT_TIMESTAMP', + }) + public createdAt: Date; + + @Column('varchar', { + length: 256, nullable: true, + }) + public name: string | null; + + @Column('varchar', { + array: true, length: 512, default: '{}', + }) + public redirectUris: string[]; +} diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 45b9643be8..797d732d0b 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,77 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseReportResolver, 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, MiBubbleGameRecord, MiReversiGame } from './_.js'; +import { + MiAbuseReportResolver, + 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, + MiIndieAuthClient, + 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, + MiBubbleGameRecord, + MiReversiGame, +} from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -219,6 +289,12 @@ const $hashtagsRepository: Provider = { inject: [DI.db], }; +const $indieAuthClientsRepository: Provider = { + provide: DI.indieAuthClientsRepository, + useFactory: (db: DataSource) => db.getRepository(MiIndieAuthClient), + inject: [DI.db], +}; + const $abuseUserReportsRepository: Provider = { provide: DI.abuseUserReportsRepository, useFactory: (db: DataSource) => db.getRepository(MiAbuseUserReport), @@ -456,6 +532,7 @@ const $abuseReportResolversRepository: Provider = { $blockingsRepository, $swSubscriptionsRepository, $hashtagsRepository, + $indieAuthClientsRepository, $abuseUserReportsRepository, $registrationTicketsRepository, $authSessionsRepository, @@ -526,6 +603,7 @@ const $abuseReportResolversRepository: Provider = { $blockingsRepository, $swSubscriptionsRepository, $hashtagsRepository, + $indieAuthClientsRepository, $abuseUserReportsRepository, $registrationTicketsRepository, $authSessionsRepository, diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index ec846defcf..309ec44a52 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -27,6 +27,7 @@ import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; import { MiGalleryPost } from '@/models/GalleryPost.js'; import { MiHashtag } from '@/models/Hashtag.js'; +import { MiIndieAuthClient } from '@/models/IndieAuthClient.js'; import { MiInstance } from '@/models/Instance.js'; import { MiMeta } from '@/models/Meta.js'; import { MiModerationLog } from '@/models/ModerationLog.js'; @@ -98,6 +99,7 @@ export { MiGalleryLike, MiGalleryPost, MiHashtag, + MiIndieAuthClient, MiInstance, MiMeta, MiModerationLog, @@ -168,6 +170,7 @@ export type FollowRequestsRepository = Repository; export type GalleryLikesRepository = Repository; export type GalleryPostsRepository = Repository; export type HashtagsRepository = Repository; +export type IndieAuthClientsRepository = Repository; export type InstancesRepository = Repository; export type MetasRepository = Repository; export type ModerationLogsRepository = Repository; diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index fb0b137c21..454b3d4b2a 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -37,6 +37,7 @@ import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; import { MiGalleryPost } from '@/models/GalleryPost.js'; import { MiHashtag } from '@/models/Hashtag.js'; +import { MiIndieAuthClient } from '@/models/IndieAuthClient.js'; import { MiInstance } from '@/models/Instance.js'; import { MiMeta } from '@/models/Meta.js'; import { MiModerationLog } from '@/models/ModerationLog.js'; @@ -172,6 +173,7 @@ export const entities = [ MiPollVote, MiEmoji, MiHashtag, + MiIndieAuthClient, MiSwSubscription, MiAbuseUserReport, MiRegistrationTicket, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 1951893a77..fdfbc55ea0 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -51,6 +51,10 @@ import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federat import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js'; +import * as ep___admin_indieAuth_create from './endpoints/admin/indie-auth/create.js'; +import * as ep___admin_indieAuth_delete from './endpoints/admin/indie-auth/delete.js'; +import * as ep___admin_indieAuth_list from './endpoints/admin/indie-auth/list.js'; +import * as ep___admin_indieAuth_update from './endpoints/admin/indie-auth/update.js'; import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; @@ -428,6 +432,10 @@ const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federati const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; const $admin_federation_updateInstance: Provider = { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default }; +const $admin_indieAuth_create: Provider = { provide: 'ep:admin/indie-auth/create', useClass: ep___admin_indieAuth_create.default }; +const $admin_indieAuth_delete: Provider = { provide: 'ep:admin/indie-auth/delete', useClass: ep___admin_indieAuth_delete.default }; +const $admin_indieAuth_list: Provider = { provide: 'ep:admin/indie-auth/list', useClass: ep___admin_indieAuth_list.default }; +const $admin_indieAuth_update: Provider = { provide: 'ep:admin/indie-auth/update', useClass: ep___admin_indieAuth_update.default }; const $admin_getIndexStats: Provider = { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }; const $admin_getTableStats: Provider = { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }; const $admin_getUserIps: Provider = { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }; @@ -809,6 +817,10 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, $admin_federation_updateInstance, + $admin_indieAuth_create, + $admin_indieAuth_delete, + $admin_indieAuth_list, + $admin_indieAuth_update, $admin_getIndexStats, $admin_getTableStats, $admin_getUserIps, @@ -1184,6 +1196,10 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, $admin_federation_updateInstance, + $admin_indieAuth_create, + $admin_indieAuth_delete, + $admin_indieAuth_list, + $admin_indieAuth_update, $admin_getIndexStats, $admin_getTableStats, $admin_getUserIps, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 7418fb24cc..16a302739b 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -51,6 +51,10 @@ import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federat import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js'; +import * as ep___admin_indieAuth_create from './endpoints/admin/indie-auth/create.js'; +import * as ep___admin_indieAuth_delete from './endpoints/admin/indie-auth/delete.js'; +import * as ep___admin_indieAuth_list from './endpoints/admin/indie-auth/list.js'; +import * as ep___admin_indieAuth_update from './endpoints/admin/indie-auth/update.js'; import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; @@ -426,6 +430,10 @@ const eps = [ ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], ['admin/federation/update-instance', ep___admin_federation_updateInstance], + ['admin/indie-auth/create', ep___admin_indieAuth_create], + ['admin/indie-auth/delete', ep___admin_indieAuth_delete], + ['admin/indie-auth/list', ep___admin_indieAuth_list], + ['admin/indie-auth/update', ep___admin_indieAuth_update], ['admin/get-index-stats', ep___admin_getIndexStats], ['admin/get-table-stats', ep___admin_getTableStats], ['admin/get-user-ips', ep___admin_getUserIps], diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts new file mode 100644 index 0000000000..a8859a0f19 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { IndieAuthClientsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'write:admin:indie-auth', + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + name: { + type: 'string', + optional: false, nullable: true, + }, + redirectUris: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + id: { type: 'string', minLength: 1 }, + name: { type: 'string', nullable: true }, + redirectUris: { + type: 'array', minItems: 1, + items: { type: 'string' }, + }, + }, + required: ['id'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.indieAuthClientsRepository) + private indieAuthClientsRepository: IndieAuthClientsRepository, + + private moderationLogService: ModerationLogService, + ) { + super(meta, paramDef, async (ps, me) => { + const indieAuthClient = await this.indieAuthClientsRepository.insert({ + id: ps.id, + createdAt: new Date(), + name: ps.name, + redirectUris: ps.redirectUris, + }).then(r => this.indieAuthClientsRepository.findOneByOrFail({ id: r.identifiers[0].id })); + + this.moderationLogService.log(me, 'createIndieAuthClient', { + clientId: indieAuthClient.id, + client: indieAuthClient, + }); + + return { + id: indieAuthClient.id, + createdAt: indieAuthClient.createdAt.toISOString(), + name: indieAuthClient.name, + redirectUris: indieAuthClient.redirectUris, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts new file mode 100644 index 0000000000..0775916ac6 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { IndieAuthClientsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'write:admin:indie-auth', + + errors: { + noSuchIndieAuthClient: { + message: 'No such client', + code: 'NO_SUCH_CLIENT', + id: '02c4e690-af0c-4dc9-9f2f-c436c3b2782d', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + id: { type: 'string' }, + }, + required: ['id'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.indieAuthClientsRepository) + private indieAuthClientsRepository: IndieAuthClientsRepository, + + private moderationLogService: ModerationLogService, + ) { + super(meta, paramDef, async (ps, me) => { + const client = await this.indieAuthClientsRepository.findOneBy({ id: ps.id }); + + if (client == null) throw new ApiError(meta.errors.noSuchIndieAuthClient); + + await this.indieAuthClientsRepository.delete(client.id); + + this.moderationLogService.log(me, 'deleteIndieAuthClient', { + clientId: client.id, + client: client, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts new file mode 100644 index 0000000000..69c2d2eb92 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { IndieAuthClientsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'read:admin:indie-auth', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + name: { + type: 'string', + optional: false, nullable: true, + }, + redirectUris: { + type: 'array', + optional: false, nullable: false, + items: { type: 'string' }, + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.indieAuthClientsRepository) + private indieAuthClientsRepository: IndieAuthClientsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.indieAuthClientsRepository.createQueryBuilder('client'); + const clients = await query.offset(ps.offset).limit(ps.limit).getMany(); + + return clients.map(client => ({ + id: client.id, + createdAt: client.createdAt.toISOString(), + name: client.name, + redirectUris: client.redirectUris, + })); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts new file mode 100644 index 0000000000..a40c373360 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { IndieAuthClientsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'write:admin:indie-auth', + + errors: { + noSuchIndieAuthClient: { + message: 'No such client', + code: 'NO_SUCH_CLIENT', + id: 'd4f9440a-45aa-495c-af66-b4d1e339d4fc', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + id: { type: 'string', minLength: 1 }, + name: { type: 'string', nullable: true }, + redirectUris: { + type: 'array', minItems: 1, + items: { type: 'string' }, + }, + }, + required: ['id'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.indieAuthClientsRepository) + private indieAuthClientsRepository: IndieAuthClientsRepository, + + private moderationLogService: ModerationLogService, + ) { + super(meta, paramDef, async (ps, me) => { + const client = await this.indieAuthClientsRepository.findOneBy({ id: ps.id }); + + if (client == null) throw new ApiError(meta.errors.noSuchIndieAuthClient); + + await this.indieAuthClientsRepository.update(client.id, { + name: ps.name, + redirectUris: ps.redirectUris, + }); + + const updatedClient = await this.indieAuthClientsRepository.findOneByOrFail({ id: client.id }); + + this.moderationLogService.log(me, 'updateIndieAuthClient', { + clientId: client.id, + before: client, + after: updatedClient, + }); + }); + } +} diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 939f00fae7..1860d30b39 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -32,7 +32,12 @@ import { HttpRequestService } from '@/core/HttpRequestService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; -import type { AccessTokensRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import type { + AccessTokensRepository, + IndieAuthClientsRepository, + UserProfilesRepository, + UsersRepository +} from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import type { MiLocalUser } from '@/models/User.js'; @@ -100,8 +105,8 @@ function validateClientId(raw: string): URL { interface ClientInformation { id: string; - redirectUris: string[]; name: string; + redirectUris: string[]; } // https://indieauth.spec.indieweb.org/#client-information-discovery @@ -246,6 +251,8 @@ export class OAuth2ProviderService { private redisClient: Redis.Redis, @Inject(DI.accessTokensRepository) private accessTokensRepository: AccessTokensRepository, + @Inject(DI.indieAuthClientsRepository) + private indieAuthClientsRepository: IndieAuthClientsRepository, @Inject(DI.usersRepository) private usersRepository: UsersRepository, @Inject(DI.userProfilesRepository) @@ -423,8 +430,10 @@ export class OAuth2ProviderService { } } + // Find client information from the database. + const registeredClientInfo = await this.indieAuthClientsRepository.findOneBy({ id: clientUrl.href }) as ClientInformation | null; // Find client information from the remote. - const clientInfo = await discoverClientInformation(this.#logger, this.httpRequestService, clientUrl.href); + const clientInfo = registeredClientInfo ?? await discoverClientInformation(this.#logger, this.httpRequestService, clientUrl.href); // Require the redirect URI to be included in an explicit list, per // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.1.3 diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index cfac5cd9d4..f4897ec73c 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -76,6 +76,9 @@ export const moderationLogTypes = [ 'createAd', 'updateAd', 'deleteAd', + 'createIndieAuthClient', + 'updateIndieAuthClient', + 'deleteIndieAuthClient', 'createAvatarDecoration', 'updateAvatarDecoration', 'deleteAvatarDecoration', @@ -242,6 +245,19 @@ export type ModerationLogPayloads = { adId: string; ad: any; }; + createIndieAuthClient: { + clientId: string; + client: any; + }; + updateIndieAuthClient: { + clientId: string; + before: any; + after: any; + }; + deleteIndieAuthClient: { + clientId: string; + client: any; + }; createAvatarDecoration: { avatarDecorationId: string; avatarDecoration: any; diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index d5d1258733..d32a4e749f 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -131,6 +131,41 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.save }} + + + + +
+ New + + + + + +
+ + + + + + + + + +
+ Save + Delete +
+
+
+ + {{ i18n.ts.more }} + +
+
@@ -155,7 +190,7 @@ import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -const summalyProxy = ref(''); +const summalyProxy = ref(''); const enableHcaptcha = ref(false); const enableMcaptcha = ref(false); const enableRecaptcha = ref(false); @@ -172,6 +207,9 @@ const enableTruemailApi = ref(false); const truemailInstance = ref(null); const truemailAuthKey = ref(null); const bannedEmailDomains = ref(''); +const indieAuthClients = ref([]); +const indieAuthOffset = ref(0); +const indieAuthHasMore = ref(false); async function init() { const meta = await misskeyApi('admin/meta'); @@ -201,15 +239,15 @@ async function init() { function save() { os.apiWithDialog('admin/update-meta', { - summalyProxy: summalyProxy.value, - sensitiveMediaDetection: sensitiveMediaDetection.value, + summalyProxy: summalyProxy.value === '' ? null : summalyProxy.value, + sensitiveMediaDetection: sensitiveMediaDetection.value as 'none' | 'all' | 'local' | 'remote', sensitiveMediaDetectionSensitivity: sensitiveMediaDetectionSensitivity.value === 0 ? 'veryLow' : sensitiveMediaDetectionSensitivity.value === 1 ? 'low' : sensitiveMediaDetectionSensitivity.value === 2 ? 'medium' : sensitiveMediaDetectionSensitivity.value === 3 ? 'high' : sensitiveMediaDetectionSensitivity.value === 4 ? 'veryHigh' : - 0, + 'veryLow', setSensitiveFlagAutomatically: setSensitiveFlagAutomatically.value, enableSensitiveMediaDetectionForVideos: enableSensitiveMediaDetectionForVideos.value, enableIpLogging: enableIpLogging.value, @@ -225,6 +263,64 @@ function save() { }); } +function indieAuthFetch(resetOffset = false) { + if (resetOffset) { + indieAuthClients.value = []; + indieAuthOffset.value = 0; + } + + misskeyApi('admin/indie-auth/list', { + offsetMode: true, + offset: indieAuthOffset.value, + limit: 10, + }).then(clients => { + indieAuthClients.value = indieAuthClients.value.concat(clients.map((client: any) => ({ + id: client.id, + name: client.name, + redirectUris: client.redirectUris.join('\n'), + createdAt: client.createdAt, + }))); + indieAuthHasMore.value = clients.length === 10; + indieAuthOffset.value += clients.length; + }); +} + +indieAuthFetch(true); + +function indieAuthAddNew() { + indieAuthClients.value.unshift({ + id: '', + name: '', + redirectUris: '', + }); +} + +function indieAuthDelete(client) { + os.confirm({ + type: 'warning', + text: i18n.tsx.deleteAreYouSure({ x: client.id }), + }).then(({ canceled }) => { + if (canceled) return; + indieAuthClients.value = indieAuthClients.value.filter(x => x !== client); + misskeyApi('admin/indie-auth/delete', client); + }); +} + +async function indieAuthSave(client) { + const params = { + id: client.id, + name: client.name, + redirectUris: client.redirectUris.split('\n'), + }; + + if (client.createdAt !== undefined) { + await misskeyApi('admin/indie-auth/update', params); + } else { + await misskeyApi('admin/indie-auth/create', params); + } + indieAuthFetch(true); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); @@ -234,3 +330,10 @@ definePageMetadata({ icon: 'ti ti-lock', }); + + diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0e137d26bf..f96b867613 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -214,6 +214,24 @@ type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['c // @public (undocumented) type AdminGetUserIpsResponse = operations['admin/get-user-ips']['responses']['200']['content']['application/json']; +// @public (undocumented) +type AdminIndieAuthCreateRequest = operations['admin/indie-auth/create']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminIndieAuthCreateResponse = operations['admin/indie-auth/create']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type AdminIndieAuthDeleteRequest = operations['admin/indie-auth/delete']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminIndieAuthListRequest = operations['admin/indie-auth/list']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminIndieAuthListResponse = operations['admin/indie-auth/list']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type AdminIndieAuthUpdateRequest = operations['admin/indie-auth/update']['requestBody']['content']['application/json']; + // @public (undocumented) type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json']; @@ -1208,6 +1226,12 @@ declare namespace entities { AdminFederationRefreshRemoteInstanceMetadataRequest, AdminFederationRemoveAllFollowingRequest, AdminFederationUpdateInstanceRequest, + AdminIndieAuthCreateRequest, + AdminIndieAuthCreateResponse, + AdminIndieAuthDeleteRequest, + AdminIndieAuthListRequest, + AdminIndieAuthListResponse, + AdminIndieAuthUpdateRequest, AdminGetIndexStatsResponse, AdminGetTableStatsResponse, AdminGetUserIpsRequest, @@ -2377,6 +2401,15 @@ type ModerationLog = { } | { type: 'deleteAd'; info: ModerationLogPayloads['deleteAd']; +} | { + type: 'createIndieAuthClient'; + info: ModerationLogPayloads['createIndieAuthClient']; +} | { + type: 'updateIndieAuthClient'; + info: ModerationLogPayloads['updateIndieAuthClient']; +} | { + type: 'deleteIndieAuthClient'; + info: ModerationLogPayloads['deleteIndieAuthClient']; } | { type: 'createAvatarDecoration'; info: ModerationLogPayloads['createAvatarDecoration']; @@ -2398,7 +2431,7 @@ type ModerationLog = { }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createIndieAuthClient", "updateIndieAuthClient", "deleteIndieAuthClient", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; // @public (undocumented) type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json']; @@ -2650,7 +2683,7 @@ type PagesUpdateRequest = operations['pages/update']['requestBody']['content'][' function parse(acct: string): Acct; // @public (undocumented) -export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "read:admin:abuse-report-resolvers", "write:admin:abuse-report-resolvers", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"]; +export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "read:admin:abuse-report-resolvers", "write:admin:abuse-report-resolvers", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:indie-auth", "read:admin:indie-auth", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"]; // @public (undocumented) type PingResponse = operations['ping']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index a92f481aac..88b8e1805d 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -499,6 +499,50 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:indie-auth* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 6e1969610a..d3ce04c6be 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -59,6 +59,12 @@ import type { AdminFederationRefreshRemoteInstanceMetadataRequest, AdminFederationRemoveAllFollowingRequest, AdminFederationUpdateInstanceRequest, + AdminIndieAuthCreateRequest, + AdminIndieAuthCreateResponse, + AdminIndieAuthDeleteRequest, + AdminIndieAuthListRequest, + AdminIndieAuthListResponse, + AdminIndieAuthUpdateRequest, AdminGetIndexStatsResponse, AdminGetTableStatsResponse, AdminGetUserIpsRequest, @@ -608,6 +614,10 @@ export type Endpoints = { 'admin/federation/refresh-remote-instance-metadata': { req: AdminFederationRefreshRemoteInstanceMetadataRequest; res: EmptyResponse }; 'admin/federation/remove-all-following': { req: AdminFederationRemoveAllFollowingRequest; res: EmptyResponse }; 'admin/federation/update-instance': { req: AdminFederationUpdateInstanceRequest; res: EmptyResponse }; + 'admin/indie-auth/create': { req: AdminIndieAuthCreateRequest; res: AdminIndieAuthCreateResponse }; + 'admin/indie-auth/delete': { req: AdminIndieAuthDeleteRequest; res: EmptyResponse }; + 'admin/indie-auth/list': { req: AdminIndieAuthListRequest; res: AdminIndieAuthListResponse }; + 'admin/indie-auth/update': { req: AdminIndieAuthUpdateRequest; res: EmptyResponse }; 'admin/get-index-stats': { req: EmptyRequest; res: AdminGetIndexStatsResponse }; 'admin/get-table-stats': { req: EmptyRequest; res: AdminGetTableStatsResponse }; 'admin/get-user-ips': { req: AdminGetUserIpsRequest; res: AdminGetUserIpsResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index 87cba9df16..ae443ebea9 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -61,6 +61,12 @@ export type AdminFederationDeleteAllFilesRequest = operations['admin/federation/ export type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json']; export type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json']; export type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json']; +export type AdminIndieAuthCreateRequest = operations['admin/indie-auth/create']['requestBody']['content']['application/json']; +export type AdminIndieAuthCreateResponse = operations['admin/indie-auth/create']['responses']['200']['content']['application/json']; +export type AdminIndieAuthDeleteRequest = operations['admin/indie-auth/delete']['requestBody']['content']['application/json']; +export type AdminIndieAuthListRequest = operations['admin/indie-auth/list']['requestBody']['content']['application/json']; +export type AdminIndieAuthListResponse = operations['admin/indie-auth/list']['responses']['200']['content']['application/json']; +export type AdminIndieAuthUpdateRequest = operations['admin/indie-auth/update']['requestBody']['content']['application/json']; export type AdminGetIndexStatsResponse = operations['admin/get-index-stats']['responses']['200']['content']['application/json']; export type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json']; export type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 75057ec416..183b72f2c3 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -418,6 +418,42 @@ export type paths = { */ post: operations['admin/federation/update-instance']; }; + '/admin/indie-auth/create': { + /** + * admin/indie-auth/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + post: operations['admin/indie-auth/create']; + }; + '/admin/indie-auth/delete': { + /** + * admin/indie-auth/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + post: operations['admin/indie-auth/delete']; + }; + '/admin/indie-auth/list': { + /** + * admin/indie-auth/list + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:indie-auth* + */ + post: operations['admin/indie-auth/list']; + }; + '/admin/indie-auth/update': { + /** + * admin/indie-auth/update + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + post: operations['admin/indie-auth/update']; + }; '/admin/get-index-stats': { /** * admin/get-index-stats @@ -7566,6 +7602,233 @@ export type operations = { }; }; }; + /** + * admin/indie-auth/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + 'admin/indie-auth/create': { + requestBody: { + content: { + 'application/json': { + id: string; + name?: string | null; + redirectUris?: string[]; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + id: string; + /** Format: date-time */ + createdAt: string; + name: string | null; + redirectUris: string[]; + }; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * admin/indie-auth/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + 'admin/indie-auth/delete': { + requestBody: { + content: { + 'application/json': { + id: string; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * admin/indie-auth/list + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:indie-auth* + */ + 'admin/indie-auth/list': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** @default 0 */ + offset?: number; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': ({ + id: string; + /** Format: date-time */ + createdAt: string; + name: string | null; + redirectUris: string[]; + })[]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * admin/indie-auth/update + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth* + */ + 'admin/indie-auth/update': { + requestBody: { + content: { + 'application/json': { + id: string; + name?: string | null; + redirectUris?: string[]; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * admin/get-index-stats * @description No description provided. diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index 7ce1b66df2..06ba3f3c78 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -78,6 +78,8 @@ export const permissions = [ 'write:admin:avatar-decorations', 'read:admin:avatar-decorations', 'write:admin:federation', + 'write:admin:indie-auth', + 'read:admin:indie-auth', 'write:admin:account', 'read:admin:account', 'write:admin:emoji', @@ -130,6 +132,9 @@ export const moderationLogTypes = [ 'createAd', 'updateAd', 'deleteAd', + 'createIndieAuthClient', + 'updateIndieAuthClient', + 'deleteIndieAuthClient', 'createAvatarDecoration', 'updateAvatarDecoration', 'deleteAvatarDecoration', @@ -296,6 +301,19 @@ export type ModerationLogPayloads = { adId: string; ad: any; }; + createIndieAuthClient: { + clientId: string; + client: any; + }; + updateIndieAuthClient: { + clientId: string; + before: any; + after: any; + }; + deleteIndieAuthClient: { + clientId: string; + client: any; + }; createAvatarDecoration: { avatarDecorationId: string; avatarDecoration: any; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 772d2bbfa1..a26b692f2e 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -113,6 +113,15 @@ export type ModerationLog = { } | { type: 'deleteAd'; info: ModerationLogPayloads['deleteAd']; +} | { + type: 'createIndieAuthClient'; + info: ModerationLogPayloads['createIndieAuthClient']; +} | { + type: 'updateIndieAuthClient'; + info: ModerationLogPayloads['updateIndieAuthClient']; +} | { + type: 'deleteIndieAuthClient'; + info: ModerationLogPayloads['deleteIndieAuthClient']; } | { type: 'createAvatarDecoration'; info: ModerationLogPayloads['createAvatarDecoration'];