""" app/admin/settings_override/routes.py Superadmin tenant settings overrides: set, list active, lift. Overrides take precedence over tenant_settings at the application layer. All actions logged with before/after values. """ import logging from datetime import datetime, timezone from flask import Blueprint, render_template, redirect, url_for, flash, request from flask_login import login_required, current_user from app.extensions import db from app.models.platform import Tenant, TenantSettingOverride from app.admin.utils import superadmin_required, log_admin_action from app.security import validate_setting_key logger = logging.getLogger(__name__) settings_override_bp = Blueprint("settings_override", __name__, url_prefix="/settings-override") @settings_override_bp.route("/tenant/") @login_required @superadmin_required def tenant_overrides(tenant_id): tenant = Tenant.query.get_or_404(tenant_id) active = ( TenantSettingOverride.query .filter_by(tenant_id=tenant_id) .filter(TenantSettingOverride.lifted_at.is_(None)) .order_by(TenantSettingOverride.overridden_at.desc()) .all() ) history = ( TenantSettingOverride.query .filter_by(tenant_id=tenant_id) .filter(TenantSettingOverride.lifted_at.isnot(None)) .order_by(TenantSettingOverride.lifted_at.desc()) .limit(50) .all() ) return render_template("admin/settings_override/list.html", tenant=tenant, active=active, history=history) @settings_override_bp.route("/tenant//set", methods=["GET", "POST"]) @login_required @superadmin_required def set_override(tenant_id): tenant = Tenant.query.get_or_404(tenant_id) if request.method == "POST": setting_key = request.form.get("setting_key", "").strip() setting_value = request.form.get("setting_value", "").strip() note = request.form.get("note", "").strip() or None if not validate_setting_key(setting_key): return render_template("admin/settings_override/form.html", tenant=tenant, error="Invalid setting key (alphanumeric, underscores, dots only).") if not setting_value: return render_template("admin/settings_override/form.html", tenant=tenant, error="Setting value is required.") # Lift any existing active override for the same key existing = ( TenantSettingOverride.query .filter_by(tenant_id=tenant_id, setting_key=setting_key) .filter(TenantSettingOverride.lifted_at.is_(None)) .first() ) old_value = None if existing: old_value = existing.setting_value existing.lifted_at = datetime.now(timezone.utc) override = TenantSettingOverride( tenant_id=tenant_id, setting_key=setting_key, setting_value=setting_value, overridden_by=current_user.id, note=note, ) db.session.add(override) db.session.flush() log_admin_action( "settings_override.set", "tenant", tenant_id, before={"setting_key": setting_key, "setting_value": old_value}, after={"setting_key": setting_key, "setting_value": setting_value, "note": note}, ) db.session.commit() logger.info("Settings override set: tenant=%s key=%s by=%s", tenant_id, setting_key, current_user.id) flash(f"Override for \'{setting_key}\' set on tenant \'{tenant.name}\'.", "success") return redirect(url_for("settings_override.tenant_overrides", tenant_id=tenant_id)) return render_template("admin/settings_override/form.html", tenant=tenant) @settings_override_bp.route("//lift", methods=["POST"]) @login_required @superadmin_required def lift_override(override_id): override = TenantSettingOverride.query.get_or_404(override_id) if override.lifted_at is not None: flash("This override has already been lifted.", "warning") return redirect(url_for("settings_override.tenant_overrides", tenant_id=override.tenant_id)) override.lifted_at = datetime.now(timezone.utc) log_admin_action( "settings_override.lift", "tenant", override.tenant_id, before={"setting_key": override.setting_key, "setting_value": override.setting_value}, after={"setting_key": override.setting_key, "lifted_at": str(override.lifted_at)}, ) db.session.commit() logger.info("Override lifted: id=%s tenant=%s key=%s by=%s", override_id, override.tenant_id, override.setting_key, current_user.id) flash(f"Override for \'{override.setting_key}\' lifted.", "success") return redirect(url_for("settings_override.tenant_overrides", tenant_id=override.tenant_id))