gateway/tests/unit/rbac/test_rbac_bootstrap.py

159 lines
5.7 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Unit tests for RBAC bootstrap initialization.
Tests that bootstrap creates correct rules and initial data.
"""
from unittest.mock import Mock, patch
from modules.interfaces.interfaceBootstrap import (
initRootMandate,
initAdminUser,
initEventUser,
initRbacRules,
_createDefaultRoleRules,
_createTableSpecificRules,
)
from modules.datamodels.datamodelUam import UserInDB, Mandate
from modules.datamodels.datamodelRbac import AccessRule, AccessRuleContext
from modules.datamodels.datamodelUam import AccessLevel
class TestRbacBootstrap:
"""Test RBAC bootstrap initialization."""
def testInitRootMandateCreatesIfNotExists(self):
"""Test that initRootMandate creates mandate if it doesn't exist."""
db = Mock()
db.getRecordset = Mock(return_value=[])
db.recordCreate = Mock(return_value={"id": "mandate1", "name": "Root"})
mandateId = initRootMandate(db)
assert mandateId == "mandate1"
db.recordCreate.assert_called_once()
callArgs = db.recordCreate.call_args
assert isinstance(callArgs[0][1], Mandate)
assert callArgs[0][1].name == "root"
assert callArgs[0][1].label == "Root"
def testInitRootMandateReturnsExisting(self):
"""Test that initRootMandate returns existing mandate ID."""
db = Mock()
db.getRecordset = Mock(return_value=[{"id": "existing_mandate"}])
mandateId = initRootMandate(db)
assert mandateId == "existing_mandate"
db.recordCreate.assert_not_called()
def testInitAdminUserCreatesWithSysadminRole(self):
"""Test that initAdminUser creates user with isSysAdmin=True."""
db = Mock()
db.getRecordset = Mock(return_value=[])
db.recordCreate = Mock(return_value={"id": "admin1", "username": "admin"})
with patch("modules.interfaces.interfaceBootstrap._getPasswordHash", return_value="hashed"):
userId = initAdminUser(db, "mandate1")
assert userId == "admin1"
db.recordCreate.assert_called_once()
callArgs = db.recordCreate.call_args
user = callArgs[0][1]
assert isinstance(user, UserInDB)
assert user.username == "admin"
assert user.isSysAdmin is True
def testInitEventUserCreatesWithSysadminRole(self):
"""Test that initEventUser creates user with isSysAdmin=True."""
db = Mock()
db.getRecordset = Mock(return_value=[])
db.recordCreate = Mock(return_value={"id": "event1", "username": "event"})
with patch("modules.interfaces.interfaceBootstrap._getPasswordHash", return_value="hashed"):
userId = initEventUser(db, "mandate1")
assert userId == "event1"
db.recordCreate.assert_called_once()
callArgs = db.recordCreate.call_args
user = callArgs[0][1]
assert isinstance(user, UserInDB)
assert user.username == "event"
assert user.isSysAdmin is True
def testCreateDefaultRoleRules(self):
"""Test that _createDefaultRoleRules creates admin + viewer generic DATA rules."""
db = Mock()
db.recordCreate = Mock()
with patch(
"modules.interfaces.interfaceBootstrap._getRoleId",
side_effect=lambda d, label: f"rid-{label}",
):
_createDefaultRoleRules(db)
assert db.recordCreate.call_count == 2
created = [c[0][1] for c in db.recordCreate.call_args_list]
byRoleId = {r.roleId: r for r in created}
adminRule = byRoleId["rid-admin"]
assert adminRule.context == AccessRuleContext.DATA
assert adminRule.item is None
assert adminRule.view is True
assert adminRule.read == AccessLevel.GROUP
assert adminRule.create == AccessLevel.GROUP
viewerRule = byRoleId["rid-viewer"]
assert viewerRule.read == AccessLevel.GROUP
assert viewerRule.create == AccessLevel.NONE
def testCreateTableSpecificRules(self):
"""Test that _createTableSpecificRules creates table-specific rules."""
db = Mock()
db.recordCreate = Mock()
with patch(
"modules.interfaces.interfaceBootstrap._getRoleId",
side_effect=lambda d, label: f"rid-{label}",
):
_createTableSpecificRules(db)
assert db.recordCreate.call_count > 0
mandateCalls = [
call for call in db.recordCreate.call_args_list if call[0][1].item == "data.uam.Mandate"
]
assert len(mandateCalls) > 0
for call in mandateCalls:
rule = call[0][1]
assert rule.view is False
assert rule.read == AccessLevel.NONE
def testInitRbacRulesSkipsIfExists(self):
"""When AccessRules already exist, init skips full init; ensure-* hooks are not exercised here."""
db = Mock()
db.getRecordset = Mock(return_value=[{"id": "existing_rule"}])
db.recordCreate = Mock()
with patch("modules.interfaces.interfaceBootstrap._ensureUiContextRules"):
with patch("modules.interfaces.interfaceBootstrap._ensureDataContextRules"):
initRbacRules(db)
db.recordCreate.assert_not_called()
def testInitRbacRulesCreatesIfNotExists(self):
"""Test that initRbacRules creates rules when the AccessRule table is empty."""
db = Mock()
db.recordCreate = Mock()
db.recordModify = Mock()
db.getRecordset = Mock(return_value=[])
with patch(
"modules.interfaces.interfaceBootstrap._getRoleId",
side_effect=lambda d, label: f"rid-{label}",
):
initRbacRules(db)
assert db.recordCreate.call_count > 0