mirror of
https://github.com/th30d4y/OpenLearnX.git
synced 2026-05-26 11:25:49 +00:00
Address code review feedback - persistent dev secrets and fix empty auth header
Co-authored-by: Stalin-143 <161853795+Stalin-143@users.noreply.github.com>
This commit is contained in:
+27
-4
@@ -117,6 +117,28 @@ def get_required_secret(env_var: str, description: str) -> str:
|
|||||||
raise ValueError(f"{description} ({env_var}) must be set in environment variables for security. Do not use default values for secrets.")
|
raise ValueError(f"{description} ({env_var}) must be set in environment variables for security. Do not use default values for secrets.")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def get_dev_fallback_secret(name: str) -> str:
|
||||||
|
"""
|
||||||
|
Generate a persistent random secret for development use only.
|
||||||
|
Stores the secret in a file to persist across restarts.
|
||||||
|
"""
|
||||||
|
import tempfile
|
||||||
|
secret_file = os.path.join(tempfile.gettempdir(), f'.openlearnx_dev_{name}')
|
||||||
|
try:
|
||||||
|
if os.path.exists(secret_file):
|
||||||
|
with open(secret_file, 'r') as f:
|
||||||
|
return f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Generate new secret and persist it
|
||||||
|
new_secret = os.urandom(32).hex()
|
||||||
|
try:
|
||||||
|
with open(secret_file, 'w') as f:
|
||||||
|
f.write(new_secret)
|
||||||
|
except Exception:
|
||||||
|
pass # If we can't persist, just return the generated secret
|
||||||
|
return new_secret
|
||||||
|
|
||||||
# Validate required secrets at startup
|
# Validate required secrets at startup
|
||||||
try:
|
try:
|
||||||
_secret_key = get_required_secret('SECRET_KEY', 'Flask secret key')
|
_secret_key = get_required_secret('SECRET_KEY', 'Flask secret key')
|
||||||
@@ -124,10 +146,11 @@ try:
|
|||||||
_admin_token = get_required_secret('ADMIN_TOKEN', 'Admin authentication token')
|
_admin_token = get_required_secret('ADMIN_TOKEN', 'Admin authentication token')
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print(f"⚠️ SECURITY WARNING: {e}")
|
print(f"⚠️ SECURITY WARNING: {e}")
|
||||||
print("⚠️ Using insecure defaults for development only. Set proper secrets in production!")
|
print("⚠️ Using persistent development secrets. Set proper secrets in production!")
|
||||||
_secret_key = os.getenv('SECRET_KEY', os.urandom(32).hex())
|
_secret_key = os.getenv('SECRET_KEY') or get_dev_fallback_secret('secret_key')
|
||||||
_jwt_secret_key = os.getenv('JWT_SECRET_KEY', os.urandom(32).hex())
|
_jwt_secret_key = os.getenv('JWT_SECRET_KEY') or get_dev_fallback_secret('jwt_secret_key')
|
||||||
_admin_token = os.getenv('ADMIN_TOKEN', os.urandom(16).hex())
|
_admin_token = os.getenv('ADMIN_TOKEN') or get_dev_fallback_secret('admin_token')
|
||||||
|
print(f"⚠️ DEV ADMIN_TOKEN (first 8 chars): {_admin_token[:8]}...")
|
||||||
|
|
||||||
app.config.update(
|
app.config.update(
|
||||||
SECRET_KEY=_secret_key,
|
SECRET_KEY=_secret_key,
|
||||||
|
|||||||
+16
-3
@@ -20,9 +20,22 @@ db = client.openlearnx
|
|||||||
JWT_SECRET = os.getenv('JWT_SECRET')
|
JWT_SECRET = os.getenv('JWT_SECRET')
|
||||||
if not JWT_SECRET:
|
if not JWT_SECRET:
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn("JWT_SECRET environment variable not set. Using randomly generated secret.", UserWarning)
|
import tempfile
|
||||||
import secrets as _secrets
|
warnings.warn("JWT_SECRET environment variable not set. Using persistent dev secret.", UserWarning)
|
||||||
JWT_SECRET = _secrets.token_hex(32)
|
# Use persistent file-based secret for development to avoid invalidating tokens on restart
|
||||||
|
_secret_file = os.path.join(tempfile.gettempdir(), '.openlearnx_dev_jwt_secret_auth')
|
||||||
|
try:
|
||||||
|
if os.path.exists(_secret_file):
|
||||||
|
with open(_secret_file, 'r') as f:
|
||||||
|
JWT_SECRET = f.read().strip()
|
||||||
|
if not JWT_SECRET:
|
||||||
|
import secrets as _secrets
|
||||||
|
JWT_SECRET = _secrets.token_hex(32)
|
||||||
|
with open(_secret_file, 'w') as f:
|
||||||
|
f.write(JWT_SECRET)
|
||||||
|
except Exception:
|
||||||
|
import secrets as _secrets
|
||||||
|
JWT_SECRET = _secrets.token_hex(32)
|
||||||
|
|
||||||
@bp.route('/nonce', methods=['POST', 'OPTIONS'])
|
@bp.route('/nonce', methods=['POST', 'OPTIONS'])
|
||||||
def get_nonce():
|
def get_nonce():
|
||||||
|
|||||||
@@ -73,10 +73,13 @@ export default function AdminDashboard() {
|
|||||||
// Helper function to get authorization headers
|
// Helper function to get authorization headers
|
||||||
const getAuthHeaders = (): Record<string, string> => {
|
const getAuthHeaders = (): Record<string, string> => {
|
||||||
const token = getAdminToken()
|
const token = getAdminToken()
|
||||||
return {
|
const headers: Record<string, string> = {
|
||||||
'Authorization': token ? `Bearer ${token}` : '',
|
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
|
if (token) {
|
||||||
|
headers['Authorization'] = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user