diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9943929 --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +# API Keys Configuration +# Copy this file to .env and replace with your actual API keys + +# INPUT API Keys (comma-separated, for POST operations) +# These keys allow writing/posting data to the API +INPUT_API_KEYS=9d207bf0-10f5-4d8f-a479-22ff5aeff8d1,f47d4a2c-24cf-4745-937e-620a5963c0b8,b7061546-75e8-444b-a2c4-f19655d07eb8 + +# READ API Keys (comma-separated, for GET operations) +# These keys allow reading data from the API +READ_API_KEYS=read_key_1,read_key_2,read_key_3 + +# Security Notes: +# - Use strong, randomly generated UUIDs for production +# - Store the .env file securely and never commit it to version control +# - Rotate keys regularly for enhanced security +# - Use different keys for different environments (dev, staging, prod) \ No newline at end of file diff --git a/.gitignore b/.gitignore index faced58..8230cae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# .env file +.env + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -150,4 +153,4 @@ input/ *.temp # Container runtime -.container_id \ No newline at end of file +.container_id diff --git a/Dockerfile b/Dockerfile index 43b6aec..20b0aef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,9 @@ RUN pip install --no-cache-dir --upgrade pip && \ # Copy application code COPY main.py . +# Copy .env.example for reference (actual .env will be mounted at runtime) +COPY .env.example . + # Create input directory and set permissions RUN mkdir -p /app/input && \ chown -R appuser:appuser /app diff --git a/main.py b/main.py index 5781d4d..35cac1c 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,11 @@ from datetime import datetime from pathlib import Path import re import bleach +import os +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() # Initialize FastAPI application app = FastAPI( @@ -20,17 +25,29 @@ app = FastAPI( INPUT_DIR = Path("input") INPUT_DIR.mkdir(exist_ok=True) # Create directory if it doesn't exist -# Authentication tokens -INPUT_TOKEN = "input_token_123" # Token for POST endpoints -READ_TOKEN = "read_token_456" # Token for GET endpoints +# Load API keys from environment variables (with fallback for backward compatibility) +INPUT_API_KEYS = [ + key.strip() for key in os.getenv("INPUT_API_KEYS", "input_token_123").split(",") + if key.strip() +] +READ_API_KEYS = [ + key.strip() for key in os.getenv("READ_API_KEYS", "read_token_456").split(",") + if key.strip() +] + +# Validate that we have at least one key for each operation +if not INPUT_API_KEYS: + raise ValueError("At least one INPUT_API_KEY must be configured") +if not READ_API_KEYS: + raise ValueError("At least one READ_API_KEY must be configured") # Security schemes input_security = HTTPBearer() read_security = HTTPBearer() def verify_input_token(credentials: HTTPAuthorizationCredentials = Depends(input_security)): - """Verify bearer token for input operations""" - if credentials.credentials != INPUT_TOKEN: + """Verify bearer token for input operations (supports multiple API keys)""" + if credentials.credentials not in INPUT_API_KEYS: raise HTTPException( status_code=401, detail="Invalid authentication token for input operations" @@ -38,8 +55,8 @@ def verify_input_token(credentials: HTTPAuthorizationCredentials = Depends(input return credentials def verify_read_token(credentials: HTTPAuthorizationCredentials = Depends(read_security)): - """Verify bearer token for read operations""" - if credentials.credentials != READ_TOKEN: + """Verify bearer token for read operations (supports multiple API keys)""" + if credentials.credentials not in READ_API_KEYS: raise HTTPException( status_code=401, detail="Invalid authentication token for read operations" diff --git a/requirements.txt b/requirements.txt index aa2fa94..719b825 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ fastapi==0.104.1 uvicorn[standard]==0.24.0 pydantic==2.5.0 -bleach==6.1.0 \ No newline at end of file +bleach==6.1.0 +python-dotenv==1.0.0 \ No newline at end of file diff --git a/run_container.sh b/run_container.sh index c466541..df54ed7 100755 --- a/run_container.sh +++ b/run_container.sh @@ -88,18 +88,48 @@ setup_data_directory() { fi } +# Check for .env file +check_env_file() { + if [ ! -f ".env" ]; then + print_warning ".env file not found" + print_status "Creating .env file from .env.example..." + + if [ -f ".env.example" ]; then + cp .env.example .env + print_warning "Please edit .env file with your actual API keys before running the container" + print_status "You can edit .env now or the container will use example keys" + + # Ask user if they want to edit now + read -p "Do you want to edit .env file now? (y/N): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + ${EDITOR:-nano} .env + fi + else + print_error ".env.example file not found. Cannot create .env file." + exit 1 + fi + else + print_status ".env file found" + fi +} + # Run container run_container() { print_status "Starting container: ${CONTAINER_NAME}" # Convert relative path to absolute path - #DATA_DIR_ABS=$(realpath "${DATA_DIR}") - DATA_DIR_ABS="/home/user/Documents/Dom.Decimal/30-Projects/30-Misc/30-00-ZeroOne/30-00-002-Work-FastAPI-REST-Endpoint/input" + DATA_DIR_ABS=$(realpath "${DATA_DIR}") + ENV_FILE_ABS=$(realpath ".env") + + print_status "Mounting data directory: ${DATA_DIR_ABS}" + print_status "Mounting .env file: ${ENV_FILE_ABS}" $CONTAINER_CMD run -d \ --name "${CONTAINER_NAME}" \ --publish "${HOST_PORT}:${CONTAINER_PORT}" \ --volume "${DATA_DIR_ABS}:/app/input:Z" \ + --volume "${ENV_FILE_ABS}:/app/.env:ro,Z" \ --restart unless-stopped \ --security-opt no-new-privileges \ --cap-drop ALL \ @@ -157,18 +187,19 @@ show_usage_info() { print_status "API Documentation: http://localhost:${HOST_PORT}/docs" print_status "Health Check: http://localhost:${HOST_PORT}/health" echo - print_status "Authentication Tokens:" - echo " - Input Token: input_token_123" - echo " - Read Token: read_token_456" + print_status "API Key Configuration:" + echo " - Keys are loaded from .env file" + echo " - Separate keys for INPUT and READ operations" + echo " - Multiple keys supported per operation type" echo print_status "Example Usage:" - echo " # Send data:" + echo " # Send data (use any key from INPUT_API_KEYS):" echo ' curl -X POST http://localhost:8000/api/run1/ \' - echo ' -H "Authorization: Bearer input_token_123" \' + echo ' -H "Authorization: Bearer " \' echo ' -d "host_a,is ok"' echo - echo " # Read results:" - echo ' curl -H "Authorization: Bearer read_token_456" \' + echo " # Read results (use any key from READ_API_KEYS):" + echo ' curl -H "Authorization: Bearer " \' echo ' http://localhost:8000/results/run1/' echo print_status "Data Directory: ${DATA_DIR_ABS}" @@ -191,6 +222,7 @@ main() { cleanup_existing_container build_image setup_data_directory + check_env_file run_container verify_container show_usage_info