Files
computer-playbook/tests/unit/python/utils/roles/test_rbac_scoped.py
T
kevinveenbirkenbachandClaude Opus 5.5 e2beab9f1c test(domains): read the primary domain from default.env in every test
The hardcoded-primary-domain lint exempted tests/ and scripts/tests/, so test
fixtures kept their own copy of the domain and would drift from
INFINITO_DOMAIN the moment it changes. The lint now scans both trees, and a
unit test pins its scope: only default.env, Markdown, inventories/, .claude/
and build/ may name the domain.

Python tests take the value from default_domain_primary(), and the workspace
helper builds its dashboard and Matomo URLs from INFINITO_DOMAIN once the repo
.env is loaded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 22:53:54 +02:00

145 lines
5.7 KiB
Python

"""The application-scoped role boundary.
``mcp`` grants reach whatever the deployment issued the provider's credentials
for, not the caller's own data, so holding it on one application must never
imply holding it on another. ``APPLICATION_SCOPED_ROLES`` is what keeps an
unscoped ``roles: [mcp]`` from becoming every deployed application at once, and
the Keycloak realm builder, the LDAP entry builder and the Open WebUI reconciler
all resolve membership through this one module.
"""
from __future__ import annotations
import unittest
from typing import ClassVar
from utils.domains.default_primary import default_domain_primary
from utils.roles.rbac.scoped import (
APPLICATION_SCOPED_ROLES,
granted_roles,
members_with_role,
)
DOMAIN = default_domain_primary()
class TestGrantedRoles(unittest.TestCase):
def test_a_scoped_grant_reaches_only_the_named_application(self) -> None:
user = {"application_roles": {"web-app-baserow": ["mcp"]}}
self.assertEqual({"mcp", "mcp-reader"}, granted_roles(user, "web-app-baserow"))
self.assertEqual(set(), granted_roles(user, "web-app-n8n"))
def test_an_unscoped_scoped_role_grants_nothing_anywhere(self) -> None:
user = {"roles": ["mcp"]}
for application_id in ("web-app-baserow", "web-app-n8n"):
with self.subTest(application_id=application_id):
self.assertEqual(set(), granted_roles(user, application_id))
def test_an_unscoped_ordinary_role_reaches_every_application(self) -> None:
user = {"roles": ["administrator"]}
for application_id in ("web-app-baserow", "web-app-n8n"):
with self.subTest(application_id=application_id):
self.assertEqual({"administrator"}, granted_roles(user, application_id))
def test_scoped_and_unscoped_grants_combine_on_the_named_application(self) -> None:
user = {
"roles": ["administrator"],
"application_roles": {"web-app-baserow": ["mcp"]},
}
self.assertEqual(
{"administrator", "mcp", "mcp-reader"},
granted_roles(user, "web-app-baserow"),
)
self.assertEqual({"administrator"}, granted_roles(user, "web-app-n8n"))
def test_two_applications_carry_independent_scoped_grants(self) -> None:
user = {
"application_roles": {
"web-app-baserow": ["mcp"],
"web-app-n8n": ["administrator"],
}
}
self.assertEqual({"mcp", "mcp-reader"}, granted_roles(user, "web-app-baserow"))
self.assertEqual({"administrator"}, granted_roles(user, "web-app-n8n"))
def test_a_user_without_any_declaration_holds_nothing(self) -> None:
self.assertEqual(set(), granted_roles({}, "web-app-baserow"))
def test_null_declarations_are_treated_as_empty(self) -> None:
user = {"roles": None, "application_roles": None}
self.assertEqual(set(), granted_roles(user, "web-app-baserow"))
def test_a_legacy_mcp_grant_confers_reading_not_writing(self) -> None:
user = {"application_roles": {"web-app-baserow": ["mcp"]}}
granted = granted_roles(user, "web-app-baserow")
self.assertIn("mcp-reader", granted)
self.assertNotIn("mcp-writer", granted)
def test_an_explicit_writer_grant_is_kept(self) -> None:
user = {"application_roles": {"web-app-baserow": ["mcp-writer"]}}
self.assertEqual({"mcp-writer"}, granted_roles(user, "web-app-baserow"))
def test_mcp_is_application_scoped(self) -> None:
self.assertIn("mcp", APPLICATION_SCOPED_ROLES)
class TestMembersWithRole(unittest.TestCase):
USERS: ClassVar[dict] = {
"biber": {"application_roles": {"web-app-baserow": ["mcp"]}},
"otter": {"roles": ["mcp"]},
"admin": {"roles": ["administrator"]},
}
def test_only_the_scoped_holder_is_a_member(self) -> None:
self.assertEqual(
["biber"], members_with_role(self.USERS, "web-app-baserow", "mcp-reader")
)
def test_the_unscoped_holder_is_a_member_of_no_application(self) -> None:
self.assertEqual([], members_with_role(self.USERS, "web-app-n8n", "mcp"))
def test_the_declared_username_wins_over_the_key(self) -> None:
users = {
"biber": {
"username": f"biber@{DOMAIN}",
"application_roles": {"web-app-baserow": ["mcp"]},
}
}
self.assertEqual(
[f"biber@{DOMAIN}"],
members_with_role(users, "web-app-baserow", "mcp-reader"),
)
def test_members_are_sorted_so_the_grant_diff_is_stable(self) -> None:
users = {
name: {"application_roles": {"web-app-baserow": ["mcp"]}}
for name in ("otter", "biber", "adler")
}
self.assertEqual(
["adler", "biber", "otter"],
members_with_role(users, "web-app-baserow", "mcp-reader"),
)
def test_no_users_yields_no_members(self) -> None:
self.assertEqual([], members_with_role(None, "web-app-baserow", "mcp"))
def test_a_null_user_entry_does_not_abort_the_scan(self) -> None:
users = {"ghost": None, "biber": {"application_roles": {"app": ["mcp"]}}}
self.assertEqual(["biber"], members_with_role(users, "app", "mcp-reader"))
class TestMcpRoleVocabulary(unittest.TestCase):
def test_both_mcp_roles_are_application_scoped(self) -> None:
from utils.roles.rbac.scoped import APPLICATION_SCOPED_ROLES, MCP_ROLES
for role in MCP_ROLES:
self.assertIn(role, APPLICATION_SCOPED_ROLES)
def test_a_writer_grant_does_not_confer_the_reader_name(self) -> None:
user = {"application_roles": {"app": ["mcp-writer"]}}
self.assertEqual({"mcp-writer"}, granted_roles(user, "app"))
if __name__ == "__main__":
unittest.main()