feat: add CORS configuration options to enhance API security
This commit is contained in:
+14
@@ -15,3 +15,17 @@ PLEXPLAYLISTSYNC_AUTH_TOKEN_SECRET=REPLACE_WITH_A_RANDOM_STRING
|
|||||||
|
|
||||||
# Token TTL seconds (optional)
|
# Token TTL seconds (optional)
|
||||||
PLEXPLAYLISTSYNC_AUTH_TOKEN_TTL_SECONDS=86400
|
PLEXPLAYLISTSYNC_AUTH_TOKEN_TTL_SECONDS=86400
|
||||||
|
|
||||||
|
# CORS allowlist (optional)
|
||||||
|
# Default is empty (CORS disabled; same-origin only).
|
||||||
|
# Accepts comma-separated list or a JSON array.
|
||||||
|
# Example:
|
||||||
|
# PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
||||||
|
# or
|
||||||
|
# PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS=["https://your.domain"]
|
||||||
|
PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS=
|
||||||
|
|
||||||
|
# Allow cookies/credentials for allowlisted origins (optional)
|
||||||
|
# 1 = enabled, 0 = disabled
|
||||||
|
# Note: if origins contains '*', credentials will be forced off.
|
||||||
|
PLEXPLAYLISTSYNC_CORS_ALLOW_CREDENTIALS=0
|
||||||
+47
-2
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
@@ -21,6 +22,50 @@ from app.utils.auth import load_auth_config, issue_token, verify_token
|
|||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_cors_allowed_origins(raw: str | None) -> list[str]:
|
||||||
|
if not raw:
|
||||||
|
return []
|
||||||
|
|
||||||
|
value = raw.strip()
|
||||||
|
if not value:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if value == "*":
|
||||||
|
return ["*"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
if value.startswith("["):
|
||||||
|
parsed = json.loads(value)
|
||||||
|
if isinstance(parsed, list):
|
||||||
|
origins = [str(item).strip() for item in parsed]
|
||||||
|
else:
|
||||||
|
origins = [str(parsed).strip()]
|
||||||
|
else:
|
||||||
|
origins = [part.strip() for part in value.split(",")]
|
||||||
|
except Exception:
|
||||||
|
logger.warning(
|
||||||
|
"Invalid PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS; CORS will be disabled."
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
|
||||||
|
return [origin for origin in origins if origin]
|
||||||
|
|
||||||
|
|
||||||
|
def _env_truthy(name: str, default: str = "0") -> bool:
|
||||||
|
value = os.getenv(name, default)
|
||||||
|
return str(value).strip().lower() in {"1", "true", "yes", "y", "on"}
|
||||||
|
|
||||||
|
|
||||||
|
_CORS_ALLOWED_ORIGINS = _parse_cors_allowed_origins(
|
||||||
|
os.getenv("PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS", "")
|
||||||
|
)
|
||||||
|
_CORS_ALLOW_CREDENTIALS = (
|
||||||
|
False
|
||||||
|
if "*" in _CORS_ALLOWED_ORIGINS
|
||||||
|
else _env_truthy("PLEXPLAYLISTSYNC_CORS_ALLOW_CREDENTIALS", "0")
|
||||||
|
)
|
||||||
|
|
||||||
# --- Optional API Authentication (username/password) ---
|
# --- Optional API Authentication (username/password) ---
|
||||||
AUTH_CONFIG = load_auth_config()
|
AUTH_CONFIG = load_auth_config()
|
||||||
|
|
||||||
@@ -107,8 +152,8 @@ async def startup_event():
|
|||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=_CORS_ALLOWED_ORIGINS,
|
||||||
allow_credentials=True,
|
allow_credentials=_CORS_ALLOW_CREDENTIALS,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|||||||
+13
-5
@@ -3,12 +3,12 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
|
command: uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
|
||||||
ports:
|
ports:
|
||||||
- "8888:8080"
|
- "8000:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- PATH_TO_YOUR_PLAYLISTS:/app/playlists
|
- ./playlists:/app/playlists
|
||||||
- PATH_TO_DATA/backup:/app/data/backup
|
- ./data/backup:/app/data/backup
|
||||||
- PATH_TO_DATA/config:/app/data/config
|
- ./data/config:/app/data/config
|
||||||
- PATH_TO_DATA/sync_artifacts:/app/data/sync_artifacts
|
- ./data/sync_artifacts:/app/data/sync_artifacts
|
||||||
environment:
|
environment:
|
||||||
- PYTHONUNBUFFERED=1
|
- PYTHONUNBUFFERED=1
|
||||||
- PYTHONDONTWRITEBYTECODE=1
|
- PYTHONDONTWRITEBYTECODE=1
|
||||||
@@ -16,6 +16,14 @@ services:
|
|||||||
- PLEXPLAYLISTSYNC_BACKUP_DIR=/app/data/backup
|
- PLEXPLAYLISTSYNC_BACKUP_DIR=/app/data/backup
|
||||||
- TZ=${TZ:-UTC}
|
- TZ=${TZ:-UTC}
|
||||||
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
||||||
|
# CORS (default: disabled / same-origin only)
|
||||||
|
# Comma-separated list or JSON array. Example:
|
||||||
|
# - PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
||||||
|
# - PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS=["https://your.domain"]
|
||||||
|
# Use '*' only if you understand the risk (credentials will be forced off).
|
||||||
|
- PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS=${PLEXPLAYLISTSYNC_CORS_ALLOWED_ORIGINS:-}
|
||||||
|
# Optional: allow cookies/credentials for allowed origins (ignored when origins contains '*')
|
||||||
|
- PLEXPLAYLISTSYNC_CORS_ALLOW_CREDENTIALS=${PLEXPLAYLISTSYNC_CORS_ALLOW_CREDENTIALS:-0}
|
||||||
# Optional API auth (protects /api/*).
|
# Optional API auth (protects /api/*).
|
||||||
# Set PLEXPLAYLISTSYNC_AUTH_ENABLED=1 to enable.
|
# Set PLEXPLAYLISTSYNC_AUTH_ENABLED=1 to enable.
|
||||||
- PLEXPLAYLISTSYNC_AUTH_ENABLED=${PLEXPLAYLISTSYNC_AUTH_ENABLED:-0}
|
- PLEXPLAYLISTSYNC_AUTH_ENABLED=${PLEXPLAYLISTSYNC_AUTH_ENABLED:-0}
|
||||||
|
|||||||
Reference in New Issue
Block a user