feat(): Criado url protegida por token, acesso via endpoint
This commit is contained in:
parent
9f6b340412
commit
73dd1ac3bd
3 changed files with 46 additions and 14 deletions
|
|
@ -31,9 +31,22 @@ def generate_storage_hash() -> str:
|
||||||
|
|
||||||
|
|
||||||
# Função que gera o token temporário para acesso ao documento
|
# Função que gera o token temporário para acesso ao documento
|
||||||
|
# def generate_temporary_token(
|
||||||
|
# expires_minutes: int,
|
||||||
|
# secret_key: str,
|
||||||
|
# algorithm: str,
|
||||||
|
# ) -> str:
|
||||||
|
# """Gera um token JWT válido por poucos minutos."""
|
||||||
|
# expire = datetime.utcnow() + timedelta(minutes=expires_minutes)
|
||||||
|
# payload = {
|
||||||
|
# "exp": expire,
|
||||||
|
# }
|
||||||
|
# return jwt.encode(payload, secret_key, algorithm=algorithm)
|
||||||
|
|
||||||
|
|
||||||
def generate_temporary_token(
|
def generate_temporary_token(
|
||||||
file_id: str,
|
file_id: str,
|
||||||
filename: str,
|
file_path: str,
|
||||||
expires_minutes: int,
|
expires_minutes: int,
|
||||||
secret_key: str,
|
secret_key: str,
|
||||||
algorithm: str,
|
algorithm: str,
|
||||||
|
|
@ -42,7 +55,7 @@ def generate_temporary_token(
|
||||||
expire = datetime.utcnow() + timedelta(minutes=expires_minutes)
|
expire = datetime.utcnow() + timedelta(minutes=expires_minutes)
|
||||||
payload = {
|
payload = {
|
||||||
"sub": file_id,
|
"sub": file_id,
|
||||||
"filename": filename,
|
"filename": file_path, # caminho relativo completo
|
||||||
"exp": expire,
|
"exp": expire,
|
||||||
}
|
}
|
||||||
return jwt.encode(payload, secret_key, algorithm=algorithm)
|
return jwt.encode(payload, secret_key, algorithm=algorithm)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,45 @@
|
||||||
from fastapi import APIRouter, Query, HTTPException
|
from fastapi import APIRouter, Query, HTTPException
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from jose import jwt, JWTError
|
from jose import jwt, JWTError
|
||||||
|
from database.mysql import SessionLocal, get_database_settings
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
# === Configuração do token temporário ===
|
||||||
|
DB_SETTINGS = get_database_settings()
|
||||||
|
SECRET_KEY = getattr(DB_SETTINGS, "aeskey", None)
|
||||||
|
ALGORITHM = "HS256"
|
||||||
|
|
||||||
@router.get("/{file_id}/{filename}")
|
# Pasta base onde os arquivos são armazenados
|
||||||
def visualizar_arquivo(file_id: str, filename: str, token: str = Query(...)):
|
STORAGE_DIR = Path("storage")
|
||||||
"""Valida o token e retorna o arquivo se autorizado."""
|
|
||||||
|
|
||||||
|
@router.get("/{file_id}/{file_path:path}")
|
||||||
|
def visualizar_arquivo(file_id: str, file_path: str, token: str = Query(...)):
|
||||||
|
"""
|
||||||
|
Valida o token e retorna o arquivo do storage, se autorizado.
|
||||||
|
Exemplo de URL:
|
||||||
|
/view/d7e8f9g0h1i2/100/57/documento.pdf?token=xxxx
|
||||||
|
"""
|
||||||
|
|
||||||
|
# --- 1. Valida token JWT ---
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
if payload["sub"] != file_id or payload["filename"] != filename:
|
if payload["sub"] != file_id or payload["filename"] != file_path:
|
||||||
raise HTTPException(status_code=403, detail="Token inválido.")
|
raise HTTPException(status_code=403, detail="Token inválido.")
|
||||||
except JWTError:
|
except JWTError:
|
||||||
raise HTTPException(status_code=401, detail="Token expirado ou inválido.")
|
raise HTTPException(status_code=401, detail="Token expirado ou inválido.")
|
||||||
|
|
||||||
file_path = f"files/{filename}"
|
# --- 2. Monta caminho real no disco ---
|
||||||
try:
|
full_path = STORAGE_DIR / file_path
|
||||||
return FileResponse(file_path, media_type="application/pdf")
|
|
||||||
except FileNotFoundError:
|
# --- 3. Retorna arquivo, se existir ---
|
||||||
|
if not full_path.exists():
|
||||||
raise HTTPException(status_code=404, detail="Arquivo não encontrado.")
|
raise HTTPException(status_code=404, detail="Arquivo não encontrado.")
|
||||||
|
|
||||||
|
return FileResponse(
|
||||||
|
full_path,
|
||||||
|
media_type="application/pdf",
|
||||||
|
headers={"Content-Disposition": "inline"}, # abre no navegador
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,6 @@ class ShowAtosRepository:
|
||||||
"url": (
|
"url": (
|
||||||
f"{URL_API}/view/{generate_storage_hash()}/{d.url.decode('utf-8')}?token="
|
f"{URL_API}/view/{generate_storage_hash()}/{d.url.decode('utf-8')}?token="
|
||||||
+ generate_temporary_token(
|
+ generate_temporary_token(
|
||||||
generate_storage_hash(),
|
|
||||||
d.url.decode("utf-8"),
|
|
||||||
10,
|
10,
|
||||||
SECRET_KEY,
|
SECRET_KEY,
|
||||||
ALGORITHM,
|
ALGORITHM,
|
||||||
|
|
@ -188,8 +186,6 @@ class ShowAtosRepository:
|
||||||
"url": (
|
"url": (
|
||||||
f"{URL_API}/view/{generate_storage_hash()}/{d.url.decode('utf-8')}?token="
|
f"{URL_API}/view/{generate_storage_hash()}/{d.url.decode('utf-8')}?token="
|
||||||
+ generate_temporary_token(
|
+ generate_temporary_token(
|
||||||
generate_storage_hash(),
|
|
||||||
d.url.decode("utf-8"),
|
|
||||||
10,
|
10,
|
||||||
SECRET_KEY,
|
SECRET_KEY,
|
||||||
ALGORITHM,
|
ALGORITHM,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue