server/__init__.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
--- a/server/__init__.py	Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,364 +0,0 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""Server subcube of cubicweb : defines objects used only on the server
-(repository) side
-
-The server module contains functions to initialize a new repository.
-"""
-from __future__ import print_function
-
-__docformat__ = "restructuredtext en"
-
-import sys
-from os.path import join, exists
-from glob import glob
-from contextlib import contextmanager
-
-from six import text_type, string_types
-from six.moves import filter
-
-from logilab.common.modutils import LazyObject
-from logilab.common.textutils import splitstrip
-from logilab.common.registry import yes
-from logilab import database
-
-from yams import BASE_GROUPS
-
-from cubicweb import CW_SOFTWARE_ROOT
-from cubicweb.appobject import AppObject
-
-class ShuttingDown(BaseException):
-    """raised when trying to access some resources while the repository is
-    shutting down. Inherit from BaseException so that `except Exception` won't
-    catch it.
-    """
-
-# server-side services #########################################################
-
-class Service(AppObject):
-    """Base class for services.
-
-    A service is a selectable object that performs an action server-side.
-    Use :class:`cubicweb.dbapi.Connection.call_service` to call them from
-    the web-side.
-
-    When inheriting this class, do not forget to define at least the __regid__
-    attribute (and probably __select__ too).
-    """
-    __registry__ = 'services'
-    __select__ = yes()
-
-    def call(self, **kwargs):
-        raise NotImplementedError
-
-
-# server-side debugging ########################################################
-
-# server debugging flags. They may be combined using binary operators.
-
-#:no debug information
-DBG_NONE = 0  #: no debug information
-#: rql execution information
-DBG_RQL  = 1
-#: executed sql
-DBG_SQL  = 2
-#: repository events
-DBG_REPO = 4
-#: multi-sources
-DBG_MS   = 8
-#: hooks
-DBG_HOOKS = 16
-#: operations
-DBG_OPS = 32
-#: security
-DBG_SEC = 64
-#: more verbosity
-DBG_MORE = 128
-#: all level enabled
-DBG_ALL  = DBG_RQL + DBG_SQL + DBG_REPO + DBG_MS + DBG_HOOKS + DBG_OPS + DBG_SEC + DBG_MORE
-
-_SECURITY_ITEMS = []
-_SECURITY_CAPS = ['read', 'add', 'update', 'delete', 'transition']
-
-#: current debug mode
-DEBUG = 0
-
-@contextmanager
-def tunesecurity(items=(), capabilities=()):
-    """Context manager to use in conjunction with DBG_SEC.
-
-    This allows some tuning of:
-    * the monitored capabilities ('read', 'add', ....)
-    * the object being checked by the security checkers
-
-    When no item is given, all of them will be watched.
-    By default all capabilities are monitored, unless specified.
-
-    Example use::
-
-      from cubicweb.server import debugged, DBG_SEC, tunesecurity
-      with debugged(DBG_SEC):
-          with tunesecurity(items=('Elephant', 'trumps'),
-                            capabilities=('update', 'delete')):
-              babar.cw_set(trumps=celeste)
-              flore.cw_delete()
-
-      ==>
-
-      check_perm: 'update' 'relation Elephant.trumps.Elephant'
-       [(ERQLExpression(Any X WHERE U has_update_permission X, X eid %(x)s, U eid %(u)s),
-       {'eid': 2167}, True)]
-      check_perm: 'delete' 'Elephant'
-       [(ERQLExpression(Any X WHERE U has_delete_permission X, X eid %(x)s, U eid %(u)s),
-       {'eid': 2168}, True)]
-
-    """
-    olditems = _SECURITY_ITEMS[:]
-    _SECURITY_ITEMS.extend(list(items))
-    oldactions = _SECURITY_CAPS[:]
-    _SECURITY_CAPS[:] = capabilities
-    yield
-    _SECURITY_ITEMS[:] = olditems
-    _SECURITY_CAPS[:] = oldactions
-
-def set_debug(debugmode):
-    """change the repository debugging mode"""
-    global DEBUG
-    if not debugmode:
-        DEBUG = 0
-        return
-    if isinstance(debugmode, string_types):
-        for mode in splitstrip(debugmode, sep='|'):
-            DEBUG |= globals()[mode]
-    else:
-        DEBUG |= debugmode
-
-class debugged(object):
-    """Context manager and decorator to help debug the repository.
-
-    It can be used either as a context manager:
-
-    >>> with debugged('DBG_RQL | DBG_REPO'):
-    ...     # some code in which you want to debug repository activity,
-    ...     # seing information about RQL being executed an repository events.
-
-    or as a function decorator:
-
-    >>> @debugged('DBG_RQL | DBG_REPO')
-    ... def some_function():
-    ...     # some code in which you want to debug repository activity,
-    ...     # seing information about RQL being executed an repository events
-
-    The debug mode will be reset to its original value when leaving the "with"
-    block or the decorated function.
-    """
-    def __init__(self, debugmode):
-        self.debugmode = debugmode
-        self._clevel = None
-
-    def __enter__(self):
-        """enter with block"""
-        self._clevel = DEBUG
-        set_debug(self.debugmode)
-
-    def __exit__(self, exctype, exc, traceback):
-        """leave with block"""
-        set_debug(self._clevel)
-        return traceback is None
-
-    def __call__(self, func):
-        """decorate function"""
-        def wrapped(*args, **kwargs):
-            _clevel = DEBUG
-            set_debug(self.debugmode)
-            try:
-                return func(*args, **kwargs)
-            finally:
-                set_debug(self._clevel)
-        return wrapped
-
-# database initialization ######################################################
-
-def create_user(session, login, pwd, *groups):
-    # monkey patch this method if you want to customize admin/anon creation
-    # (that maybe necessary if you change CWUser's schema)
-    user = session.create_entity('CWUser', login=login, upassword=pwd)
-    for group in groups:
-        session.execute('SET U in_group G WHERE U eid %(u)s, G name %(group)s',
-                        {'u': user.eid, 'group': text_type(group)})
-    return user
-
-def init_repository(config, interactive=True, drop=False, vreg=None,
-                    init_config=None):
-    """initialise a repository database by creating tables add filling them
-    with the minimal set of entities (ie at least the schema, base groups and
-    a initial user)
-    """
-    from cubicweb.repoapi import get_repository, connect
-    from cubicweb.server.repository import Repository
-    from cubicweb.server.utils import manager_userpasswd
-    from cubicweb.server.sqlutils import sqlexec, sqlschema, sql_drop_all_user_tables
-    from cubicweb.server.sqlutils import _SQL_DROP_ALL_USER_TABLES_FILTER_FUNCTION as drop_filter
-    # configuration to avoid db schema loading and user'state checking
-    # on connection
-    config.creating = True
-    config.consider_user_state = False
-    config.cubicweb_appobject_path = set(('hooks', 'entities'))
-    config.cube_appobject_path = set(('hooks', 'entities'))
-    # only enable the system source at initialization time
-    repo = Repository(config, vreg=vreg)
-    if init_config is not None:
-        # further config initialization once it has been bootstrapped
-        init_config(config)
-    schema = repo.schema
-    sourcescfg = config.read_sources_file()
-    source = sourcescfg['system']
-    driver = source['db-driver']
-    with repo.internal_cnx() as cnx:
-        sqlcnx = cnx.cnxset.cnx
-        sqlcursor = cnx.cnxset.cu
-        execute = sqlcursor.execute
-        if drop:
-            helper = database.get_db_helper(driver)
-            dropsql = sql_drop_all_user_tables(helper, sqlcursor)
-            # We may fail dropping some tables because of table dependencies, in a first pass.
-            # So, we try a second drop sequence to drop remaining tables if needed.
-            # Note that 2 passes is an arbitrary choice as it seems enough for our usecases
-            # (looping may induce infinite recursion when user have no rights for example).
-            # Here we try to keep code simple and backend independent. That's why we don't try to
-            # distinguish remaining tables (missing privileges, dependencies, ...).
-            failed = sqlexec(dropsql, execute, cnx=sqlcnx,
-                             pbtitle='-> dropping tables (first pass)')
-            if failed:
-                failed = sqlexec(failed, execute, cnx=sqlcnx,
-                                 pbtitle='-> dropping tables (second pass)')
-                remainings = list(filter(drop_filter, helper.list_tables(sqlcursor)))
-                assert not remainings, 'Remaining tables: %s' % ', '.join(remainings)
-        handler = config.migration_handler(schema, interactive=False, repo=repo, cnx=cnx)
-        # install additional driver specific sql files
-        handler.cmd_install_custom_sql_scripts()
-        for cube in reversed(config.cubes()):
-            handler.cmd_install_custom_sql_scripts(cube)
-        _title = '-> creating tables '
-        print(_title, end=' ')
-        # schema entities and relations tables
-        # can't skip entities table even if system source doesn't support them,
-        # they are used sometimes by generated sql. Keeping them empty is much
-        # simpler than fixing this...
-        schemasql = sqlschema(schema, driver)
-        #skip_entities=[str(e) for e in schema.entities()
-        #               if not repo.system_source.support_entity(str(e))])
-        failed = sqlexec(schemasql, execute, pbtitle=_title, delimiter=';;')
-        if failed:
-            print('The following SQL statements failed. You should check your schema.')
-            print(failed)
-            raise Exception('execution of the sql schema failed, you should check your schema')
-        sqlcursor.close()
-        sqlcnx.commit()
-    with repo.internal_cnx() as cnx:
-        # insert entity representing the system source
-        ssource = cnx.create_entity('CWSource', type=u'native', name=u'system')
-        repo.system_source.eid = ssource.eid
-        cnx.execute('SET X cw_source X WHERE X eid %(x)s', {'x': ssource.eid})
-        # insert base groups and default admin
-        print('-> inserting default user and default groups.')
-        try:
-            login = text_type(sourcescfg['admin']['login'])
-            pwd = sourcescfg['admin']['password']
-        except KeyError:
-            if interactive:
-                msg = 'enter login and password of the initial manager account'
-                login, pwd = manager_userpasswd(msg=msg, confirm=True)
-            else:
-                login, pwd = text_type(source['db-user']), source['db-password']
-        # sort for eid predicatability as expected in some server tests
-        for group in sorted(BASE_GROUPS):
-            cnx.create_entity('CWGroup', name=text_type(group))
-        admin = create_user(cnx, login, pwd, u'managers')
-        cnx.execute('SET X owned_by U WHERE X is IN (CWGroup,CWSource), U eid %(u)s',
-                        {'u': admin.eid})
-        cnx.commit()
-    repo.shutdown()
-    # re-login using the admin user
-    config._cubes = None # avoid assertion error
-    repo = get_repository(config=config)
-    with connect(repo, login, password=pwd) as cnx:
-        with cnx.security_enabled(False, False):
-            repo.system_source.eid = ssource.eid # redo this manually
-            handler = config.migration_handler(schema, interactive=False,
-                                               cnx=cnx, repo=repo)
-            # serialize the schema
-            initialize_schema(config, schema, handler)
-            # yoo !
-            cnx.commit()
-            repo.system_source.init_creating()
-            cnx.commit()
-    repo.shutdown()
-    # restore initial configuration
-    config.creating = False
-    config.consider_user_state = True
-    # (drop instance attribute to get back to class attribute)
-    del config.cubicweb_appobject_path
-    del config.cube_appobject_path
-    print('-> database for instance %s initialized.' % config.appid)
-
-
-def initialize_schema(config, schema, mhandler, event='create'):
-    from cubicweb.server.schemaserial import serialize_schema
-    cnx = mhandler.cnx
-    cubes = config.cubes()
-    # deactivate every hooks but those responsible to set metadata
-    # so, NO INTEGRITY CHECKS are done, to have quicker db creation.
-    # Active integrity is kept else we may pb such as two default
-    # workflows for one entity type.
-    with cnx.deny_all_hooks_but('metadata', 'activeintegrity'):
-        # execute cubicweb's pre<event> script
-        mhandler.cmd_exec_event_script('pre%s' % event)
-        # execute cubes pre<event> script if any
-        for cube in reversed(cubes):
-            mhandler.cmd_exec_event_script('pre%s' % event, cube)
-        # execute instance's pre<event> script (useful in tests)
-        mhandler.cmd_exec_event_script('pre%s' % event, apphome=True)
-        # enter instance'schema into the database
-        serialize_schema(cnx, schema)
-        cnx.commit()
-        # execute cubicweb's post<event> script
-        mhandler.cmd_exec_event_script('post%s' % event)
-        # execute cubes'post<event> script if any
-        for cube in reversed(cubes):
-            mhandler.cmd_exec_event_script('post%s' % event, cube)
-        # execute instance's post<event> script (useful in tests)
-        mhandler.cmd_exec_event_script('post%s' % event, apphome=True)
-
-
-# sqlite'stored procedures have to be registered at connection opening time
-from logilab.database import SQL_CONNECT_HOOKS
-
-# add to this set relations which should have their add security checking done
-# *BEFORE* adding the actual relation (done after by default)
-BEFORE_ADD_RELATIONS = set(('owned_by',))
-
-# add to this set relations which should have their add security checking done
-# *at COMMIT TIME* (done after by default)
-ON_COMMIT_ADD_RELATIONS = set(())
-
-# available sources registry
-SOURCE_TYPES = {'native': LazyObject('cubicweb.server.sources.native', 'NativeSQLSource'),
-                'datafeed': LazyObject('cubicweb.server.sources.datafeed', 'DataFeedSource'),
-                'ldapfeed': LazyObject('cubicweb.server.sources.ldapfeed', 'LDAPFeedSource'),
-                }