from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Protocol @dataclass(frozen=True) class Account: vendor_id: int subaccount_id: int vendor_uuid: str = field(default="", repr=False, compare=False) @property def is_subaccount(self) -> bool: return self.subaccount_id > 0 def __getattribute__(self, item: str) -> Any: if item == "vendor_uuid" and not object.__getattribute__(self, item): raise ValueError("Vendor UUID not set") return object.__getattribute__(self, item) class HasAccount(Protocol): @property def account(self) -> Account: ... @dataclass(frozen=True) class AccountAccess: accounts: list[Account] = field(default_factory=list) vendor_ids: list[int] = field(init=False) subaccount_ids: list[int] = field(init=False) def __post_init__(self) -> None: vendor_ids = [] subaccount_ids = [] for account in self.accounts: if account.vendor_id and not account.subaccount_id: vendor_ids.append(account.vendor_id) else: subaccount_ids.append(account.subaccount_id) object.__setattr__(self, "vendor_ids", vendor_ids) object.__setattr__(self, "subaccount_ids", subaccount_ids) @property def authorized(self) -> bool: return len(self.accounts) > 0 def get_allowed_vendor_ids_for(self, vendor_ids: list[int]) -> list[int]: return list(set(self.vendor_ids) & set(vendor_ids)) def get_allowed_subaccount_ids_for(self, subaccount_ids: list[int]) -> list[int]: return list(set(self.subaccount_ids) & set(subaccount_ids)) def has_access_to_vendor(self, vendor_id: int) -> bool: return vendor_id in self.get_allowed_vendor_ids_for([vendor_id]) def has_access_to_subaccount(self, subaccount_id: int) -> bool: return subaccount_id in self.get_allowed_subaccount_ids_for([subaccount_id]) def has_access(self, account: Account) -> bool: return self.has_access_to_vendor(account.vendor_id) or ( account.is_subaccount and self.has_access_to_subaccount(account.subaccount_id) ) def allowed_for(self, target: HasAccount) -> bool: return self.has_access(target.account) # Filters def filter_vendor_ids(self, vendor_id: int | None = None) -> list[int]: if vendor_id: return self.get_allowed_vendor_ids_for([vendor_id]) return self.vendor_ids def filter_subaccount_ids(self, subaccount_id: int | None = None) -> list[int]: if subaccount_id is not None: return self.get_allowed_subaccount_ids_for([subaccount_id]) return self.subaccount_ids