- profile_utils: 40 tests, 100% coverage (sanitize, normalize, mask, validate payload) - security_badge_service: 17 tests, 100% coverage (role/permission extraction, security summary) - services_mapping: 4 tests, 100% coverage (client resolution, get_suggestions) - superset_lookup_service: 10 tests, 100% coverage (resolve environment, lookup success/degraded) - notification_providers: 16 tests, 95% coverage (SMTP, Telegram, Slack providers) - llm_provider: 25 tests, 91% coverage (mask_api_key, CRUD with encrypted API keys) - rbac_permission_catalog: 12 tests, 79% coverage (route scanning, sync to DB)
208 lines
8.2 KiB
Python
208 lines
8.2 KiB
Python
# #region Test.SecurityBadgeService [C:3] [TYPE Module] [SEMANTICS test,profile,security,permissions,badges]
|
|
# @BRIEF Tests for security_badge_service.py — role extraction, permission pairs, security summary.
|
|
# @RELATION BINDS_TO -> [SecurityBadgeService]
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
import pytest
|
|
|
|
|
|
def make_user(**kwargs):
|
|
"""Helper to create a mock User with roles."""
|
|
from src.models.auth import User
|
|
user = MagicMock(spec=User)
|
|
for k, v in kwargs.items():
|
|
setattr(user, k, v)
|
|
return user
|
|
|
|
|
|
def make_role(name, permissions=None):
|
|
role = MagicMock()
|
|
role.name = name
|
|
role.permissions = permissions or []
|
|
return role
|
|
|
|
|
|
def make_permission(resource, action):
|
|
perm = MagicMock()
|
|
perm.resource = resource
|
|
perm.action = action
|
|
return perm
|
|
|
|
|
|
class TestCollectUserPermissionPairs:
|
|
"""_collect_user_permission_pairs — extract (resource, ACTION) tuples."""
|
|
|
|
def test_no_roles_returns_empty(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
user = make_user(roles=[])
|
|
result = svc._collect_user_permission_pairs(user)
|
|
assert result == set()
|
|
|
|
def test_role_with_permissions(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
perms = [make_permission("dashboard", "read"), make_permission("dataset", "write")]
|
|
role = make_role("Editor", perms)
|
|
user = make_user(roles=[role])
|
|
result = svc._collect_user_permission_pairs(user)
|
|
assert ("dashboard", "READ") in result
|
|
assert ("dataset", "WRITE") in result
|
|
|
|
def test_permission_without_resource_skipped(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
perms = [make_permission("", "read"), make_permission(None, "write")]
|
|
role = make_role("Viewer", perms)
|
|
user = make_user(roles=[role])
|
|
result = svc._collect_user_permission_pairs(user)
|
|
assert result == set()
|
|
|
|
def test_multiple_roles_dedup(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
p1 = make_permission("dashboard", "read")
|
|
role1 = make_role("Admin", [p1])
|
|
role2 = make_role("Editor", [p1])
|
|
user = make_user(roles=[role1, role2])
|
|
result = svc._collect_user_permission_pairs(user)
|
|
assert len(result) == 1
|
|
|
|
|
|
class TestBuildSecuritySummary:
|
|
"""build_security_summary — full security snapshot."""
|
|
|
|
def test_no_roles(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
user = make_user(roles=[], auth_source="database")
|
|
summary = svc.build_security_summary(user)
|
|
assert summary.read_only is True
|
|
assert summary.roles == []
|
|
assert summary.current_role is None
|
|
assert summary.auth_source == "database"
|
|
|
|
def test_single_role(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
role = make_role("Viewer")
|
|
user = make_user(roles=[role], auth_source="ldap")
|
|
summary = svc.build_security_summary(user)
|
|
assert summary.roles == ["Viewer"]
|
|
assert summary.current_role == "Viewer"
|
|
|
|
def test_admin_role_detected(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
role = make_role("Admin")
|
|
user = make_user(roles=[role], auth_source="database")
|
|
summary = svc.build_security_summary(user)
|
|
assert "Admin" in summary.roles
|
|
assert summary.current_role == "Admin"
|
|
|
|
def test_roles_sorted(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
role_z = make_role("ZViewer")
|
|
role_a = make_role("AAdmin")
|
|
user = make_user(roles=[role_z, role_a])
|
|
summary = svc.build_security_summary(user)
|
|
assert summary.roles == ["AAdmin", "ZViewer"]
|
|
|
|
def test_discover_permissions_fallback(self):
|
|
"""When discover_declared_permissions raises, fallback to user permissions."""
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
perms = [make_permission("dashboard", "read")]
|
|
role = make_role("Editor", perms)
|
|
user = make_user(roles=[role])
|
|
|
|
with patch("src.services.security_badge_service.discover_declared_permissions",
|
|
side_effect=RuntimeError("Plugin load failed")):
|
|
svc = SecurityBadgeService(plugin_loader=MagicMock())
|
|
summary = svc.build_security_summary(user)
|
|
# Should still produce permission states
|
|
assert len(summary.permissions) >= 0
|
|
|
|
def test_is_admin_allows_all(self):
|
|
"""Admin user sees all declared permissions as allowed."""
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
perms = [make_permission("dashboard", "read")]
|
|
role = make_role("Admin", perms)
|
|
user = make_user(roles=[role])
|
|
|
|
with patch("src.services.security_badge_service.discover_declared_permissions",
|
|
return_value={("dashboard", "READ"), ("dataset", "WRITE")}):
|
|
svc = SecurityBadgeService()
|
|
summary = svc.build_security_summary(user)
|
|
admin_perm = next((p for p in summary.permissions if p.key == "dashboard"), None)
|
|
assert admin_perm is not None
|
|
assert admin_perm.allowed is True
|
|
|
|
def test_permission_key_format_read(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
key = svc._format_permission_key("dashboard", "READ")
|
|
assert key == "dashboard"
|
|
|
|
def test_permission_key_format_write(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
key = svc._format_permission_key("dataset", "WRITE")
|
|
assert key == "dataset:write"
|
|
|
|
|
|
class TestFormatPermissionKey:
|
|
"""_format_permission_key — compact UI key format."""
|
|
|
|
def test_read_omits_action(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
assert svc._format_permission_key("dashboard", "READ") == "dashboard"
|
|
|
|
def test_write_includes_action(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
assert svc._format_permission_key("dataset", "WRITE") == "dataset:write"
|
|
|
|
def test_empty_resource(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
result = svc._format_permission_key("", "READ")
|
|
assert result is not None
|
|
assert isinstance(result, str)
|
|
|
|
|
|
class TestPermissionEdgeCases:
|
|
"""Edge cases for permission processing."""
|
|
|
|
def test_empty_role_name_skipped(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
role_empty = make_role("")
|
|
role_none = make_role(None)
|
|
user = make_user(roles=[role_empty, role_none])
|
|
summary = svc.build_security_summary(user)
|
|
assert summary.roles == []
|
|
|
|
def test_role_name_with_whitespace_sanitized(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
role = make_role(" Editor ")
|
|
user = make_user(roles=[role])
|
|
summary = svc.build_security_summary(user)
|
|
assert "Editor" in summary.roles
|
|
|
|
def test_collect_with_none_permissions(self):
|
|
from src.services.security_badge_service import SecurityBadgeService
|
|
svc = SecurityBadgeService()
|
|
role = make_role("Viewer", permissions=None)
|
|
user = make_user(roles=[role])
|
|
result = svc._collect_user_permission_pairs(user)
|
|
assert result == set()
|
|
# #endregion Test.SecurityBadgeService
|