cubicweb/pyramid/auth.py
changeset 11631 faf279e33298
parent 11593 73bf8377a3d5
child 11811 f09efeead7f9
--- /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())