diff -r 1817f8946c22 -r faf279e33298 cubicweb/pyramid/auth.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cubicweb/pyramid/auth.py Mon Sep 26 14:52:12 2016 +0200 @@ -0,0 +1,180 @@ +import datetime +import logging +import warnings + +from zope.interface import implementer + +from pyramid.settings import asbool +from pyramid.authorization import ACLAuthorizationPolicy +from cubicweb.pyramid.core import get_principals +from pyramid_multiauth import MultiAuthenticationPolicy + +from pyramid.authentication import AuthTktAuthenticationPolicy + +from pyramid.interfaces import IAuthenticationPolicy + +log = logging.getLogger(__name__) + + +@implementer(IAuthenticationPolicy) +class UpdateLoginTimeAuthenticationPolicy(object): + """An authentication policy that update the user last_login_time. + + The update is done in the 'remember' method, which is called by the login + views login, + + Usually used via :func:`includeme`. + """ + + def authenticated_userid(self, request): + pass + + def effective_principals(self, request): + return () + + def remember(self, request, principal, **kw): + try: + repo = request.registry['cubicweb.repository'] + with repo.internal_cnx() as cnx: + cnx.execute( + "SET U last_login_time %(now)s WHERE U eid %(user)s", { + 'now': datetime.datetime.now(), + 'user': principal}) + cnx.commit() + except: + log.exception("Failed to update last_login_time") + return () + + def forget(self, request): + return () + + +class CWAuthTktAuthenticationPolicy(AuthTktAuthenticationPolicy): + """ + An authentication policy that inhibate the call the 'remember' if a + 'persistent' argument is passed to it, and is equal to the value that + was passed to the constructor. + + This allow to combine two policies with different settings and select them + by just setting this argument. + """ + def __init__(self, secret, persistent, defaults={}, prefix='', **settings): + self.persistent = persistent + unset = object() + kw = {} + # load string settings + for name in ('cookie_name', 'path', 'domain', 'hashalg'): + value = settings.get(prefix + name, defaults.get(name, unset)) + if value is not unset: + kw[name] = value + # load boolean settings + for name in ('secure', 'include_ip', 'http_only', 'wild_domain', + 'parent_domain', 'debug'): + value = settings.get(prefix + name, defaults.get(name, unset)) + if value is not unset: + kw[name] = asbool(value) + # load int settings + for name in ('timeout', 'reissue_time', 'max_age'): + value = settings.get(prefix + name, defaults.get(name, unset)) + if value is not unset: + kw[name] = int(value) + super(CWAuthTktAuthenticationPolicy, self).__init__(secret, **kw) + + def remember(self, request, principals, **kw): + if 'persistent' not in kw or kw.pop('persistent') == self.persistent: + return super(CWAuthTktAuthenticationPolicy, self).remember( + request, principals, **kw) + else: + return () + + +def includeme(config): + """ Activate the CubicWeb AuthTkt authentication policy. + + Usually called via ``config.include('cubicweb.pyramid.auth')``. + + See also :ref:`defaults_module` + """ + settings = config.registry.settings + + policies = [] + + if asbool(settings.get('cubicweb.auth.update_login_time', True)): + policies.append(UpdateLoginTimeAuthenticationPolicy()) + + if asbool(settings.get('cubicweb.auth.authtkt', True)): + session_prefix = 'cubicweb.auth.authtkt.session.' + persistent_prefix = 'cubicweb.auth.authtkt.persistent.' + + try: + secret = config.registry['cubicweb.config']['pyramid-auth-secret'] + warnings.warn( + "pyramid-auth-secret from all-in-one is now " + "cubicweb.auth.authtkt.[session|persistent].secret", + DeprecationWarning) + except: + secret = 'notsosecret' + + session_secret = settings.get( + session_prefix + 'secret', secret) + persistent_secret = settings.get( + persistent_prefix + 'secret', secret) + + if 'notsosecret' in (session_secret, persistent_secret): + warnings.warn(''' + + !! SECURITY WARNING !! + + The authentication cookies are signed with a static secret key. + + Configure the following options in your pyramid.ini file: + + - cubicweb.auth.authtkt.session.secret + - cubicweb.auth.authtkt.persistent.secret + + YOU SHOULD STOP THIS INSTANCE unless your really know what you + are doing !! + + ''') + + policies.append( + CWAuthTktAuthenticationPolicy( + session_secret, False, + defaults={ + 'hashalg': 'sha512', + 'cookie_name': 'auth_tkt', + 'timeout': 1200, + 'reissue_time': 120, + 'http_only': True, + 'secure': True + }, + prefix=session_prefix, + **settings + ) + ) + + policies.append( + CWAuthTktAuthenticationPolicy( + persistent_secret, True, + defaults={ + 'hashalg': 'sha512', + 'cookie_name': 'pauth_tkt', + 'max_age': 3600*24*30, + 'reissue_time': 3600*24, + 'http_only': True, + 'secure': True + }, + prefix=persistent_prefix, + **settings + ) + ) + + kw = {} + if asbool(settings.get('cubicweb.auth.groups_principals', True)): + kw['callback'] = get_principals + + authpolicy = MultiAuthenticationPolicy(policies, **kw) + config.registry['cubicweb.authpolicy'] = authpolicy + + config.set_authentication_policy(authpolicy) + config.set_authorization_policy(ACLAuthorizationPolicy())