import { CanActivate, ExecutionContext, Injectable, Logger, UnauthorizedException, } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { timingSafeEqual } from 'crypto'; /** * حارس السوبر-أدمن (مالك المنصة) — يحرس `admin/*` و`platform/*`. * * السوبر-أدمن يدير **كل** المستأجرين، فهو خارج نموذج أدوار المستأجر تماماً * (rider/driver/dispatcher/admin): أدمن المستأجر «سيرو» يجب ألّا يرقّي اشتراكه * بنفسه. لذلك المصادقة بسرّ المنصة لا بـJWT مستأجر. * * مؤقّت حتى يوجد نموذج مستخدم منصة حقيقي؛ يوثَّق في docs/19. */ @Injectable() export class PlatformGuard implements CanActivate { private readonly logger = new Logger('PlatformGuard'); constructor(private readonly config: ConfigService) {} canActivate(context: ExecutionContext): boolean { const req = context.switchToHttp().getRequest(); const provided = req.headers?.['x-platform-secret']; const expected = this.config.get('platform.secret'); // سرّ غير مضبوط = **منع الجميع**، لا سماح للجميع. تركه فارغاً في الإنتاج // خطأ تشغيلي شائع، وفتحُ الباب عنده أسوأ من إغلاقه. if (!expected) { this.logger.error('PLATFORM_SECRET is not set — refusing all platform access'); throw new UnauthorizedException('platform access disabled'); } if (typeof provided !== 'string' || !PlatformGuard.safeEqual(provided, expected)) { throw new UnauthorizedException('invalid platform secret'); } return true; } /** مقارنة ثابتة الزمن — المقارنة العادية تسرّب السرّ حرفاً حرفاً بالتوقيت. */ private static safeEqual(a: string, b: string): boolean { const ba = Buffer.from(a); const bb = Buffer.from(b); if (ba.length !== bb.length) return false; return timingSafeEqual(ba, bb); } }