Utilities Module (app.utils)¶
The utilities module provides core functionality for configuration management and user administration in Trunk8's multi-user environment.
Overview¶
The app.utils
module contains:
- ConfigLoader - TOML configuration management with automatic reloading
- UserManager - Multi-user authentication and account management
ConfigLoader Class¶
Overview¶
class ConfigLoader:
"""
Manages TOML configuration files with automatic reloading.
Handles application config, user-specific links config, and themes config
with file modification tracking and automatic reloading.
"""
Initialization¶
Default Configuration Files:
config/config.toml
- Application settingsconfig/themes.toml
- Available themes- User-specific links files based on current user context
Core Methods¶
load_all_configs()¶
def load_all_configs(self) -> None:
"""
Load all configuration files.
Checks modification times and reloads only changed files.
"""
Features:
- Automatic modification time tracking
- Selective reloading of changed files
- Error handling for missing or corrupt files
- Default value creation
set_user_context()¶
def set_user_context(self, username: Optional[str]) -> None:
"""
Set the current user context for configuration loading.
Args:
username: Current active username
"""
Features:
- User-specific configuration isolation
- Dynamic user switching support
- Admin user context management
save_app_config()¶
def save_app_config(self) -> bool:
"""
Save application configuration to file.
Returns:
True if saved successfully, False otherwise.
"""
Configuration Properties¶
app_config¶
Structure:
links_config¶
Structure:
[links.shortcode]
type = "redirect"
url = "https://example.com"
expiration_date = "2024-12-31T23:59:59"
themes_config¶
Structure:
Error Handling¶
# Configuration loading errors
try:
config = toml.load(config_file)
except FileNotFoundError:
# Create default configuration
except toml.TomlDecodeError:
# Handle invalid TOML syntax
except Exception as e:
# General error handling
UserManager Class¶
Overview¶
class UserManager:
"""
Manages user accounts, authentication, and user-specific data access.
Handles user creation, authentication, password management, and provides
admin functionality to manage all users and their data.
"""
Initialization¶
def __init__(self, users_file: str = "users/users.toml") -> None:
"""
Initialize the UserManager.
Args:
users_file: Path to the users configuration file.
"""
Authentication Methods¶
authenticate_user()¶
def authenticate_user(self, username: str, password: str) -> Optional[Dict[str, Any]]:
"""
Authenticate a user with username and password.
For admin user: password is checked against TRUNK8_ADMIN_PASSWORD environment variable.
For other users: password is checked against stored hash.
Args:
username: Username to authenticate.
password: Plain text password.
Returns:
User data dict if authenticated, None otherwise.
"""
Authentication Modes:
- Admin User: Uses
TRUNK8_ADMIN_PASSWORD
environment variable - Regular Users: Uses SHA-256 hashed passwords stored in users.toml
User Management Methods¶
create_user()¶
def create_user(
self, username: str, password: str, display_name: str, is_admin: bool = False
) -> bool:
"""
Create a new user.
Args:
username: Unique username.
password: Plain text password.
display_name: Display name for the user.
is_admin: Whether user has admin privileges.
Returns:
True if user created successfully, False otherwise.
"""
Creation Process:
- Validate username uniqueness
- Hash password using SHA-256
- Create user entry in users.toml
- Create user directory structure
- Initialize empty links.toml
get_user()¶
def get_user(self, username: str) -> Optional[Dict[str, Any]]:
"""
Get user data by username.
Args:
username: Username to retrieve.
Returns:
User data dict if found, None otherwise.
"""
list_users()¶
is_admin()¶
def is_admin(self, username: str) -> bool:
"""
Check if user has admin privileges.
Args:
username: Username to check.
Returns:
True if user is admin, False otherwise.
"""
File Path Methods¶
get_user_links_file()¶
def get_user_links_file(self, username: str) -> str:
"""
Get path to user's links.toml file.
Args:
username: Username.
Returns:
Path to user's links file.
"""
get_user_assets_dir()¶
def get_user_assets_dir(self, username: str) -> str:
"""
Get path to user's assets directory.
Args:
username: Username.
Returns:
Path to user's assets directory.
"""
User Deletion¶
delete_user()¶
def delete_user(self, username: str, requesting_user: str) -> bool:
"""
Delete a user and all associated data (admin only).
This method performs a cascading deletion that removes:
- User account from the configuration
- All user's links
- All user's uploaded files and assets
- User's entire directory structure
Args:
username: Username to delete.
requesting_user: Username making the request (must be admin).
Returns:
True if deleted successfully, False otherwise.
"""
get_user_deletion_preview()¶
def get_user_deletion_preview(self, username: str) -> Optional[Dict[str, Any]]:
"""
Get a preview of what will be deleted when a user is removed.
This method provides information about what data will be lost
without actually performing the deletion.
Args:
username: Username to preview deletion for.
Returns:
Dictionary with deletion preview information, or None if user not found.
"""
Preview Information:
- Number of links to be deleted
- Number of files to be removed
- Total storage space to be freed
- List of directories to be deleted
Password Management¶
change_password()¶
def change_password(self, username: str, new_password: str) -> bool:
"""
Change user password.
Args:
username: Username.
new_password: New plain text password.
Returns:
True if changed successfully, False otherwise.
"""
Internal Methods¶
_hash_password()¶
def _hash_password(self, password: str) -> str:
"""
Hash a password using SHA-256.
Args:
password: Plain text password.
Returns:
Hashed password string.
"""
_create_user_directory()¶
def _create_user_directory(self, username: str) -> None:
"""
Create user directory structure.
Args:
username: Username to create directory for.
"""
Creates:
users/{username}/
directoryusers/{username}/assets/
directoryusers/{username}/links.toml
file
_cleanup_user_data()¶
def _cleanup_user_data(self, username: str) -> Tuple[bool, str, Dict[str, int]]:
"""
Clean up all user data including links and assets.
Args:
username: Username to clean up data for.
Returns:
Tuple of (success, message, cleanup_stats).
cleanup_stats contains counts of deleted items.
"""
User Data Structure¶
users.toml Format¶
[users.admin]
is_admin = true
display_name = "Administrator"
created_at = "2024-01-01T00:00:00"
# password_hash not stored for admin (uses environment variable)
[users.username]
password_hash = "sha256_hash_here"
is_admin = false
display_name = "User Display Name"
created_at = "2024-01-01T00:00:00"
Directory Structure¶
users/
├── users.toml # User management data
├── admin/
│ ├── links.toml # Admin's links
│ └── assets/ # Admin's files
└── username/
├── links.toml # User's links
└── assets/ # User's files
Integration Examples¶
Application Factory Integration¶
# In create_app()
config_loader = ConfigLoader()
user_manager = UserManager()
# Load configurations
config_loader.load_all_configs()
# Make available to app
app.config_loader = config_loader
app.user_manager = user_manager
Request Context Integration¶
# Before each request
@app.before_request
def load_user_context():
current_user = get_current_user()
config_loader.set_user_context(current_user)
config_loader.load_all_configs()
Authentication Integration¶
# In login route
user_data = user_manager.authenticate_user(username, password)
if user_data:
session['authenticated'] = True
session['username'] = username
session['is_admin'] = user_data.get('is_admin', False)
Performance Considerations¶
Configuration Caching¶
- File modification time tracking
- Selective reloading of changed files
- In-memory configuration storage
- Efficient TOML parsing
User Management¶
- Lazy loading of user data
- Efficient directory operations
- Bulk user operations support
- Optimized file cleanup
Security Features¶
Password Security¶
- SHA-256 password hashing
- Environment variable for admin password
- No plaintext password storage
- Secure password validation
Data Isolation¶
- User-specific directory structure
- Isolated configuration files
- Access control validation
- Admin privilege checking
Error Handling¶
Configuration Errors¶
# File not found
except FileNotFoundError:
self._create_default_config()
# Invalid TOML syntax
except toml.TomlDecodeError as e:
print(f"Invalid TOML syntax: {e}")
# Permission errors
except PermissionError:
print("Permission denied accessing config file")
User Management Errors¶
# User not found
if username not in users:
return None
# Admin deletion protection
if username == "admin":
return False
# Permission validation
if not self.is_admin(requesting_user):
return False
Testing Support¶
Configuration Testing¶
def test_config_loading():
config_loader = ConfigLoader()
config_loader.load_all_configs()
assert 'app' in config_loader.app_config
def test_user_context():
config_loader = ConfigLoader()
config_loader.set_user_context('testuser')
assert config_loader._current_user == 'testuser'
User Management Testing¶
def test_user_creation():
user_manager = UserManager()
success = user_manager.create_user('testuser', 'password', 'Test User')
assert success == True
def test_authentication():
user_manager = UserManager()
user_data = user_manager.authenticate_user('testuser', 'password')
assert user_data is not None
Next Steps¶
- Application Factory - Integration with Flask application
- Authentication Module - Authentication system usage
- Links Module - Configuration integration