8 __docformat__ = "restructuredtext en" |
8 __docformat__ = "restructuredtext en" |
9 |
9 |
10 import sys |
10 import sys |
11 from time import clock, time |
11 from time import clock, time |
12 |
12 |
|
13 from logilab.common.deprecation import obsolete |
|
14 |
13 from rql import BadRQLQuery |
15 from rql import BadRQLQuery |
14 |
16 |
15 from cubicweb import set_log_methods |
17 from cubicweb import set_log_methods, cwvreg |
16 from cubicweb import (ValidationError, Unauthorized, AuthenticationError, |
18 from cubicweb import ( |
17 NoSelectableObject, RepositoryError) |
19 ValidationError, Unauthorized, AuthenticationError, NoSelectableObject, |
18 from cubicweb.cwvreg import CubicWebRegistry |
20 RepositoryError) |
19 from cubicweb.web import (LOGGER, StatusResponse, DirectResponse, Redirect, NotFound, |
21 from cubicweb.web import LOGGER, component |
20 RemoteCallFailed, ExplicitLogin, InvalidSession) |
22 from cubicweb.web import ( |
|
23 StatusResponse, DirectResponse, Redirect, NotFound, |
|
24 RemoteCallFailed, ExplicitLogin, InvalidSession) |
21 from cubicweb.web.component import Component |
25 from cubicweb.web.component import Component |
22 |
26 |
23 # make session manager available through a global variable so the debug view can |
27 # make session manager available through a global variable so the debug view can |
24 # print information about web session |
28 # print information about web session |
25 SESSION_MANAGER = None |
29 SESSION_MANAGER = None |
26 |
30 |
27 class AbstractSessionManager(Component): |
31 class AbstractSessionManager(component.Component): |
28 """manage session data associated to a session identifier""" |
32 """manage session data associated to a session identifier""" |
29 id = 'sessionmanager' |
33 id = 'sessionmanager' |
30 |
34 |
31 def __init__(self): |
35 def __init__(self): |
32 self.session_time = self.vreg.config['http-session-time'] or None |
36 self.session_time = self.vreg.config['http-session-time'] or None |
36 self.cleanup_anon_session_time = self.vreg.config['cleanup-anonymous-session-time'] or 120 |
40 self.cleanup_anon_session_time = self.vreg.config['cleanup-anonymous-session-time'] or 120 |
37 assert self.cleanup_anon_session_time > 0 |
41 assert self.cleanup_anon_session_time > 0 |
38 if self.session_time: |
42 if self.session_time: |
39 assert self.cleanup_session_time < self.session_time |
43 assert self.cleanup_session_time < self.session_time |
40 assert self.cleanup_anon_session_time < self.session_time |
44 assert self.cleanup_anon_session_time < self.session_time |
41 self.authmanager = self.vreg.select_component('authmanager') |
45 self.authmanager = self.vreg.select('components', 'authmanager') |
42 assert self.authmanager, 'no authentication manager found' |
|
43 |
46 |
44 def clean_sessions(self): |
47 def clean_sessions(self): |
45 """cleanup sessions which has not been unused since a given amount of |
48 """cleanup sessions which has not been unused since a given amount of |
46 time. Return the number of sessions which have been closed. |
49 time. Return the number of sessions which have been closed. |
47 """ |
50 """ |
85 corrupted...) |
88 corrupted...) |
86 """ |
89 """ |
87 raise NotImplementedError() |
90 raise NotImplementedError() |
88 |
91 |
89 |
92 |
90 class AbstractAuthenticationManager(Component): |
93 class AbstractAuthenticationManager(component.Component): |
91 """authenticate user associated to a request and check session validity""" |
94 """authenticate user associated to a request and check session validity""" |
92 id = 'authmanager' |
95 id = 'authmanager' |
93 |
96 |
94 def authenticate(self, req): |
97 def authenticate(self, req): |
95 """authenticate user and return corresponding user object |
98 """authenticate user and return corresponding user object |
108 identifier |
111 identifier |
109 """ |
112 """ |
110 SESSION_VAR = '__session' |
113 SESSION_VAR = '__session' |
111 |
114 |
112 def __init__(self, appli): |
115 def __init__(self, appli): |
113 self.session_manager = appli.vreg.select_component('sessionmanager') |
116 self.session_manager = appli.vreg.select('components', 'sessionmanager') |
114 assert self.session_manager, 'no session manager found' |
|
115 global SESSION_MANAGER |
117 global SESSION_MANAGER |
116 SESSION_MANAGER = self.session_manager |
118 SESSION_MANAGER = self.session_manager |
117 if not 'last_login_time' in appli.vreg.schema: |
119 if not 'last_login_time' in appli.vreg.schema: |
118 self._update_last_login_time = lambda x: None |
120 self._update_last_login_time = lambda x: None |
119 |
121 |
207 req.remove_cookie(req.get_cookie(), self.SESSION_VAR) |
209 req.remove_cookie(req.get_cookie(), self.SESSION_VAR) |
208 raise AuthenticationError() |
210 raise AuthenticationError() |
209 |
211 |
210 |
212 |
211 class CubicWebPublisher(object): |
213 class CubicWebPublisher(object): |
212 """Central registry for the web application. This is one of the central |
214 """the publisher is a singleton hold by the web frontend, and is responsible |
213 object in the web application, coupling dynamically loaded objects with |
215 to publish HTTP request. |
214 the application's schema and the application's configuration objects. |
|
215 |
|
216 It specializes the VRegistry by adding some convenience methods to |
|
217 access to stored objects. Currently we have the following registries |
|
218 of objects known by the web application (library may use some others |
|
219 additional registries): |
|
220 * controllers, which are directly plugged into the application |
|
221 object to handle request publishing |
|
222 * views |
|
223 * templates |
|
224 * components |
|
225 * actions |
|
226 """ |
216 """ |
227 |
217 |
228 def __init__(self, config, debug=None, |
218 def __init__(self, config, debug=None, |
229 session_handler_fact=CookieSessionHandler, |
219 session_handler_fact=CookieSessionHandler, |
230 vreg=None): |
220 vreg=None): |
231 super(CubicWebPublisher, self).__init__() |
221 super(CubicWebPublisher, self).__init__() |
232 # connect to the repository and get application's schema |
222 # connect to the repository and get application's schema |
233 if vreg is None: |
223 if vreg is None: |
234 vreg = CubicWebRegistry(config, debug=debug) |
224 vreg = cwvreg.CubicWebRegistry(config, debug=debug) |
235 self.vreg = vreg |
225 self.vreg = vreg |
236 self.info('starting web application from %s', config.apphome) |
226 self.info('starting web application from %s', config.apphome) |
237 self.repo = config.repository(vreg) |
227 self.repo = config.repository(vreg) |
238 if not vreg.initialized: |
228 if not vreg.initialized: |
239 self.config.init_cubes(self.repo.get_cubes()) |
229 self.config.init_cubes(self.repo.get_cubes()) |
248 else: |
238 else: |
249 self._query_log = None |
239 self._query_log = None |
250 self.publish = self.main_publish |
240 self.publish = self.main_publish |
251 # instantiate session and url resolving helpers |
241 # instantiate session and url resolving helpers |
252 self.session_handler = session_handler_fact(self) |
242 self.session_handler = session_handler_fact(self) |
253 self.url_resolver = vreg.select_component('urlpublisher') |
243 self.url_resolver = vreg.select('components', 'urlpublisher') |
254 |
244 |
255 def connect(self, req): |
245 def connect(self, req): |
256 """return a connection for a logged user object according to existing |
246 """return a connection for a logged user object according to existing |
257 sessions (i.e. a new connection may be created or an already existing |
247 sessions (i.e. a new connection may be created or an already existing |
258 one may be reused |
248 one may be reused |
259 """ |
249 """ |
260 self.session_handler.set_session(req) |
250 self.session_handler.set_session(req) |
261 |
|
262 def select_controller(self, oid, req): |
|
263 """return the most specific view according to the resultset""" |
|
264 vreg = self.vreg |
|
265 try: |
|
266 return vreg.select(vreg.registry_objects('controllers', oid), |
|
267 req=req, appli=self) |
|
268 except NoSelectableObject: |
|
269 raise Unauthorized(req._('not authorized')) |
|
270 |
251 |
271 # publish methods ######################################################### |
252 # publish methods ######################################################### |
272 |
253 |
273 def log_publish(self, path, req): |
254 def log_publish(self, path, req): |
274 """wrapper around _publish to log all queries executed for a given |
255 """wrapper around _publish to log all queries executed for a given |
290 except Exception: |
271 except Exception: |
291 self.exception('error while logging queries') |
272 self.exception('error while logging queries') |
292 finally: |
273 finally: |
293 self._logfile_lock.release() |
274 self._logfile_lock.release() |
294 |
275 |
|
276 @obsolete("use vreg.select('controllers', ...)") |
|
277 def select_controller(self, oid, req): |
|
278 try: |
|
279 controller = self.vreg.select('controllers', oid, req=req, |
|
280 appli=self) |
|
281 except NoSelectableObject: |
|
282 raise Unauthorized(req._('not authorized')) |
|
283 |
295 def main_publish(self, path, req): |
284 def main_publish(self, path, req): |
296 """method called by the main publisher to process <path> |
285 """method called by the main publisher to process <path> |
297 |
286 |
298 should return a string containing the resulting page or raise a |
287 should return a string containing the resulting page or raise a |
299 `NotFound` exception |
288 `NotFound` exception |
314 # to avoid callbacks being unregistered before they could be called) |
303 # to avoid callbacks being unregistered before they could be called) |
315 tstart = clock() |
304 tstart = clock() |
316 try: |
305 try: |
317 try: |
306 try: |
318 ctrlid, rset = self.url_resolver.process(req, path) |
307 ctrlid, rset = self.url_resolver.process(req, path) |
319 controller = self.select_controller(ctrlid, req) |
308 try: |
|
309 controller = self.vreg.select('controllers', ctrlid, req, |
|
310 appli=self) |
|
311 except NoSelectableObject: |
|
312 raise Unauthorized(req._('not authorized')) |
320 req.update_search_state() |
313 req.update_search_state() |
321 result = controller.publish(rset=rset) |
314 result = controller.publish(rset=rset) |
322 if req.cnx is not None: |
315 if req.cnx is not None: |
323 # req.cnx is None if anonymous aren't allowed and we are |
316 # req.cnx is None if anonymous aren't allowed and we are |
324 # displaying the cookie authentication form |
317 # displaying the cookie authentication form |
382 try: |
375 try: |
383 req.data['ex'] = ex |
376 req.data['ex'] = ex |
384 if tb: |
377 if tb: |
385 req.data['excinfo'] = excinfo |
378 req.data['excinfo'] = excinfo |
386 req.form['vid'] = 'error' |
379 req.form['vid'] = 'error' |
387 errview = self.vreg.select_view('error', req, None) |
380 errview = self.vreg.select('views', 'error', req) |
388 template = self.main_template_id(req) |
381 template = self.main_template_id(req) |
389 content = self.vreg.main_template(req, template, view=errview) |
382 content = self.vreg.main_template(req, template, view=errview) |
390 except: |
383 except: |
391 content = self.vreg.main_template(req, 'error-template') |
384 content = self.vreg.main_template(req, 'error-template') |
392 raise StatusResponse(500, content) |
385 raise StatusResponse(500, content) |
397 def loggedout_content(self, req): |
390 def loggedout_content(self, req): |
398 return self.vreg.main_template(req, 'loggedout') |
391 return self.vreg.main_template(req, 'loggedout') |
399 |
392 |
400 def notfound_content(self, req): |
393 def notfound_content(self, req): |
401 req.form['vid'] = '404' |
394 req.form['vid'] = '404' |
402 view = self.vreg.select_view('404', req, None) |
395 view = self.vreg.select('views', '404', req) |
403 template = self.main_template_id(req) |
396 template = self.main_template_id(req) |
404 return self.vreg.main_template(req, template, view=view) |
397 return self.vreg.main_template(req, template, view=view) |
405 |
398 |
406 def main_template_id(self, req): |
399 def main_template_id(self, req): |
407 template = req.property_value('ui.main-template') |
400 template = req.property_value('ui.main-template') |