/
h0tnanny
/
IotPlatform
Обзор
Документация
Войти
/
h0tnanny
/
IotPlatform
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
src/middleware/auth.ts
123 строки
4 KB
h0tnanny
fix readonly permissions
01 фев 2026, 23:32
01 фев 2026, 23:32
01880f6
Код
Авторство
О чём код?
import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; import { authConfig } from '../config/auth'; import { PermissionRepository } from '../repositories/PermissionRepository'; export interface JwtPayload { userId: string; username: string; role: string; } export interface AuthRequest extends Request { user?: JwtPayload; /** Уровень доступа к текущему ресурсу (устанавливается requireResourceAccess) */ resourcePermission?: 'viewer' | 'editor' | 'owner'; } /** * Middleware: проверяет JWT токен из заголовка Authorization */ export function authenticate(req: AuthRequest, res: Response, next: NextFunction): void { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { res.status(401).json({ error: { code: 'UNAUTHORIZED', message: 'Токен авторизации не предоставлен' }, }); return; } const token = authHeader.split(' ')[1]; try { const decoded = jwt.verify(token, authConfig.jwtSecret) as JwtPayload; req.user = decoded; next(); } catch (_err) { res.status(401).json({ error: { code: 'UNAUTHORIZED', message: 'Недействительный или истекший токен' }, }); } } /** * Middleware: проверяет роль пользователя */ export function requireRole(...roles: string[]) { return (req: AuthRequest, res: Response, next: NextFunction): void => { if (!req.user) { res.status(401).json({ error: { code: 'UNAUTHORIZED', message: 'Необходима авторизация' }, }); return; } if (!roles.includes(req.user.role)) { res.status(403).json({ error: { code: 'FORBIDDEN', message: 'Недостаточно прав' }, }); return; } next(); }; } /** * Middleware: проверяет доступ к ресурсу (owner, shared, admin) */ export function requireResourceAccess(resourceType: 'workflow' | 'equipment', minPermission: 'viewer' | 'editor' = 'viewer') { const permissionRepo = new PermissionRepository(); return async (req: AuthRequest, res: Response, next: NextFunction): Promise<void> => { if (!req.user) { res.status(401).json({ error: { code: 'UNAUTHORIZED', message: 'Необходима авторизация' }, }); return; } const resourceId = req.params.id; if (!resourceId) { next(); return; } try { const access = await permissionRepo.checkAccess( resourceType, resourceId, req.user.userId ); // Админ: доступ ко всему, уровень по БД или editor по умолчанию if (req.user.role === 'admin') { req.resourcePermission = access?.permission ?? 'editor'; next(); return; } if (!access) { res.status(403).json({ error: { code: 'FORBIDDEN', message: 'Нет доступа к данному ресурсу' }, }); return; } if (minPermission === 'editor' && access.permission === 'viewer') { res.status(403).json({ error: { code: 'FORBIDDEN', message: 'Недостаточно прав для редактирования' }, }); return; } req.resourcePermission = access.permission; next(); } catch (_err) { res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: 'Ошибка проверки доступа' }, }); } }; }