11import logging
22import os
33import re
4+ import secrets
45import zipfile
56from pathlib import Path
67from typing import Iterable , Optional
78
8- from fastapi import FastAPI , HTTPException
9+ from fastapi import Depends , FastAPI , HTTPException
910from fastapi .responses import StreamingResponse
11+ from fastapi .security import HTTPBasic , HTTPBasicCredentials
1012
1113
1214logging .basicConfig (level = logging .INFO )
@@ -22,6 +24,11 @@ def get_zotero_root() -> Path:
2224
2325
2426app = FastAPI (title = "Zotero WebDAV PDF Proxy" )
27+ basic_auth_scheme = HTTPBasic (auto_error = False )
28+
29+ BASIC_AUTH_USER = os .getenv ("PDF_PROXY_BASIC_AUTH_USER" )
30+ BASIC_AUTH_PASSWORD = os .getenv ("PDF_PROXY_BASIC_AUTH_PASSWORD" )
31+ BASIC_AUTH_REALM = os .getenv ("PDF_PROXY_BASIC_AUTH_REALM" , "Zotero PDF Proxy" )
2532
2633
2734def validate_key (key : str ) -> None :
@@ -103,12 +110,35 @@ def iterator() -> Iterable[bytes]:
103110 return StreamingResponse (iterator (), media_type = "application/pdf" , headers = headers )
104111
105112
106- @app .get ("/health" )
113+ def enforce_basic_auth (
114+ credentials : Optional [HTTPBasicCredentials ] = Depends (basic_auth_scheme ),
115+ ) -> None :
116+ auth_configured = BASIC_AUTH_USER and BASIC_AUTH_PASSWORD
117+ if not auth_configured :
118+ return
119+
120+ unauthorized = HTTPException (
121+ status_code = 401 ,
122+ detail = "Unauthorized" ,
123+ headers = {"WWW-Authenticate" : f'Basic realm="{ BASIC_AUTH_REALM } "' },
124+ )
125+
126+ if credentials is None :
127+ raise unauthorized
128+
129+ username_valid = secrets .compare_digest (credentials .username , BASIC_AUTH_USER )
130+ password_valid = secrets .compare_digest (credentials .password , BASIC_AUTH_PASSWORD )
131+
132+ if not username_valid or not password_valid :
133+ raise unauthorized
134+
135+
136+ @app .get ("/health" , dependencies = [Depends (enforce_basic_auth )])
107137def health () -> dict :
108138 return {"status" : "ok" }
109139
110140
111- @app .get ("/pdf/{key}" )
141+ @app .get ("/pdf/{key}" , dependencies = [ Depends ( enforce_basic_auth )] )
112142def get_pdf (key : str ):
113143 validate_key (key )
114144
@@ -128,4 +158,3 @@ def get_pdf(key: str):
128158 except Exception as error :
129159 logger .exception ("Unexpected error while processing key=%s" , key )
130160 raise HTTPException (status_code = 500 , detail = "Internal server error" ) from error
131-
0 commit comments