64e6636224e6d7f42c78e86fd909c8d5085863a3
[odoo/odoo.git] / openerp / addons / base / ir / ir_http.py
1 #----------------------------------------------------------
2 # ir_http modular http routing
3 #----------------------------------------------------------
4 import logging
5 import re
6 import sys
7
8 import werkzeug.exceptions
9 import werkzeug.routing
10 import werkzeug.urls
11 import werkzeug.utils
12
13 import openerp
14 import openerp.exceptions
15 import openerp.models
16 from openerp import http
17 from openerp.http import request
18 from openerp.osv import osv, orm
19
20 _logger = logging.getLogger(__name__)
21
22 UID_PLACEHOLDER = object()
23
24 class ModelConverter(werkzeug.routing.BaseConverter):
25
26     def __init__(self, url_map, model=False):
27         super(ModelConverter, self).__init__(url_map)
28         self.model = model
29         self.regex = '([0-9]+)'
30
31     def to_python(self, value):
32         m = re.match(self.regex, value)
33         return request.registry[self.model].browse(
34             request.cr, UID_PLACEHOLDER, int(m.group(1)), context=request.context)
35
36     def to_url(self, value):
37         return value.id
38
39 class ModelsConverter(werkzeug.routing.BaseConverter):
40
41     def __init__(self, url_map, model=False):
42         super(ModelsConverter, self).__init__(url_map)
43         self.model = model
44         # TODO add support for slug in the form [A-Za-z0-9-] bla-bla-89 -> id 89
45         self.regex = '([0-9,]+)'
46
47     def to_python(self, value):
48         return request.registry[self.model].browse(request.cr, UID_PLACEHOLDER, [int(i) for i in value.split(',')], context=request.context)
49
50     def to_url(self, value):
51         return ",".join(i.id for i in value)
52
53 class ir_http(osv.AbstractModel):
54     _name = 'ir.http'
55     _description = "HTTP routing"
56
57     def _get_converters(self):
58         return {'model': ModelConverter, 'models': ModelsConverter}
59
60     def _find_handler(self, return_rule=False):
61         return self.routing_map().bind_to_environ(request.httprequest.environ).match(return_rule=return_rule)
62
63     def _auth_method_user(self):
64         request.uid = request.session.uid
65         if not request.uid:
66             raise http.SessionExpiredException("Session expired")
67
68     def _auth_method_none(self):
69         request.uid = None
70
71     def _auth_method_public(self):
72         if not request.session.uid:
73             dummy, request.uid = self.pool['ir.model.data'].get_object_reference(request.cr, openerp.SUPERUSER_ID, 'base', 'public_user')
74         else:
75             request.uid = request.session.uid
76
77     def _authenticate(self, auth_method='user'):
78         try:
79             if request.session.uid:
80                 try:
81                     request.session.check_security()
82                     # what if error in security.check()
83                     #   -> res_users.check()
84                     #   -> res_users.check_credentials()
85                 except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException):
86                     # All other exceptions mean undetermined status (e.g. connection pool full),
87                     # let them bubble up
88                     request.session.logout(keep_db=True)
89             getattr(self, "_auth_method_%s" % auth_method)()
90         except (openerp.exceptions.AccessDenied, openerp.http.SessionExpiredException):
91             raise
92         except Exception:
93             _logger.exception("Exception during request Authentication.")
94             raise openerp.exceptions.AccessDenied()
95         return auth_method
96
97     def _handle_exception(self, exception):
98         # If handle_exception returns something different than None, it will be used as a response
99         try:
100             return request._handle_exception(exception)
101         except openerp.exceptions.AccessDenied:
102             return werkzeug.exceptions.Forbidden()
103
104     def _dispatch(self):
105         # locate the controller method
106         try:
107             rule, arguments = self._find_handler(return_rule=True)
108             func = rule.endpoint
109         except werkzeug.exceptions.NotFound, e:
110             return self._handle_exception(e)
111
112         # check authentication level
113         try:
114             auth_method = self._authenticate(func.routing["auth"])
115         except Exception as e:
116             return self._handle_exception(e)
117
118         processing = self._postprocess_args(arguments, rule)
119         if processing:
120             return processing
121
122
123         # set and execute handler
124         try:
125             request.set_handler(func, arguments, auth_method)
126             result = request.dispatch()
127             if isinstance(result, Exception):
128                 raise result
129         except Exception, e:
130             return self._handle_exception(e)
131
132         return result
133
134     def _postprocess_args(self, arguments, rule):
135         """ post process arg to set uid on browse records """
136         for name, arg in arguments.items():
137             if isinstance(arg, orm.browse_record) and arg._uid is UID_PLACEHOLDER:
138                 arguments[name] = arg.sudo(request.uid)
139                 try:
140                     arg.exists()
141                 except openerp.models.MissingError:
142                     return self._handle_exception(werkzeug.exceptions.NotFound())
143
144     def routing_map(self):
145         if not hasattr(self, '_routing_map'):
146             _logger.info("Generating routing map")
147             cr = request.cr
148             m = request.registry.get('ir.module.module')
149             ids = m.search(cr, openerp.SUPERUSER_ID, [('state', '=', 'installed'), ('name', '!=', 'web')], context=request.context)
150             installed = set(x['name'] for x in m.read(cr, 1, ids, ['name'], context=request.context))
151             if openerp.tools.config['test_enable']:
152                 installed.add(openerp.modules.module.current_test)
153             mods = [''] + openerp.conf.server_wide_modules + sorted(installed)
154             self._routing_map = http.routing_map(mods, False, converters=self._get_converters())
155
156         return self._routing_map
157
158 def convert_exception_to(to_type, with_message=False):
159     """ Should only be called from an exception handler. Fetches the current
160     exception data from sys.exc_info() and creates a new exception of type
161     ``to_type`` with the original traceback.
162
163     If ``with_message`` is ``True``, sets the new exception's message to be
164     the stringification of the original exception. If ``False``, does not
165     set the new exception's message. Otherwise, uses ``with_message`` as the
166     new exception's message.
167
168     :type with_message: str|bool
169     """
170     etype, original, tb = sys.exc_info()
171     try:
172         if with_message is False:
173             message = None
174         elif with_message is True:
175             message = str(original)
176         else:
177             message = str(with_message)
178
179         raise to_type, message, tb
180     except to_type, e:
181         return e
182
183 # vim:et: