diff --git a/app/main.py b/app/main.py index acf6e93..f644fe6 100644 --- a/app/main.py +++ b/app/main.py @@ -43,15 +43,36 @@ app = FastAPI( app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) -# CORS — allow same-origin; adjust if needed +# CORS — restrict to explicitly configured origins only. +# Set ALLOWED_ORIGINS in .env as a comma-separated list of allowed origins, +# e.g. ALLOWED_ORIGINS=https://myapp.example.com +# If unset, no cross-origin requests are allowed (same-origin only). +_raw_origins = os.environ.get("ALLOWED_ORIGINS", "") +_allowed_origins = [o.strip() for o in _raw_origins.split(",") if o.strip()] + app.add_middleware( CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], + allow_origins=_allowed_origins, + allow_credentials=False, + allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allow_headers=["Authorization", "Content-Type"], ) +# --------------------------------------------------------------------------- +# Security headers middleware +# --------------------------------------------------------------------------- +@app.middleware("http") +async def add_security_headers(request: Request, call_next): + """Attach standard security headers to every response.""" + response = await call_next(request) + response.headers["X-Content-Type-Options"] = "nosniff" + response.headers["X-Frame-Options"] = "DENY" + response.headers["X-XSS-Protection"] = "1; mode=block" + response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" + response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains" + return response + + # --------------------------------------------------------------------------- # Routers # --------------------------------------------------------------------------- diff --git a/app/routers/auth.py b/app/routers/auth.py index abd72b5..b02a48a 100644 --- a/app/routers/auth.py +++ b/app/routers/auth.py @@ -386,9 +386,9 @@ async def azure_callback( except HTTPException: raise - except Exception as exc: + except Exception: logger.exception("Azure AD authentication error") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Azure AD authentication error: {exc}", + detail="Azure AD authentication failed. Please try again or contact support.", ) diff --git a/app/routers/users.py b/app/routers/users.py index 6fafa69..0e8d9c9 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -33,6 +33,12 @@ async def create_user( db: Session = Depends(get_db), ): """Create a new local user.""" + if current_user.role != "admin": + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Only admins can create new users.", + ) + existing = db.query(User).filter(User.username == payload.username).first() if existing: raise HTTPException(