diff --git a/locales/index.d.ts b/locales/index.d.ts index 344c4c51e6..7f20c6803f 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -6005,6 +6005,22 @@ export interface Locale extends ILocale { * New */ "new": string; + /** + * Restrict to roles + */ + "onlyForRoles": string; + /** + * Change role restrictions + */ + "onlyForRolesChange": string; + /** + * Shown to everyone + */ + "onlyForRolesUnrestricted": string; + /** + * Shown to members of {roles} roles + */ + "onlyForRolesRestricted": ParameterizedString<"roles">; /** * Throw confetti */ @@ -7651,6 +7667,10 @@ export interface Locale extends ILocale { * Can appear in trending notes / users */ "canTrend": string; + /** + * Can view federation stats and details of remote instances + */ + "canViewFederation": string; }; "_condition": { /** diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 10e1315dd2..afaa645383 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -70,6 +70,7 @@ export type RolePolicies = { canImportUserLists: boolean; chatAvailability: 'available' | 'readonly' | 'unavailable'; canTrend: boolean; + canViewFederation: boolean; }; export const DEFAULT_POLICIES: RolePolicies = { @@ -110,6 +111,7 @@ export const DEFAULT_POLICIES: RolePolicies = { canImportUserLists: true, chatAvailability: 'available', canTrend: true, + canViewFederation: true, }; @Injectable() @@ -472,6 +474,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { canImportUserLists: calc('canImportUserLists', vs => vs.some(v => v === true)), chatAvailability: calc('chatAvailability', aggregateChatAvailability), canTrend: calc('canTrend', vs => vs.some(v => v === true)), + canViewFederation: calc('canViewFederation', vs => vs.some(v => v === true)), }; } diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index 363be921ed..daa86b8a6c 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -313,6 +313,10 @@ export const packedRolePoliciesSchema = { type: 'boolean', optional: false, nullable: false, }, + canViewFederation: { + type: 'boolean', + optional: false, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index bd15700670..6e8d95aece 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -12,6 +12,8 @@ import { schema } from '@/core/chart/charts/entities/federation.js'; export const meta = { tags: ['charts'], + requiredRolePolicy: 'canViewFederation', + res: getJsonSchema(schema), allowGet: true, diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index e1053d05d8..cafa729d67 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -12,6 +12,8 @@ import { schema } from '@/core/chart/charts/entities/instance.js'; export const meta = { tags: ['charts'], + requiredRolePolicy: 'canViewFederation', + res: getJsonSchema(schema), allowGet: true, diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index e7b528dda1..9add00ccde 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -10,6 +10,7 @@ import { FollowingEntityService } from '@/core/entities/FollowingEntityService.j export const meta = { tags: ['federation'], + requiredRolePolicy: 'canViewFederation', requireCredential: true, kind: 'read:account', diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 070a1c8407..849bb61fb4 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -10,6 +10,7 @@ import { FollowingEntityService } from '@/core/entities/FollowingEntityService.j export const meta = { tags: ['federation'], + requiredRolePolicy: 'canViewFederation', requireCredential: true, kind: 'read:account', diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index eeaebc3708..da0535d0b9 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -14,6 +14,7 @@ import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; export const meta = { tags: ['federation'], + requiredRolePolicy: 'canViewFederation', requireCredential: false, allowGet: true, cacheSec: 3600, diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 8168f9296f..ea3349c17b 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -13,6 +13,7 @@ import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], + requiredRolePolicy: 'canViewFederation', requireCredential: false, res: { diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index 54d29c2faa..605df455b4 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -14,6 +14,7 @@ import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], + requiredRolePolicy: 'canViewFederation', requireCredential: false, allowGet: true, diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index e3bc2989df..b41138a842 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -13,6 +13,7 @@ import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], + requiredRolePolicy: 'canViewFederation', requireCredential: false, res: { diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 8e9bf86705..5bbfd314a4 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -178,6 +178,7 @@ export const ROLE_POLICIES = [ 'canImportUserLists', 'chatAvailability', 'canTrend', + 'canViewFederation', ] as const; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade', 'followmouse']; diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue index 90391005bc..7428a2c288 100644 --- a/packages/frontend/src/components/MkInstanceStats.vue +++ b/packages/frontend/src/components/MkInstanceStats.vue @@ -93,7 +93,7 @@ import { useChartTooltip } from '@/use/use-chart-tooltip.js'; import { $i } from '@/i.js'; import * as os from '@/os.js'; import { misskeyApiGet } from '@/utility/misskey-api.js'; -import { instance } from '@/instance.js'; +import { instance, policies } from '@/instance.js'; import { i18n } from '@/i18n.js'; import MkHeatmap from '@/components/MkHeatmap.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; @@ -103,7 +103,7 @@ import { initChart } from '@/utility/init-chart.js'; initChart(); -const shouldShowFederation = computed(() => instance.federation !== 'none' || $i?.isModerator); +const shouldShowFederation = computed(() => (instance.federation !== 'none' || $i?.isModerator) && policies.canViewFederation); const chartLimit = 500; const chartSpan = ref<'hour' | 'day'>('hour'); diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue index a1111bd8c9..99fd93bab3 100644 --- a/packages/frontend/src/components/MkWidgets.vue +++ b/packages/frontend/src/components/MkWidgets.vue @@ -58,7 +58,7 @@ import MkButton from '@/components/MkButton.vue'; import { widgets as widgetDefs, federationWidgets } from '@/widgets/index.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; +import { instance, policies } from '@/instance.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -68,7 +68,7 @@ const props = defineProps<{ }>(); const _widgetDefs = computed(() => { - if (instance.federation === 'none') { + if (instance.federation === 'none' || !policies.value.canViewFederation) { return widgetDefs.filter(x => !federationWidgets.includes(x)); } else { return widgetDefs; diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 6b7b650a75..44c8662c69 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -22,10 +22,11 @@ SPDX-License-Identifier: AGPL-3.0-only