d79652b494
- Introduced new authentication settings in config.py including `auth_enabled`, `admin_username`, `admin_password`, and `session_secret`. - Added validation for `session_secret` to ensure it meets security requirements when authentication is enabled. - Updated main.py to conditionally mount static files and log warnings if the directory is not found. - Removed unused email template files and added new authentication and notification setup documentation. - Implemented authentication configuration validation in validators.py and updated settings display. - Enhanced the user interface with a new login template and SVG assets for branding. - Added comprehensive guides for setting up authentication and notifications in the documentation.
266 lines
9.4 KiB
Python
266 lines
9.4 KiB
Python
"""
|
|
Module for displaying and organizing settings information
|
|
"""
|
|
|
|
import logging
|
|
from app.config import settings
|
|
from app.utils.config_validator.masking import mask_sensitive_value
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def dump_all_settings():
|
|
"""Log all settings values for diagnostic purposes"""
|
|
logger.info("--- DUMPING ALL SETTINGS FOR DIAGNOSTIC PURPOSES ---")
|
|
for key in dir(settings):
|
|
if not key.startswith('_') and not callable(getattr(settings, key)):
|
|
value = getattr(settings, key)
|
|
# Mask sensitive values in logs
|
|
if key.lower().find('password') >= 0 or key.lower().find('secret') >= 0 or key.lower().find('token') >= 0 or key.lower().find('key') >= 0:
|
|
if value:
|
|
if isinstance(value, str) and len(value) > 10:
|
|
visible_start = max(1, len(value) // 3)
|
|
visible_end = max(1, len(value) // 4)
|
|
value = f"{value[:visible_start]}{'*' * (len(value) - visible_start - visible_end)}{value[-visible_end:]}"
|
|
else:
|
|
value = f"{value[:2]}{'*' * (len(value) - 4)}{value[-2:]}" if isinstance(value, str) and len(value) > 4 else "****"
|
|
|
|
# Special handling for notification URLs
|
|
if key == 'notification_urls' and value:
|
|
try:
|
|
from app.utils.notification import _mask_sensitive_url
|
|
if isinstance(value, list):
|
|
masked_urls = [_mask_sensitive_url(url) for url in value]
|
|
logger.info(f"{key}: {masked_urls}")
|
|
else:
|
|
logger.info(f"{key}: {_mask_sensitive_url(value)}")
|
|
continue # Skip the default logging
|
|
except (ImportError, AttributeError):
|
|
pass # Fall back to default logging if _mask_sensitive_url is not available
|
|
|
|
logger.info(f"{key}: {value}")
|
|
logger.info("--- END OF SETTINGS DUMP ---")
|
|
|
|
def get_settings_for_display(show_values=False):
|
|
"""
|
|
Group settings into logical categories and check if they are configured.
|
|
Returns a dictionary with categories as keys and lists of setting items as values.
|
|
Each setting item is a dict with name, value, and is_configured.
|
|
|
|
If show_values is False, sensitive values are masked.
|
|
"""
|
|
# First include system info with version in result
|
|
result = {
|
|
"System Info": [
|
|
{
|
|
"name": "App Version",
|
|
"value": settings.version,
|
|
"is_configured": True
|
|
},
|
|
{
|
|
"name": "Build Date",
|
|
"value": settings.build_date,
|
|
"is_configured": True
|
|
}
|
|
]
|
|
}
|
|
|
|
# Define categories and their settings
|
|
categories = {
|
|
"Core": [
|
|
"debug", # Explicitly include debug setting
|
|
"external_hostname",
|
|
"workdir",
|
|
"database_url",
|
|
"redis_url",
|
|
"gotenberg_url",
|
|
"allow_file_delete" # Added allow_file_delete to Core settings
|
|
],
|
|
"Authentication": [
|
|
"auth_enabled",
|
|
"session_secret",
|
|
"admin_username",
|
|
"admin_password",
|
|
"authentik_client_id",
|
|
"authentik_client_secret",
|
|
"authentik_config_url",
|
|
"oauth_provider_name"
|
|
],
|
|
"Email": [
|
|
"email_host",
|
|
"email_port",
|
|
"email_username",
|
|
"email_password",
|
|
"email_use_tls",
|
|
"email_sender",
|
|
"email_default_recipient"
|
|
],
|
|
"IMAP": [
|
|
"imap1_host",
|
|
"imap1_port",
|
|
"imap1_username",
|
|
"imap1_password",
|
|
"imap1_ssl",
|
|
"imap1_poll_interval_minutes",
|
|
"imap1_delete_after_process",
|
|
"imap2_host",
|
|
"imap2_port",
|
|
"imap2_username",
|
|
"imap2_password",
|
|
"imap2_ssl",
|
|
"imap2_poll_interval_minutes",
|
|
"imap2_delete_after_process"
|
|
],
|
|
"Dropbox": [
|
|
"dropbox_app_key",
|
|
"dropbox_app_secret",
|
|
"dropbox_folder",
|
|
"dropbox_refresh_token"
|
|
],
|
|
"NextCloud": [
|
|
"nextcloud_upload_url",
|
|
"nextcloud_username",
|
|
"nextcloud_password",
|
|
"nextcloud_folder"
|
|
],
|
|
"Paperless": [
|
|
"paperless_host",
|
|
"paperless_ngx_api_token"
|
|
],
|
|
"Google Drive": [
|
|
"google_drive_use_oauth",
|
|
"google_drive_client_id",
|
|
"google_drive_client_secret",
|
|
"google_drive_refresh_token",
|
|
"google_drive_credentials_json",
|
|
"google_drive_folder_id",
|
|
"google_drive_delegate_to"
|
|
],
|
|
"OneDrive": [
|
|
"onedrive_client_id",
|
|
"onedrive_client_secret",
|
|
"onedrive_tenant_id",
|
|
"onedrive_refresh_token",
|
|
"onedrive_folder_path"
|
|
],
|
|
"WebDAV": [
|
|
"webdav_url",
|
|
"webdav_username",
|
|
"webdav_password",
|
|
"webdav_folder",
|
|
"webdav_verify_ssl"
|
|
],
|
|
"SFTP": [
|
|
"sftp_host",
|
|
"sftp_port",
|
|
"sftp_username",
|
|
"sftp_password",
|
|
"sftp_folder",
|
|
"sftp_private_key",
|
|
"sftp_private_key_passphrase"
|
|
],
|
|
"FTP": [
|
|
"ftp_host",
|
|
"ftp_port",
|
|
"ftp_username",
|
|
"ftp_password",
|
|
"ftp_folder",
|
|
"ftp_use_tls",
|
|
"ftp_allow_plaintext"
|
|
],
|
|
"S3/AWS": [
|
|
"aws_access_key_id",
|
|
"aws_secret_access_key",
|
|
"aws_region",
|
|
"s3_bucket_name",
|
|
"s3_folder_prefix",
|
|
"s3_storage_class",
|
|
"s3_acl"
|
|
],
|
|
"AI Services": [
|
|
"openai_api_key",
|
|
"openai_base_url",
|
|
"openai_model",
|
|
"azure_ai_key",
|
|
"azure_endpoint",
|
|
"azure_region"
|
|
],
|
|
"Monitoring": [
|
|
"uptime_kuma_url",
|
|
"uptime_kuma_ping_interval"
|
|
],
|
|
"Notifications": [
|
|
"notification_urls",
|
|
"notify_on_task_failure",
|
|
"notify_on_credential_failure",
|
|
"notify_on_startup",
|
|
"notify_on_shutdown"
|
|
]
|
|
}
|
|
|
|
# Handle any settings that don't fit into the predefined categories
|
|
all_settings = set([key for key in dir(settings)
|
|
if not key.startswith('_') and
|
|
not callable(getattr(settings, key)) and
|
|
key not in ["model_computed_fields", "model_config",
|
|
"model_extra", "model_fields",
|
|
"model_fields_set"]])
|
|
|
|
# Ensure 'version' is excluded since we display it separately
|
|
all_settings.discard("version")
|
|
|
|
categorized_settings = set()
|
|
for cat_settings in categories.values():
|
|
categorized_settings.update(cat_settings)
|
|
|
|
uncategorized = all_settings - categorized_settings
|
|
if uncategorized:
|
|
categories["Other"] = list(uncategorized)
|
|
|
|
# Build the result
|
|
for category, setting_keys in categories.items():
|
|
items = []
|
|
for key in setting_keys:
|
|
if hasattr(settings, key):
|
|
value = getattr(settings, key)
|
|
|
|
# List of patterns that indicate sensitive values
|
|
sensitive_patterns = [
|
|
'password', 'secret', 'token', 'api_key', 'private_key',
|
|
'credentials', 'access_key', 'ai_key'
|
|
]
|
|
|
|
# Check if this is a sensitive value that should be masked
|
|
is_sensitive = any(
|
|
pattern in key.lower() for pattern in sensitive_patterns
|
|
)
|
|
|
|
# Special handling for "auth" to avoid matching prefixes like "authentik"
|
|
if not is_sensitive and "auth" in key.lower():
|
|
# Only mark as sensitive if "auth" is a standalone word or at the end
|
|
# This avoids matching "authentik" as sensitive
|
|
parts = key.lower().split('_')
|
|
is_sensitive = any(part == "auth" for part in parts) or key.lower().endswith("auth")
|
|
|
|
# Mask sensitive values regardless of debug mode
|
|
# Other values are only hidden if debug mode is off AND show_values is False
|
|
if (is_sensitive or not show_values) and value:
|
|
if is_sensitive:
|
|
value = mask_sensitive_value(value)
|
|
|
|
# Check if the setting is configured (has a non-None value)
|
|
# For boolean settings, consider them configured even if False
|
|
is_configured = value is not None
|
|
if is_configured and isinstance(value, str):
|
|
is_configured = len(value) > 0
|
|
|
|
items.append({
|
|
"name": key,
|
|
"value": value,
|
|
"is_configured": is_configured
|
|
})
|
|
|
|
if items: # Only add categories that have items
|
|
result[category] = items
|
|
|
|
return result
|