Aug 27 - Update free-plan gate, MFA is no longer bypassable via the app
This commit is contained in:
@@ -37,14 +37,41 @@ from control.models import Plan, Tenant, ProvisioningJob
|
||||
from control.seed import seed_plans
|
||||
from control.tenant_migrate import bootstrap_tenant, chain_head
|
||||
|
||||
# What a COMPLETE tenant database holds: the baseline tables plus everything
|
||||
# the guarded tail (phase33 onward) adds. This list used to stop at the
|
||||
# baseline, which is why it passed while bootstrap was producing databases that
|
||||
# could not serve a login — the expectation encoded the bug.
|
||||
EXPECTED_TABLES = {
|
||||
# baseline (0003_add_user_active)
|
||||
'api_device_tokens', 'api_refresh_tokens', 'areas', 'audit_logs', 'broadcasts',
|
||||
'checklist_items', 'customer_assignments', 'device_registrations', 'facilities',
|
||||
'facility_score_alerts', 'inspection_results', 'inspection_templates',
|
||||
'inspections', 'inspector_assignments', 'issue_comments', 'issue_followers',
|
||||
'issues', 'notification_matrix', 'notification_preferences', 'notifications',
|
||||
'projects', 'scheduled_reports', 'support_ticket_replies', 'support_tickets',
|
||||
'users',
|
||||
'tenant_settings', 'users',
|
||||
# guarded tail
|
||||
'inspection_schedules', # phase34
|
||||
'issue_work_orders', # phase36
|
||||
'project_notification_recipients', # phase37
|
||||
'support_chat_sessions', # phase40
|
||||
'support_chat_messages', # phase40
|
||||
'support_knowledge', # phase40
|
||||
'user_notification_matrix', # phase54
|
||||
'template_contracts', # phase55
|
||||
}
|
||||
|
||||
# Columns added after the baseline, on tables the baseline already creates.
|
||||
# A missing one here is what broke login: SQLAlchemy SELECTs every mapped
|
||||
# column, so the first User.query fails with "Unknown column".
|
||||
EXPECTED_COLUMNS = {
|
||||
'users': ['mfa_enabled', 'mfa_secret', 'mfa_recovery_codes', 'ui_theme'],
|
||||
'facilities': ['qr_token'],
|
||||
'areas': ['qr_token'],
|
||||
'issues': ['handler_type', 'facility_handler_name'],
|
||||
'inspections': ['inspection_schedule_id', 'follow_up_requested_by',
|
||||
'follow_up_requested_at', 'follow_up_assigned_to'],
|
||||
'support_knowledge': ['sort_order'],
|
||||
}
|
||||
|
||||
|
||||
@@ -148,6 +175,22 @@ def verify(args):
|
||||
if need not in ucols:
|
||||
print(f" !! users.{need} missing"); ok = False
|
||||
print(f" users columns: {len(ucols)} (incl id/username/email/role)")
|
||||
|
||||
# Columns added AFTER the baseline. These are the ones that decide
|
||||
# whether the tenant can serve a request at all: SQLAlchemy SELECTs
|
||||
# every mapped column, so one missing here means the first query
|
||||
# against that table raises "Unknown column" — for users, that is the
|
||||
# login. Checked explicitly because a table-only check passed happily
|
||||
# while every post-baseline column was absent.
|
||||
for table, needed in sorted(EXPECTED_COLUMNS.items()):
|
||||
if table not in built:
|
||||
continue # already reported as a missing table above
|
||||
have = {c['name'] for c in insp.get_columns(table)}
|
||||
gone = [c for c in needed if c not in have]
|
||||
if gone:
|
||||
print(f" !! {table} missing post-baseline columns: {gone}"); ok = False
|
||||
if ok:
|
||||
print(" post-baseline columns: all present")
|
||||
finally:
|
||||
engine.dispose()
|
||||
return ok
|
||||
|
||||
Reference in New Issue
Block a user