feat: Add authentication configuration and validation
- 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.
This commit is contained in:
@@ -8,7 +8,9 @@ ALLOW_FILE_DELETE=true # Allow deletion of file records
|
||||
|
||||
# **Authentication**
|
||||
AUTH_ENABLED=true
|
||||
SESSION_SECRET=<atLeast32Characters>
|
||||
# Generate a secure random string, for example:
|
||||
# python -c "import secrets; print(secrets.token_hex(32))"
|
||||
SESSION_SECRET=b39fd43f68d0491ca942f28a16e484b1e763fe9accf4445ca2669a5f3b179eb4
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=your_secure_password
|
||||
|
||||
@@ -16,6 +18,7 @@ ADMIN_PASSWORD=your_secure_password
|
||||
AUTHENTIK_CLIENT_ID=<yourAuthentikAppClientID>
|
||||
AUTHENTIK_CLIENT_SECRET=<yourAuthentikClientSecret>
|
||||
AUTHENTIK_CONFIG_URL=<ConfigUrlOfYourApp, e.g. https://authentik.example.com/application/o/docuelevate/.well-known/openid-configuration>
|
||||
OAUTH_PROVIDER_NAME="Authentik SSO"
|
||||
|
||||
# **AI/ML Services**
|
||||
# OpenAI
|
||||
|
||||
+114
-7
@@ -1,10 +1,13 @@
|
||||
import os
|
||||
import inspect
|
||||
import hashlib
|
||||
from functools import wraps
|
||||
|
||||
from authlib.integrations.starlette_client import OAuth
|
||||
from fastapi import APIRouter, Request, status
|
||||
from starlette.responses import RedirectResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
import pathlib
|
||||
|
||||
from app.config import settings
|
||||
|
||||
@@ -12,7 +15,15 @@ oauth = OAuth()
|
||||
|
||||
AUTH_ENABLED = settings.auth_enabled
|
||||
|
||||
if AUTH_ENABLED:
|
||||
# Set up templates for authentication
|
||||
templates_dir = pathlib.Path(__file__).parents[1] / "frontend" / "templates"
|
||||
templates = Jinja2Templates(directory=str(templates_dir))
|
||||
|
||||
# Configure OAuth provider if credentials are provided
|
||||
OAUTH_CONFIGURED = False
|
||||
OAUTH_PROVIDER_NAME = "Single Sign-On"
|
||||
|
||||
if AUTH_ENABLED and settings.authentik_client_id and settings.authentik_client_secret:
|
||||
oauth.register(
|
||||
name="authentik",
|
||||
client_id=settings.authentik_client_id,
|
||||
@@ -20,6 +31,8 @@ if AUTH_ENABLED:
|
||||
server_metadata_url=settings.authentik_config_url,
|
||||
client_kwargs={"scope": "openid profile email"},
|
||||
)
|
||||
OAUTH_CONFIGURED = True
|
||||
OAUTH_PROVIDER_NAME = settings.oauth_provider_name or "Authentik SSO"
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -46,29 +59,123 @@ def require_login(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_gravatar_url(email):
|
||||
"""Generate a Gravatar URL for the given email"""
|
||||
email = email.lower().strip()
|
||||
email_hash = hashlib.md5(email.encode('utf-8')).hexdigest()
|
||||
return f"https://www.gravatar.com/avatar/{email_hash}?d=identicon"
|
||||
|
||||
|
||||
if AUTH_ENABLED:
|
||||
@router.get("/login")
|
||||
async def login(request: Request):
|
||||
redirect_uri = request.url_for("auth")
|
||||
"""Show login page with appropriate authentication options"""
|
||||
return templates.TemplateResponse(
|
||||
"login.html",
|
||||
{
|
||||
"request": request,
|
||||
"error": request.query_params.get("error"),
|
||||
"message": request.query_params.get("message"),
|
||||
"show_oauth": OAUTH_CONFIGURED,
|
||||
"oauth_provider_name": OAUTH_PROVIDER_NAME,
|
||||
"app_version": settings.version # Changed from app_version to version
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/oauth-login")
|
||||
async def oauth_login(request: Request):
|
||||
"""Handle OAuth login flow"""
|
||||
if not OAUTH_CONFIGURED:
|
||||
return RedirectResponse(
|
||||
url="/login?error=OAuth+not+configured",
|
||||
status_code=status.HTTP_302_FOUND
|
||||
)
|
||||
|
||||
redirect_uri = request.url_for("oauth_callback")
|
||||
return await oauth.authentik.authorize_redirect(request, redirect_uri)
|
||||
|
||||
@router.get("/auth")
|
||||
async def auth(request: Request):
|
||||
@router.get("/oauth-callback")
|
||||
async def oauth_callback(request: Request):
|
||||
"""Handle OAuth callback from provider"""
|
||||
try:
|
||||
token = await oauth.authentik.authorize_access_token(request)
|
||||
userinfo = token.get("userinfo")
|
||||
request.session["user"] = dict(userinfo)
|
||||
if not userinfo:
|
||||
return RedirectResponse(
|
||||
url="/login?error=Failed+to+retrieve+user+information",
|
||||
status_code=status.HTTP_302_FOUND
|
||||
)
|
||||
|
||||
# Store user info in session
|
||||
user_data = dict(userinfo)
|
||||
|
||||
# Add Gravatar picture if no picture is provided
|
||||
if not user_data.get("picture") and user_data.get("email"):
|
||||
user_data["picture"] = get_gravatar_url(user_data["email"])
|
||||
|
||||
request.session["user"] = user_data
|
||||
|
||||
# Log the successful authentication
|
||||
print(f"User authenticated via OAuth: {user_data.get('email', 'No email')}")
|
||||
|
||||
# Redirect to original destination or default
|
||||
redirect_url = request.session.pop("redirect_after_login", "/upload")
|
||||
return RedirectResponse(url=redirect_url)
|
||||
except Exception as e:
|
||||
print(f"OAuth authentication error: {str(e)}")
|
||||
return RedirectResponse(
|
||||
url=f"/login?error=Authentication+failed:+{str(e)}",
|
||||
status_code=status.HTTP_302_FOUND
|
||||
)
|
||||
|
||||
@router.post("/auth")
|
||||
async def auth(request: Request):
|
||||
"""Handle local username/password authentication"""
|
||||
form_data = await request.form()
|
||||
username = form_data.get("username")
|
||||
password = form_data.get("password")
|
||||
|
||||
if (username == settings.admin_username and
|
||||
password == settings.admin_password):
|
||||
# Create user session
|
||||
request.session["user"] = {
|
||||
"id": "admin",
|
||||
"name": "Administrator",
|
||||
"email": f"{username}@local.docuelevate",
|
||||
"preferred_username": username,
|
||||
"picture": "/static/images/default-avatar.svg",
|
||||
"is_admin": True
|
||||
}
|
||||
# Redirect to original destination or default
|
||||
redirect_url = request.session.pop("redirect_after_login", "/upload")
|
||||
return RedirectResponse(url=redirect_url, status_code=302)
|
||||
else:
|
||||
return RedirectResponse(
|
||||
url="/login?error=Invalid+username+or+password",
|
||||
status_code=302
|
||||
)
|
||||
|
||||
@router.get("/logout")
|
||||
async def logout(request: Request):
|
||||
"""Handle user logout"""
|
||||
request.session.pop("user", None)
|
||||
return RedirectResponse(url="/")
|
||||
return RedirectResponse(
|
||||
url="/login?message=You+have+been+logged+out+successfully",
|
||||
status_code=302
|
||||
)
|
||||
|
||||
|
||||
@router.get("/api/auth/whoami")
|
||||
@require_login
|
||||
async def whoami(request: Request):
|
||||
"""API endpoint to get current user information"""
|
||||
user = request.session.get("user")
|
||||
return user or {"error": "Not authenticated"}
|
||||
|
||||
|
||||
@router.get("/private")
|
||||
@require_login
|
||||
async def private_page(request: Request):
|
||||
"""A protected endpoint that requires login."""
|
||||
user = request.session.get("user") # e.g. {"email": "...", ...}
|
||||
user = request.session.get("user")
|
||||
return {"message": "This is a protected page.", "user": user}
|
||||
|
||||
+16
-1
@@ -37,11 +37,17 @@ class Settings(BaseSettings):
|
||||
gotenberg_url: str
|
||||
external_hostname: str = "localhost" # Default to localhost
|
||||
|
||||
# Authentication settings
|
||||
auth_enabled: bool = True # Default to enabled
|
||||
admin_username: Optional[str] = None
|
||||
admin_password: Optional[str] = None
|
||||
session_secret: Optional[str] = None
|
||||
|
||||
# Authentik
|
||||
authentik_client_id: Optional[str] = None
|
||||
authentik_client_secret: Optional[str] = None
|
||||
authentik_config_url: Optional[str] = None
|
||||
auth_enabled: bool = True # Default to enabled
|
||||
oauth_provider_name: Optional[str] = None # Name to display for the OAuth provider
|
||||
|
||||
# IMAP 1
|
||||
imap1_host: Optional[str] = None
|
||||
@@ -162,6 +168,15 @@ class Settings(BaseSettings):
|
||||
return []
|
||||
return v
|
||||
|
||||
@validator('session_secret')
|
||||
def validate_session_secret(cls, v, values):
|
||||
"""Validate that session_secret is set and has sufficient length when auth is enabled"""
|
||||
if values.get('auth_enabled') and not v:
|
||||
raise ValueError("SESSION_SECRET must be set when AUTH_ENABLED=True")
|
||||
if values.get('auth_enabled') and v and len(v) < 32:
|
||||
raise ValueError("SESSION_SECRET must be at least 32 characters long")
|
||||
return v
|
||||
|
||||
# Get build date from environment or file
|
||||
@property
|
||||
def build_date(self) -> str:
|
||||
|
||||
+7
-3
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import logging
|
||||
import pathlib
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request, status
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -43,9 +44,12 @@ app.add_middleware(TrustedHostMiddleware, allowed_hosts=[
|
||||
"127.0.0.1"
|
||||
])
|
||||
|
||||
# Mount the static folder for CSS/JS:
|
||||
frontend_static_dir = Path(__file__).parent.parent / "frontend" / "static"
|
||||
app.mount("/static", StaticFiles(directory=frontend_static_dir), name="static")
|
||||
# Mount the static files directory
|
||||
static_dir = pathlib.Path(__file__).parents[1] / "frontend" / "static"
|
||||
if os.path.exists(static_dir):
|
||||
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
||||
else:
|
||||
print(f"WARNING: Static directory not found at {static_dir}. Static files will not be served.")
|
||||
|
||||
@app.on_event("startup")
|
||||
def on_startup():
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# This directory contains email templates
|
||||
# The .gitkeep file ensures the directory is included in git
|
||||
@@ -1,161 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document from DocuElevate</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.logo {
|
||||
max-width: 200px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.container {
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 25px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 20px;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #3182ce;
|
||||
color: white !important;
|
||||
padding: 10px 20px;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.attachment-info {
|
||||
background-color: #ebf8ff;
|
||||
border-left: 4px solid #3182ce;
|
||||
padding: 10px 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.metadata-section {
|
||||
background-color: #f0fff4;
|
||||
border-left: 4px solid #38a169;
|
||||
padding: 10px 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.metadata-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #2f855a;
|
||||
}
|
||||
.metadata-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.metadata-table th, .metadata-table td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.metadata-table th {
|
||||
width: 40%;
|
||||
color: #4a5568;
|
||||
}
|
||||
.metadata-value {
|
||||
word-break: break-word;
|
||||
}
|
||||
.document-type {
|
||||
display: inline-block;
|
||||
background-color: #3182ce;
|
||||
color: white;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.85em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
{% if has_logo %}
|
||||
<img src="cid:logo" alt="DocuElevate Logo" class="logo">
|
||||
{% endif %}
|
||||
<h1>Document Delivery</h1>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p class="message">
|
||||
{% if custom_message %}
|
||||
{{ custom_message }}
|
||||
{% else %}
|
||||
A document has been shared with you from DocuElevate.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<div class="attachment-info">
|
||||
<strong>Attached Document:</strong> {{ filename }}
|
||||
</div>
|
||||
|
||||
{% if has_metadata %}
|
||||
<div class="metadata-section">
|
||||
<div class="metadata-title">Document Metadata</div>
|
||||
|
||||
{% if metadata.document_type %}
|
||||
<span class="document-type">{{ metadata.document_type }}</span>
|
||||
{% endif %}
|
||||
|
||||
<table class="metadata-table">
|
||||
{% for key, value in metadata.items() %}
|
||||
{% if key != "document_type" and value %}
|
||||
<tr>
|
||||
<th>{{ key | replace("_", " ") | title }}</th>
|
||||
<td class="metadata-value">
|
||||
{% if value is mapping %}
|
||||
{% for subkey, subvalue in value.items() %}
|
||||
<strong>{{ subkey | replace("_", " ") | title }}:</strong> {{ subvalue }}<br>
|
||||
{% endfor %}
|
||||
{% elif value is iterable and value is not string %}
|
||||
{{ value | join(", ") }}
|
||||
{% else %}
|
||||
{{ value }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p>You can find the attached document in this email. Please let us know if you have any questions.</p>
|
||||
|
||||
{% if app_url %}
|
||||
<p>
|
||||
<a href="{{ app_url }}" class="button">Visit DocuElevate</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>This is an automated message from DocuElevate Document Processing System.</p>
|
||||
<p>© {{ now().year }} DocuElevate</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -6,6 +6,7 @@ from app.utils.config_validator.validators import (
|
||||
validate_email_config,
|
||||
validate_storage_configs,
|
||||
validate_notification_config,
|
||||
validate_auth_config,
|
||||
check_all_configs
|
||||
)
|
||||
from app.utils.config_validator.masking import mask_sensitive_value
|
||||
@@ -19,6 +20,7 @@ __all__ = [
|
||||
'validate_email_config',
|
||||
'validate_storage_configs',
|
||||
'validate_notification_config',
|
||||
'validate_auth_config',
|
||||
'mask_sensitive_value',
|
||||
'get_provider_status',
|
||||
'get_settings_for_display',
|
||||
|
||||
@@ -11,6 +11,29 @@ def get_provider_status():
|
||||
"""
|
||||
providers = {}
|
||||
|
||||
# Add Authentication configuration
|
||||
auth_enabled = getattr(settings, 'auth_enabled', False)
|
||||
using_oidc = bool(getattr(settings, 'authentik_client_id', None) and
|
||||
getattr(settings, 'authentik_client_secret', None) and
|
||||
getattr(settings, 'authentik_config_url', None))
|
||||
|
||||
auth_method = "OIDC" if using_oidc else "Basic Auth" if auth_enabled else "None"
|
||||
|
||||
providers["Authentication"] = {
|
||||
"name": "Authentication",
|
||||
"icon": "fa-solid fa-lock",
|
||||
"configured": bool(auth_enabled and
|
||||
(getattr(settings, 'admin_username', None) or
|
||||
using_oidc)),
|
||||
"enabled": auth_enabled,
|
||||
"description": "Access control and user authentication",
|
||||
"details": {
|
||||
"method": auth_method,
|
||||
"provider_name": getattr(settings, 'oauth_provider_name', 'Not set') if using_oidc else "N/A",
|
||||
"session_security": "Configured" if getattr(settings, 'session_secret', None) else "Not configured"
|
||||
}
|
||||
}
|
||||
|
||||
# Add Notification configuration - Make sure this provider is near the top of the list
|
||||
providers["Notifications"] = {
|
||||
"name": "Notifications",
|
||||
|
||||
@@ -77,9 +77,13 @@ def get_settings_for_display(show_values=False):
|
||||
],
|
||||
"Authentication": [
|
||||
"auth_enabled",
|
||||
"session_secret",
|
||||
"admin_username",
|
||||
"admin_password",
|
||||
"authentik_client_id",
|
||||
"authentik_client_secret",
|
||||
"authentik_config_url"
|
||||
"authentik_config_url",
|
||||
"oauth_provider_name"
|
||||
],
|
||||
"Email": [
|
||||
"email_host",
|
||||
|
||||
@@ -33,6 +33,35 @@ def validate_email_config():
|
||||
|
||||
return issues
|
||||
|
||||
def validate_auth_config():
|
||||
"""Validates authentication configuration settings"""
|
||||
issues = []
|
||||
|
||||
# If auth is enabled, check for required settings
|
||||
if getattr(settings, 'auth_enabled', False):
|
||||
# Check for session secret
|
||||
if not getattr(settings, 'session_secret', None):
|
||||
issues.append("SESSION_SECRET is not configured but AUTH_ENABLED is True")
|
||||
elif len(getattr(settings, 'session_secret', '')) < 32:
|
||||
issues.append("SESSION_SECRET must be at least 32 characters long")
|
||||
|
||||
# Check if using simple authentication or OIDC
|
||||
using_simple_auth = bool(getattr(settings, 'admin_username', None) and
|
||||
getattr(settings, 'admin_password', None))
|
||||
|
||||
using_oidc = bool(getattr(settings, 'authentik_client_id', None) and
|
||||
getattr(settings, 'authentik_client_secret', None) and
|
||||
getattr(settings, 'authentik_config_url', None))
|
||||
|
||||
if not using_simple_auth and not using_oidc:
|
||||
issues.append("Neither simple authentication nor OIDC are properly configured")
|
||||
|
||||
# If using OIDC, check for provider name
|
||||
if using_oidc and not getattr(settings, 'oauth_provider_name', None):
|
||||
issues.append("OAUTH_PROVIDER_NAME is not configured but OIDC is enabled")
|
||||
|
||||
return issues
|
||||
|
||||
def validate_storage_configs():
|
||||
"""Validates configuration for all storage providers"""
|
||||
issues = {}
|
||||
@@ -176,6 +205,13 @@ def check_all_configs():
|
||||
if hasattr(settings, 'debug') and settings.debug:
|
||||
dump_all_settings()
|
||||
|
||||
# Check auth config
|
||||
auth_issues = validate_auth_config()
|
||||
if auth_issues:
|
||||
logger.warning(f"Authentication configuration issues: {', '.join(auth_issues)}")
|
||||
else:
|
||||
logger.info("Authentication configuration OK")
|
||||
|
||||
# Check email config
|
||||
email_issues = validate_email_config()
|
||||
if email_issues:
|
||||
@@ -200,6 +236,7 @@ def check_all_configs():
|
||||
|
||||
# Return all identified issues
|
||||
return {
|
||||
'auth': auth_issues,
|
||||
'email': email_issues,
|
||||
'storage': storage_issues,
|
||||
'notification': notification_issues
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
# Setting up Authentication
|
||||
|
||||
This guide explains how to configure authentication for DocuElevate to secure your installation.
|
||||
|
||||
## Required Configuration Parameters
|
||||
|
||||
| **Variable** | **Description** |
|
||||
|----------------------------|----------------------------------------------------------|
|
||||
| `AUTH_ENABLED` | Enable or disable authentication (`True`/`False`) |
|
||||
| `SESSION_SECRET` | Secret key for session encryption (min 32 characters) |
|
||||
| `ADMIN_USERNAME` | Username for basic authentication |
|
||||
| `ADMIN_PASSWORD` | Password for basic authentication |
|
||||
| `AUTHENTIK_CLIENT_ID` | Client ID for OpenID Connect authentication |
|
||||
| `AUTHENTIK_CLIENT_SECRET` | Client secret for OpenID Connect authentication |
|
||||
| `AUTHENTIK_CONFIG_URL` | OpenID Connect discovery URL |
|
||||
| `OAUTH_PROVIDER_NAME` | Display name for the OAuth provider button |
|
||||
|
||||
For a complete list of configuration options, see the [Configuration Guide](ConfigurationGuide.md).
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
DocuElevate supports two primary authentication methods:
|
||||
|
||||
1. **Simple Authentication** - Basic username/password authentication managed by DocuElevate
|
||||
2. **OpenID Connect** - Integration with identity providers like Authentik, Keycloak, or Auth0
|
||||
|
||||
## Session Security
|
||||
|
||||
DocuElevate uses FastAPI's session management to maintain user sessions. The session data is stored in cookies that are encrypted and signed using your application's secret key. This prevents tampering with session data while ensuring users remain authenticated between requests.
|
||||
|
||||
The `SESSION_SECRET` is automatically used by the web framework to:
|
||||
|
||||
1. Encrypt and sign session cookies
|
||||
2. Protect against cross-site request forgery (CSRF) attacks
|
||||
3. Secure other session-related functionality
|
||||
|
||||
Always use a strong, randomly generated secret key of at least 32 characters for production environments.
|
||||
|
||||
### Generating a Secure Session Secret
|
||||
|
||||
You can generate a secure random string using Python:
|
||||
|
||||
```python
|
||||
import secrets
|
||||
print(secrets.token_hex(32)) # Outputs a 64-character hex string (32 bytes)
|
||||
```
|
||||
|
||||
Or using OpenSSL:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
Make sure to keep this secret value confidential and don't reuse it across different applications.
|
||||
|
||||
## Setting up Simple Authentication
|
||||
|
||||
For smaller deployments or testing, simple authentication is easy to set up:
|
||||
|
||||
1. In your `.env` file, set:
|
||||
```
|
||||
AUTH_ENABLED=True
|
||||
SESSION_SECRET=your-secure-random-string-at-least-32-chars
|
||||
ADMIN_USERNAME=your_admin_username
|
||||
ADMIN_PASSWORD=your_secure_password
|
||||
```
|
||||
|
||||
2. Restart DocuElevate to apply the changes
|
||||
|
||||
3. When you navigate to the application, you'll be prompted to log in with the credentials you set
|
||||
|
||||
|
||||
## Setting up OpenID Connect with Authentik
|
||||
|
||||
For larger deployments or when you need more advanced authentication features, OpenID Connect (OIDC) is recommended:
|
||||
|
||||
### 1. Create an Application in Authentik
|
||||
|
||||
1. Log in to your Authentik admin interface
|
||||
2. Navigate to "Applications" > "Applications"
|
||||
3. Click "Create"
|
||||
4. Fill in the following details:
|
||||
- **Name**: DocuElevate
|
||||
- **Slug**: docuelevate
|
||||
- **Provider**: Create a new OAuth2/OIDC Provider
|
||||
- **Launch URL**: The URL of your DocuElevate instance (e.g., https://docuelevate.example.com)
|
||||
|
||||
5. For the OAuth2/OIDC Provider settings:
|
||||
- **Client Type**: Confidential
|
||||
- **Redirect URIs**: https://docuelevate.example.com/auth (adjust for your domain)
|
||||
- **Signing Key**: Select an appropriate signing key
|
||||
- **Scopes**: Select "openid", "email", and "profile" at minimum
|
||||
|
||||
6. Save the provider and then the application
|
||||
|
||||
7. Note down the **Client ID** and **Client Secret** from the provider details
|
||||
|
||||
### 2. Configure DocuElevate
|
||||
|
||||
1. In your `.env` file, set:
|
||||
```
|
||||
AUTH_ENABLED=True
|
||||
SESSION_SECRET=your-secure-random-string-at-least-32-chars
|
||||
AUTHENTIK_CLIENT_ID=your_client_id_from_authentik
|
||||
AUTHENTIK_CLIENT_SECRET=your_client_secret_from_authentik
|
||||
AUTHENTIK_CONFIG_URL=https://auth.example.com/application/o/docuelevate/.well-known/openid-configuration
|
||||
OAUTH_PROVIDER_NAME=Authentik SSO
|
||||
```
|
||||
|
||||
2. Adjust the `AUTHENTIK_CONFIG_URL` to match your Authentik instance and application slug
|
||||
|
||||
3. Restart DocuElevate to apply the changes
|
||||
|
||||
### 3. Test the Authentication
|
||||
|
||||
1. Navigate to your DocuElevate instance
|
||||
2. You should be redirected to the Authentik login page
|
||||
3. After successful authentication, you'll be redirected back to DocuElevate
|
||||
|
||||
## Using Other OpenID Connect Providers
|
||||
|
||||
DocuElevate can work with any OpenID Connect-compliant provider, not just Authentik:
|
||||
|
||||
### Keycloak Setup
|
||||
|
||||
1. Create a client in Keycloak with:
|
||||
- **Client ID**: your preferred client ID
|
||||
- **Access Type**: confidential
|
||||
- **Valid Redirect URIs**: https://docuelevate.example.com/auth
|
||||
|
||||
2. Get the client secret from the "Credentials" tab
|
||||
|
||||
3. Configure DocuElevate with:
|
||||
```
|
||||
AUTHENTIK_CLIENT_ID=your_keycloak_client_id
|
||||
AUTHENTIK_CLIENT_SECRET=your_keycloak_client_secret
|
||||
AUTHENTIK_CONFIG_URL=https://keycloak.example.com/auth/realms/your-realm/.well-known/openid-configuration
|
||||
OAUTH_PROVIDER_NAME=Keycloak SSO
|
||||
```
|
||||
|
||||
### Auth0 Setup
|
||||
|
||||
1. Create a new application in Auth0
|
||||
2. Get your client ID and secret
|
||||
3. Set the callback URL to https://docuelevate.example.com/auth
|
||||
4. Configure DocuElevate with:
|
||||
```
|
||||
AUTHENTIK_CLIENT_ID=your_auth0_client_id
|
||||
AUTHENTIK_CLIENT_SECRET=your_auth0_client_secret
|
||||
AUTHENTIK_CONFIG_URL=https://your-tenant.auth0.com/.well-known/openid-configuration
|
||||
OAUTH_PROVIDER_NAME=Auth0
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Always use HTTPS** in production to protect authentication tokens and passwords
|
||||
2. Generate a strong, random `SESSION_SECRET` (at least 32 characters)
|
||||
3. Use strong passwords for simple authentication
|
||||
4. Consider using a password manager to generate and store your admin credentials
|
||||
5. Restrict the scopes requested from your OIDC provider to only what's needed
|
||||
6. Consider setting up user groups and permissions in your identity provider
|
||||
7. If using simple authentication in production, consider implementing rate limiting for login attempts
|
||||
|
||||
## Troubleshooting Authentication Issues
|
||||
|
||||
If you encounter issues with authentication:
|
||||
|
||||
1. **Login failures with simple authentication**:
|
||||
- Verify that the username and password exactly match the values in your `.env` file
|
||||
- Check if there are leading or trailing spaces in your credentials
|
||||
- Ensure your `.env` file is properly loaded by the application
|
||||
|
||||
2. **Session issues**:
|
||||
- Check that your `SESSION_SECRET` is set correctly
|
||||
- Clear browser cookies and cache if experiencing persistent login issues
|
||||
|
||||
3. **OIDC issues**:
|
||||
- **Redirect URI mismatch**: Ensure the redirect URI in your provider configuration exactly matches your DocuElevate URL + "/auth"
|
||||
- **SSL-related errors**: Make sure your certificates are valid and trusted
|
||||
- **Provider connectivity**: Ensure DocuElevate can reach your identity provider
|
||||
|
||||
4. **Token validation errors**:
|
||||
- Check that the clocks are synchronized between DocuElevate and the identity provider
|
||||
- Verify that the signing keys are correctly configured
|
||||
|
||||
5. **Debug OpenID information**:
|
||||
- For most providers, you can visit the `/.well-known/openid-configuration` endpoint to verify their settings
|
||||
|
||||
For more general configuration issues, see the [Configuration Troubleshooting Guide](ConfigurationTroubleshooting.md).
|
||||
@@ -35,9 +35,13 @@ DocuElevate can monitor multiple IMAP mailboxes for document attachments. Each m
|
||||
| **Variable** | **Description** |
|
||||
|-------------------------|---------------------------------------------------------------|
|
||||
| `AUTH_ENABLED` | Enable or disable authentication (`true`/`false`). |
|
||||
| `AUTHENTIK_CLIENT_ID` | Client ID for Authentik OAuth2. |
|
||||
| `AUTHENTIK_CLIENT_SECRET` | Client secret for Authentik OAuth2. |
|
||||
| `SESSION_SECRET` | Secret key used to encrypt sessions and cookies (at least 32 chars). |
|
||||
| `ADMIN_USERNAME` | Username for basic authentication (when not using OIDC). |
|
||||
| `ADMIN_PASSWORD` | Password for basic authentication (when not using OIDC). |
|
||||
| `AUTHENTIK_CLIENT_ID` | Client ID for Authentik OAuth2/OIDC authentication. |
|
||||
| `AUTHENTIK_CLIENT_SECRET` | Client secret for Authentik OAuth2/OIDC authentication. |
|
||||
| `AUTHENTIK_CONFIG_URL` | Configuration URL for Authentik OpenID Connect. |
|
||||
| `OAUTH_PROVIDER_NAME` | Display name for the OAuth provider button. |
|
||||
|
||||
### OpenAI & Azure Document Intelligence
|
||||
|
||||
@@ -162,6 +166,18 @@ For detailed setup instructions, see the [OneDrive Setup Guide](OneDriveSetup.md
|
||||
|
||||
For detailed setup instructions, see the [Amazon S3 Setup Guide](AmazonS3Setup.md).
|
||||
|
||||
### Notification System
|
||||
|
||||
| **Variable** | **Description** |
|
||||
|----------------------------|----------------------------------------------------------|
|
||||
| `NOTIFICATION_URLS` | Comma-separated list of Apprise notification URLs |
|
||||
| `NOTIFY_ON_TASK_FAILURE` | Send notifications on task failures (`True`/`False`) |
|
||||
| `NOTIFY_ON_CREDENTIAL_FAILURE` | Send notifications on credential failures (`True`/`False`) |
|
||||
| `NOTIFY_ON_STARTUP` | Send notification when system starts (`True`/`False`) |
|
||||
| `NOTIFY_ON_SHUTDOWN` | Send notification when system shuts down (`True`/`False`)|
|
||||
|
||||
For detailed setup instructions, see the [Notifications Setup Guide](NotificationsSetup.md).
|
||||
|
||||
### Uptime Kuma
|
||||
|
||||
| **Variable** | **Description** |
|
||||
@@ -209,9 +225,13 @@ AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT=https://...
|
||||
|
||||
# Authentication
|
||||
AUTH_ENABLED=true
|
||||
SESSION_SECRET=a-very-long-and-secure-random-secret-key-string-for-session-encryption
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=your_secure_password
|
||||
AUTHENTIK_CLIENT_ID=...
|
||||
AUTHENTIK_CLIENT_SECRET=...
|
||||
AUTHENTIK_CONFIG_URL=https://auth.example.com/.well-known/openid-configuration
|
||||
OAUTH_PROVIDER_NAME=Authentik SSO
|
||||
|
||||
# Storage services
|
||||
PAPERLESS_NGX_API_TOKEN=...
|
||||
@@ -270,6 +290,14 @@ EMAIL_USE_TLS=True
|
||||
EMAIL_SENDER=DocuElevate System <docuelevate@example.com>
|
||||
EMAIL_DEFAULT_RECIPIENT=recipient@example.com
|
||||
|
||||
# Notification Settings
|
||||
# Configure notification services using Apprise URL format
|
||||
NOTIFICATION_URLS=discord://webhook_id/webhook_token,mailto://user:pass@gmail.com,tgram://bot_token/chat_id
|
||||
NOTIFY_ON_TASK_FAILURE=True
|
||||
NOTIFY_ON_CREDENTIAL_FAILURE=True
|
||||
NOTIFY_ON_STARTUP=True
|
||||
NOTIFY_ON_SHUTDOWN=False
|
||||
|
||||
# OneDrive (Personal Account)
|
||||
ONEDRIVE_CLIENT_ID=12345678-1234-1234-1234-123456789012
|
||||
ONEDRIVE_CLIENT_SECRET=your_client_secret
|
||||
|
||||
@@ -13,6 +13,8 @@ DocuElevate is designed to be highly configurable through environment variables,
|
||||
- [Dropbox Setup](DropboxSetup.md) - How to set up Dropbox integration
|
||||
- [OneDrive Setup](OneDriveSetup.md) - How to set up Microsoft OneDrive/Graph integration
|
||||
- [Amazon S3 Setup](AmazonS3Setup.md) - How to set up Amazon S3 integration
|
||||
- [Authentication Setup](AuthenticationSetup.md) - How to set up user authentication
|
||||
- [Notifications Setup](NotificationsSetup.md) - How to set up system notifications
|
||||
|
||||
## Configuration File Location
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ This guide provides instructions for deploying DocuElevate in various environmen
|
||||
- Paperless NGX instance
|
||||
- SMTP server (for email notifications)
|
||||
- IMAP server(s) (for email attachment processing)
|
||||
- Notification services (Discord, Telegram, etc. for system alerts)
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# Setting up System Notifications
|
||||
|
||||
This guide explains how to set up the notification system for DocuElevate, which allows you to receive alerts about important system events.
|
||||
|
||||
## Required Configuration Parameters
|
||||
|
||||
| **Variable** | **Description** |
|
||||
|----------------------------|----------------------------------------------------------|
|
||||
| `NOTIFICATION_URLS` | Comma-separated list of Apprise notification URLs |
|
||||
| `NOTIFY_ON_TASK_FAILURE` | Send notifications on task failures (`True`/`False`) |
|
||||
| `NOTIFY_ON_CREDENTIAL_FAILURE` | Send notifications on credential failures (`True`/`False`) |
|
||||
| `NOTIFY_ON_STARTUP` | Send notification when system starts (`True`/`False`) |
|
||||
| `NOTIFY_ON_SHUTDOWN` | Send notification when system shuts down (`True`/`False`)|
|
||||
|
||||
For a complete list of configuration options, see the [Configuration Guide](ConfigurationGuide.md).
|
||||
|
||||
## Overview
|
||||
|
||||
DocuElevate uses the Apprise library to provide a flexible notification system that supports over 70 different notification services, including:
|
||||
|
||||
- Email
|
||||
- SMS
|
||||
- Messaging apps (Telegram, Discord, Slack, Matrix, etc.)
|
||||
- Push notification services (Pushover, Pushbullet, etc.)
|
||||
- Web hooks
|
||||
- And many more
|
||||
|
||||
## Setting up Notifications
|
||||
|
||||
### 1. Choose Your Notification Services
|
||||
|
||||
First, decide which notification services you want to use. The Apprise library supports a wide range of services, each with its own URL format. Here are some common examples:
|
||||
|
||||
- **Discord**: `discord://webhook_id/webhook_token`
|
||||
- **Telegram**: `tgram://bot_token/chat_id`
|
||||
- **Email**: `mailto://user:pass@example.com`
|
||||
- **Pushover**: `pover://user_key/app_token`
|
||||
- **Slack**: `slack://tokenA/tokenB/tokenC`
|
||||
- **Matrix**: `matrix://username:password@domain/#room`
|
||||
- **Microsoft Teams**: `msteams://token_a/token_b/token_c`
|
||||
- **Gotify**: `gotify://hostname/token`
|
||||
|
||||
For a complete list of supported services and their URL formats, see the [Apprise Wiki](https://github.com/caronc/apprise/wiki).
|
||||
|
||||
### 2. Configure Your Notification URLs
|
||||
|
||||
Set the `NOTIFICATION_URLS` environment variable with a comma-separated list of your notification service URLs:
|
||||
|
||||
```dotenv
|
||||
NOTIFICATION_URLS=discord://webhook_id/webhook_token,mailto://user:pass@gmail.com,tgram://bot_token/chat_id
|
||||
```
|
||||
|
||||
You can specify as many notification services as you need.
|
||||
|
||||
### 3. Configure Notification Triggers
|
||||
|
||||
DocuElevate can send notifications for various system events. Configure which events should trigger notifications:
|
||||
|
||||
```dotenv
|
||||
NOTIFY_ON_TASK_FAILURE=True # Notify when background tasks fail
|
||||
NOTIFY_ON_CREDENTIAL_FAILURE=True # Notify when service credentials fail (e.g. API token expired)
|
||||
NOTIFY_ON_STARTUP=True # Notify when the system starts
|
||||
NOTIFY_ON_SHUTDOWN=False # Notify when the system shuts down
|
||||
```
|
||||
|
||||
## Automated Credential Checking
|
||||
|
||||
DocuElevate includes a powerful credential monitoring system that regularly checks the validity of your configured service credentials. This helps you proactively address authentication issues before they affect your document processing workflows.
|
||||
|
||||
### How Credential Checking Works
|
||||
|
||||
1. **Regular Monitoring**: The system automatically checks all configured service credentials at regular intervals (every 5 minutes) and at startup.
|
||||
|
||||
2. **Smart Notifications**: When credential failures are detected, the system sends notifications through your configured notification channels.
|
||||
|
||||
3. **Notification Rate Limiting**: To prevent notification spam, alerts follow a progressive notification strategy:
|
||||
- First 3 failures: Notification sent for each failure
|
||||
- Subsequent failures: Notifications suppressed until credential is restored
|
||||
- Recovery: Notification sent when credentials are working again
|
||||
|
||||
4. **Services Monitored**:
|
||||
- OpenAI API
|
||||
- Azure Document Intelligence
|
||||
- Dropbox
|
||||
- Google Drive
|
||||
- OneDrive/Microsoft Graph
|
||||
- Other configured storage providers
|
||||
|
||||
### Notification Content
|
||||
|
||||
When a credential failure is detected, the notification includes:
|
||||
- The affected service name
|
||||
- The specific error message
|
||||
- A reminder to check and update credentials
|
||||
|
||||
Example notification:
|
||||
```
|
||||
Subject: Credential Failure: Dropbox
|
||||
|
||||
The credentials for Dropbox have failed:
|
||||
Invalid refresh token: Token has been revoked or expired.
|
||||
|
||||
Please check and update the credentials in the system settings.
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
To control credential check notifications:
|
||||
|
||||
```dotenv
|
||||
# Enable/disable credential failure notifications
|
||||
NOTIFY_ON_CREDENTIAL_FAILURE=True
|
||||
```
|
||||
|
||||
When set to `False`, the system will still perform the checks but won't send notifications about failures.
|
||||
|
||||
### Troubleshooting Credential Issues
|
||||
|
||||
If you receive credential failure notifications:
|
||||
|
||||
1. **Check token expiration**: For OAuth-based services (Google Drive, OneDrive, Dropbox), refresh tokens may have expired.
|
||||
|
||||
2. **Verify API keys**: Ensure your API keys for services like OpenAI and Azure Document Intelligence are still valid.
|
||||
|
||||
3. **Check service status**: The service itself might be experiencing downtime.
|
||||
|
||||
4. **Review quota limits**: Some services have usage quotas that might have been exceeded.
|
||||
|
||||
5. **Regenerate credentials**: Use the built-in auth wizards to generate new tokens:
|
||||
- Go to Settings > [Service Name] Setup
|
||||
- Click "Refresh Token" or "Start Authentication Flow"
|
||||
- Complete the authentication process to generate new credentials
|
||||
|
||||
### Viewing Credential Status
|
||||
|
||||
You can view the current status of your service credentials in the system dashboard:
|
||||
|
||||
1. Navigate to the Status page in the DocuElevate interface
|
||||
2. Check the Service Status section
|
||||
3. Each service will show its current status (Valid, Invalid, or Not Configured)
|
||||
|
||||
## Service-Specific Setup Instructions
|
||||
|
||||
### Discord Notifications
|
||||
|
||||
1. Go to your Discord server
|
||||
2. Select a channel or create a new one for notifications
|
||||
3. Go to Server Settings > Integrations > Webhooks
|
||||
4. Click "New Webhook" and set up a webhook for your channel
|
||||
5. Copy the webhook URL (it will look like `https://discord.com/api/webhooks/123456789/abcdefg`)
|
||||
6. Extract the webhook ID and token (the parts after `webhooks/`)
|
||||
7. Format your Apprise URL as: `discord://123456789/abcdefg`
|
||||
|
||||
### Telegram Notifications
|
||||
|
||||
1. Start a chat with [@BotFather](https://t.me/botfather) on Telegram
|
||||
2. Create a new bot using the `/newbot` command
|
||||
3. Note the bot token provided by BotFather
|
||||
4. Start a chat with your new bot or add it to a group
|
||||
5. Get the chat ID:
|
||||
- For direct messages: send a message to the bot, then visit `https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates`
|
||||
- For group chats: add the bot to the group, send a message mentioning the bot, then check the same URL
|
||||
6. Format your Apprise URL as: `tgram://bot_token/chat_id`
|
||||
|
||||
### Email Notifications
|
||||
|
||||
To send notifications via email, configure the Apprise URL with your SMTP server details:
|
||||
|
||||
```
|
||||
# Gmail example
|
||||
mailto://your-email@gmail.com:password@smtp.gmail.com?smtp=587
|
||||
```
|
||||
|
||||
For Gmail, you'll need to use an app password if you have 2FA enabled.
|
||||
|
||||
## Task Failure Notifications
|
||||
|
||||
In addition to credential monitoring, DocuElevate can notify you about background task failures. When enabled, the system will send notifications whenever a background processing task encounters an error.
|
||||
|
||||
To configure task failure notifications:
|
||||
|
||||
```dotenv
|
||||
# Enable/disable task failure notifications
|
||||
NOTIFY_ON_TASK_FAILURE=True
|
||||
```
|
||||
|
||||
Task failure notifications include:
|
||||
- Task name and ID
|
||||
- Error type and message
|
||||
- Task arguments (for debugging)
|
||||
|
||||
## Testing Your Notification Setup
|
||||
|
||||
To test your notification setup once configured:
|
||||
|
||||
1. Start the DocuElevate application
|
||||
2. If `NOTIFY_ON_STARTUP=True`, you should receive a notification when the system starts
|
||||
3. Check the application logs for any errors related to notifications
|
||||
|
||||
## Security Considerations
|
||||
|
||||
When setting up notifications:
|
||||
|
||||
1. **Protect your credentials**: Keep your notification URLs secure, as they often contain access tokens or passwords.
|
||||
2. **Use environment variables**: Store notification URLs in environment variables rather than hard-coding them.
|
||||
3. **Limit notification content**: Be mindful of sending sensitive information in notifications.
|
||||
4. **Consider encryption**: For highly sensitive environments, consider using encrypted notification channels.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you're not receiving notifications:
|
||||
|
||||
1. **Check service connectivity**: Ensure the DocuElevate server can access the notification services.
|
||||
2. **Verify URL format**: Double-check the format of your notification URLs.
|
||||
3. **Check application logs**: Look for errors related to the notification system in logs.
|
||||
4. **Test services individually**: Try configuring one notification service at a time to isolate issues.
|
||||
5. **Check service-specific limits**: Some services have rate limits on notifications.
|
||||
|
||||
For more general configuration issues, see the [Configuration Troubleshooting Guide](ConfigurationTroubleshooting.md).
|
||||
@@ -13,6 +13,8 @@ Welcome to the DocuElevate documentation. This directory contains comprehensive
|
||||
- [Dropbox Setup](DropboxSetup.md) - How to set up Dropbox integration
|
||||
- [OneDrive Setup](OneDriveSetup.md) - How to set up Microsoft OneDrive/Graph integration
|
||||
- [Amazon S3 Setup](AmazonS3Setup.md) - How to set up Amazon S3 integration
|
||||
- [Authentication Setup](AuthenticationSetup.md) - How to set up user authentication
|
||||
- [Notifications Setup](NotificationsSetup.md) - How to set up system notifications
|
||||
- [Configuration Troubleshooting](ConfigurationTroubleshooting.md) - Solutions to common configuration issues
|
||||
- [Troubleshooting](Troubleshooting.md) - General troubleshooting and solutions to common issues
|
||||
|
||||
|
||||
@@ -11,6 +11,28 @@ DocuElevate offers an intuitive web interface for uploading, managing, and proce
|
||||
1. Navigate to your DocuElevate instance (typically at `http://your-server-address:8000`)
|
||||
2. If authentication is enabled, you'll be prompted to log in using your credentials
|
||||
|
||||
### Authentication
|
||||
|
||||
DocuElevate supports two main authentication methods:
|
||||
|
||||
#### Basic Authentication
|
||||
If basic authentication is configured:
|
||||
1. You'll see a simple login form
|
||||
2. Enter your username and password as configured in the system
|
||||
3. Click "Log In" to access DocuElevate
|
||||
|
||||
#### OpenID Connect (OIDC)
|
||||
If OpenID Connect authentication is configured:
|
||||
1. You'll see a login button
|
||||
2. Clicking this will redirect you to your identity provider (e.g., Authentik, Keycloak, Auth0)
|
||||
3. Log in with your existing credentials on that platform
|
||||
4. You'll be redirected back to DocuElevate after successful authentication
|
||||
|
||||
#### User Sessions
|
||||
- Once authenticated, your session will remain active until you log out or it expires
|
||||
- Click the "Logout" button in the top navigation bar to end your session
|
||||
- For security, sessions automatically expire after a period of inactivity
|
||||
|
||||
### Main Interface
|
||||
|
||||
DocuElevate features a simple navigation system with the following main sections:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M96 128a128 128 0 1 0 256 0A128 128 0 1 0 96 128zm94.5 200.2l18.6 31L175.8 483.1l-36-146.9c-2-8.1-9.8-13.4-17.9-11.3C51.9 342.4 0 405.8 0 481.3c0 17 13.8 30.7 30.7 30.7l131.7 0c0 0 0 0 .1 0l5.5 0 112 0 5.5 0c0 0 0 0 .1 0l131.7 0c17 0 30.7-13.8 30.7-30.7c0-75.5-51.9-138.9-121.9-156.4c-8.1-2-15.9 3.3-17.9 11.3l-36 146.9L238.9 359.2l18.6-31c6.4-10.7-1.3-24.2-13.7-24.2L224 304l-19.7 0c-12.4 0-20.1 13.6-13.7 24.2z"/></svg>
|
||||
|
After Width: | Height: | Size: 643 B |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="140" height="24" viewBox="0 0 124 24">
|
||||
<style>
|
||||
.icon {
|
||||
fill: #3B82F6;
|
||||
}
|
||||
.text {
|
||||
font-family: "Segoe UI", "Helvetica Neue", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
fill: #3B82F6;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Folder+gear icon -->
|
||||
<g class="icon" transform="translate(0, 0)">
|
||||
<path d="m17.212 20.404l-.108-.885q-.57-.125-.938-.33q-.368-.204-.7-.577l-.835.334l-.539-.815l.689-.577q-.165-.531-.165-1.035t.165-1.034l-.689-.577l.539-.816l.835.335q.332-.393.7-.588q.369-.195.938-.32l.108-.885h1l.107.885q.57.125.938.32t.7.588l.835-.335l.539.816l-.689.576q.166.531.166 1.035t-.166 1.035l.689.577l-.539.815l-.834-.335q-.333.373-.701.578q-.369.205-.938.33l-.107.885zm.5-1.731q.882 0 1.518-.635q.636-.636.636-1.519t-.636-1.518t-1.518-.636t-1.519.636t-.635 1.518t.635 1.519t1.518.635M4 18V6v4.435V10zm.616 1q-.691 0-1.153-.462T3 17.384V6.616q0-.691.463-1.153T4.615 5h4.981l2 2h7.789q.69 0 1.153.463T21 8.616v2.294q-.238-.152-.479-.265q-.24-.112-.521-.21v-1.82q0-.269-.173-.442T19.385 8h-8.19l-2-2h-4.58q-.269 0-.442.173T4 6.616v10.769q0 .269.173.442t.443.173h6.748q.055.275.131.515t.186.485z"/>
|
||||
</g>
|
||||
|
||||
<!-- Text -->
|
||||
<text class="text" x="28" y="17">DocuElevate</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>DocuElevate - Login</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
||||
integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
</head>
|
||||
<body class="bg-gray-100 h-screen flex items-center justify-center">
|
||||
<div class="bg-white rounded-lg shadow-lg p-8 max-w-md w-full">
|
||||
<div class="flex justify-center mb-6">
|
||||
<img src="/static/images/logo_writing.svg" alt="DocuElevate Logo" class="h-16">
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-center text-gray-800 mb-6">Welcome to DocuElevate</h1>
|
||||
|
||||
{% if error %}
|
||||
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6" role="alert">
|
||||
<p>{{ error }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if message %}
|
||||
<div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 mb-6" role="alert">
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Local authentication form -->
|
||||
<div class="mb-8" id="local-auth">
|
||||
<h2 class="text-lg font-semibold mb-4 text-gray-700">Sign in with username</h2>
|
||||
<form method="POST" action="/auth" class="space-y-4">
|
||||
<div>
|
||||
<label for="username" class="block text-sm font-medium text-gray-700">Username</label>
|
||||
<input type="text" id="username" name="username" required
|
||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
|
||||
<input type="password" id="password" name="password" required
|
||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
Sign in
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if show_oauth %}
|
||||
<!-- OAuth option -->
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-gray-300"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-sm">
|
||||
<span class="px-2 bg-white text-gray-500">Or continue with</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid grid-cols-1 gap-3">
|
||||
<a href="/oauth-login"
|
||||
class="w-full inline-flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
<span class="sr-only">Sign in with SSO</span>
|
||||
<i class="fas fa-lock mr-2"></i>
|
||||
{{ oauth_provider_name }}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-8 text-center">
|
||||
<a href="/" class="text-sm font-medium text-blue-600 hover:text-blue-500">
|
||||
Return to Home
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Optional footer with version info -->
|
||||
<div class="fixed bottom-4 text-center w-full text-xs text-gray-500">
|
||||
DocuElevate {{ app_version|default('', true) }}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user