fix(auth): standalone auto-login qua IP LAN cua chinh may — _is_local_client (own interface IPs)

This commit is contained in:
2026-08-23 15:40:49 +07:00
parent 6edfe1db32
commit 6176cf3f8e
+30 -2
View File
@@ -1,6 +1,7 @@
import uuid
import time
import threading
import socket
from fastapi import APIRouter, HTTPException, Header, Depends, Request, Response
from fastapi.responses import JSONResponse
from pydantic import BaseModel, EmailStr
@@ -60,6 +61,33 @@ def _is_loopback(host: Optional[str]) -> bool:
host = host.lower()
return host in ("127.0.0.1", "::1", "localhost") or host.startswith("127.")
_LOCAL_IPS_CACHE = {"at": 0.0, "ips": set()}
def _own_interface_ips(max_age=60.0) -> set:
"""Các IP interface cua CHINH may (standalone): truy cap qua IP LAN
cua may (vd http://192.168.1.12:8000) van la may nay -> auto-login.
Client LAN may khac co source IP khac -> khong match -> van can mat khau."""
now = time.time()
c = _LOCAL_IPS_CACHE
if now - c["at"] > max_age:
ips = set()
try:
ips = set(socket.gethostbyname_ex(socket.gethostname())[2])
except Exception:
pass
c["ips"] = {i for i in ips if not _is_loopback(i)}
c["at"] = now
return c["ips"]
def _is_local_client(host: Optional[str]) -> bool:
"""May chay engine (loopback) hoac IP interface cua chinh may -> local.
Dung chung cho get_current_user + /auth/local."""
if _is_loopback(host):
return True
if not host:
return False
return host in _own_interface_ips()
def _loopback_admin_user() -> dict:
"""Identity admin cho loopback — real user_id từ DB; không bắt buộc đổi
mật khẩu (standalone tự vào thẳng DAW)."""
@@ -94,7 +122,7 @@ def get_current_user(request: Request, authorization: Optional[str] = Header(Non
if not token:
# Standalone (loopback): không cần mật khẩu — auto admin. LAN vẫn 401.
host = request.client.host if request.client else None
if _is_loopback(host):
if _is_local_client(host):
return _loopback_admin_user()
raise HTTPException(status_code=401, detail="Thiếu Token xác thực hoặc Token không hợp lệ")
payload = decode_token(token)
@@ -168,7 +196,7 @@ async def local_login(request: Request, response: Response):
"""Auto-login standalone: chỉ cho phép từ loopback (máy chạy engine).
Client LAN nhận 403 → buộc đăng nhập mật khẩu qua /login."""
host = request.client.host if request.client else None
if not _is_loopback(host):
if not _is_local_client(host):
raise HTTPException(status_code=403, detail="Truy cập từ xa — cần đăng nhập")
u = _loopback_admin_user()
token = create_token(u["user_id"], u["username"], u["role"], False)