server/repository.py
changeset 5590 a56eb02f9ce7
parent 5557 1a534c596bff
parent 5587 72679e450f6d
child 5627 a7e40cccdc9b
equal deleted inserted replaced
5578:6b9fee0c5c42 5590:a56eb02f9ce7
    23 
    23 
    24 * brings these classes all together to provide a single access
    24 * brings these classes all together to provide a single access
    25   point to a cubicweb instance.
    25   point to a cubicweb instance.
    26 * handles session management
    26 * handles session management
    27 * provides method for pyro registration, to call if pyro is enabled
    27 * provides method for pyro registration, to call if pyro is enabled
    28 
       
    29 
       
    30 """
    28 """
       
    29 
    31 from __future__ import with_statement
    30 from __future__ import with_statement
    32 
    31 
    33 __docformat__ = "restructuredtext en"
    32 __docformat__ = "restructuredtext en"
    34 
    33 
    35 import sys
    34 import sys
       
    35 import threading
    36 import Queue
    36 import Queue
    37 from os.path import join
    37 from os.path import join
    38 from datetime import datetime
    38 from datetime import datetime
    39 from time import time, localtime, strftime
    39 from time import time, localtime, strftime
    40 
    40 
   307         self._available_pools.put_nowait(pool)
   307         self._available_pools.put_nowait(pool)
   308 
   308 
   309     def pinfo(self):
   309     def pinfo(self):
   310         # XXX: session.pool is accessed from a local storage, would be interesting
   310         # XXX: session.pool is accessed from a local storage, would be interesting
   311         #      to see if there is a pool set in any thread specific data)
   311         #      to see if there is a pool set in any thread specific data)
   312         import threading
       
   313         return '%s: %s (%s)' % (self._available_pools.qsize(),
   312         return '%s: %s (%s)' % (self._available_pools.qsize(),
   314                                 ','.join(session.user.login for session in self._sessions.values()
   313                                 ','.join(session.user.login for session in self._sessions.values()
   315                                          if session.pool),
   314                                          if session.pool),
   316                                 threading.currentThread())
   315                                 threading.currentThread())
   317     def shutdown(self):
   316     def shutdown(self):
   354             self.info('sql cache usage: %s/%s (%s%%)', hits+ misses, nocache,
   353             self.info('sql cache usage: %s/%s (%s%%)', hits+ misses, nocache,
   355                       ((hits + misses) * 100) / (hits + misses + nocache))
   354                       ((hits + misses) * 100) / (hits + misses + nocache))
   356         except ZeroDivisionError:
   355         except ZeroDivisionError:
   357             pass
   356             pass
   358 
   357 
       
   358     def _login_from_email(self, login):
       
   359         session = self.internal_session()
       
   360         try:
       
   361             rset = session.execute('Any L WHERE U login L, U primary_email M, '
       
   362                                    'M address %(login)s', {'login': login},
       
   363                                    build_descr=False)
       
   364             if rset.rowcount == 1:
       
   365                 login = rset[0][0]
       
   366         finally:
       
   367             session.close()
       
   368         return login
       
   369 
       
   370     def authenticate_user(self, session, login, **kwargs):
       
   371         """validate login / password, raise AuthenticationError on failure
       
   372         return associated CWUser instance on success
       
   373         """
       
   374         if self.vreg.config['allow-email-login'] and '@' in login:
       
   375             login = self._login_from_email(login)
       
   376         for source in self.sources:
       
   377             if source.support_entity('CWUser'):
       
   378                 try:
       
   379                     eid = source.authenticate(session, login, **kwargs)
       
   380                     break
       
   381                 except AuthenticationError:
       
   382                     continue
       
   383         else:
       
   384             raise AuthenticationError('authentication failed with all sources')
       
   385         cwuser = self._build_user(session, eid)
       
   386         if self.config.consider_user_state and \
       
   387                not cwuser.cw_adapt_to('IWorkflowable').state in cwuser.AUTHENTICABLE_STATES:
       
   388             raise AuthenticationError('user is not in authenticable state')
       
   389         return cwuser
       
   390 
       
   391     def _build_user(self, session, eid):
       
   392         """return a CWUser entity for user with the given eid"""
       
   393         cls = self.vreg['etypes'].etype_class('CWUser')
       
   394         rql = cls.fetch_rql(session.user, ['X eid %(x)s'])
       
   395         rset = session.execute(rql, {'x': eid})
       
   396         assert len(rset) == 1, rset
       
   397         cwuser = rset.get_entity(0, 0)
       
   398         # pylint: disable-msg=W0104
       
   399         # prefetch / cache cwuser's groups and properties. This is especially
       
   400         # useful for internal sessions to avoid security insertions
       
   401         cwuser.groups
       
   402         cwuser.properties
       
   403         return cwuser
       
   404 
       
   405     # public (dbapi) interface ################################################
       
   406 
   359     def stats(self): # XXX restrict to managers session?
   407     def stats(self): # XXX restrict to managers session?
   360         import threading
       
   361         results = {}
   408         results = {}
   362         querier = self.querier
   409         querier = self.querier
   363         source = self.system_source
   410         source = self.system_source
   364         for size, maxsize, hits, misses, title in (
   411         for size, maxsize, hits, misses, title in (
   365             (len(querier._rql_cache), self.config['rql-cache-size'],
   412             (len(querier._rql_cache), self.config['rql-cache-size'],
   375         results['nb_open_sessions'] = len(self._sessions)
   422         results['nb_open_sessions'] = len(self._sessions)
   376         results['nb_active_threads'] = threading.activeCount()
   423         results['nb_active_threads'] = threading.activeCount()
   377         results['looping_tasks'] = ', '.join(str(t) for t in self._looping_tasks)
   424         results['looping_tasks'] = ', '.join(str(t) for t in self._looping_tasks)
   378         results['available_pools'] = self._available_pools.qsize()
   425         results['available_pools'] = self._available_pools.qsize()
   379         return results
   426         return results
   380 
       
   381     def _login_from_email(self, login):
       
   382         session = self.internal_session()
       
   383         try:
       
   384             rset = session.execute('Any L WHERE U login L, U primary_email M, '
       
   385                                    'M address %(login)s', {'login': login},
       
   386                                    build_descr=False)
       
   387             if rset.rowcount == 1:
       
   388                 login = rset[0][0]
       
   389         finally:
       
   390             session.close()
       
   391         return login
       
   392 
       
   393     def authenticate_user(self, session, login, **kwargs):
       
   394         """validate login / password, raise AuthenticationError on failure
       
   395         return associated CWUser instance on success
       
   396         """
       
   397         if self.vreg.config['allow-email-login'] and '@' in login:
       
   398             login = self._login_from_email(login)
       
   399         for source in self.sources:
       
   400             if source.support_entity('CWUser'):
       
   401                 try:
       
   402                     eid = source.authenticate(session, login, **kwargs)
       
   403                     break
       
   404                 except AuthenticationError:
       
   405                     continue
       
   406         else:
       
   407             raise AuthenticationError('authentication failed with all sources')
       
   408         cwuser = self._build_user(session, eid)
       
   409         if self.config.consider_user_state and \
       
   410                not cwuser.cw_adapt_to('IWorkflowable').state in cwuser.AUTHENTICABLE_STATES:
       
   411             raise AuthenticationError('user is not in authenticable state')
       
   412         return cwuser
       
   413 
       
   414     def _build_user(self, session, eid):
       
   415         """return a CWUser entity for user with the given eid"""
       
   416         cls = self.vreg['etypes'].etype_class('CWUser')
       
   417         rql = cls.fetch_rql(session.user, ['X eid %(x)s'])
       
   418         rset = session.execute(rql, {'x': eid})
       
   419         assert len(rset) == 1, rset
       
   420         cwuser = rset.get_entity(0, 0)
       
   421         # pylint: disable-msg=W0104
       
   422         # prefetch / cache cwuser's groups and properties. This is especially
       
   423         # useful for internal sessions to avoid security insertions
       
   424         cwuser.groups
       
   425         cwuser.properties
       
   426         return cwuser
       
   427 
       
   428     # public (dbapi) interface ################################################
       
   429 
   427 
   430     def get_schema(self):
   428     def get_schema(self):
   431         """return the instance schema. This is a public method, not
   429         """return the instance schema. This is a public method, not
   432         requiring a session id
   430         requiring a session id
   433         """
   431         """