pyramid_cubicweb/tools.py
author Christophe de Vienne <christophe@unlish.com>
Thu, 26 Feb 2015 00:56:32 +0100
changeset 11562 a49f08423f02
parent 11551 444cd2bba89d
permissions -rw-r--r--
[auth] Use a second authtkt policy for 'rememberme' The former solution was buggy because the expire time of the auth cookie, if set through 'remember', was lost on the first cookie reissuing. The new approach, make possible thanks to multiauth, use two different cookies. One for session bounded authentication (no 'rememberme'), and one for long lasting authentication (w 'rememberme'). The choice between the two of them is done by adding a 'persistent' argument to the top-level 'security.remember' call. Passing this argument will inhibate a policy or the other. The two policies are (a little) configurable through the 'cubicweb.auth.authtkt.[session|persistent].*' variables. Related to #4985962

"""Various tools.

.. warning::

    This module should be considered as internal implementation details. Use
    with caution, as the API may change without notice.
"""

#: A short-term cache for user clones.
#: used by cached_build_user to speed-up repetitive calls to build_user
#: The expiration is handled in a dumb and brutal way: the whole cache is
#: cleared every 5 minutes.
_user_cache = {}


def clone_user(repo, user):
    """Clone a CWUser instance.

    .. warning::

        The returned clone is detached from any cnx.
        Before using it in any way, it should be attached to a cnx that has not
        this user already loaded.
    """
    CWUser = repo.vreg['etypes'].etype_class('CWUser')
    clone = CWUser(
        None,
        rset=user.cw_rset.copy(),
        row=user.cw_row,
        col=user.cw_col,
        groups=set(user._groups) if hasattr(user, '_groups') else None,
        properties=dict(user._properties)
        if hasattr(user, '_properties') else None)
    clone.cw_attr_cache = dict(user.cw_attr_cache)
    return clone


def cnx_attach_entity(cnx, entity):
    """Attach an entity to a cnx."""
    entity._cw = cnx
    if entity.cw_rset:
        entity.cw_rset.req = cnx


def cached_build_user(repo, eid):
    """Cached version of
    :meth:`cubicweb.server.repository.Repository._build_user`
    """
    with repo.internal_cnx() as cnx:
        if eid in _user_cache:
            entity = clone_user(repo, _user_cache[eid])
            # XXX the cnx is needed here so that the CWUser instance has an
            # access to the vreg, which it needs when its 'prefered_language'
            # property is accessed.
            # If this property did not need a cnx to access a vreg, we could
            # avoid the internal_cnx() and save more time.
            cnx_attach_entity(cnx, entity)
            return entity

        user = repo._build_user(cnx, eid)
        user.cw_clear_relation_cache()
        _user_cache[eid] = clone_user(repo, user)
        return user


def clear_cache():
    """Clear the user cache"""
    _user_cache.clear()


def includeme(config):
    """Start the cache maintenance loop task.

    Automatically included by :func:`pyramid_cubicweb.make_cubicweb_application`.
    """
    repo = config.registry['cubicweb.repository']
    interval = int(config.registry.settings.get(
        'cubicweb.usercache.expiration_time', 60*5))
    repo.looping_task(interval, clear_cache)