# -*- coding: utf-8 -*- from openerp import http from openerp.http import request from passlib.context import CryptContext import os import jwt import json from werkzeug.wrappers import Response RESOURCES_MAP = None JWT_SECRET_KEY = '@MjSk$2016?' JWT_HEADER = 'Authorization' JWT_HEADER_PREFIX = 'JWT' CRYPT_CONTEXT = CryptContext(['pbkdf2_sha512', 'md5_crypt'], deprecated=['md5_crypt']) with open(os.path.dirname(__file__) + '/resources.json') as resources: RESOURCES_MAP = json.load(resources) ''' Class for manage authentication ''' class Auth(http.Controller): # -------------------------------------------------------------------------- # Generate JWT token based on username and password field # -------------------------------------------------------------------------- @http.route(['/api/jwt'], type = 'http', auth = 'none', methods = ['POST'], cors = '*') def get_jwt(self, **args): try: user = request.env['res.users'].sudo().search([('login', '=', args['username']), ('active', '=', True)]) if not user: return self.make_response({'error': 'invalid user'}) # bad request if not self.get_crypt_context().verify(args['password'], user.password_crypt): return self.make_response({'error': 'invalid password'}, status = 400) # bad request payload = { 'uid': user.id, 'password': args['password'] } encoded = jwt.encode(payload, JWT_SECRET_KEY, algorithm = 'HS256') user.write({'jwt_token': encoded}) return self.make_response({'token': encoded}) except Exception, e: return self.make_response({'error': 'fields required'}, status = 400) # bad request # -------------------------------------------------------------------------- # Check JWT token auth # -------------------------------------------------------------------------- @http.route(['/api/check'], type = 'http', auth = 'none', methods = ['POST'], cors = '*') def check_token(self, **args): try: user = request.env['res.users'].sudo().search([('jwt_token', '=', args['token'])]) if not user: return self.make_response({'error' : 'invalid token'}, status = 400) # bad request decoded = jwt.decode(args['token'], JWT_SECRET_KEY, algorithms = ['HS256']) if not self.get_crypt_context().verify(decoded['password'], user.password_crypt): return self.make_response({'error' : 'invalid token'}, status = 400) # bad request return self.make_response({'token': 'valid'}) except Exception, e: return self.make_response({'error': 'token required'}, status = 400) # bad request # -------------------------------------------------------------------------- # Get context for encryption # -------------------------------------------------------------------------- def get_crypt_context(self): return CRYPT_CONTEXT # -------------------------------------------------------------------------- # Make JSON response # -------------------------------------------------------------------------- def make_response(self, data, status = 200): return Response(json.dumps(data), status = status, mimetype = 'application/json') ''' Class for manage rest api interaction ''' class ApiManager(http.Controller): # -------------------------------------------------------------------------- # Restify your request # -------------------------------------------------------------------------- @http.route([ '/api/', '/api//' ], type = 'http', auth = 'none', cors = '*') def restify(self, resource, uid = None): if not self.valid_token(): return self.make_response({'error': 'denied resource'}, status = 403) # access denied if not self.resource_exists(resource): return self.make_response({'error': 'resource not available'}, status = 404) # not found http_verb = request.httprequest.method if http_verb == 'GET': return self.http_get(resource, uid) if http_verb == 'POST': return json.dumps({'verb': 'POST'}, sort_keys = True) if http_verb == 'PUT' or http_verb == 'PATCH': return json.dumps({'verb': 'PUT OR PATCH'}) if http_verb == 'DELETE': return self.http_delete(resource, uid) return self.make_response({'error': 'method not allowed'}, status = 405) # method not allowed # -------------------------------------------------------------------------- # Manage GET request # -------------------------------------------------------------------------- def http_get(self, resource, uid): model, filters = self.resource_inflater(resource) data = [] if uid != None: filters.append(('id', '=', uid)) result = request.env[model].sudo().search(filters) for item in result: data.append(item.dump()) return self.make_response(data); # -------------------------------------------------------------------------- # Manage DELETE request # -------------------------------------------------------------------------- def http_delete(self, resource, uid): if uid == None: return self.make_response({'error': 'uid not provided'}) model, filters = self.resource_inflater(resource) result = request.env[model].sudo().browse(uid) if not result.exists(): return self.make_response({'error': 'cannot be deleted'}) return self.make_response({'response': result.unlink()}) # -------------------------------------------------------------------------- # Make JSON response # -------------------------------------------------------------------------- def make_response(self, data, status = 200): return Response(json.dumps(data), status = status, mimetype = 'application/json') # -------------------------------------------------------------------------- # Check if resource is available # -------------------------------------------------------------------------- def resource_exists(self, resource): try: model = RESOURCES_MAP[resource]['module'] module_name = model.replace('.', '_') module = request.env['ir.module.module'].sudo().search([('name', '=', module_name)]) return True if module.state == 'installed' and len(module) != 0 else False except Exception, e: return False; # -------------------------------------------------------------------------- # Manage JWT token validity # -------------------------------------------------------------------------- def valid_token(self): try: auth_header = request.httprequest.headers[JWT_HEADER] if not auth_header.startswith(JWT_HEADER_PREFIX): return False jwt_token = auth_header[4:] if not jwt_token: return False user = request.env['res.users'].sudo().search([('jwt_token', '=', jwt_token)]) if not user: return False decoded = jwt.decode(jwt_token, JWT_SECRET_KEY, algorithms = ['HS256']) if not self.get_crypt_context().verify(decoded['password'], user.password_crypt): return False return True except Exception, e: print e return False # -------------------------------------------------------------------------- # Manage GET request # -------------------------------------------------------------------------- def resource_inflater(self, resource): try: model = RESOURCES_MAP[resource]['model'] filters = [] for i in range(len(RESOURCES_MAP[resource]['filters'])): filters.append(tuple(RESOURCES_MAP[resource]['filters'][i])) return (model, filters) except Exception, e: return (None, None) # -------------------------------------------------------------------------- # Get context for encryption # -------------------------------------------------------------------------- def get_crypt_context(self): return CRYPT_CONTEXT