diff --git a/modules/database/services/bootstrap_service.py b/modules/database/services/bootstrap_service.py index 46b2fb9..448824b 100644 --- a/modules/database/services/bootstrap_service.py +++ b/modules/database/services/bootstrap_service.py @@ -261,7 +261,12 @@ class BootstrapService: permissions = dict(BASE_PERMISSIONS) permissions["platform_admin"] = platform_admin permissions["platform_super_admin"] = super_admin + role_permissions = ROLE_PERMISSIONS.get(active.get("role") or "", {}) + permissions.update(role_permissions) if platform_admin: + # Platform authority is additive and must not be reduced by a user's + # school membership role (for example a platform admin who also has + # a teacher/student membership). permissions.update({ "can_create_school": True, "can_manage_school": True, @@ -271,8 +276,6 @@ class BootstrapService: "can_manage_classes": True, "can_view_student_data": True, }) - role_permissions = ROLE_PERMISSIONS.get(active.get("role") or "", {}) - permissions.update(role_permissions) return permissions def _school_status(self, active: MembershipRow, memberships: List[MembershipRow], admin_profile: Optional[Dict[str, Any]]) -> str: diff --git a/tests/test_me_bootstrap.py b/tests/test_me_bootstrap.py index ec62f88..731850a 100644 --- a/tests/test_me_bootstrap.py +++ b/tests/test_me_bootstrap.py @@ -218,6 +218,21 @@ def test_platform_admin_uses_admin_profiles_without_relying_on_profile_super_adm assert payload["permissions"]["can_create_school"] is True +def test_platform_admin_membership_role_does_not_reduce_platform_permissions(): + payload = build(credentials={"sub": USER_ID, "email": "admin@example.test"}, tables={ + "profiles": [profile(user_type="student")], + "admin_profiles": [{"id": USER_ID, "admin_role": "owner", "is_super_admin": True}], + "institute_memberships": [membership(INST_A, "student")], + "institutes": [institute()], + }) + + assert payload["school_status"] == "platform_admin" + assert payload["permissions"]["platform_admin"] is True + assert payload["permissions"]["can_manage_school"] is True + assert payload["permissions"]["can_manage_calendar"] is True + assert payload["permissions"]["can_view_student_data"] is True + + def test_neo4j_probe_failure_does_not_block_supabase_bootstrap_state(): def failing_probe(**_kwargs): raise RuntimeError("connection refused with host details that must not leak")