"""Generate RBAC permissions for dashboard_for_sage module paths. Run from Sage root with Sage venv: cd ~/repos/sage && ./py3/bin/python ../dashboard_for_sage/scripts/load_path.py Or set SAGE_ROOT environment variable. """ import os import sys import asyncio # Ensure Sage root is in path sage_root = os.environ.get('SAGE_ROOT') if sage_root and sage_root not in sys.path: sys.path.insert(0, sage_root) from sqlor.dbpools import DBPools from appPublic.jsonConfig import getConfig from appPublic.dictObject import DictObject from appPublic.uniqueID import getID # ── Permission definitions ── # Format: (path, role) # Dashboard files are accessible to all logined users except menu.ui (any) paths = [ # Module root and index ("/dashboard_for_sage", "logined"), ("/dashboard_for_sage/index.ui", "logined"), # Menu — must be any so unauthenticated users can see nav ("/dashboard_for_sage/menu.ui", "any"), # Shell ("/dashboard_for_sage/shell.ui", "logined"), ("/dashboard_for_sage/shell_theme.css", "any"), ("/dashboard_for_sage/shell_theme.js", "any"), # Global menu ("/dashboard_for_sage/global_menu.ui", "logined"), # Stat cards ("/dashboard_for_sage/stat_today_usage.ui", "logined"), ("/dashboard_for_sage/stat_today_amount.ui", "logined"), ("/dashboard_for_sage/stat_total_users.ui", "logined"), ("/dashboard_for_sage/stat_active_users.ui", "logined"), ("/dashboard_for_sage/stat_concurrent.ui", "logined"), ("/dashboard_for_sage/stat_errors.ui", "logined"), ("/dashboard_for_sage/stat_new_users_month.ui", "logined"), ("/dashboard_for_sage/stat_total_orgs.ui", "logined"), # Legacy stat cards (backward compat) ("/dashboard_for_sage/today_usage.ui", "logined"), ("/dashboard_for_sage/today_amount.ui", "logined"), ("/dashboard_for_sage/total_users.ui", "logined"), ("/dashboard_for_sage/concurrent_users.ui", "logined"), ("/dashboard_for_sage/accounting_errors.ui", "logined"), # Top 5 ranking cards ("/dashboard_for_sage/table_top_users.ui", "logined"), ("/dashboard_for_sage/table_top_users_amount.ui", "logined"), ("/dashboard_for_sage/table_top_users_count.ui", "logined"), ("/dashboard_for_sage/table_top_providers_amount.ui", "logined"), ("/dashboard_for_sage/table_top_providers_count.ui", "logined"), ("/dashboard_for_sage/top_users_amount.ui", "logined"), # Charts ("/dashboard_for_sage/chart_top_models.ui", "logined"), ("/dashboard_for_sage/top_models_chart.ui", "logined"), ("/dashboard_for_sage/user_today_models_chart.ui", "logined"), ("/dashboard_for_sage/all_today_models_chart.ui", "logined"), # Customer monitoring ("/dashboard_for_sage/customer_usage.ui", "logined"), ("/dashboard_for_sage/customer_daily_chart.ui", "logined"), ("/dashboard_for_sage/customer_monthly_chart.ui", "logined"), ("/dashboard_for_sage/customer_daily_trend.ui", "logined"), # API endpoints ("/dashboard_for_sage/api/top_models.dspy", "logined"), ("/dashboard_for_sage/api/user_today_models.dspy", "logined"), ("/dashboard_for_sage/api/all_today_models.dspy", "logined"), ("/dashboard_for_sage/api/customer_daily_models.dspy", "logined"), ("/dashboard_for_sage/api/customer_monthly_models.dspy", "logined"), ("/dashboard_for_sage/api/customer_daily_trend.dspy", "logined"), ] async def add_roleperm(sor, roleid, permid): """Add role-permission mapping if not exists.""" ns = {'roleid': roleid, 'permid': permid} recs = await sor.R('rolepermission', ns.copy()) if not recs: ns['id'] = getID() await sor.C('rolepermission', ns.copy()) async def add_roles_perm(sor, perm, roles): """Register permission for special roles.""" if roles in [['any'], ['anonymous'], ['logined']]: role = roles[0] await add_roleperm(sor, role, perm.id) return for role in roles: if '.' in role: orgtypeid, name = role.split('.', 1) else: orgtypeid, name = '*', role ns = {'orgtypeid': orgtypeid, 'name': name} roles_rec = await sor.R('role', ns.copy()) if not roles_rec: ns['id'] = getID() await sor.C('role', ns.copy()) else: ns['id'] = roles_rec[0].id await add_roleperm(sor, ns['id'], perm.id) # Remove 'any' fallback for this perm ns_any = {'roleid': 'any', 'permid': perm.id} existing = await sor.R('rolepermission', ns_any.copy()) if existing: await sor.D('rolepermission', {'id': existing[0].id}) async def main(): config = getConfig('.') db = DBPools(config.databases) cnt = 0 async with db.sqlorContext('sage') as sor: for path, role in paths: ns = {'path': path} recs = await sor.R('permission', ns.copy()) if recs: # Permission exists, skip (idempotent) continue cnt += 1 pid = getID() ns['id'] = pid await sor.C('permission', ns.copy()) perm = DictObject(**ns) await add_roles_perm(sor, perm, [role]) print(f'{cnt} path(s) inserted for dashboard_for_sage') if cnt == 0: print('All paths already registered — no changes needed.') if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main())