web/application.py
changeset 2058 7ef12c03447c
parent 1977 606923dff11b
child 2235 d5987f75c97c
equal deleted inserted replaced
2057:0a0cbccafcb5 2058:7ef12c03447c
     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')