http_handler.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. # -*- coding: utf-8 -*-
  2. from openerp import http
  3. from openerp.http import request
  4. from passlib.context import CryptContext
  5. from werkzeug.wrappers import Response
  6. import os
  7. import jwt
  8. import json
  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'}, 400) # bad request
  29. if not self.get_crypt_context().verify(args['password'], user.password_crypt):
  30. return self.make_response({'error': 'invalid password'}, 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'}, 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'}, 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'}, 400) # bad request
  52. return self.make_response({'token': 'valid'})
  53. except Exception, e:
  54. return self.make_response({'error': 'token required'}, 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, content_type = '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': 'unauthorized resource'}, 401) # access denied
  82. if not self.resource_exists(resource):
  83. return self.make_response({'error': 'resource not available'}, 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'}, 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. headers = [('Content-Type', 'application/json')]
  122. if status == 401:
  123. headers.append(('WWW-Authenticate', 'JWT'))
  124. return Response(json.dumps(data), status = status, headers = headers)
  125. # --------------------------------------------------------------------------
  126. # Check if resource is available
  127. # --------------------------------------------------------------------------
  128. def resource_exists(self, resource):
  129. try:
  130. model = RESOURCES_MAP[resource]['module']
  131. module_name = model.replace('.', '_')
  132. module = request.env['ir.module.module'].sudo().search([('name', '=', module_name)])
  133. return True if module.state == 'installed' and len(module) != 0 else False
  134. except Exception, e:
  135. return False;
  136. # --------------------------------------------------------------------------
  137. # Manage JWT token validity
  138. # --------------------------------------------------------------------------
  139. def valid_token(self):
  140. try:
  141. print request.httprequest.headers
  142. auth_header = request.httprequest.headers[JWT_HEADER]
  143. if not auth_header.startswith(JWT_HEADER_PREFIX):
  144. return False
  145. jwt_token = auth_header[4:]
  146. if not jwt_token:
  147. return False
  148. user = request.env['res.users'].sudo().search([('jwt_token', '=', jwt_token)])
  149. if not user:
  150. return False
  151. decoded = jwt.decode(jwt_token, JWT_SECRET_KEY, algorithms = ['HS256'])
  152. if not self.get_crypt_context().verify(decoded['password'], user.password_crypt):
  153. return False
  154. return True
  155. except Exception, e:
  156. print e
  157. return False
  158. # --------------------------------------------------------------------------
  159. # Manage GET request
  160. # --------------------------------------------------------------------------
  161. def resource_inflater(self, resource):
  162. try:
  163. model = RESOURCES_MAP[resource]['model']
  164. filters = []
  165. for i in range(len(RESOURCES_MAP[resource]['filters'])):
  166. filters.append(tuple(RESOURCES_MAP[resource]['filters'][i]))
  167. return (model, filters)
  168. except Exception, e:
  169. return (None, None)
  170. # --------------------------------------------------------------------------
  171. # Get context for encryption
  172. # --------------------------------------------------------------------------
  173. def get_crypt_context(self):
  174. return CRYPT_CONTEXT