diff --git a/locales/index.d.ts b/locales/index.d.ts index 782918e82b..6dfc76546a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -13511,6 +13511,28 @@ export interface Locale extends ILocale { * Permissions */ "permissions": string; + /** + * Override rank + */ + "overrideRank": string; + /** + * Overrides the user rank (admin, moderator, or user) for apps using this token. + */ + "overrideRankDescription": string; + "_ranks": { + /** + * Admin + */ + "admin": string; + /** + * Moderator + */ + "mod": string; + /** + * User + */ + "user": string; + }; } declare const locales: { [lang: string]: Locale; diff --git a/packages/backend/src/models/AccessToken.ts b/packages/backend/src/models/AccessToken.ts index 6f98c14ec1..ede40bde3a 100644 --- a/packages/backend/src/models/AccessToken.ts +++ b/packages/backend/src/models/AccessToken.ts @@ -8,6 +8,9 @@ import { id } from './util/id.js'; import { MiUser } from './User.js'; import { MiApp } from './App.js'; +export const accessTokenRanks = ['user', 'mod', 'admin'] as const; +export type AccessTokenRank = typeof accessTokenRanks[number]; + @Entity('access_token') export class MiAccessToken { @PrimaryColumn(id()) @@ -87,4 +90,11 @@ export class MiAccessToken { default: false, }) public fetched: boolean; + + @Column('enum', { + enum: accessTokenRanks, + nullable: true, + comment: 'Limits the user\' rank (user, moderator, or admin) when using this token. If null (default), then uses the user\'s actual rank.', + }) + public rank: AccessTokenRank | null; } diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index 9435284d77..f6f05045e3 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -60,6 +60,7 @@ export const paramDef = { grantees: { type: 'array', uniqueItems: true, items: { type: 'string', } }, + rank: { type: 'string', enum: ['admin', 'mod', 'user'], nullable: true }, }, required: ['session', 'permission'], } as const; @@ -109,6 +110,7 @@ export default class extends Endpoint { // eslint- description: ps.description, iconUrl: ps.iconUrl, permission: ps.permission, + rank: ps.rank, }); // Insert shared access grants diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index ebfa3dd314..9678982f99 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -28,6 +28,22 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + + + + + + + + + @@ -82,9 +98,10 @@ import MkButton from './MkButton.vue'; import MkInfo from './MkInfo.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; -import { iAmAdmin } from '@/i.js'; +import { $i, iAmAdmin } from '@/i.js'; import MkFolder from '@/components/MkFolder.vue'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; +import MkSelect from '@/components/MkSelect.vue'; import * as os from '@/os'; import { instance } from '@/instance'; @@ -102,7 +119,7 @@ const props = withDefaults(defineProps<{ const emit = defineEmits<{ (ev: 'closed'): void; - (ev: 'done', result: { name: string | null, permissions: string[], grantees: string[] }): void; + (ev: 'done', result: { name: string | null, permissions: string[], grantees: string[], rank: string }): void; }>(); const defaultPermissions = Misskey.permissions.filter(p => !p.startsWith('read:admin') && !p.startsWith('write:admin')); @@ -113,6 +130,12 @@ const name = ref(props.initialName); const permissionSwitches = ref({} as Record<(typeof Misskey.permissions)[number], boolean>); const permissionSwitchesForAdmin = ref({} as Record<(typeof Misskey.permissions)[number], boolean>); const grantees = ref([]); +const rank = ref<'admin' | 'mod' | 'user'>( + $i?.isAdmin + ? 'admin' + : $i?.isModerator + ? 'mod' + : 'user'); if (props.initialPermissions) { for (const kind of props.initialPermissions) { @@ -138,6 +161,7 @@ function ok(): void { ...(iAmAdmin ? Object.keys(permissionSwitchesForAdmin.value).filter(p => permissionSwitchesForAdmin.value[p]) : []), ], grantees: grantees.value.map(g => g.id), + rank: rank.value, }); dialog.value?.close(); } diff --git a/packages/frontend/src/pages/settings/connect.vue b/packages/frontend/src/pages/settings/connect.vue index 4c0156f779..8398592279 100644 --- a/packages/frontend/src/pages/settings/connect.vue +++ b/packages/frontend/src/pages/settings/connect.vue @@ -84,12 +84,13 @@ const pagination = { function generateToken() { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, { done: async result => { - const { name, permissions, grantees } = result; + const { name, permissions, grantees, rank } = result; const { token } = await misskeyApi('miauth/gen-token', { session: null, name: name, permission: permissions, grantees: grantees, + rank: rank, }); os.alert({ diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts index 9e9d492dc2..fa31a594ca 100644 --- a/packages/frontend/src/plugin.ts +++ b/packages/frontend/src/plugin.ts @@ -109,11 +109,12 @@ export async function authorizePlugin(plugin: Plugin) { initialPermissions: plugin.permissions, }, { done: async result => { - const { name, permissions } = result; + const { name, permissions, rank } = result; const { token } = await misskeyApi('miauth/gen-token', { session: null, name: name, permission: permissions, + rank: rank, }); res(token); }, diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 6d85ed4bde..1a67ef2efa 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -26379,6 +26379,8 @@ export type operations = { iconUrl?: string | null; permission: string[]; grantees?: string[]; + /** @enum {string|null} */ + rank?: 'admin' | 'mod' | 'user'; }; }; }; diff --git a/sharkey-locales/en-US.yml b/sharkey-locales/en-US.yml index 68bc7ca624..003fc40f63 100644 --- a/sharkey-locales/en-US.yml +++ b/sharkey-locales/en-US.yml @@ -692,3 +692,9 @@ noSharedAccess: "You have not been granted shared access to any accounts" expand: "Expand" collapse: "Collapse" permissions: "Permissions" +overrideRank: "Override rank" +overrideRankDescription: "Overrides the user rank (admin, moderator, or user) for apps using this token." +_ranks: + admin: "Admin" + mod: "Moderator" + user: "User"