"""The role "ATOMA Reader": read the dashboards granted to it, nothing else. Runs inside the Superset container, after `superset init` (stacks/fuel-monitoring/superset/init.sh): docker exec /app/.venv/bin/python /app/petrolyst-roles/atoma_reader.py Creates the role if needed and gives it exactly PERMISSIONS: missing ones are added, any other is removed (also one added by hand in the UI). A second run changes nothing. Readers reach a dashboard only when it lists this role (DASHBOARD_RBAC; dashboards/atoma/import_bundle.py attaches it), and petrolyst_reader.py adds the rules a role cannot express. The list was established on the local stack by reading the ATOMA dashboard as a reader: everything the dashboard asks the server for, and nothing more. No menu access, nothing to write, no Explore, CSV, sharing, drill or query view. """ from __future__ import annotations import sys ROLE = "ATOMA Reader" PERMISSIONS = [ ("can_read", "Dashboard"), # the dashboard and its charts' definitions ("can_dashboard", "Superset"), # the dashboard page ("can_dashboard_permalink", "Superset"), # a dashboard permalink ("can_read", "Chart"), # chart data ("can_time_range", "Api"), # the Viewing period filter ("can_read", "DashboardFilterStateRestApi"), # native filter state ("can_write", "DashboardFilterStateRestApi"), ("can_read", "DashboardPermalinkRestApi"), ("can_read", "CurrentUserRestApi"), # who is signed in ("can_read", "SecurityRestApi"), # the CSRF token ("can_get", "MenuApi"), ("can_log", "Superset"), # Superset's event log ] def sync(name: str, permissions: list[tuple[str, str]]) -> int: """Give the role `name` exactly `permissions`, creating it if needed (atoma_guest.py too).""" from superset.app import create_app app = create_app() with app.app_context(): from superset import db, security_manager as sm wanted = [] for permission, view in permissions: pvm = sm.find_permission_view_menu(permission, view) if pvm is None: print(f"no permission {permission} on {view}: run `superset init` first?", file=sys.stderr) return 1 wanted.append(pvm) role = sm.find_role(name) or sm.add_role(name) added = [pvm for pvm in wanted if pvm not in role.permissions] removed = [pvm for pvm in role.permissions if pvm not in wanted] for pvm in added: sm.add_permission_role(role, pvm) for pvm in removed: sm.del_permission_role(role, pvm) db.session.commit() print(f'role "{name}": {len(wanted)} permissions, {len(added)} added, ' f"{len(removed)} removed") for pvm in removed: print(f" removed {pvm.permission.name} on {pvm.view_menu.name}") return 0 def main() -> int: return sync(ROLE, PERMISSIONS) if __name__ == "__main__": sys.exit(main())