37 lines
1.1 KiB
Python
37 lines
1.1 KiB
Python
"""Capability matcher helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fnmatch import fnmatch
|
|
|
|
|
|
def _normalize_capability(capability: str) -> str:
|
|
return capability.strip().lower()
|
|
|
|
|
|
def capability_matches(granted: str, required: str) -> bool:
|
|
granted = _normalize_capability(granted)
|
|
required = _normalize_capability(required)
|
|
if granted == required:
|
|
return True
|
|
# A granted wildcard such as "test.*" should match required "test.unit"
|
|
if "*" in granted:
|
|
return fnmatch(required, granted)
|
|
# A required wildcard is treated as a suffix/prefix requirement for callers.
|
|
if "*" in required:
|
|
return fnmatch(granted, required)
|
|
return False
|
|
|
|
|
|
def has_capabilities(granted_caps: list[str], required_caps: list[str]) -> bool:
|
|
granted = [_normalize_capability(c) for c in granted_caps]
|
|
for required in required_caps:
|
|
required = _normalize_capability(required)
|
|
if not any(capability_matches(g, required) for g in granted):
|
|
return False
|
|
return True
|
|
|
|
|
|
def normalize_capabilities(caps: list[str]) -> list[str]:
|
|
return sorted({_normalize_capability(cap) for cap in caps})
|