[auth] Use a second authtkt policy for 'rememberme'
authorChristophe de Vienne <christophe@unlish.com>
Thu, 26 Feb 2015 00:56:32 +0100
changeset 11562 a49f08423f02
parent 11561 25d93d14f8b6
child 11563 f9473eb6a8a9
[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
pyramid_cubicweb/auth.py
pyramid_cubicweb/login.py
pyramid_cubicweb/tests/test_login.py
--- a/pyramid_cubicweb/auth.py	Thu Feb 12 19:21:39 2015 +0100
+++ b/pyramid_cubicweb/auth.py	Thu Feb 26 00:56:32 2015 +0100
@@ -49,6 +49,27 @@
         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, **kw):
+        self.persistent = persistent
+        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.
 
@@ -82,8 +103,36 @@
             ''')
 
         policies.append(
-            AuthTktAuthenticationPolicy(
-                secret, hashalg='sha512', reissue_time=3600))
+            CWAuthTktAuthenticationPolicy(
+                secret, False, hashalg='sha512',
+                cookie_name=settings.get(
+                    'cubicweb.auth.authtkt.session.cookie_name',
+                    'auth_tkt'),
+                timeout=int(settings.get(
+                    'cubicweb.auth.authtkt.session.timeout',
+                    1200)),
+                reissue_time=int(settings.get(
+                    'cubicweb.auth.authtkt.session.reissue_time',
+                    120))
+            )
+        )
+
+        policies.append(
+            CWAuthTktAuthenticationPolicy(
+                secret, True, hashalg='sha512',
+                cookie_name=settings.get(
+                    'cubicweb.auth.authtkt.persistent.cookie_name',
+                    'pauth_tkt'),
+                max_age=int(settings.get(
+                    'cubicweb.auth.authtkt.persistent.max_age',
+                    3600*24*30  # defaults to 1 month
+                )),
+                reissue_time=int(settings.get(
+                    'cubicweb.auth.authtkt.persistent.reissue_time',
+                    3600*24
+                ))
+            )
+        )
 
     kw = {}
     if asbool(settings.get('cubicweb.auth.groups_principals', True)):
--- a/pyramid_cubicweb/login.py	Thu Feb 12 19:21:39 2015 +0100
+++ b/pyramid_cubicweb/login.py	Thu Feb 26 00:56:32 2015 +0100
@@ -2,6 +2,7 @@
 from pyramid import security
 from pyramid.httpexceptions import HTTPSeeOther
 from pyramid.view import view_config
+from pyramid.settings import asbool
 
 import cubicweb
 
@@ -52,10 +53,9 @@
         del request.cw_request.post['__password']
         return login_form(request)
 
-    max_age = None
-    if request.params.get('__setauthcookie') == '1':
-        max_age = 604800
-    headers = security.remember(request, user_eid, max_age=max_age)
+    headers = security.remember(
+        request, user_eid,
+        persistent=asbool(request.params.get('__setauthcookie', False)))
 
     new_path = request.params.get('postlogin_path', '/')
 
--- a/pyramid_cubicweb/tests/test_login.py	Thu Feb 12 19:21:39 2015 +0100
+++ b/pyramid_cubicweb/tests/test_login.py	Thu Feb 26 00:56:32 2015 +0100
@@ -18,17 +18,24 @@
         res = self.webapp.post('/login', {
             '__login': self.admlogin, '__password': self.admpassword})
         self.assertEqual(res.status_int, 303)
-        cookie = self.webapp.cookiejar._cookies[
-            'localhost.local']['/']['auth_tkt']
-        self.assertIsNone(cookie.expires)
+
+        cookies = self.webapp.cookiejar._cookies['localhost.local']['/']
+        self.assertNotIn('pauth_tkt', cookies)
+        self.assertIn('auth_tkt', cookies)
+        self.assertIsNone(cookies['auth_tkt'].expires)
 
         res = self.webapp.get('/logout')
         self.assertEqual(res.status_int, 303)
 
+        self.assertNotIn('auth_tkt', cookies)
+        self.assertNotIn('pauth_tkt', cookies)
+
         res = self.webapp.post('/login', {
             '__login': self.admlogin, '__password': self.admpassword,
             '__setauthcookie': 1})
         self.assertEqual(res.status_int, 303)
-        cookie = self.webapp.cookiejar._cookies[
-            'localhost.local']['/']['auth_tkt']
-        self.assertIsNotNone(cookie.expires)
+
+        cookies = self.webapp.cookiejar._cookies['localhost.local']['/']
+        self.assertNotIn('auth_tkt', cookies)
+        self.assertIn('pauth_tkt', cookies)
+        self.assertIsNotNone(cookies['pauth_tkt'].expires)