import json from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, delete from app.api.deps import db_session, get_current_user, require_admin from app.core.security import encrypt_secret, CSRF_COOKIE from app.models.user import User from app.models.router import Router, RouterCredential, CredentialMethod from app.models.dashboard import Permission from app.schemas.router import RouterCreate, RouterOut, CredentialCreate router = APIRouter() def require_csrf(request: Request): # minimalny CSRF: nagłówek X-CSRF-Token musi równać się cookie mt_csrf cookie = request.cookies.get(CSRF_COOKIE) header = request.headers.get("X-CSRF-Token") if not cookie or not header or cookie != header: raise HTTPException(status_code=403, detail="CSRF check failed") @router.get("", response_model=list[RouterOut]) async def list_routers(user: User = Depends(get_current_user), session: AsyncSession = Depends(db_session)): if user.role == "admin": res = await session.execute(select(Router)) rows = res.scalars().all() else: res = await session.execute( select(Router).join(Permission, Permission.router_id == Router.id) .where(Permission.user_id == user.id, Permission.can_view == True) # noqa ) rows = res.scalars().all() return [RouterOut( id=r.id, name=r.name, host=r.host, port_rest=r.port_rest, port_ssh=r.port_ssh, port_api=r.port_api, verify_ssl=r.verify_ssl, preferred_method=r.preferred_method, tags=r.tags ) for r in rows] @router.post("", response_model=RouterOut) async def create_router(payload: RouterCreate, request: Request, user: User = Depends(get_current_user), session: AsyncSession = Depends(db_session)): require_admin(user) require_csrf(request) r = Router(**payload.model_dump()) session.add(r) await session.commit() await session.refresh(r) return RouterOut( id=r.id, name=r.name, host=r.host, port_rest=r.port_rest, port_ssh=r.port_ssh, port_api=r.port_api, verify_ssl=r.verify_ssl, preferred_method=r.preferred_method, tags=r.tags ) @router.post("/{router_id}/credentials") async def add_credential(router_id: int, payload: CredentialCreate, request: Request, user: User = Depends(get_current_user), session: AsyncSession = Depends(db_session)): require_admin(user) require_csrf(request) res = await session.execute(select(Router).where(Router.id == router_id)) r = res.scalar_one_or_none() if not r: raise HTTPException(status_code=404, detail="Router not found") method = payload.method.lower() if method not in ("rest", "ssh", "api"): raise HTTPException(status_code=400, detail="Bad method") c = RouterCredential( router_id=router_id, method=CredentialMethod(method), username=payload.username, secret_encrypted=encrypt_secret(payload.secret), extra_json=json.dumps(payload.extra_json or {}), ) session.add(c) await session.commit() return {"ok": True} @router.post("/{router_id}/grant") async def grant_router(router_id: int, target_user_id: int, can_edit: bool = False, request: Request = None, user: User = Depends(get_current_user), session: AsyncSession = Depends(db_session)): require_admin(user) require_csrf(request) # upsert permission res = await session.execute(select(Permission).where(Permission.user_id == target_user_id, Permission.router_id == router_id)) p = res.scalar_one_or_none() if not p: p = Permission(user_id=target_user_id, router_id=router_id, can_view=True, can_edit=can_edit) session.add(p) else: p.can_view = True p.can_edit = bool(can_edit) await session.commit() return {"ok": True}