http_handler.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. # -*- coding: utf-8 -*-
  2. from openerp import http
  3. from openerp.http import request
  4. from passlib.context import CryptContext
  5. import os
  6. import jwt
  7. import json
  8. from werkzeug.wrappers import Response
  9. RESOURCES_MAP = None
  10. JWT_SECRET_KEY = '@MjSk$2016?'
  11. JWT_HEADER = 'Authorization'
  12. JWT_HEADER_PREFIX = 'JWT'
  13. CRYPT_CONTEXT = CryptContext(['pbkdf2_sha512', 'md5_crypt'], deprecated=['md5_crypt'])
  14. with open(os.path.dirname(__file__) + '/resources.json') as resources:
  15. RESOURCES_MAP = json.load(resources)
  16. '''
  17. Class for manage authentication
  18. '''
  19. class Auth(http.Controller):
  20. # --------------------------------------------------------------------------
  21. # Generate JWT token based on username and password field
  22. # --------------------------------------------------------------------------
  23. @http.route(['/api/jwt'], type = 'http', auth = 'none', methods = ['POST'], cors = '*')
  24. def get_jwt(self, **args):
  25. try:
  26. user = request.env['res.users'].sudo().search([('login', '=', args['username']), ('active', '=', True)])
  27. if not user:
  28. return self.make_response({'error': 'invalid user'}) # bad request
  29. if not self.get_crypt_context().verify(args['password'], user.password_crypt):
  30. return self.make_response({'error': 'invalid password'}, status = 400) # bad request
  31. payload = {
  32. 'uid': user.id,
  33. 'password': args['password']
  34. }
  35. encoded = jwt.encode(payload, JWT_SECRET_KEY, algorithm = 'HS256')
  36. user.write({'jwt_token': encoded})
  37. return self.make_response({'token': encoded})
  38. except Exception, e:
  39. return self.make_response({'error': 'fields required'}, status = 400) # bad request
  40. # --------------------------------------------------------------------------
  41. # Check JWT token auth
  42. # --------------------------------------------------------------------------
  43. @http.route(['/api/check'], type = 'http', auth = 'none', methods = ['POST'], cors = '*')
  44. def check_token(self, **args):
  45. try:
  46. user = request.env['res.users'].sudo().search([('jwt_token', '=', args['token'])])
  47. if not user:
  48. return self.make_response({'error' : 'invalid token'}, status = 400) # bad request
  49. decoded = jwt.decode(args['token'], JWT_SECRET_KEY, algorithms = ['HS256'])
  50. if not self.get_crypt_context().verify(decoded['password'], user.password_crypt):
  51. return self.make_response({'error' : 'invalid token'}, status = 400) # bad request
  52. return self.make_response({'token': 'valid'})
  53. except Exception, e:
  54. return self.make_response({'error': 'token required'}, status = 400) # bad request
  55. # --------------------------------------------------------------------------
  56. # Get context for encryption
  57. # --------------------------------------------------------------------------
  58. def get_crypt_context(self):
  59. return CRYPT_CONTEXT
  60. # --------------------------------------------------------------------------
  61. # Make JSON response
  62. # --------------------------------------------------------------------------
  63. def make_response(self, data, status = 200):
  64. return Response(json.dumps(data), status = status, mimetype = 'application/json')
  65. '''
  66. Class for manage rest api interaction
  67. '''
  68. class ApiManager(http.Controller):
  69. # --------------------------------------------------------------------------
  70. # Restify your request
  71. # --------------------------------------------------------------------------
  72. @http.route([
  73. '/api/<any(customers, leads, opportunities):resource>',
  74. '/api/<any(customers, leads, opportunities):resource>/<int:uid>'
  75. ],
  76. type = 'http',
  77. auth = 'none',
  78. cors = '*')
  79. def restify(self, resource, uid = None):
  80. if not self.valid_token():
  81. return self.make_response({'error': 'denied resource'}, status = 403) # access denied
  82. if not self.resource_exists(resource):
  83. return self.make_response({'error': 'resource not available'}, status = 404) # not found
  84. http_verb = request.httprequest.method
  85. if http_verb == 'GET':
  86. return self.http_get(resource, uid)
  87. if http_verb == 'POST':
  88. return json.dumps({'verb': 'POST'}, sort_keys = True)
  89. if http_verb == 'PUT' or http_verb == 'PATCH':
  90. return json.dumps({'verb': 'PUT OR PATCH'})
  91. if http_verb == 'DELETE':
  92. return self.http_delete(resource, uid)
  93. return self.make_response({'error': 'method not allowed'}, status = 405) # method not allowed
  94. # --------------------------------------------------------------------------
  95. # Manage GET request
  96. # --------------------------------------------------------------------------
  97. def http_get(self, resource, uid):
  98. model, filters = self.resource_inflater(resource)
  99. data = []
  100. if uid != None:
  101. filters.append(('id', '=', uid))
  102. result = request.env[model].sudo().search(filters)
  103. for item in result:
  104. data.append(item.dump())
  105. return self.make_response(data);
  106. # --------------------------------------------------------------------------
  107. # Manage DELETE request
  108. # --------------------------------------------------------------------------
  109. def http_delete(self, resource, uid):
  110. if uid == None:
  111. return self.make_response({'error': 'uid not provided'})
  112. model, filters = self.resource_inflater(resource)
  113. result = request.env[model].sudo().browse(uid)
  114. if not result.exists():
  115. return self.make_response({'error': 'cannot be deleted'})
  116. return self.make_response({'response': result.unlink()})
  117. # --------------------------------------------------------------------------
  118. # Make JSON response
  119. # --------------------------------------------------------------------------
  120. def make_response(self, data, status = 200):
  121. return Response(json.dumps(data), status = status, mimetype = 'application/json')
  122. # --------------------------------------------------------------------------
  123. # Check if resource is available
  124. # --------------------------------------------------------------------------
  125. def resource_exists(self, resource):
  126. try:
  127. model = RESOURCES_MAP[resource]['module']
  128. module_name = model.replace('.', '_')
  129. module = request.env['ir.module.module'].sudo().search([('name', '=', module_name)])
  130. return True if module.state == 'installed' and len(module) != 0 else False
  131. except Exception, e:
  132. return False;
  133. # --------------------------------------------------------------------------
  134. # Manage JWT token validity
  135. # --------------------------------------------------------------------------
  136. def valid_token(self):
  137. try:
  138. auth_header = request.httprequest.headers[JWT_HEADER]
  139. if not auth_header.startswith(JWT_HEADER_PREFIX):
  140. return False
  141. jwt_token = auth_header[4:]
  142. if not jwt_token:
  143. return False
  144. user = request.env['res.users'].sudo().search([('jwt_token', '=', jwt_token)])
  145. if not user:
  146. return False
  147. decoded = jwt.decode(jwt_token, JWT_SECRET_KEY, algorithms = ['HS256'])
  148. if not self.get_crypt_context().verify(decoded['password'], user.password_crypt):
  149. return False
  150. return True
  151. except Exception, e:
  152. print e
  153. return False
  154. # --------------------------------------------------------------------------
  155. # Manage GET request
  156. # --------------------------------------------------------------------------
  157. def resource_inflater(self, resource):
  158. try:
  159. model = RESOURCES_MAP[resource]['model']
  160. filters = []
  161. for i in range(len(RESOURCES_MAP[resource]['filters'])):
  162. filters.append(tuple(RESOURCES_MAP[resource]['filters'][i]))
  163. return (model, filters)
  164. except Exception, e:
  165. return (None, None)
  166. # --------------------------------------------------------------------------
  167. # Get context for encryption
  168. # --------------------------------------------------------------------------
  169. def get_crypt_context(self):
  170. return CRYPT_CONTEXT