TISbackup/libtisbackup/auth/example_integration.py
k3nny f12d89f3da feat(auth): add pluggable authentication system for Flask routes
Implement comprehensive authentication system with support for
Basic Auth, Flask-Login, and OAuth2 providers.

Features:
- Pluggable architecture via factory pattern
- Multiple authentication providers:
  * None: No authentication (development/testing)
  * Basic Auth: HTTP Basic with bcrypt support
  * Flask-Login: Session-based with multiple users
  * OAuth2: Google, GitHub, GitLab, and generic providers
- Decorator-based route protection (@auth.require_auth)
- User authorization by domain or email (OAuth)
- bcrypt password hashing support
- Comprehensive documentation and examples

Components:
- libtisbackup/auth/__init__.py: Factory function and exports
- libtisbackup/auth/base.py: Base provider interface
- libtisbackup/auth/basic_auth.py: HTTP Basic Auth implementation
- libtisbackup/auth/flask_login_auth.py: Flask-Login implementation
- libtisbackup/auth/oauth_auth.py: OAuth2 implementation
- libtisbackup/auth/example_integration.py: Integration examples
- libtisbackup/auth/README.md: API reference and examples

Documentation:
- AUTHENTICATION.md: Complete authentication guide
  * Setup instructions for each provider
  * Configuration examples
  * Security best practices
  * Troubleshooting guide
  * Migration guide
- samples/auth-config-examples.ini: Configuration templates

Dependencies:
- Add optional dependencies in pyproject.toml:
  * auth-basic: bcrypt>=4.0.0
  * auth-login: flask-login>=0.6.0, bcrypt>=4.0.0
  * auth-oauth: authlib>=1.3.0, requests>=2.32.0
  * auth-all: All auth providers

Installation:
```bash
# Install specific provider
uv sync --extra auth-basic

# Install all providers
uv sync --extra auth-all
```

Usage:
```python
from libtisbackup.auth import get_auth_provider

# Initialize
auth = get_auth_provider("basic", {
    "username": "admin",
    "password": "$2b$12$...",
    "use_bcrypt": True
})
auth.init_app(app)

# Protect routes
@app.route("/")
@auth.require_auth
def index():
    user = auth.get_current_user()
    return f"Hello {user['username']}"
```

Security features:
- bcrypt password hashing (work factor 12)
- OAuth domain/user restrictions
- Session-based authentication
- Clear separation of concerns
- Environment variable support for secrets

OAuth providers supported:
- Google (OpenID Connect)
- GitHub
- GitLab
- Generic OAuth2 provider

Breaking change: None - new feature, backward compatible
Users can continue without authentication (type=none)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 02:02:46 +02:00

122 lines
3.7 KiB
Python

#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Example integration of authentication providers with Flask app
This file shows how to integrate the authentication system into tisbackup_gui.py
"""
from flask import Flask, jsonify, render_template, request, redirect, url_for, session
from libtisbackup.auth import get_auth_provider
# Example configuration from tisbackup_gui.ini
auth_config = {
"type": "basic", # or "flask-login", "oauth", "none"
"basic": {
"username": "admin",
"password": "$2b$12$...", # bcrypt hash
"use_bcrypt": True,
"realm": "TISBackup Admin"
},
"flask-login": {
"users_file": "/etc/tis/users.txt", # username:bcrypt_hash per line
"use_bcrypt": True,
"login_view": "login"
},
"oauth": {
"provider": "google",
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"redirect_uri": "http://localhost:8080/oauth/callback",
"authorized_domains": ["example.com"],
"authorized_users": ["admin@example.com"]
}
}
def create_app_with_auth():
"""Example: Create Flask app with authentication."""
app = Flask(__name__)
app.secret_key = "your-secret-key"
# Initialize authentication provider
auth_type = auth_config.get("type", "none")
provider_config = auth_config.get(auth_type, {})
auth = get_auth_provider(auth_type, provider_config)
auth.init_app(app)
# Protected routes
@app.route("/")
@auth.require_auth
def index():
user = auth.get_current_user()
return render_template("index.html", user=user)
@app.route("/api/backups")
@auth.require_auth
def api_backups():
return jsonify({"backups": []})
# Public routes (no auth required)
@app.route("/health")
def health():
return jsonify({"status": "ok"})
# Flask-Login specific routes
if auth_type == "flask-login":
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
if auth.verify_password(username, password):
auth.login_user(username)
return redirect(url_for("index"))
else:
return render_template("login.html", error="Invalid credentials")
return render_template("login.html")
@app.route("/logout")
def logout():
auth.logout()
return redirect(url_for("login"))
# OAuth specific routes
if auth_type == "oauth":
@app.route("/oauth/login")
def oauth_login():
redirect_uri = url_for("oauth_callback", _external=True)
return auth.oauth_client.authorize_redirect(redirect_uri)
@app.route("/oauth/callback")
def oauth_callback():
try:
token = auth.oauth_client.authorize_access_token()
user_info = auth.oauth_client.get(auth.userinfo_endpoint).json()
# Check authorization
if not auth.is_user_authorized(user_info):
return "Unauthorized: Your email/domain is not authorized", 403
# Store user in session
session["oauth_user"] = user_info
session["oauth_token"] = token
return redirect(url_for("index"))
except Exception as e:
return f"OAuth callback error: {e}", 500
@app.route("/logout")
def logout():
auth.logout()
return redirect(url_for("oauth_login"))
return app
if __name__ == "__main__":
app = create_app_with_auth()
app.run(debug=True, port=8080)