A multipurpose python flask API server and administration SPA
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
4.2 KiB

  1. """Role service for Corvus."""
  2. from collections import defaultdict
  3. from enum import Enum
  4. from typing import Optional, List, Set, Dict
  5. class Role(Enum):
  6. """User role definitions."""
  7. OWNER = 'OWNER'
  8. ADMIN = 'ADMIN'
  9. AUDITOR = 'AUDITOR'
  10. MODERATOR = 'MODERATOR'
  11. USER = 'USER'
  12. ANONYMOUS = 'ANONYMOUS'
  13. NONE = 'NONE'
  14. def __str__(self) -> str:
  15. """Return the value of the enum."""
  16. return self.value
  17. class RoleTree(defaultdict):
  18. """Simple tree structure to handle hierarchy."""
  19. def __call__(self, data: Role, power: int) -> 'RoleTree':
  20. """Handle direct calls to the tree."""
  21. return RoleTree(self, data, power)
  22. parent: Optional['RoleTree']
  23. data: Role
  24. power: int
  25. roles: Dict[Role, List['RoleTree']]
  26. def __init__(
  27. self,
  28. parent: Optional['RoleTree'],
  29. data: Role,
  30. power: int = None,
  31. **kwargs: dict) -> None:
  32. """Configure a RoleTree."""
  33. super().__init__(**kwargs)
  34. self.parent: Optional[RoleTree] = parent
  35. self.data: Role = data
  36. self.power: int = power if power is not None else 1
  37. self.default_factory = self # type: ignore
  38. self.roles: Dict[Role, List[RoleTree]] = {data: [self]}
  39. def populate(
  40. self, children: Dict[Role, Optional[dict]]) -> List['RoleTree']:
  41. """Populate a RoleTree from a dictionary of a Role hierarchy."""
  42. role_list: List[RoleTree] = [self]
  43. for child_role in children.keys():
  44. element = children[child_role]
  45. new_node = self(child_role, self.power + 1)
  46. if isinstance(element, dict) and element:
  47. role_list.extend(new_node.populate(element))
  48. else:
  49. role_list.append(new_node)
  50. self[child_role] = new_node
  51. for role_tree in role_list:
  52. if role_tree.data not in self.roles.keys():
  53. self.roles[role_tree.data] = []
  54. self.roles[role_tree.data].append(role_tree)
  55. return role_list
  56. def find_role(self, request_role: Role) -> List['RoleTree']:
  57. """Identify all instances of a role."""
  58. try:
  59. return self.roles[request_role]
  60. except KeyError:
  61. return []
  62. def get_parent_roles(self) -> List[Role]:
  63. """Return all the roles from self to the highest parent."""
  64. if self.parent is not None:
  65. return [self.data] + self.parent.get_parent_roles()
  66. return [self.data]
  67. def get_children_roles(self) -> List[Role]:
  68. """Return all the roles from self to the lowest child."""
  69. if self.roles and (
  70. len(self.roles.keys()) > 1 or len(self.roles[self.data]) > 1):
  71. child_roles = [self.data]
  72. for role in self.roles.keys():
  73. for role_tree in self.roles[role]:
  74. if role_tree.data != self.data:
  75. child_roles.extend(role_tree.get_children_roles())
  76. return child_roles
  77. return [self.data]
  78. def find_roles_in_hierarchy(self, request_role: Role) -> Set[Role]:
  79. """Find a set of all roles that fall within the hierarchy."""
  80. roles: List[Role] = []
  81. role_trees = self.find_role(request_role)
  82. for role_tree in role_trees:
  83. roles.extend(role_tree.get_parent_roles())
  84. return set(roles)
  85. def find_children_roles(self, request_role: Role) -> Set[Role]:
  86. """Find all children roles, including this role."""
  87. roles: List[Role] = []
  88. role_trees = self.find_role(request_role)
  89. for role_tree in role_trees:
  90. roles.extend(role_tree.get_children_roles())
  91. return set(roles)
  92. def __str__(self) -> str:
  93. """Represent the tree with the value of the node."""
  94. return 'RoleTree.%s(%d)' % (self.data, self.power)
  95. ROLES = RoleTree(None, Role.OWNER, 0)
  96. ROLE_LIST = sorted(
  97. ROLES.populate({
  98. Role.ADMIN: {
  99. Role.MODERATOR: {
  100. Role.USER: {
  101. Role.ANONYMOUS: None
  102. }
  103. },
  104. Role.AUDITOR: {
  105. Role.USER: None
  106. }
  107. }
  108. }),
  109. key=lambda rt: rt.power)