"""Base classes and helpers for User Access Policy (UAP) configuration"""
from tropicsquare.config.base import BaseConfig
[docs]
class UapPermissionField:
"""Represents an 8-bit UAP permission field.
Each field contains permission bits for 4 pairing key slots:
- Bit 0: Pairing Key slot 0 has access
- Bit 1: Pairing Key slot 1 has access
- Bit 2: Pairing Key slot 2 has access
- Bit 3: Pairing Key slot 3 has access
- Bits 4-7: Reserved
"""
[docs]
def __init__(self, value: int = 0xFF) -> None:
"""Initialize permission field.
:param value: 8-bit permission value (default: 0xFF = all slots have access)
"""
self._value = value & 0xFF
[docs]
def get_slot_permission(self, slot: int) -> bool:
"""Check if pairing key slot has access.
:param slot: Slot number (0-3)
:returns: True if slot has access
"""
if not 0 <= slot <= 3:
raise ValueError("Slot must be 0-3, got {}".format(slot))
return bool((self._value >> slot) & 1)
[docs]
def set_slot_permission(self, slot: int, has_access: bool) -> None:
"""Set permission for pairing key slot.
:param slot: Slot number (0-3)
:param has_access: True to grant access, False to deny
"""
if not 0 <= slot <= 3:
raise ValueError("Slot must be 0-3, got {}".format(slot))
if has_access:
self._value |= (1 << slot)
else:
self._value &= ~(1 << slot)
@property
def pkey_slot_0(self) -> bool:
"""Pairing Key slot 0 has access."""
return self.get_slot_permission(0)
@pkey_slot_0.setter
def pkey_slot_0(self, value: bool) -> None:
self.set_slot_permission(0, value)
@property
def pkey_slot_1(self) -> bool:
"""Pairing Key slot 1 has access."""
return self.get_slot_permission(1)
@pkey_slot_1.setter
def pkey_slot_1(self, value: bool) -> None:
self.set_slot_permission(1, value)
@property
def pkey_slot_2(self) -> bool:
"""Pairing Key slot 2 has access."""
return self.get_slot_permission(2)
@pkey_slot_2.setter
def pkey_slot_2(self, value: bool) -> None:
self.set_slot_permission(2, value)
@property
def pkey_slot_3(self) -> bool:
"""Pairing Key slot 3 has access."""
return self.get_slot_permission(3)
@pkey_slot_3.setter
def pkey_slot_3(self, value: bool) -> None:
self.set_slot_permission(3, value)
@property
def value(self) -> int:
"""Raw 8-bit value."""
return self._value
@value.setter
def value(self, val: int) -> None:
self._value = val & 0xFF
[docs]
def to_dict(self) -> dict:
"""Export as dictionary."""
return {
'pkey_slot_0': self.pkey_slot_0,
'pkey_slot_1': self.pkey_slot_1,
'pkey_slot_2': self.pkey_slot_2,
'pkey_slot_3': self.pkey_slot_3
}
[docs]
def __str__(self) -> str:
"""Format as table cells: x | x | | x
No leading/trailing pipes - will be added by parent class.
Returns 4 cells separated by ' | '.
"""
parts = []
for i in range(4):
if self.get_slot_permission(i):
parts.append("x")
else:
parts.append(" ") # 1 space for alignment
return " | ".join(parts)
[docs]
class UapMultiSlotConfig(BaseConfig):
"""Base class for UAP configs with multiple slots.
Used for configs that have 4 slots, each with 8-bit permission field.
"""
def _get_slot_field(self, slot_pos: int) -> UapPermissionField:
"""Get 8-bit permission field at slot position.
:param slot_pos: Bit position of slot (0, 8, 16, or 24)
:returns: Permission field at the specified slot position
:rtype: UapPermissionField
"""
field_value = (self._value >> slot_pos) & 0xFF
return UapPermissionField(field_value)
def _set_slot_field(self, slot_pos: int, field: UapPermissionField) -> None:
"""Set 8-bit permission field at slot position.
:param slot_pos: Bit position of slot (0, 8, 16, or 24)
:param field: UapPermissionField object
"""
field_value = field.value
# Clear existing field and set new value
mask = 0xFF << slot_pos
self._value = (self._value & ~mask) | (field_value << slot_pos)
[docs]
def __str__(self) -> str:
"""Table row: ClassName | slot_0 || slot_1 || slot_2 || slot_3 |
Uses || to visually separate 4 different slot permission fields.
"""
s0 = str(self._get_slot_field(0))
s1 = str(self._get_slot_field(8))
s2 = str(self._get_slot_field(16))
s3 = str(self._get_slot_field(24))
return "{:26s} | {} || {} || {} || {} |".format(
self.__class__.__name__,
s0, s1, s2, s3
)
[docs]
class UapSingleFieldConfig(BaseConfig):
"""Base class for UAP configs with single 8-bit permission field."""
[docs]
def __init__(self, value: int = 0xFFFFFFFF) -> None:
"""Initialize with default all-access value."""
super().__init__(value)
@property
def permissions(self) -> UapPermissionField:
"""Get permission field (8 bits at position 0)."""
field_value = self._value & 0xFF
return UapPermissionField(field_value)
@permissions.setter
def permissions(self, field: UapPermissionField) -> None:
"""Set permission field."""
self._value = (self._value & ~0xFF) | field.value
[docs]
def to_dict(self) -> dict:
"""Export as dictionary."""
return {
'permissions': self.permissions.to_dict()
}
[docs]
def __str__(self) -> str:
"""Table row: ClassName | permissions cells |"""
perm_str = str(self.permissions)
return "{:26s} | {} |".format(
self.__class__.__name__,
perm_str
)
[docs]
class UapDualFieldConfig(BaseConfig):
"""Base class for UAP configs with two 8-bit permission fields (CFG and FUNC)."""
[docs]
def __init__(self, value: int = 0xFFFFFFFF) -> None:
"""Initialize with default all-access value."""
super().__init__(value)
@property
def cfg_permissions(self) -> UapPermissionField:
"""Get CFG permission field (8 bits at position 0)."""
field_value = self._value & 0xFF
return UapPermissionField(field_value)
@cfg_permissions.setter
def cfg_permissions(self, field: UapPermissionField) -> None:
"""Set CFG permission field."""
self._value = (self._value & ~0xFF) | field.value
@property
def func_permissions(self) -> UapPermissionField:
"""Get FUNC permission field (8 bits at position 8)."""
field_value = (self._value >> 8) & 0xFF
return UapPermissionField(field_value)
@func_permissions.setter
def func_permissions(self, field: UapPermissionField) -> None:
"""Set FUNC permission field."""
self._value = (self._value & ~0xFF00) | (field.value << 8)
[docs]
def to_dict(self) -> dict:
"""Export as dictionary."""
return {
'cfg_permissions': self.cfg_permissions.to_dict(),
'func_permissions': self.func_permissions.to_dict()
}
[docs]
def __str__(self) -> str:
"""Table row: ClassName | cfg cells || func cells |
Uses || to visually separate cfg and func permission fields.
"""
cfg_str = str(self.cfg_permissions)
func_str = str(self.func_permissions)
return "{:26s} | {} || {} |".format(
self.__class__.__name__,
cfg_str,
func_str
)