Phase 1: first-run setup wizard, bash installer, Docker profiles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+28
-2
@@ -40,7 +40,7 @@ from managers import (
|
||||
network_manager, wireguard_manager, peer_registry,
|
||||
email_manager, calendar_manager, file_manager,
|
||||
routing_manager, vault_manager, container_manager,
|
||||
cell_link_manager, auth_manager,
|
||||
cell_link_manager, auth_manager, setup_manager,
|
||||
firewall_manager, EventType,
|
||||
)
|
||||
# Re-exports: tests do `from app import CellManager` and `from app import _resolve_peer_dns`
|
||||
@@ -158,6 +158,28 @@ def enrich_log_context():
|
||||
'user': user
|
||||
})
|
||||
|
||||
@app.before_request
|
||||
def enforce_setup():
|
||||
"""Block API requests until the first-run wizard has been completed.
|
||||
|
||||
The setup routes, /health, and all non-/api/ paths are always allowed
|
||||
through. Any other /api/* request while setup is incomplete receives
|
||||
a 428 with a redirect hint to /setup.
|
||||
|
||||
Skipped entirely when app.config['TESTING'] is True so unit tests remain
|
||||
unaffected without needing to mark setup as complete.
|
||||
"""
|
||||
if app.config.get('TESTING'):
|
||||
return None
|
||||
path = request.path
|
||||
if (path.startswith('/api/setup') or
|
||||
path == '/health' or
|
||||
not path.startswith('/api/')):
|
||||
return None
|
||||
if not setup_manager.is_setup_complete():
|
||||
return jsonify({'error': 'Setup required', 'redirect': '/setup'}), 428
|
||||
|
||||
|
||||
@app.before_request
|
||||
def enforce_auth():
|
||||
"""Enforce session-based authentication and role-based access control.
|
||||
@@ -232,7 +254,7 @@ def check_csrf():
|
||||
if request.method not in ('POST', 'PUT', 'DELETE', 'PATCH'):
|
||||
return None
|
||||
path = request.path
|
||||
if not path.startswith('/api/') or path.startswith('/api/auth/'):
|
||||
if not path.startswith('/api/') or path.startswith('/api/auth/') or path.startswith('/api/setup/'):
|
||||
return None
|
||||
# peer-sync uses IP+pubkey auth — no session, no CSRF token possible
|
||||
if path.startswith('/api/cells/peer-sync/'):
|
||||
@@ -409,6 +431,10 @@ service_bus.register_service('container', container_manager)
|
||||
# Register auth blueprint
|
||||
app.register_blueprint(auth_routes.auth_bp)
|
||||
|
||||
# Register setup blueprint (no auth required — runs before any account exists)
|
||||
from routes.setup import setup_bp
|
||||
app.register_blueprint(setup_bp)
|
||||
|
||||
# Register service blueprints (routes extracted from this file)
|
||||
from routes.email import bp as _email_bp
|
||||
from routes.calendar import bp as _calendar_bp
|
||||
|
||||
Reference in New Issue
Block a user