server/session.py
changeset 4913 083b4d454192
parent 4899 c666d265fb95
child 4924 d2fc161bee3f
equal deleted inserted replaced
4912:9767cc516b4f 4913:083b4d454192
    10 __docformat__ = "restructuredtext en"
    10 __docformat__ = "restructuredtext en"
    11 
    11 
    12 import sys
    12 import sys
    13 import threading
    13 import threading
    14 from time import time
    14 from time import time
       
    15 from uuid import uuid4
    15 
    16 
    16 from logilab.common.deprecation import deprecated
    17 from logilab.common.deprecation import deprecated
    17 from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
    18 from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
    18 from yams import BASE_TYPES
    19 from yams import BASE_TYPES
    19 
    20 
    20 from cubicweb import Binary, UnknownEid
    21 from cubicweb import Binary, UnknownEid, schema
    21 from cubicweb.req import RequestSessionBase
    22 from cubicweb.req import RequestSessionBase
    22 from cubicweb.dbapi import ConnectionProperties
    23 from cubicweb.dbapi import ConnectionProperties
    23 from cubicweb.utils import make_uid
    24 from cubicweb.utils import make_uid
    24 from cubicweb.rqlrewrite import RQLRewriter
    25 from cubicweb.rqlrewrite import RQLRewriter
    25 
    26 
    26 ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
    27 ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
       
    28 
       
    29 NO_UNDO_TYPES = schema.SCHEMA_TYPES.copy()
       
    30 NO_UNDO_TYPES.add('CWCache')
       
    31 # XXX rememberme,forgotpwd,apycot,vcsfile
    27 
    32 
    28 def is_final(rqlst, variable, args):
    33 def is_final(rqlst, variable, args):
    29     # try to find if this is a final var or not
    34     # try to find if this is a final var or not
    30     for select in rqlst.children:
    35     for select in rqlst.children:
    31         for sol in select.solutions:
    36         for sol in select.solutions:
   108 
   113 
   109 class Session(RequestSessionBase):
   114 class Session(RequestSessionBase):
   110     """tie session id, user, connections pool and other session data all
   115     """tie session id, user, connections pool and other session data all
   111     together
   116     together
   112     """
   117     """
       
   118     is_internal_session = False
   113 
   119 
   114     def __init__(self, user, repo, cnxprops=None, _id=None):
   120     def __init__(self, user, repo, cnxprops=None, _id=None):
   115         super(Session, self).__init__(repo.vreg)
   121         super(Session, self).__init__(repo.vreg)
   116         self.id = _id or make_uid(user.login.encode('UTF8'))
   122         self.id = _id or make_uid(user.login.encode('UTF8'))
   117         cnxprops = cnxprops or ConnectionProperties('inmemory')
   123         cnxprops = cnxprops or ConnectionProperties('inmemory')
   118         self.user = user
   124         self.user = user
   119         self.repo = repo
   125         self.repo = repo
   120         self.cnxtype = cnxprops.cnxtype
   126         self.cnxtype = cnxprops.cnxtype
   121         self.creation = time()
   127         self.creation = time()
   122         self.timestamp = self.creation
   128         self.timestamp = self.creation
   123         self.is_internal_session = False
       
   124         self.default_mode = 'read'
   129         self.default_mode = 'read'
       
   130         # support undo for Create Update Delete entity / Add Remove relation
       
   131         if repo.config.creating or repo.config.repairing or self.is_internal_session:
       
   132             self.undo_actions = ()
       
   133         else:
       
   134             self.undo_actions = set(repo.config['undo-support'].upper())
       
   135             if self.undo_actions - set('CUDAR'):
       
   136                 raise Exception('bad undo-support string in configuration')
   125         # short cut to querier .execute method
   137         # short cut to querier .execute method
   126         self._execute = repo.querier.execute
   138         self._execute = repo.querier.execute
   127         # shared data, used to communicate extra information between the client
   139         # shared data, used to communicate extra information between the client
   128         # and the rql server
   140         # and the rql server
   129         self.data = {}
   141         self.data = {}
   332         #                      \-> potentially, other calls to querier.execute
   344         #                      \-> potentially, other calls to querier.execute
   333         #
   345         #
   334         # so we can't rely on simply checking session.read_security, but
   346         # so we can't rely on simply checking session.read_security, but
   335         # recalling the first transition from DEFAULT_SECURITY to something
   347         # recalling the first transition from DEFAULT_SECURITY to something
   336         # else (False actually) is not perfect but should be enough
   348         # else (False actually) is not perfect but should be enough
   337         self._threaddata.dbapi_query = oldmode is self.DEFAULT_SECURITY
   349         #
       
   350         # also reset dbapi_query to true when we go back to DEFAULT_SECURITY
       
   351         self._threaddata.dbapi_query = (oldmode is self.DEFAULT_SECURITY
       
   352                                         or activated is self.DEFAULT_SECURITY)
   338         return oldmode
   353         return oldmode
   339 
   354 
   340     @property
   355     @property
   341     def write_security(self):
   356     def write_security(self):
   342         """return a boolean telling if write security is activated or not"""
   357         """return a boolean telling if write security is activated or not"""
   687                         operation.handle_event('%s_event' % trstate)
   702                         operation.handle_event('%s_event' % trstate)
   688                     except:
   703                     except:
   689                         self.critical('error while %sing', trstate,
   704                         self.critical('error while %sing', trstate,
   690                                       exc_info=sys.exc_info())
   705                                       exc_info=sys.exc_info())
   691                 self.info('%s session %s done', trstate, self.id)
   706                 self.info('%s session %s done', trstate, self.id)
       
   707                 return self.transaction_uuid(set=False)
   692             finally:
   708             finally:
   693                 self._clear_thread_data()
   709                 self._clear_thread_data()
   694                 self._touch()
   710                 self._touch()
   695                 if reset_pool:
   711                 if reset_pool:
   696                     self.reset_pool(ignoremode=True)
   712                     self.reset_pool(ignoremode=True)
   767         if index is None:
   783         if index is None:
   768             self.pending_operations.append(operation)
   784             self.pending_operations.append(operation)
   769         else:
   785         else:
   770             self.pending_operations.insert(index, operation)
   786             self.pending_operations.insert(index, operation)
   771 
   787 
       
   788     # undo support ############################################################
       
   789 
       
   790     def undoable_action(self, action, ertype):
       
   791         return action in self.undo_actions and not ertype in NO_UNDO_TYPES
       
   792         # XXX elif transaction on mark it partial
       
   793 
       
   794     def transaction_uuid(self, set=True):
       
   795         try:
       
   796             return self.transaction_data['tx_uuid']
       
   797         except KeyError:
       
   798             if not set:
       
   799                 return
       
   800             self.transaction_data['tx_uuid'] = uuid = uuid4().hex
       
   801             self.repo.system_source.start_undoable_transaction(self, uuid)
       
   802             return uuid
       
   803 
       
   804     def transaction_inc_action_counter(self):
       
   805         num = self.transaction_data.setdefault('tx_action_count', 0) + 1
       
   806         self.transaction_data['tx_action_count'] = num
       
   807         return num
       
   808 
   772     # querier helpers #########################################################
   809     # querier helpers #########################################################
   773 
   810 
   774     @property
   811     @property
   775     def rql_rewriter(self):
   812     def rql_rewriter(self):
   776         # in thread local storage since the rewriter isn't thread safe
   813         # in thread local storage since the rewriter isn't thread safe
   888         return self.entity_from_eid(eid)
   925         return self.entity_from_eid(eid)
   889 
   926 
   890 
   927 
   891 class InternalSession(Session):
   928 class InternalSession(Session):
   892     """special session created internaly by the repository"""
   929     """special session created internaly by the repository"""
       
   930     is_internal_session = True
   893 
   931 
   894     def __init__(self, repo, cnxprops=None):
   932     def __init__(self, repo, cnxprops=None):
   895         super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
   933         super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
   896                                               _id='internal')
   934                                               _id='internal')
   897         self.user.req = self # XXX remove when "vreg = user.req.vreg" hack in entity.py is gone
   935         self.user.req = self # XXX remove when "vreg = user.req.vreg" hack in entity.py is gone
   898         self.cnxtype = 'inmemory'
   936         self.cnxtype = 'inmemory'
   899         self.is_internal_session = True
       
   900         self.disable_hook_categories('integrity')
   937         self.disable_hook_categories('integrity')
   901 
   938 
   902 
   939 
   903 class InternalManager(object):
   940 class InternalManager(object):
   904     """a manager user with all access rights used internally for task such as
   941     """a manager user with all access rights used internally for task such as