#!/usr/bin/python3 # -*- coding: utf-8 -*- """ HTTP Basic Authentication provider """ import base64 import logging from functools import wraps from flask import request from .base import AuthProvider class BasicAuthProvider(AuthProvider): """HTTP Basic Authentication provider. Configuration: username: Required username password: Required password (plain text, or hashed with bcrypt) realm: Authentication realm (default: 'TISBackup') use_bcrypt: If True, password is bcrypt hash (default: False) """ def __init__(self, config): super().__init__(config) self.logger = logging.getLogger("tisbackup.auth") self.username = config.get("username") self.password = config.get("password") self.realm = config.get("realm", "TISBackup") self.use_bcrypt = config.get("use_bcrypt", False) if not self.username or not self.password: raise ValueError("BasicAuth requires 'username' and 'password' in config") if self.use_bcrypt: try: import bcrypt self.bcrypt = bcrypt except ImportError: raise ImportError("bcrypt library required for password hashing. Install with: pip install bcrypt") def is_authenticated(self): """Check if request has valid Basic Auth credentials.""" auth = request.authorization if not auth: return False if auth.username != self.username: self.logger.warning(f"Failed authentication attempt for user: {auth.username}") return False if self.use_bcrypt: # Compare bcrypt hash password_bytes = auth.password.encode('utf-8') hash_bytes = self.password.encode('utf-8') if isinstance(self.password, str) else self.password return self.bcrypt.checkpw(password_bytes, hash_bytes) else: # Plain text comparison (not recommended for production) return auth.password == self.password def handle_unauthorized(self): """Return 401 with WWW-Authenticate header.""" from flask import Response return Response( 'Authentication required', 401, {'WWW-Authenticate': f'Basic realm="{self.realm}"'} ) def get_current_user(self): """Get current authenticated user.""" auth = request.authorization if auth and self.is_authenticated(): return {"username": auth.username, "auth_type": "basic"} return None