22 """ |
22 """ |
23 from __future__ import print_function |
23 from __future__ import print_function |
24 |
24 |
25 __docformat__ = "restructuredtext en" |
25 __docformat__ = "restructuredtext en" |
26 |
26 |
27 import sys |
|
28 from os.path import join, exists |
|
29 from glob import glob |
|
30 from contextlib import contextmanager |
27 from contextlib import contextmanager |
31 |
28 |
32 from six import text_type, string_types |
29 from six import text_type, string_types |
33 from six.moves import filter |
30 from six.moves import filter |
34 |
31 |
37 from logilab.common.registry import yes |
34 from logilab.common.registry import yes |
38 from logilab import database |
35 from logilab import database |
39 |
36 |
40 from yams import BASE_GROUPS |
37 from yams import BASE_GROUPS |
41 |
38 |
42 from cubicweb import CW_SOFTWARE_ROOT |
|
43 from cubicweb.appobject import AppObject |
39 from cubicweb.appobject import AppObject |
|
40 |
44 |
41 |
45 class ShuttingDown(BaseException): |
42 class ShuttingDown(BaseException): |
46 """raised when trying to access some resources while the repository is |
43 """raised when trying to access some resources while the repository is |
47 shutting down. Inherit from BaseException so that `except Exception` won't |
44 shutting down. Inherit from BaseException so that `except Exception` won't |
48 catch it. |
45 catch it. |
88 #: security |
85 #: security |
89 DBG_SEC = 64 |
86 DBG_SEC = 64 |
90 #: more verbosity |
87 #: more verbosity |
91 DBG_MORE = 128 |
88 DBG_MORE = 128 |
92 #: all level enabled |
89 #: all level enabled |
93 DBG_ALL = DBG_RQL + DBG_SQL + DBG_REPO + DBG_MS + DBG_HOOKS + DBG_OPS + DBG_SEC + DBG_MORE |
90 DBG_ALL = DBG_RQL + DBG_SQL + DBG_REPO + DBG_MS + DBG_HOOKS + DBG_OPS + DBG_SEC + DBG_MORE |
94 |
91 |
95 _SECURITY_ITEMS = [] |
92 _SECURITY_ITEMS = [] |
96 _SECURITY_CAPS = ['read', 'add', 'update', 'delete', 'transition'] |
93 _SECURITY_CAPS = ['read', 'add', 'update', 'delete', 'transition'] |
97 |
94 |
98 #: current debug mode |
95 #: current debug mode |
99 DEBUG = 0 |
96 DEBUG = 0 |
|
97 |
100 |
98 |
101 @contextmanager |
99 @contextmanager |
102 def tunesecurity(items=(), capabilities=()): |
100 def tunesecurity(items=(), capabilities=()): |
103 """Context manager to use in conjunction with DBG_SEC. |
101 """Context manager to use in conjunction with DBG_SEC. |
104 |
102 |
133 oldactions = _SECURITY_CAPS[:] |
131 oldactions = _SECURITY_CAPS[:] |
134 _SECURITY_CAPS[:] = capabilities |
132 _SECURITY_CAPS[:] = capabilities |
135 yield |
133 yield |
136 _SECURITY_ITEMS[:] = olditems |
134 _SECURITY_ITEMS[:] = olditems |
137 _SECURITY_CAPS[:] = oldactions |
135 _SECURITY_CAPS[:] = oldactions |
|
136 |
138 |
137 |
139 def set_debug(debugmode): |
138 def set_debug(debugmode): |
140 """change the repository debugging mode""" |
139 """change the repository debugging mode""" |
141 global DEBUG |
140 global DEBUG |
142 if not debugmode: |
141 if not debugmode: |
146 for mode in splitstrip(debugmode, sep='|'): |
145 for mode in splitstrip(debugmode, sep='|'): |
147 DEBUG |= globals()[mode] |
146 DEBUG |= globals()[mode] |
148 else: |
147 else: |
149 DEBUG |= debugmode |
148 DEBUG |= debugmode |
150 |
149 |
|
150 |
151 class debugged(object): |
151 class debugged(object): |
152 """Context manager and decorator to help debug the repository. |
152 """Context manager and decorator to help debug the repository. |
153 |
153 |
154 It can be used either as a context manager: |
154 It can be used either as a context manager: |
155 |
155 |
182 return traceback is None |
182 return traceback is None |
183 |
183 |
184 def __call__(self, func): |
184 def __call__(self, func): |
185 """decorate function""" |
185 """decorate function""" |
186 def wrapped(*args, **kwargs): |
186 def wrapped(*args, **kwargs): |
187 _clevel = DEBUG |
|
188 set_debug(self.debugmode) |
187 set_debug(self.debugmode) |
189 try: |
188 try: |
190 return func(*args, **kwargs) |
189 return func(*args, **kwargs) |
191 finally: |
190 finally: |
192 set_debug(self._clevel) |
191 set_debug(self._clevel) |
193 return wrapped |
192 return wrapped |
|
193 |
194 |
194 |
195 # database initialization ###################################################### |
195 # database initialization ###################################################### |
196 |
196 |
197 def create_user(session, login, pwd, *groups): |
197 def create_user(session, login, pwd, *groups): |
198 # monkey patch this method if you want to customize admin/anon creation |
198 # monkey patch this method if you want to customize admin/anon creation |
200 user = session.create_entity('CWUser', login=login, upassword=pwd) |
200 user = session.create_entity('CWUser', login=login, upassword=pwd) |
201 for group in groups: |
201 for group in groups: |
202 session.execute('SET U in_group G WHERE U eid %(u)s, G name %(group)s', |
202 session.execute('SET U in_group G WHERE U eid %(u)s, G name %(group)s', |
203 {'u': user.eid, 'group': text_type(group)}) |
203 {'u': user.eid, 'group': text_type(group)}) |
204 return user |
204 return user |
|
205 |
205 |
206 |
206 def init_repository(config, interactive=True, drop=False, vreg=None, |
207 def init_repository(config, interactive=True, drop=False, vreg=None, |
207 init_config=None): |
208 init_config=None): |
208 """initialise a repository database by creating tables add filling them |
209 """initialise a repository database by creating tables add filling them |
209 with the minimal set of entities (ie at least the schema, base groups and |
210 with the minimal set of entities (ie at least the schema, base groups and |
287 # sort for eid predicatability as expected in some server tests |
288 # sort for eid predicatability as expected in some server tests |
288 for group in sorted(BASE_GROUPS): |
289 for group in sorted(BASE_GROUPS): |
289 cnx.create_entity('CWGroup', name=text_type(group)) |
290 cnx.create_entity('CWGroup', name=text_type(group)) |
290 admin = create_user(cnx, login, pwd, u'managers') |
291 admin = create_user(cnx, login, pwd, u'managers') |
291 cnx.execute('SET X owned_by U WHERE X is IN (CWGroup,CWSource), U eid %(u)s', |
292 cnx.execute('SET X owned_by U WHERE X is IN (CWGroup,CWSource), U eid %(u)s', |
292 {'u': admin.eid}) |
293 {'u': admin.eid}) |
293 cnx.commit() |
294 cnx.commit() |
294 repo.shutdown() |
295 repo.shutdown() |
295 # re-login using the admin user |
296 # re-login using the admin user |
296 config._cubes = None # avoid assertion error |
297 config._cubes = None # avoid assertion error |
297 repo = get_repository(config=config) |
298 repo = get_repository(config=config) |
298 # replace previous schema by the new repo's one. This is necessary so that we give the proper |
299 # replace previous schema by the new repo's one. This is necessary so that we give the proper |
299 # schema to `initialize_schema` above since it will initialize .eid attribute of schema elements |
300 # schema to `initialize_schema` above since it will initialize .eid attribute of schema elements |
300 schema = repo.schema |
301 schema = repo.schema |
301 with connect(repo, login, password=pwd) as cnx: |
302 with connect(repo, login, password=pwd) as cnx: |
302 with cnx.security_enabled(False, False): |
303 with cnx.security_enabled(False, False): |
303 repo.system_source.eid = ssource.eid # redo this manually |
304 repo.system_source.eid = ssource.eid # redo this manually |
304 handler = config.migration_handler(schema, interactive=False, |
305 handler = config.migration_handler(schema, interactive=False, |
305 cnx=cnx, repo=repo) |
306 cnx=cnx, repo=repo) |
306 # serialize the schema |
307 # serialize the schema |
307 initialize_schema(config, schema, handler) |
308 initialize_schema(config, schema, handler) |
308 # yoo ! |
309 # yoo ! |
346 # execute instance's post<event> script (useful in tests) |
347 # execute instance's post<event> script (useful in tests) |
347 mhandler.cmd_exec_event_script('post%s' % event, apphome=True) |
348 mhandler.cmd_exec_event_script('post%s' % event, apphome=True) |
348 |
349 |
349 |
350 |
350 # sqlite'stored procedures have to be registered at connection opening time |
351 # sqlite'stored procedures have to be registered at connection opening time |
351 from logilab.database import SQL_CONNECT_HOOKS |
352 from logilab.database import SQL_CONNECT_HOOKS # noqa |
352 |
353 |
353 # add to this set relations which should have their add security checking done |
354 # add to this set relations which should have their add security checking done |
354 # *BEFORE* adding the actual relation (done after by default) |
355 # *BEFORE* adding the actual relation (done after by default) |
355 BEFORE_ADD_RELATIONS = set(('owned_by',)) |
356 BEFORE_ADD_RELATIONS = set(('owned_by',)) |
356 |
357 |