gateway/tests/unit/rbac/test_rbac_bootstrap.py
2025-12-15 21:55:26 +01:00

175 lines
6.8 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.
"""
import pytest
from unittest.mock import Mock, MagicMock, patch
from modules.interfaces.interfaceBootstrap import (
initBootstrap,
initRootMandate,
initAdminUser,
initEventUser,
initRbacRules,
createDefaultRoleRules,
createTableSpecificRules
)
from modules.datamodels.datamodelUam import UserInDB, Mandate, AuthAuthority
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=[]) # No existing mandates
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"
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 sysadmin role."""
db = Mock()
db.getRecordset = Mock(return_value=[]) # No existing users
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 "sysadmin" in user.roleLabels
def testInitEventUserCreatesWithSysadminRole(self):
"""Test that initEventUser creates user with sysadmin role."""
db = Mock()
db.getRecordset = Mock(return_value=[]) # No existing users
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 "sysadmin" in user.roleLabels
def testCreateDefaultRoleRules(self):
"""Test that createDefaultRoleRules creates correct default rules."""
db = Mock()
db.recordCreate = Mock()
createDefaultRoleRules(db)
# Should create 4 default rules (sysadmin, admin, user, viewer)
assert db.recordCreate.call_count == 4
# Check sysadmin rule
sysadminCall = [call for call in db.recordCreate.call_args_list
if call[0][1].roleLabel == "sysadmin"][0]
sysadminRule = sysadminCall[0][1]
assert sysadminRule.context == AccessRuleContext.DATA
assert sysadminRule.item is None
assert sysadminRule.view == True
assert sysadminRule.read == AccessLevel.ALL
assert sysadminRule.create == AccessLevel.ALL
# Check user rule
userCall = [call for call in db.recordCreate.call_args_list
if call[0][1].roleLabel == "user"][0]
userRule = userCall[0][1]
assert userRule.read == AccessLevel.MY
assert userRule.create == AccessLevel.MY
def testCreateTableSpecificRules(self):
"""Test that createTableSpecificRules creates table-specific rules."""
db = Mock()
db.recordCreate = Mock()
createTableSpecificRules(db)
# Should create multiple rules for different tables
assert db.recordCreate.call_count > 0
# Check that Mandate table rules are created
mandateCalls = [call for call in db.recordCreate.call_args_list
if call[0][1].item == "Mandate"]
assert len(mandateCalls) > 0
# Check sysadmin rule for Mandate
sysadminMandateCall = [call for call in mandateCalls
if call[0][1].roleLabel == "sysadmin"][0]
sysadminRule = sysadminMandateCall[0][1]
assert sysadminRule.view == True
assert sysadminRule.read == AccessLevel.ALL
# Check that other roles have view=False for Mandate
otherMandateCalls = [call for call in mandateCalls
if call[0][1].roleLabel != "sysadmin"]
for call in otherMandateCalls:
rule = call[0][1]
assert rule.view == False
def testInitRbacRulesSkipsIfExists(self):
"""Test that initRbacRules skips default rule creation if rules already exist, but adds missing table-specific rules."""
db = Mock()
# Mock existing rules - include rules for ChatWorkflow and Prompt to prevent adding missing rules
# Need rules for all required roles to fully prevent creation
existingRules = []
for table in ["ChatWorkflow", "Prompt"]:
for role in ["sysadmin", "admin", "user", "viewer"]:
existingRules.append({
"id": f"rule_{table}_{role}",
"item": table,
"context": AccessRuleContext.DATA.value,
"roleLabel": role
})
db.getRecordset = Mock(return_value=existingRules)
db.recordCreate = Mock()
initRbacRules(db)
# Should not create new rules since all required tables already have rules for all roles
db.recordCreate.assert_not_called()
def testInitRbacRulesCreatesIfNotExists(self):
"""Test that initRbacRules creates rules if they don't exist."""
db = Mock()
db.getRecordset = Mock(side_effect=[
[], # No existing rules
[] # After creating default rules
])
db.recordCreate = Mock()
initRbacRules(db)
# Should create rules
assert db.recordCreate.call_count > 0