manager.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. from datetime import datetime
  2. import json
  3. import logging
  4. from typing import List, Optional, Union
  5. import uuid
  6. from werkzeug.security import generate_password_hash
  7. from .models import Permission, PermissionView, RegisterUser, Role, User, ViewMenu
  8. from ..manager import BaseSecurityManager
  9. from ... import const as c
  10. from ...models.mongoengine.interface import MongoEngineInterface
  11. log = logging.getLogger(__name__)
  12. class SecurityManager(BaseSecurityManager):
  13. """
  14. Responsible for authentication, registering security views,
  15. role and permission auto management
  16. If you want to change anything just inherit and override, then
  17. pass your own security manager to AppBuilder.
  18. """
  19. user_model = User
  20. """ Override to set your own User Model """
  21. role_model = Role
  22. """ Override to set your own User Model """
  23. permission_model = Permission
  24. viewmenu_model = ViewMenu
  25. permissionview_model = PermissionView
  26. registeruser_model = RegisterUser
  27. def __init__(self, appbuilder):
  28. """
  29. SecurityManager contructor
  30. param appbuilder:
  31. F.A.B AppBuilder main object
  32. """
  33. super(SecurityManager, self).__init__(appbuilder)
  34. log.warning("MongoEngine is deprecated and will be removed in version 5.")
  35. user_datamodel = MongoEngineInterface(self.user_model)
  36. if self.auth_type == c.AUTH_DB:
  37. self.userdbmodelview.datamodel = user_datamodel
  38. elif self.auth_type == c.AUTH_LDAP:
  39. self.userldapmodelview.datamodel = user_datamodel
  40. elif self.auth_type == c.AUTH_OID:
  41. self.useroidmodelview.datamodel = user_datamodel
  42. elif self.auth_type == c.AUTH_OAUTH:
  43. self.useroauthmodelview.datamodel = user_datamodel
  44. elif self.auth_type == c.AUTH_REMOTE_USER:
  45. self.userremoteusermodelview.datamodel = user_datamodel
  46. self.userstatschartview.datamodel = MongoEngineInterface(self.user_model)
  47. if self.auth_user_registration:
  48. self.registerusermodelview.datamodel = MongoEngineInterface(
  49. self.registeruser_model
  50. )
  51. self.rolemodelview.datamodel = MongoEngineInterface(self.role_model)
  52. self.permissionmodelview.datamodel = MongoEngineInterface(self.permission_model)
  53. self.viewmenumodelview.datamodel = MongoEngineInterface(self.viewmenu_model)
  54. self.permissionviewmodelview.datamodel = MongoEngineInterface(
  55. self.permissionview_model
  56. )
  57. self.create_db()
  58. def find_register_user(self, registration_hash):
  59. return self.registeruser_model.objects(
  60. registration_hash=registration_hash
  61. ).first()
  62. def add_register_user(
  63. self, username, first_name, last_name, email, password="", hashed_password=""
  64. ):
  65. try:
  66. register_user = self.registeruser_model()
  67. register_user.first_name = first_name
  68. register_user.last_name = last_name
  69. register_user.username = username
  70. register_user.email = email
  71. if hashed_password:
  72. register_user.password = hashed_password
  73. else:
  74. register_user.password = generate_password_hash(password)
  75. register_user.registration_hash = str(uuid.uuid1())
  76. register_user.save()
  77. return register_user
  78. except Exception as e:
  79. log.error(c.LOGMSG_ERR_SEC_ADD_REGISTER_USER, e)
  80. return False
  81. def del_register_user(self, register_user):
  82. try:
  83. register_user.delete()
  84. except Exception as e:
  85. log.error(c.LOGMSG_ERR_SEC_DEL_REGISTER_USER, e)
  86. def find_user(self, username=None, email=None):
  87. if username:
  88. return self.user_model.objects(username=username).first()
  89. elif email:
  90. return self.user_model.objects(email=email).first()
  91. def get_all_users(self):
  92. return User.objects
  93. def add_user(
  94. self,
  95. username,
  96. first_name,
  97. last_name,
  98. email,
  99. role,
  100. password="",
  101. hashed_password="",
  102. ):
  103. """
  104. Generic function to create user
  105. """
  106. try:
  107. user = self.user_model()
  108. user.first_name = first_name
  109. user.last_name = last_name
  110. user.username = username
  111. user.email = email
  112. user.active = True
  113. user.roles = role if isinstance(role, list) else [role]
  114. if hashed_password:
  115. user.password = hashed_password
  116. else:
  117. user.password = generate_password_hash(password)
  118. user.save()
  119. log.info(c.LOGMSG_INF_SEC_ADD_USER, username)
  120. return user
  121. except Exception as e:
  122. log.error(c.LOGMSG_ERR_SEC_ADD_USER, e)
  123. return False
  124. def count_users(self):
  125. return len(self.user_model.objects)
  126. def update_user(self, user):
  127. try:
  128. user.save()
  129. except Exception as e:
  130. log.error(c.LOGMSG_ERR_SEC_UPD_USER, e)
  131. return False
  132. def get_user_by_id(self, pk):
  133. return self.user_model.objects(pk=pk).first()
  134. def load_user(self, pk):
  135. return self.get_user_by_id(pk)
  136. """
  137. -----------------------
  138. PERMISSION MANAGEMENT
  139. -----------------------
  140. """
  141. def add_role(
  142. self, name: str, permissions: Optional[List[PermissionView]] = None
  143. ) -> Optional[Role]:
  144. if not permissions:
  145. permissions = []
  146. role = self.find_role(name)
  147. if role is None:
  148. try:
  149. role = self.role_model(name=name, permissions=permissions)
  150. role.save()
  151. log.info(c.LOGMSG_INF_SEC_ADD_ROLE, name)
  152. return role
  153. except Exception as e:
  154. log.error(c.LOGMSG_ERR_SEC_ADD_ROLE, e)
  155. return role
  156. def update_role(self, pk, name: str) -> Optional[Role]:
  157. try:
  158. role = self.role_model.objects(id=pk).update(name=name)
  159. log.info(c.LOGMSG_INF_SEC_UPD_ROLE, role)
  160. except Exception as e:
  161. log.error(c.LOGMSG_ERR_SEC_UPD_ROLE, e)
  162. return
  163. def find_role(self, name):
  164. return self.role_model.objects(name=name).first()
  165. def get_all_roles(self):
  166. return self.role_model.objects
  167. def get_public_permissions(self):
  168. role = self.find_role(self.auth_role_public)
  169. return role.permissions
  170. def find_permission(self, name):
  171. """
  172. Finds and returns a Permission by name
  173. """
  174. return self.permission_model.objects(name=name).first()
  175. def exist_permission_on_roles(
  176. self, view_name: str, permission_name: str, role_ids: List[int]
  177. ) -> bool:
  178. for role_id in role_ids:
  179. role = self.role_model.objects(id=role_id).first()
  180. permissions = role.permissions
  181. if permissions:
  182. for permission in permissions:
  183. if (view_name == permission.view_menu.name) and (
  184. permission_name == permission.permission.name
  185. ):
  186. return True
  187. return False
  188. def add_permission(self, name):
  189. """
  190. Adds a permission to the backend, model permission
  191. :param name:
  192. name of the permission: 'can_add','can_edit' etc...
  193. """
  194. perm = self.find_permission(name)
  195. if perm is None:
  196. try:
  197. perm = self.permission_model(name=name)
  198. perm.save()
  199. return perm
  200. except Exception as e:
  201. log.error(c.LOGMSG_ERR_SEC_ADD_PERMISSION, e)
  202. return perm
  203. def del_permission(self, name):
  204. """
  205. Deletes a permission from the backend, model permission
  206. :param name:
  207. name of the permission: 'can_add','can_edit' etc...
  208. """
  209. perm = self.find_permission(name)
  210. if perm:
  211. try:
  212. perm.delete()
  213. except Exception as e:
  214. log.error(c.LOGMSG_ERR_SEC_DEL_PERMISSION, e)
  215. """
  216. ----------------------
  217. PRIMITIVES VIEW MENU
  218. ----------------------
  219. """
  220. def find_view_menu(self, name):
  221. """
  222. Finds and returns a ViewMenu by name
  223. """
  224. return self.viewmenu_model.objects(name=name).first()
  225. def get_all_view_menu(self):
  226. return self.viewmenu_model.objects
  227. def add_view_menu(self, name):
  228. """
  229. Adds a view or menu to the backend, model view_menu
  230. param name:
  231. name of the view menu to add
  232. """
  233. view_menu = self.find_view_menu(name)
  234. if view_menu is None:
  235. try:
  236. view_menu = self.viewmenu_model(name=name)
  237. view_menu.save()
  238. return view_menu
  239. except Exception as e:
  240. log.error(c.LOGMSG_ERR_SEC_ADD_VIEWMENU, e)
  241. return view_menu
  242. def del_view_menu(self, name):
  243. """
  244. Deletes a ViewMenu from the backend
  245. :param name:
  246. name of the ViewMenu
  247. """
  248. obj = self.find_view_menu(name)
  249. if obj:
  250. try:
  251. obj.delete()
  252. except Exception as e:
  253. log.error(c.LOGMSG_ERR_SEC_DEL_PERMISSION, e)
  254. """
  255. ----------------------
  256. PERMISSION VIEW MENU
  257. ----------------------
  258. """
  259. def find_permission_view_menu(self, permission_name, view_menu_name):
  260. """
  261. Finds and returns a PermissionView by names
  262. """
  263. permission = self.find_permission(permission_name)
  264. view_menu = self.find_view_menu(view_menu_name)
  265. if permission and view_menu:
  266. return self.permissionview_model.objects(
  267. permission=permission, view_menu=view_menu
  268. ).first()
  269. def find_permissions_view_menu(self, view_menu):
  270. """
  271. Finds all permissions from ViewMenu, returns list of PermissionView
  272. :param view_menu: ViewMenu object
  273. :return: list of PermissionView objects
  274. """
  275. return self.permissionview_model.objects(view_menu=view_menu)
  276. def add_permission_view_menu(self, permission_name, view_menu_name):
  277. """
  278. Adds a permission on a view or menu to the backend
  279. :param permission_name:
  280. name of the permission to add: 'can_add','can_edit' etc...
  281. :param view_menu_name:
  282. name of the view menu to add
  283. """
  284. if not (permission_name and view_menu_name):
  285. return None
  286. pv = self.find_permission_view_menu(permission_name, view_menu_name)
  287. if pv:
  288. return pv
  289. vm = self.add_view_menu(view_menu_name)
  290. perm = self.add_permission(permission_name)
  291. pv = self.permissionview_model()
  292. pv.view_menu, pv.permission = vm, perm
  293. try:
  294. pv.save()
  295. log.info(c.LOGMSG_INF_SEC_ADD_PERMVIEW, pv)
  296. return pv
  297. except Exception as e:
  298. log.error(c.LOGMSG_ERR_SEC_ADD_PERMVIEW, e)
  299. def del_permission_view_menu(self, permission_name, view_menu_name, cascade=True):
  300. try:
  301. pv = self.find_permission_view_menu(permission_name, view_menu_name)
  302. # delete permission on view
  303. pv.delete()
  304. if not cascade:
  305. return
  306. # if no more permission on permission view, delete permission
  307. pv = self.permissionview_model.objects(permission=pv.permission)
  308. if not pv:
  309. self.del_permission(pv.permission.name)
  310. log.info(c.LOGMSG_INF_SEC_DEL_PERMVIEW, permission_name, view_menu_name)
  311. except Exception as e:
  312. log.error(c.LOGMSG_ERR_SEC_DEL_PERMVIEW, e)
  313. def exist_permission_on_views(self, lst, item):
  314. for i in lst:
  315. if i.permission.name == item:
  316. return True
  317. return False
  318. def exist_permission_on_view(self, lst, permission, view_menu):
  319. for i in lst:
  320. if i.permission.name == permission and i.view_menu.name == view_menu:
  321. return True
  322. return False
  323. def add_permission_role(self, role, perm_view):
  324. """
  325. Add permission-ViewMenu object to Role
  326. :param role:
  327. The role object
  328. :param perm_view:
  329. The PermissionViewMenu object
  330. """
  331. if perm_view and perm_view not in role.permissions:
  332. try:
  333. role.permissions.append(perm_view)
  334. role.save()
  335. log.info(c.LOGMSG_INF_SEC_ADD_PERMROLE, perm_view, role.name)
  336. except Exception as e:
  337. log.error(c.LOGMSG_ERR_SEC_ADD_PERMROLE, e)
  338. def del_permission_role(self, role, perm_view):
  339. """
  340. Remove permission-ViewMenu object to Role
  341. :param role:
  342. The role object
  343. :param perm_view:
  344. The PermissionViewMenu object
  345. """
  346. if perm_view in role.permissions:
  347. try:
  348. role.permissions.remove(perm_view)
  349. role.save()
  350. log.info(c.LOGMSG_INF_SEC_DEL_PERMROLE, perm_view, role.name)
  351. except Exception as e:
  352. log.error(c.LOGMSG_ERR_SEC_DEL_PERMROLE, e)
  353. def export_roles(
  354. self, path: Optional[str] = None, indent: Optional[Union[int, str]] = None
  355. ) -> None:
  356. """Exports roles to JSON file."""
  357. timestamp = datetime.now().strftime("%Y%m%dT%H%M%S")
  358. filename = path or f"roles_export_{timestamp}.json"
  359. roles = self.get_all_roles()
  360. serialized_roles = []
  361. for role in roles:
  362. serialized_role = {"name": role.name, "permissions": []}
  363. for pvm in role.permissions:
  364. permission = pvm.permission
  365. view_menu = pvm.view_menu
  366. permission_view_menu = {
  367. "permission": {"name": permission.name},
  368. "view_menu": {"name": view_menu.name},
  369. }
  370. serialized_role["permissions"].append(permission_view_menu)
  371. serialized_roles.append(serialized_role)
  372. with open(filename, "w") as fd:
  373. fd.write(json.dumps(serialized_roles, indent=indent))
  374. def import_roles(self, path: str) -> None:
  375. """Imports roles from JSON file."""
  376. with open(path, "r") as fd:
  377. serialized_roles = json.loads(fd.read())
  378. for role_kwargs in serialized_roles:
  379. role = self.add_role(role_kwargs["name"])
  380. permission_view_menus = [
  381. self.add_permission_view_menu(
  382. permission_name=pvm_kwargs["permission"]["name"],
  383. view_menu_name=pvm_kwargs["view_menu"]["name"],
  384. )
  385. for pvm_kwargs in role_kwargs["permissions"]
  386. ]
  387. role.permissions = permission_view_menus
  388. role.save()
  389. def get_first_user(self) -> "User":
  390. return self.user_model.objects.first()
  391. def noop_user_update(self, user: "User") -> None:
  392. pass