Jun 28 - Code optimize - Fix Critical/High issues
This commit is contained in:
@@ -7,6 +7,8 @@ One row per tenant DB. Created on first save; reads fall back to defaults
|
|||||||
when no row exists so tenant-zero needs zero data migration.
|
when no row exists so tenant-zero needs zero data migration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sqlalchemy.exc
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.utils.time_utils import now_eastern
|
from app.utils.time_utils import now_eastern
|
||||||
|
|
||||||
@@ -44,9 +46,9 @@ class TenantSettings(db.Model):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
row = cls.query.first()
|
row = cls.query.first()
|
||||||
except Exception:
|
except (sqlalchemy.exc.ProgrammingError, sqlalchemy.exc.OperationalError):
|
||||||
# ProgrammingError: Table 'tenant_settings' doesn't exist
|
# Table 'tenant_settings' doesn't exist — phase33 migration not yet applied.
|
||||||
# (phase33 migration not yet applied to this tenant DB)
|
# Any other DB error (connection failure, permission error) is re-raised.
|
||||||
return None
|
return None
|
||||||
if row is not None:
|
if row is not None:
|
||||||
return row
|
return row
|
||||||
|
|||||||
+8
-1
@@ -524,6 +524,7 @@ def reset_password(token):
|
|||||||
# ── MT-4: Superadmin impersonation ────────────────────────────────────────────
|
# ── MT-4: Superadmin impersonation ────────────────────────────────────────────
|
||||||
|
|
||||||
@bp.route('/impersonate')
|
@bp.route('/impersonate')
|
||||||
|
@login_required
|
||||||
def impersonate_entry():
|
def impersonate_entry():
|
||||||
"""
|
"""
|
||||||
Validate a superadmin impersonation token and bind the session to a tenant.
|
Validate a superadmin impersonation token and bind the session to a tenant.
|
||||||
@@ -531,7 +532,8 @@ def impersonate_entry():
|
|||||||
GET /auth/impersonate?token=<signed_token>
|
GET /auth/impersonate?token=<signed_token>
|
||||||
Sets session['impersonating_tenant_id'] which the tenancy middleware reads
|
Sets session['impersonating_tenant_id'] which the tenancy middleware reads
|
||||||
to short-circuit normal Host resolution for the duration of the session.
|
to short-circuit normal Host resolution for the duration of the session.
|
||||||
This route is CSRF-exempt by nature — the HMAC token already provides auth.
|
Requires an authenticated user so the HMAC token alone cannot grant access
|
||||||
|
to an anonymous session.
|
||||||
"""
|
"""
|
||||||
from flask import session as flask_session
|
from flask import session as flask_session
|
||||||
token = request.args.get('token', '')
|
token = request.args.get('token', '')
|
||||||
@@ -550,6 +552,11 @@ def impersonate_entry():
|
|||||||
tenant_id = payload.get('tid')
|
tenant_id = payload.get('tid')
|
||||||
superadmin_id = payload.get('said')
|
superadmin_id = payload.get('said')
|
||||||
|
|
||||||
|
if tenant_id is None:
|
||||||
|
logger.warning('AUTH | impersonate_invalid | reason=missing tid in payload')
|
||||||
|
flash('Invalid impersonation token (missing tenant).', 'danger')
|
||||||
|
return redirect(url_for('auth.login'))
|
||||||
|
|
||||||
flask_session['impersonating_tenant_id'] = tenant_id
|
flask_session['impersonating_tenant_id'] = tenant_id
|
||||||
flask_session['impersonating_superadmin_id'] = superadmin_id
|
flask_session['impersonating_superadmin_id'] = superadmin_id
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from app.utils.decorators import supervisor_required, admin_required
|
|||||||
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE
|
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE
|
||||||
from app.utils.scope import get_customer_scope, get_inspector_scope
|
from app.utils.scope import get_customer_scope, get_inspector_scope
|
||||||
from app.tenancy.gates import quota_soft_check
|
from app.tenancy.gates import quota_soft_check
|
||||||
from app.tenancy.gates import feature_required
|
|
||||||
|
|
||||||
bp = Blueprint('facilities', __name__, url_prefix='/facilities')
|
bp = Blueprint('facilities', __name__, url_prefix='/facilities')
|
||||||
|
|
||||||
@@ -17,8 +16,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
@bp.route('/')
|
@bp.route('/')
|
||||||
@login_required
|
@login_required
|
||||||
@supervisor_required
|
|
||||||
@feature_required('scheduled_reports')
|
|
||||||
def list_facilities():
|
def list_facilities():
|
||||||
if current_user.role == 'customer':
|
if current_user.role == 'customer':
|
||||||
cids = get_customer_scope(current_user) or []
|
cids = get_customer_scope(current_user) or []
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ def branding():
|
|||||||
branding_allowed = tenant.allow_branding
|
branding_allowed = tenant.allow_branding
|
||||||
|
|
||||||
settings = TenantSettings.get_or_default()
|
settings = TenantSettings.get_or_default()
|
||||||
|
if settings is None:
|
||||||
|
flash('Branding settings are not available yet — the phase33 migration has not been applied to this tenant DB. '
|
||||||
|
'Run: python -m control.tenant_migrate upgrade --tenant <slug>', 'warning')
|
||||||
|
return redirect(url_for('dashboard.index'))
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if not branding_allowed:
|
if not branding_allowed:
|
||||||
|
|||||||
Reference in New Issue
Block a user