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 |