Add latest changes
Dieser Commit ist enthalten in:
@@ -11,10 +11,25 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
async def validate_api_key(request: Request, db: Session = Depends(get_db)):
|
||||
"""Validate API key from X-API-Key header against system_api_key table"""
|
||||
# Debug: Log all headers
|
||||
headers_dict = dict(request.headers)
|
||||
# logger.warning(f"DEBUG: All headers: {headers_dict}")
|
||||
# logger.warning(f"DEBUG: Header keys: {list(headers_dict.keys())}")
|
||||
|
||||
# Try different variations
|
||||
api_key = request.headers.get("X-API-Key")
|
||||
if not api_key:
|
||||
api_key = request.headers.get("x-api-key")
|
||||
if not api_key:
|
||||
# Try case-insensitive search
|
||||
for key, value in headers_dict.items():
|
||||
if key.lower() == "x-api-key":
|
||||
api_key = value
|
||||
logger.warning(f"DEBUG: Found API key under header: {key}")
|
||||
break
|
||||
|
||||
if not api_key:
|
||||
logger.warning("API request without API key")
|
||||
logger.warning(f"API request without API key. Headers: {list(request.headers.keys())}")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="API key required",
|
||||
@@ -22,13 +37,43 @@ async def validate_api_key(request: Request, db: Session = Depends(get_db)):
|
||||
)
|
||||
|
||||
# Query the system API key
|
||||
cursor = db.execute(
|
||||
text("SELECT api_key FROM system_api_key WHERE id = 1")
|
||||
)
|
||||
result = cursor.fetchone()
|
||||
try:
|
||||
cursor = db.execute(
|
||||
text("SELECT api_key FROM system_api_key WHERE id = 1")
|
||||
)
|
||||
result = cursor.fetchone()
|
||||
# logger.warning(f"DEBUG: DB query result: {result}")
|
||||
except Exception as e:
|
||||
logger.error(f"Database query error: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Database error"
|
||||
)
|
||||
|
||||
if not result or result[0] != api_key:
|
||||
logger.warning(f"Invalid API key attempt: {api_key[:8]}...")
|
||||
if not result:
|
||||
logger.warning(f"No API key found in database")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Invalid API key"
|
||||
)
|
||||
|
||||
logger.warning(f"DEBUG: Found API key in request: {api_key}")
|
||||
logger.warning(f"DEBUG: API key from DB: {result[0]}")
|
||||
logger.warning(f"DEBUG: API key match: {result[0] == api_key}")
|
||||
logger.warning(f"DEBUG: Types - DB: {type(result[0])}, Request: {type(api_key)}")
|
||||
|
||||
if result[0] != api_key:
|
||||
logger.warning(f"API key mismatch!")
|
||||
logger.warning(f"Expected (DB): '{result[0]}'")
|
||||
logger.warning(f"Got (Request): '{api_key}'")
|
||||
logger.warning(f"API key lengths - DB: {len(result[0])}, Request: {len(api_key)}")
|
||||
|
||||
# Character by character comparison
|
||||
for i, (c1, c2) in enumerate(zip(result[0], api_key)):
|
||||
if c1 != c2:
|
||||
logger.warning(f"First difference at position {i}: DB='{c1}' (ord={ord(c1)}), Request='{c2}' (ord={ord(c2)})")
|
||||
break
|
||||
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Invalid API key"
|
||||
|
||||
@@ -10,7 +10,7 @@ class Settings(BaseSettings):
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||
|
||||
DATABASE_URL: str = "postgresql://license_user:license_password@db:5432/license_db"
|
||||
DATABASE_URL: str = "postgresql://adminuser:supergeheimespasswort@postgres:5432/meinedatenbank"
|
||||
|
||||
|
||||
ALLOWED_ORIGINS: List[str] = [
|
||||
|
||||
116
v2_lizenzserver/app/core/scheduler.py
Normale Datei
116
v2_lizenzserver/app/core/scheduler.py
Normale Datei
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
Background scheduler for License Server
|
||||
Handles periodic tasks like session cleanup
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db.database import SessionLocal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
scheduler = BackgroundScheduler()
|
||||
|
||||
|
||||
def cleanup_expired_sessions():
|
||||
"""Clean up sessions that haven't sent heartbeat within timeout period"""
|
||||
db: Session = SessionLocal()
|
||||
try:
|
||||
# Get session timeout from config (default 60 seconds)
|
||||
result = db.execute(text("""
|
||||
SELECT session_timeout
|
||||
FROM client_configs
|
||||
WHERE client_name = 'Account Forger'
|
||||
""")).first()
|
||||
|
||||
timeout_seconds = result[0] if result else 60
|
||||
|
||||
# Find expired sessions
|
||||
expired_sessions = db.execute(text(f"""
|
||||
SELECT id, license_id, hardware_fingerprint, session_token
|
||||
FROM license_sessions
|
||||
WHERE ended_at IS NULL
|
||||
AND last_heartbeat < NOW() - INTERVAL '{timeout_seconds} seconds'
|
||||
""")).fetchall()
|
||||
|
||||
if expired_sessions:
|
||||
logger.info(f"Found {len(expired_sessions)} expired sessions to clean up")
|
||||
|
||||
# Mark sessions as ended
|
||||
for session in expired_sessions:
|
||||
db.execute(text("""
|
||||
UPDATE license_sessions
|
||||
SET ended_at = NOW(), end_reason = 'timeout'
|
||||
WHERE id = :session_id
|
||||
"""), {"session_id": session[0]})
|
||||
|
||||
logger.info(f"Ended session {session[0]} for license {session[1]} due to timeout")
|
||||
|
||||
db.commit()
|
||||
logger.info(f"Successfully cleaned up {len(expired_sessions)} expired sessions")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error cleaning up sessions: {str(e)}")
|
||||
db.rollback()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def cleanup_old_sessions():
|
||||
"""Remove old ended sessions from database (older than 30 days)"""
|
||||
db: Session = SessionLocal()
|
||||
try:
|
||||
result = db.execute(text("""
|
||||
DELETE FROM license_sessions
|
||||
WHERE ended_at IS NOT NULL
|
||||
AND ended_at < NOW() - INTERVAL '30 days'
|
||||
"""))
|
||||
|
||||
if result.rowcount > 0:
|
||||
db.commit()
|
||||
logger.info(f"Cleaned up {result.rowcount} old sessions")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error cleaning up old sessions: {str(e)}")
|
||||
db.rollback()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def init_scheduler():
|
||||
"""Initialize and start the background scheduler"""
|
||||
# Add job to cleanup expired sessions every 30 seconds
|
||||
scheduler.add_job(
|
||||
func=cleanup_expired_sessions,
|
||||
trigger=IntervalTrigger(seconds=30),
|
||||
id='cleanup_expired_sessions',
|
||||
name='Cleanup expired sessions',
|
||||
replace_existing=True
|
||||
)
|
||||
|
||||
# Add job to cleanup old sessions daily at 3 AM
|
||||
scheduler.add_job(
|
||||
func=cleanup_old_sessions,
|
||||
trigger='cron',
|
||||
hour=3,
|
||||
minute=0,
|
||||
id='cleanup_old_sessions',
|
||||
name='Cleanup old sessions',
|
||||
replace_existing=True
|
||||
)
|
||||
|
||||
scheduler.start()
|
||||
logger.info("Background scheduler started")
|
||||
logger.info("- Session cleanup runs every 30 seconds")
|
||||
logger.info("- Old session cleanup runs daily at 3:00 AM")
|
||||
|
||||
|
||||
def shutdown_scheduler():
|
||||
"""Shutdown the scheduler gracefully"""
|
||||
if scheduler.running:
|
||||
scheduler.shutdown()
|
||||
logger.info("Background scheduler stopped")
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren