Initial commit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Middleware module for the Flask application.
|
||||
"""
|
||||
from flask import Flask, request, jsonify, g, current_app
|
||||
from flask.typing import ResponseReturnValue
|
||||
from functools import wraps
|
||||
import base64
|
||||
from typing import Callable, Any, TypeVar, cast
|
||||
import logging
|
||||
|
||||
# Configure logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
F = TypeVar('F', bound=Callable[..., Any])
|
||||
|
||||
def register_middleware(app: Flask) -> None:
|
||||
"""
|
||||
Register middleware with the Flask application.
|
||||
|
||||
Args:
|
||||
app: Flask application instance
|
||||
"""
|
||||
# Register CORS if needed
|
||||
try:
|
||||
from flask_cors import CORS
|
||||
CORS(app, resources={r"/*": {"origins": app.config.get('CORS_ORIGINS', '*')}})
|
||||
except ImportError:
|
||||
logger.warning("flask-cors not installed. CORS support disabled.")
|
||||
|
||||
@app.before_request
|
||||
def before_request() -> None:
|
||||
"""Process request before it reaches the view function."""
|
||||
# Log incoming requests
|
||||
logger.debug(f"Received {request.method} request to {request.path}")
|
||||
|
||||
# You can add more global middleware here if needed
|
||||
|
||||
def basic_auth_required(f: F) -> F:
|
||||
"""
|
||||
Decorator for endpoints that require basic authentication.
|
||||
|
||||
Args:
|
||||
f: Function to decorate
|
||||
|
||||
Returns:
|
||||
Decorated function
|
||||
"""
|
||||
@wraps(f)
|
||||
def decorated(*args: Any, **kwargs: Any) -> ResponseReturnValue:
|
||||
auth = request.headers.get('Authorization')
|
||||
if not auth or not auth.startswith('Basic '):
|
||||
logger.warning("Authentication failed: No Basic Auth header")
|
||||
return jsonify({
|
||||
'resultCode': '01',
|
||||
'resultDescription': 'Authentication required'
|
||||
}), 401
|
||||
|
||||
try:
|
||||
auth_decoded = base64.b64decode(auth[6:]).decode('utf-8')
|
||||
username, password = auth_decoded.split(':', 1)
|
||||
|
||||
if username != current_app.config['API_USERNAME'] or password != current_app.config['API_PASSWORD']:
|
||||
logger.warning(f"Authentication failed: Invalid credentials for user {username}")
|
||||
return jsonify({
|
||||
'resultCode': '01',
|
||||
'resultDescription': 'Invalid credentials'
|
||||
}), 401
|
||||
|
||||
g.user = username
|
||||
logger.debug(f"Authentication successful for user {username}")
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"Authentication error: {str(e)}")
|
||||
return jsonify({
|
||||
'resultCode': '01',
|
||||
'resultDescription': 'Invalid authentication format'
|
||||
}), 401
|
||||
|
||||
return cast(F, decorated)
|
||||
|
||||
def api_key_required(f: F) -> F:
|
||||
"""
|
||||
Decorator for endpoints that require API key authentication.
|
||||
|
||||
Args:
|
||||
f: Function to decorate
|
||||
|
||||
Returns:
|
||||
Decorated function
|
||||
"""
|
||||
@wraps(f)
|
||||
def decorated(*args: Any, **kwargs: Any) -> ResponseReturnValue:
|
||||
app_id = request.headers.get('appID')
|
||||
api_key = request.headers.get('apiKey')
|
||||
|
||||
if not app_id or not api_key:
|
||||
logger.warning("API key authentication failed: Missing appID or apiKey")
|
||||
return jsonify({
|
||||
'resultCode': '01',
|
||||
'resultDescription': 'API key authentication required'
|
||||
}), 401
|
||||
|
||||
# Validate against configured API keys
|
||||
if app_id != current_app.config['SIMBRELLA_APP_ID'] or api_key != current_app.config['SIMBRELLA_API_KEY']:
|
||||
logger.warning(f"API key authentication failed: Invalid appID or apiKey")
|
||||
return jsonify({
|
||||
'resultCode': '01',
|
||||
'resultDescription': 'Invalid API keys'
|
||||
}), 401
|
||||
|
||||
g.api_client = 'simbrella'
|
||||
logger.debug(f"API key authentication successful")
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return cast(F, decorated)
|
||||
Reference in New Issue
Block a user