diff --git a/scripts/load_path.py b/scripts/load_path.py new file mode 100644 index 0000000..f23cca7 --- /dev/null +++ b/scripts/load_path.py @@ -0,0 +1,141 @@ +"""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 / theme files + ("/dashboard_for_sage/shell.ui", "logined"), + ("/dashboard_for_sage/shell_theme.css", "logined"), + ("/dashboard_for_sage/shell_theme.js", "logined"), + + # 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"), + + # API endpoints + ("/dashboard_for_sage/api/top_models.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())