Compare commits

..

1 Commits

Author SHA1 Message Date
AI Station Server d27421a267 security: remove hardcoded credentials and externalize user profiles
- DATABASE_URL now fails fast if not set (no default with password)
- User profiles loaded from JSON config (config/user_profiles.json)
- Add .env.example as template for environment variables
- Add config/user_profiles.json.example as template for user configuration
- Update .gitignore to exclude sensitive config files
2026-01-20 17:21:24 +01:00
4 changed files with 104 additions and 48 deletions

23
.env.example Normal file
View File

@ -0,0 +1,23 @@
# AI Station - Environment Variables Template
# Copy this file to .env and fill in your values
# IMPORTANT: Never commit .env to version control!
# === GOOGLE OAUTH ===
OAUTH_GOOGLE_CLIENT_ID=your_client_id_here
OAUTH_GOOGLE_CLIENT_SECRET=your_client_secret_here
# === CHAINLIT CONFIGURATION ===
CHAINLIT_URL=https://ai.dffm.it
CHAINLIT_AUTH_SECRET=generate_a_secure_random_string_here
# === DATABASE ===
# Format: postgresql+asyncpg://username:password@hostname:port/database_name
DATABASE_URL=postgresql+asyncpg://ai_user:your_secure_password@postgres:5432/ai_station
# === AI SERVICES ===
OLLAMA_URL=http://192.168.1.243:11434
QDRANT_URL=http://qdrant:6333
BGE_API_URL=http://192.168.1.243:8001/embed
# === OPTIONAL ===
# GEMINI_API_KEY=your_gemini_api_key_here

3
.gitignore vendored
View File

@ -6,4 +6,5 @@ workspaces/*
qdrant_storage/.files/
__pycache__/
.env
.files/
.files/
config/user_profiles.json

74
app.py
View File

@ -1,5 +1,6 @@
import os
import re
import json
import uuid
import shutil
import requests
@ -37,10 +38,15 @@ except ImportError:
# =========================
# CONFIG
# =========================
DATABASE_URL = os.getenv(
"DATABASE_URL",
"postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station",
)
# SECURITY: Fail fast if DATABASE_URL is not set
DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
raise EnvironmentError(
"DATABASE_URL environment variable is required. "
"Set it via: export DATABASE_URL='postgresql+asyncpg://user:pass@host:5432/db'"
)
# Service URLs - can be overridden via environment
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://192.168.1.243:11434")
QDRANT_URL = os.getenv("QDRANT_URL", "http://qdrant:6333")
BGE_API_URL = os.getenv("BGE_API_URL", "http://192.168.1.243:8001/embed")
@ -64,50 +70,24 @@ os.makedirs(STORAGE_DIR, exist_ok=True)
os.makedirs(WORKSPACES_DIR, exist_ok=True)
# =========================
# USER PROFILES
# USER PROFILES (loaded from JSON for security)
# =========================
USER_PROFILES = {
"giuseppe@defranceschi.pro": {
"role": "admin",
"name": "Giuseppe",
"workspace": "admin_workspace",
"rag_collection": "admin_docs",
"capabilities": ["debug", "all"],
"show_code": True,
},
"federica.tecchio@gmail.com": {
"role": "business",
"name": "Federica",
"workspace": "business_workspace",
"rag_collection": "contabilita",
"capabilities": ["basic_chat"],
"show_code": False,
},
"riccardob545@gmail.com": {
"role": "engineering",
"name": "Riccardo",
"workspace": "engineering_workspace",
"rag_collection": "engineering_docs",
"capabilities": ["code"],
"show_code": True,
},
"giuliadefranceschi05@gmail.com": {
"role": "architecture",
"name": "Giulia",
"workspace": "architecture_workspace",
"rag_collection": "architecture_manuals",
"capabilities": ["visual"],
"show_code": False,
},
"giuseppe.defranceschi@gmail.com": {
"role": "architecture",
"name": "Giuseppe",
"workspace": "architecture_workspace",
"rag_collection": "architecture_manuals",
"capabilities": ["visual"],
"show_code": False,
},
}
def load_user_profiles() -> Dict[str, Dict]:
"""Load user profiles from JSON config file."""
config_path = os.getenv("USER_PROFILES_PATH", "config/user_profiles.json")
try:
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
return config.get("user_profiles", {})
except FileNotFoundError:
print(f"WARNING: User profiles config not found at {config_path}")
return {}
except json.JSONDecodeError as e:
print(f"WARNING: Invalid JSON in user profiles config: {e}")
return {}
# Load profiles at startup
USER_PROFILES = load_user_profiles()
GUEST_PROFILE = {
"role": "guest",

View File

@ -0,0 +1,52 @@
{
"user_profiles": {
"giuseppe@defranceschi.pro": {
"role": "admin",
"name": "Giuseppe",
"workspace": "admin_workspace",
"rag_collection": "admin_docs",
"capabilities": ["debug", "all"],
"show_code": true
},
"federica.tecchio@gmail.com": {
"role": "business",
"name": "Federica",
"workspace": "business_workspace",
"rag_collection": "contabilita",
"capabilities": ["basic_chat"],
"show_code": false
},
"riccardob545@gmail.com": {
"role": "engineering",
"name": "Riccardo",
"workspace": "engineering_workspace",
"rag_collection": "engineering_docs",
"capabilities": ["code"],
"show_code": true
},
"giuliadefranceschi05@gmail.com": {
"role": "architecture",
"name": "Giulia",
"workspace": "architecture_workspace",
"rag_collection": "architecture_manuals",
"capabilities": ["visual"],
"show_code": false
},
"giuseppe.defranceschi@gmail.com": {
"role": "architecture",
"name": "Giuseppe",
"workspace": "architecture_workspace",
"rag_collection": "architecture_manuals",
"capabilities": ["visual"],
"show_code": false
}
},
"guest_profile": {
"role": "guest",
"name": "Guest",
"workspace": "guest",
"rag_collection": "public",
"capabilities": ["basic_chat"],
"show_code": false
}
}