view_manager.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. from array import array
  2. from msilib.schema import Class
  3. from re import I
  4. import threading
  5. from tkinter.ttk import Separator
  6. from typing import Type
  7. from xmlrpc.client import boolean
  8. from flask import session,Flask,request, jsonify
  9. from flask_restful import Resource, Api
  10. import flask_restful
  11. from itsdangerous import json
  12. from sqlalchemy import delete
  13. from utility.app_logging import logger_name
  14. import logging
  15. import persistence
  16. import Model.isp_model as isp_model
  17. import Model.model_manager as model_manager
  18. from werkzeug.security import check_password_hash
  19. import anytree
  20. import View.view_privilege as privileges
  21. from datetime import timedelta
  22. from flask_session import Session
  23. import View.view_error_management as view_error_management
  24. logger = logging.getLogger(logger_name + ".VIEW")
  25. __app__ = Flask("OpenIsp")
  26. __app__.secret_key = "aseqzdwxc"
  27. __app__.permanent_session_lifetime = timedelta(minutes=2)
  28. __app__.config["SESSION_PERMANENT"] = False
  29. __app__.config["SESSION_TYPE"] = "filesystem"
  30. __resource_array__ : array
  31. __id_counter__ : int = 1
  32. from werkzeug.serving import make_server
  33. class ServerThread(threading.Thread):
  34. def __init__(self, app,ip,port):
  35. threading.Thread.__init__(self)
  36. self.server = make_server(ip, port, app)
  37. self.ctx = app.app_context()
  38. self.ctx.push()
  39. def run(self):
  40. logger.info('starting server')
  41. self.server.serve_forever()
  42. def shutdown(self):
  43. self.server.shutdown()
  44. __server_process__ : ServerThread
  45. def get_roles_ids() :
  46. return session["roles_ids"]
  47. def init() :
  48. @__app__.before_request
  49. def before_request_func():
  50. print(request.headers.__dict__)
  51. global __id_counter__
  52. logger.debug("before_request is running!")
  53. if not "client_id" in session :
  54. session["client_id"] = str(__id_counter__)
  55. logger.debug("client_id is " + session["client_id"])
  56. __id_counter__ = __id_counter__ + 1
  57. if not request.path == "/api/login" and not "username" in session :
  58. logger.warning("Unauthorized client with id " + session["client_id"] + " try to access application")
  59. resp = jsonify({'message' : 'Unauthorized'})
  60. resp.status_code = 401
  61. return resp
  62. if "username" in session :
  63. logger.debug("request from " + session["username"])
  64. from flask.logging import default_handler
  65. __app__.logger.removeHandler(default_handler)
  66. privileges.init()
  67. view_error_management.define_error_management(__app__)
  68. @__app__.route('/api/login',methods = ['POST'])
  69. def login():
  70. _json = request.json
  71. _username = _json['username']
  72. _password = _json['password']
  73. with persistence.get_Session_Instance() as sess :
  74. Item = sess.query(isp_model.user_account).filter(isp_model.user_account.nickname == _username).first()
  75. if not isinstance(Item,isp_model.user_account) :
  76. logger.warning("user tried to login with unknown account name : " + _username)
  77. resp = jsonify({'message' : 'Bad Request - user account not found'})
  78. resp.status_code = 400
  79. return resp
  80. if not check_password_hash(Item.password,_password) :
  81. logger.warning("user with account name '" + _username + "' tried to login with invalid password")
  82. resp = jsonify({'message' : 'Bad Request - invalid password for this account'})
  83. resp.status_code = 400
  84. return resp
  85. session["username"] = _username
  86. session["account_data"] = model_manager.ModelObjectToJsonString(Item)
  87. session["roles_ids"] = [privileges.inventory_read_only_role.id]
  88. logger.info("account " + _username + " logged IN successfully with id : " + session["client_id"])
  89. resp = jsonify({'message' : 'login successful'})
  90. resp.status_code = 200
  91. return resp
  92. @__app__.route('/api/logout',methods = ['POST'])
  93. def logout():
  94. logger.info("account " + session["username"] + " logged OUT with id : " + session["client_id"])
  95. session.clear()
  96. return jsonify('logout')
  97. @__app__.route('/routes',methods = ['GET'])
  98. @privileges.manager.require_authorization(required_role=privileges.inventory_admin_role,ids_getter=get_roles_ids)
  99. def routes():
  100. routes = []
  101. for route in __app__.url_map.iter_rules():
  102. routes.append('%s' % route)
  103. return jsonify(routes)
  104. def run() :
  105. global __server_process__
  106. __server_process__ = ServerThread(__app__,"0.0.0.0",8000)
  107. __server_process__.start()
  108. logger.info('View server started')
  109. def stop() :
  110. global __server_process__
  111. __server_process__.shutdown()
  112. logger.info('View server stopped')
  113. class ResourceNode(anytree.NodeMixin) :
  114. def __init__(self, url_key : str, resource = None, parent=None, children=None):
  115. super(ResourceNode, self).__init__()
  116. self.url_key = url_key
  117. self.parent = parent
  118. if children:
  119. self.children = children
  120. if resource :
  121. self.resource = resource
  122. # we check if the resource class has an http API method
  123. get = getattr(self, "get", None)
  124. post = getattr(self, "post", None)
  125. delete = getattr(self, "delete", None)
  126. update = getattr(self, "update", None)
  127. patch = getattr(self, "patch", None)
  128. if not callable(get) and not callable(post) \
  129. and not callable(delete) and not callable(update) \
  130. and not callable(patch) :
  131. raise BaseException("class does have an api class")
  132. url_slice = None
  133. resource = None
  134. def add_resource_tree(node : ResourceNode) :
  135. if not node.is_root() :
  136. raise BaseException("node is not a root node")
  137. logger.debug("adding resource tree for API")
  138. logger.debug(anytree.RenderTree(node, style=anytree.AsciiStyle()).by_attr("url_slice"))
  139. for node in anytree.PreOrderIter(node) :
  140. logger.debug("iterating though node " + node.url_key)
  141. if node.resource is not None :
  142. logger.debug("resource found")
  143. url = ""
  144. parent : ResourceNode
  145. for parent in node.ancestors :
  146. url = url + node.separator + parent.url_key
  147. print("url built for the node : " + url)
  148. __api__.add_resource(node.resource, url)
  149. __resource_array__.append(node)
  150. #node.ancestors ( all parent )
  151. def delete_resource_tree(node : ResourceNode, cascade : boolean = True) :
  152. if not cascade and not node.is_leaf() :
  153. raise BaseException("cannot delete only a node when it's not a leaf !")
  154. raise BaseException("Not implemented yet") #TODO