Home Assistant Unofficial Reference 2024.12.1
util.py
Go to the documentation of this file.
1 """Helpers to deal with permissions."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from functools import wraps
7 from typing import cast
8 
9 from .const import SUBCAT_ALL
10 from .models import PermissionLookup
11 from .types import CategoryType, SubCategoryDict, ValueType
12 
13 type LookupFunc = Callable[[PermissionLookup, SubCategoryDict, str], ValueType | None]
14 type SubCatLookupType = dict[str, LookupFunc]
15 
16 
18  perm_lookup: PermissionLookup, lookup_dict: SubCategoryDict, object_id: str
19 ) -> ValueType:
20  """Look up permission for all."""
21  # In case of ALL category, lookup_dict IS the schema.
22  return cast(ValueType, lookup_dict)
23 
24 
26  policy: CategoryType, subcategories: SubCatLookupType, perm_lookup: PermissionLookup
27 ) -> Callable[[str, str], bool]:
28  """Compile policy into a function that tests policy.
29 
30  Subcategories are mapping key -> lookup function, ordered by highest
31  priority first.
32  """
33  # None, False, empty dict
34  if not policy:
35 
36  def apply_policy_deny_all(entity_id: str, key: str) -> bool:
37  """Decline all."""
38  return False
39 
40  return apply_policy_deny_all
41 
42  if policy is True:
43 
44  def apply_policy_allow_all(entity_id: str, key: str) -> bool:
45  """Approve all."""
46  return True
47 
48  return apply_policy_allow_all
49 
50  assert isinstance(policy, dict)
51 
52  funcs: list[Callable[[str, str], bool | None]] = []
53 
54  for key, lookup_func in subcategories.items():
55  lookup_value = policy.get(key)
56 
57  # If any lookup value is `True`, it will always be positive
58  if isinstance(lookup_value, bool):
59  return lambda object_id, key: True
60 
61  if lookup_value is not None:
62  funcs.append(_gen_dict_test_func(perm_lookup, lookup_func, lookup_value))
63 
64  if len(funcs) == 1:
65  func = funcs[0]
66 
67  @wraps(func)
68  def apply_policy_func(object_id: str, key: str) -> bool:
69  """Apply a single policy function."""
70  return func(object_id, key) is True
71 
72  return apply_policy_func
73 
74  def apply_policy_funcs(object_id: str, key: str) -> bool:
75  """Apply several policy functions."""
76  for func in funcs:
77  if (result := func(object_id, key)) is not None:
78  return result
79  return False
80 
81  return apply_policy_funcs
82 
83 
85  perm_lookup: PermissionLookup, lookup_func: LookupFunc, lookup_dict: SubCategoryDict
86 ) -> Callable[[str, str], bool | None]:
87  """Generate a lookup function."""
88 
89  def test_value(object_id: str, key: str) -> bool | None:
90  """Test if permission is allowed based on the keys."""
91  schema: ValueType = lookup_func(perm_lookup, lookup_dict, object_id)
92 
93  if schema is None or isinstance(schema, bool):
94  return schema
95 
96  assert isinstance(schema, dict)
97 
98  return schema.get(key)
99 
100  return test_value
101 
102 
103 def test_all(policy: CategoryType, key: str) -> bool:
104  """Test if a policy has an ALL access for a specific key."""
105  if not isinstance(policy, dict):
106  return bool(policy)
107 
108  all_policy = policy.get(SUBCAT_ALL)
109 
110  if not isinstance(all_policy, dict):
111  return bool(all_policy)
112 
113  return all_policy.get(key, False) # type: ignore[no-any-return]
Callable[[str, str], bool|None] _gen_dict_test_func(PermissionLookup perm_lookup, LookupFunc lookup_func, SubCategoryDict lookup_dict)
Definition: util.py:86
Callable[[str, str], bool] compile_policy(CategoryType policy, SubCatLookupType subcategories, PermissionLookup perm_lookup)
Definition: util.py:27
ValueType lookup_all(PermissionLookup perm_lookup, SubCategoryDict lookup_dict, str object_id)
Definition: util.py:19
bool test_all(CategoryType policy, str key)
Definition: util.py:103