You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
4.0 KiB

5 years ago
  1. # -*- coding: utf-8 -*-
  2. import json
  3. import logging
  4. from functools import wraps
  5. from odoo import http
  6. import werkzeug
  7. _logger = logging.getLogger(__name__)
  8. class ApiException(Exception):
  9. INVALID_REQUEST = 'invalid_request'
  10. def __init__(self, message, code=None):
  11. super(Exception, self).__init__(message)
  12. self.code = code if code else self.INVALID_REQUEST
  13. def to_json(self):
  14. return {
  15. 'error': self.code,
  16. 'error_message': self.message
  17. }
  18. def resource(path, method, auth='user', clients=None):
  19. assert auth in ['user', 'client']
  20. def endpoint_decorator(func):
  21. @http.route(path, auth='public', type='http', methods=[method, 'OPTIONS'], csrf=False)
  22. @wraps(func)
  23. def func_wrapper(self, req, **query):
  24. cors_headers = {
  25. 'Access-Control-Allow-Origin': '*',
  26. 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-Debug-Mode, Authorization',
  27. 'Access-Control-Max-Age': 60 * 60 * 24,
  28. 'Access-Control-Allow-Methods': 'OPTIONS, HEAD, GET, POST, PUT, DELETE'
  29. }
  30. if req.httprequest.method == 'OPTIONS':
  31. return http.Response(
  32. status=200,
  33. headers=cors_headers
  34. )
  35. try:
  36. access_token = None
  37. if 'Authorization' in req.httprequest.headers:
  38. authorization_header = req.httprequest.headers['Authorization']
  39. if authorization_header[:7] == 'Bearer ':
  40. access_token = authorization_header.split(' ', 1)[1]
  41. if access_token is None:
  42. access_token = query.get('access_token')
  43. if not access_token:
  44. raise ApiException(
  45. 'access_token param is missing',
  46. 'invalid_request',
  47. )
  48. if auth == 'user':
  49. token = req.env['galicea_openid_connect.access_token'].sudo().search(
  50. [('token', '=', access_token)]
  51. )
  52. if not token:
  53. raise ApiException(
  54. 'access_token is invalid',
  55. 'invalid_request',
  56. )
  57. req.uid = token.user_id.id
  58. elif auth == 'client':
  59. token = req.env['galicea_openid_connect.client_access_token'].sudo().search(
  60. [('token', '=', access_token)]
  61. )
  62. if not token:
  63. raise ApiException(
  64. 'access_token is invalid',
  65. 'invalid_request',
  66. )
  67. req.uid = token.client_id.system_user_id.id
  68. if clients:
  69. if token.client_id.id not in map(lambda c: req.env.ref(c).id, clients):
  70. raise ApiException('Access denied', 'restricted_app')
  71. ctx = req.context.copy()
  72. ctx.update({'client_id': token.client_id.id})
  73. req.context = ctx
  74. response = func(self, req, **query)
  75. return werkzeug.Response(
  76. response=json.dumps(response),
  77. headers=cors_headers,
  78. status=200
  79. )
  80. except Exception as e:
  81. status = 400
  82. if not isinstance(e, ApiException):
  83. _logger.exception('Unexpected exception while processing API request')
  84. e = ApiException('Unexpected server error', 'server_error')
  85. status = 500
  86. return werkzeug.Response(
  87. response=json.dumps(e.to_json()),
  88. status=status,
  89. headers=cors_headers
  90. )
  91. return func_wrapper
  92. return endpoint_decorator