fix(api): пускать к бэкапу и миграции только глобального админа
Скачивание и восстановление файла БД, JSON-бэкап и legacy-импорт затрагивают все spaces и содержат учётные данные API, поэтому прав vps:settings:admin в одном space для них недостаточно. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
273883f657
commit
0b9b1cad54
3 files changed
+21
No files matched your search
@@ -186,6 +186,20 @@ export function requireSpaceRole(
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Whole-DB operations (backup download/restore, legacy import) cross every space and carry API credentials,
|
||||
* so per-space settings admin is not enough.
|
||||
*/
|
||||
export function requireGlobalAdmin(request: FastifyRequest, reply: FastifyReply): boolean {
|
||||
const user = request.authUser
|
||||
if (!user) return true
|
||||
if (user.isAdmin || hasPermission(user.permissions, 'vps:spaces:admin')) return true
|
||||
void reply.code(403).send({
|
||||
error: { code: 'FORBIDDEN', message: 'Операция доступна только глобальному администратору' },
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
export function canWriteInSpace(request: FastifyRequest): boolean {
|
||||
const role = request.spaceRole ?? 'viewer'
|
||||
return roleAtLeast(role, 'member')
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
type FullBackupPayload,
|
||||
} from '../services/backup-full.js'
|
||||
import { restartScheduler } from '../services/scheduler.js'
|
||||
import { requireGlobalAdmin } from '../plugins/space.js'
|
||||
|
||||
/** Лимит тела для импорта бэкапа (Fastify default = 1 MiB → 413). */
|
||||
function backupBodyLimitBytes(): number {
|
||||
@@ -29,6 +30,10 @@ function backupBodyLimitBytes(): number {
|
||||
export const backupRoutes: FastifyPluginAsync = async (app) => {
|
||||
const bodyLimit = backupBodyLimitBytes()
|
||||
|
||||
app.addHook('preHandler', async (req, reply) => {
|
||||
if (!requireGlobalAdmin(req, reply)) return reply
|
||||
})
|
||||
|
||||
app.addContentTypeParser(
|
||||
'application/octet-stream',
|
||||
{ parseAs: 'buffer', bodyLimit },
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import type { FastifyPluginAsync } from 'fastify'
|
||||
import { consolidateAllProviderApiSources, getSqlite } from '@cfdm/db'
|
||||
import { requireGlobalAdmin } from '../plugins/space.js'
|
||||
|
||||
export const migrateRoutes: FastifyPluginAsync = async (app) => {
|
||||
app.post('/api/migrate', async (req, reply) => {
|
||||
if (!requireGlobalAdmin(req, reply)) return reply
|
||||
const data = req.body
|
||||
if (!data || typeof data !== 'object') {
|
||||
return reply.code(400).send({ error: { code: 'VALIDATION', message: 'Invalid payload' } })
|
||||
|
||||
Reference in new issue
Block a user