authentik.tenants.flags
1from collections.abc import Callable 2from copy import copy 3from functools import wraps 4from typing import TYPE_CHECKING, Any, Literal 5 6from django.db import DatabaseError, InternalError, ProgrammingError 7 8from authentik.lib.utils.reflection import all_subclasses 9 10if TYPE_CHECKING: 11 from authentik.tenants.models import Tenant 12 13 14class Flag[T]: 15 default: T | None = None 16 visibility: Literal["none"] | Literal["public"] | Literal["authenticated"] = "none" 17 description: str | None = None 18 19 def __init_subclass__(cls, key: str, **kwargs): 20 cls.__key = key 21 22 @property 23 def key(self) -> str: 24 return self.__key 25 26 @classmethod 27 def get(cls) -> T | None: 28 from authentik.tenants.utils import get_current_tenant 29 30 flags = {} 31 try: 32 flags: dict[str, Any] = get_current_tenant(["flags"]).flags 33 except DatabaseError, ProgrammingError, InternalError: 34 pass 35 value = flags.get(cls.__key, None) 36 if value is None: 37 return cls().get_default() 38 return value 39 40 def get_default(self) -> T | None: 41 return self.default 42 43 @staticmethod 44 def available( 45 visibility: Literal["none"] | Literal["public"] | Literal["authenticated"] | None = None, 46 ): 47 flags = all_subclasses(Flag) 48 if visibility: 49 for flag in flags: 50 if flag.visibility == visibility: 51 yield flag 52 else: 53 yield from flags 54 55 56def patch_flag[T](flag: Flag[T], value: T): 57 """Decorator for tests to set a flag to a value""" 58 59 def wrapper_outer(func: Callable): 60 """Set a flag for a test""" 61 from authentik.tenants.utils import get_current_tenant 62 63 def cleanup(tenant: Tenant, flags: dict[str, Any]): 64 tenant.flags = flags 65 tenant.save() 66 67 @wraps(func) 68 def wrapper(*args, **kwargs): 69 tenant = get_current_tenant() 70 old_flags = copy(tenant.flags) 71 tenant.flags[flag().key] = value 72 tenant.save() 73 try: 74 res = func(*args, **kwargs) 75 cleanup(tenant, old_flags) 76 return res 77 finally: 78 cleanup(tenant, old_flags) 79 80 return wrapper 81 82 return wrapper_outer
class
Flag(typing.Generic[T]):
15class Flag[T]: 16 default: T | None = None 17 visibility: Literal["none"] | Literal["public"] | Literal["authenticated"] = "none" 18 description: str | None = None 19 20 def __init_subclass__(cls, key: str, **kwargs): 21 cls.__key = key 22 23 @property 24 def key(self) -> str: 25 return self.__key 26 27 @classmethod 28 def get(cls) -> T | None: 29 from authentik.tenants.utils import get_current_tenant 30 31 flags = {} 32 try: 33 flags: dict[str, Any] = get_current_tenant(["flags"]).flags 34 except DatabaseError, ProgrammingError, InternalError: 35 pass 36 value = flags.get(cls.__key, None) 37 if value is None: 38 return cls().get_default() 39 return value 40 41 def get_default(self) -> T | None: 42 return self.default 43 44 @staticmethod 45 def available( 46 visibility: Literal["none"] | Literal["public"] | Literal["authenticated"] | None = None, 47 ): 48 flags = all_subclasses(Flag) 49 if visibility: 50 for flag in flags: 51 if flag.visibility == visibility: 52 yield flag 53 else: 54 yield from flags
Abstract base class for generic types.
On Python 3.12 and newer, generic classes implicitly inherit from Generic when they declare a parameter list after the class's name::
class Mapping[KT, VT]:
def __getitem__(self, key: KT) -> VT:
...
# Etc.
On older versions of Python, however, generic classes have to explicitly inherit from Generic.
After a class has been declared to be generic, it can then be used as follows::
def lookup_name[KT, VT](mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
try:
return mapping[key]
except KeyError:
return default
@classmethod
def
get(cls) -> T | None:
27 @classmethod 28 def get(cls) -> T | None: 29 from authentik.tenants.utils import get_current_tenant 30 31 flags = {} 32 try: 33 flags: dict[str, Any] = get_current_tenant(["flags"]).flags 34 except DatabaseError, ProgrammingError, InternalError: 35 pass 36 value = flags.get(cls.__key, None) 37 if value is None: 38 return cls().get_default() 39 return value
57def patch_flag[T](flag: Flag[T], value: T): 58 """Decorator for tests to set a flag to a value""" 59 60 def wrapper_outer(func: Callable): 61 """Set a flag for a test""" 62 from authentik.tenants.utils import get_current_tenant 63 64 def cleanup(tenant: Tenant, flags: dict[str, Any]): 65 tenant.flags = flags 66 tenant.save() 67 68 @wraps(func) 69 def wrapper(*args, **kwargs): 70 tenant = get_current_tenant() 71 old_flags = copy(tenant.flags) 72 tenant.flags[flag().key] = value 73 tenant.save() 74 try: 75 res = func(*args, **kwargs) 76 cleanup(tenant, old_flags) 77 return res 78 finally: 79 cleanup(tenant, old_flags) 80 81 return wrapper 82 83 return wrapper_outer
Decorator for tests to set a flag to a value