server/session.py
changeset 7350 c2452cd57026
parent 7349 43416f63eca9
child 7352 d68f9319bfda
equal deleted inserted replaced
7349:43416f63eca9 7350:c2452cd57026
    96         self.session = session
    96         self.session = session
    97         self.mode = mode
    97         self.mode = mode
    98         self.categories = categories
    98         self.categories = categories
    99 
    99 
   100     def __enter__(self):
   100     def __enter__(self):
   101         self.oldmode = self.session.set_hooks_mode(self.mode)
   101         self.oldmode, self.changes = self.session.init_hooks_mode_categories(
   102         if self.mode is self.session.HOOKS_DENY_ALL:
   102             self.mode, self.categories)
   103             self.changes = self.session.enable_hook_categories(*self.categories)
       
   104         else:
       
   105             self.changes = self.session.disable_hook_categories(*self.categories)
       
   106 
   103 
   107     def __exit__(self, exctype, exc, traceback):
   104     def __exit__(self, exctype, exc, traceback):
   108         if self.changes:
   105         self.session.reset_hooks_mode_categories(self.oldmode, self.mode, self.changes)
   109             if self.mode is self.session.HOOKS_DENY_ALL:
   106 
   110                 self.session.disable_hook_categories(*self.changes)
   107 
   111             else:
       
   112                 self.session.enable_hook_categories(*self.changes)
       
   113         self.session.set_hooks_mode(self.oldmode)
       
   114 
       
   115 INDENT = ''
       
   116 class security_enabled(object):
   108 class security_enabled(object):
   117     """context manager to control security w/ session.execute, since by
   109     """context manager to control security w/ session.execute, since by
   118     default security is disabled on queries executed on the repository
   110     default security is disabled on queries executed on the repository
   119     side.
   111     side.
   120     """
   112     """
   122         self.session = session
   114         self.session = session
   123         self.read = read
   115         self.read = read
   124         self.write = write
   116         self.write = write
   125 
   117 
   126     def __enter__(self):
   118     def __enter__(self):
   127 #        global INDENT
   119         self.oldread, self.oldwrite = self.session.init_security(
   128         if self.read is not None:
   120             self.read, self.write)
   129             self.oldread = self.session.set_read_security(self.read)
       
   130 #            print INDENT + 'read', self.read, self.oldread
       
   131         if self.write is not None:
       
   132             self.oldwrite = self.session.set_write_security(self.write)
       
   133 #            print INDENT + 'write', self.write, self.oldwrite
       
   134 #        INDENT += '  '
       
   135 
   121 
   136     def __exit__(self, exctype, exc, traceback):
   122     def __exit__(self, exctype, exc, traceback):
   137 #        global INDENT
   123         self.session.reset_security(self.oldread, self.oldwrite)
   138 #        INDENT = INDENT[:-2]
       
   139         if self.read is not None:
       
   140             self.session.set_read_security(self.oldread)
       
   141 #            print INDENT + 'reset read to', self.oldread
       
   142         if self.write is not None:
       
   143             self.session.set_write_security(self.oldwrite)
       
   144 #            print INDENT + 'reset write to', self.oldwrite
       
   145 
   124 
   146 
   125 
   147 class TransactionData(object):
   126 class TransactionData(object):
   148     def __init__(self, txid):
   127     def __init__(self, txid):
   149         self.transactionid = txid
   128         self.transactionid = txid
       
   129         self.ctx_count = 0
       
   130 
   150 
   131 
   151 class Session(RequestSessionBase):
   132 class Session(RequestSessionBase):
   152     """tie session id, user, connections pool and other session data all
   133     """tie session id, user, connections pool and other session data all
   153     together
   134     together
   154     """
   135     """
   207     def hijack_user(self, user):
   188     def hijack_user(self, user):
   208         """return a fake request/session using specified user"""
   189         """return a fake request/session using specified user"""
   209         session = Session(user, self.repo)
   190         session = Session(user, self.repo)
   210         threaddata = session._threaddata
   191         threaddata = session._threaddata
   211         threaddata.pool = self.pool
   192         threaddata.pool = self.pool
       
   193         # we attributed a pool, need to update ctx_count else it will be freed
       
   194         # while undesired
       
   195         threaddata.ctx_count = 1
   212         # share pending_operations, else operation added in the hi-jacked
   196         # share pending_operations, else operation added in the hi-jacked
   213         # session such as SendMailOp won't ever be processed
   197         # session such as SendMailOp won't ever be processed
   214         threaddata.pending_operations = self.pending_operations
   198         threaddata.pending_operations = self.pending_operations
   215         # everything in transaction_data should be copied back but the entity
   199         # everything in transaction_data should be copied back but the entity
   216         # type cache we don't want to avoid security pb
   200         # type cache we don't want to avoid security pb
   231         """
   215         """
   232         self.add_relations([(rtype, [(fromeid,  toeid)])])
   216         self.add_relations([(rtype, [(fromeid,  toeid)])])
   233 
   217 
   234     def add_relations(self, relations):
   218     def add_relations(self, relations):
   235         '''set many relation using a shortcut similar to the one in add_relation
   219         '''set many relation using a shortcut similar to the one in add_relation
   236         
   220 
   237         relations is a list of 2-uples, the first element of each
   221         relations is a list of 2-uples, the first element of each
   238         2-uple is the rtype, and the second is a list of (fromeid,
   222         2-uple is the rtype, and the second is a list of (fromeid,
   239         toeid) tuples
   223         toeid) tuples
   240         '''
   224         '''
   241         edited_entities = {}
   225         edited_entities = {}
   403 
   387 
   404     # security control #########################################################
   388     # security control #########################################################
   405 
   389 
   406     DEFAULT_SECURITY = object() # evaluated to true by design
   390     DEFAULT_SECURITY = object() # evaluated to true by design
   407 
   391 
       
   392     def init_security(self, read, write):
       
   393         if read is None:
       
   394             oldread = None
       
   395         else:
       
   396             oldread = self.set_read_security(read)
       
   397         if write is None:
       
   398             oldwrite = None
       
   399         else:
       
   400             oldwrite = self.set_write_security(write)
       
   401         self._threaddata.ctx_count += 1
       
   402         return oldread, oldwrite
       
   403 
       
   404     def reset_security(self, read, write):
       
   405         txstore = self._threaddata
       
   406         txstore.ctx_count -= 1
       
   407         if txstore.ctx_count == 0:
       
   408             self._clear_thread_storage(txstore)
       
   409         else:
       
   410             if read is not None:
       
   411                 self.set_read_security(read)
       
   412             if write is not None:
       
   413                 self.set_write_security(write)
       
   414 
   408     @property
   415     @property
   409     def read_security(self):
   416     def read_security(self):
   410         """return a boolean telling if read security is activated or not"""
   417         """return a boolean telling if read security is activated or not"""
   411         txstore = self._threaddata
   418         txstore = self._threaddata
   412         if txstore is None:
   419         if txstore is None:
   498         assert mode is self.HOOKS_ALLOW_ALL or mode is self.HOOKS_DENY_ALL
   505         assert mode is self.HOOKS_ALLOW_ALL or mode is self.HOOKS_DENY_ALL
   499         oldmode = getattr(self._threaddata, 'hooks_mode', self.HOOKS_ALLOW_ALL)
   506         oldmode = getattr(self._threaddata, 'hooks_mode', self.HOOKS_ALLOW_ALL)
   500         self._threaddata.hooks_mode = mode
   507         self._threaddata.hooks_mode = mode
   501         return oldmode
   508         return oldmode
   502 
   509 
       
   510     def init_hooks_mode_categories(self, mode, categories):
       
   511         oldmode = self.set_hooks_mode(mode)
       
   512         if mode is self.HOOKS_DENY_ALL:
       
   513             changes = self.enable_hook_categories(*categories)
       
   514         else:
       
   515             changes = self.disable_hook_categories(*categories)
       
   516         self._threaddata.ctx_count += 1
       
   517         return oldmode, changes
       
   518 
       
   519     def reset_hooks_mode_categories(self, oldmode, mode, categories):
       
   520         txstore = self._threaddata
       
   521         txstore.ctx_count -= 1
       
   522         if txstore.ctx_count == 0:
       
   523             self._clear_thread_storage(txstore)
       
   524         else:
       
   525             if categories:
       
   526                 if mode is self.HOOKS_DENY_ALL:
       
   527                     return self.disable_hook_categories(*categories)
       
   528                 else:
       
   529                     return self.enable_hook_categories(*categories)
       
   530             self.set_hooks_mode(oldmode)
       
   531 
   503     @property
   532     @property
   504     def disabled_hook_categories(self):
   533     def disabled_hook_categories(self):
   505         try:
   534         try:
   506             return getattr(self._threaddata, 'disabled_hook_cats')
   535             return getattr(self._threaddata, 'disabled_hook_cats')
   507         except AttributeError:
   536         except AttributeError:
   622             self.reset_pool(True)
   651             self.reset_pool(True)
   623             raise Exception('try to set pool on a closed session')
   652             raise Exception('try to set pool on a closed session')
   624         if self.pool is None:
   653         if self.pool is None:
   625             # get pool first to avoid race-condition
   654             # get pool first to avoid race-condition
   626             self._threaddata.pool = pool = self.repo._get_pool()
   655             self._threaddata.pool = pool = self.repo._get_pool()
       
   656             self._threaddata.ctx_count += 1
   627             try:
   657             try:
   628                 pool.pool_set()
   658                 pool.pool_set()
   629             except:
   659             except:
   630                 self._threaddata.pool = None
   660                 self._threaddata.pool = None
   631                 self.repo._free_pool(pool)
   661                 self.repo._free_pool(pool)
   656         pool = getattr(self._threaddata, 'pool', None)
   686         pool = getattr(self._threaddata, 'pool', None)
   657         if pool is not None and (ignoremode or self.mode == 'read'):
   687         if pool is not None and (ignoremode or self.mode == 'read'):
   658             # even in read mode, we must release the current transaction
   688             # even in read mode, we must release the current transaction
   659             self._free_thread_pool(threading.currentThread(), pool)
   689             self._free_thread_pool(threading.currentThread(), pool)
   660             del self._threaddata.pool
   690             del self._threaddata.pool
       
   691             self._threaddata.ctx_count -= 1
   661 
   692 
   662     def _touch(self):
   693     def _touch(self):
   663         """update latest session usage timestamp and reset mode to read"""
   694         """update latest session usage timestamp and reset mode to read"""
   664         self.timestamp = time()
   695         self.timestamp = time()
   665         self.local_perm_cache.clear() # XXX simply move in transaction_data, no?
   696         self.local_perm_cache.clear() # XXX simply move in transaction_data, no?
   755             txstore = self.__threaddata.txdata
   786             txstore = self.__threaddata.txdata
   756         except AttributeError:
   787         except AttributeError:
   757             pass
   788             pass
   758         else:
   789         else:
   759             if reset_pool:
   790             if reset_pool:
   760                 self._tx_data.pop(txstore.transactionid, None)
   791                 self.reset_pool()
   761                 try:
   792                 if txstore.ctx_count == 0:
   762                     del self.__threaddata.txdata
   793                     self._clear_thread_storage(txstore)
   763                 except AttributeError:
   794                 else:
   764                     pass
   795                     self._clear_tx_storage(txstore)
   765             else:
   796             else:
   766                 for name in ('commit_state', 'transaction_data',
   797                 self._clear_tx_storage(txstore)
   767                              'pending_operations', '_rewriter'):
   798 
   768                     try:
   799     def _clear_thread_storage(self, txstore):
   769                         delattr(txstore, name)
   800         self._tx_data.pop(txstore.transactionid, None)
   770                     except AttributeError:
   801         try:
   771                         continue
   802             del self.__threaddata.txdata
       
   803         except AttributeError:
       
   804             pass
       
   805 
       
   806     def _clear_tx_storage(self, txstore):
       
   807         for name in ('commit_state', 'transaction_data',
       
   808                      'pending_operations', '_rewriter'):
       
   809             try:
       
   810                 delattr(txstore, name)
       
   811             except AttributeError:
       
   812                 continue
   772 
   813 
   773     def commit(self, reset_pool=True):
   814     def commit(self, reset_pool=True):
   774         """commit the current session's transaction"""
   815         """commit the current session's transaction"""
   775         if self.pool is None:
   816         if self.pool is None:
   776             assert not self.pending_operations
   817             assert not self.pending_operations