Tres Seaver
2013-11-13 4f3525217ad912ab9e002d99cd07643431724ff9
Make cookie expiration date RFC-2616 compliant.

- Weekday / month names must be locale-independent.

- 'GMT' zone must be included.

Quasi-merge of PR #11.
3 files modified
104 ■■■■ changed files
CHANGES.rst 6 ●●●●● patch | view | raw | blame | history
repoze/who/plugins/auth_tkt.py 26 ●●●● patch | view | raw | blame | history
repoze/who/plugins/tests/test_authtkt.py 72 ●●●●● patch | view | raw | blame | history
CHANGES.rst
@@ -1,6 +1,12 @@
repoze.who Changelog
====================
Unreleased
----------
- Make cookie expiration date RFC-2616 compliant (independent of locale,
  including 'GMT' zone). See https://github.com/repoze/repoze.who/pull/11
2.2 (2013-05-17)
----------------
repoze/who/plugins/auth_tkt.py
@@ -1,8 +1,12 @@
import datetime
from calendar import timegm
from email.utils import formatdate
from codecs import utf_8_decode
from codecs import utf_8_encode
import os
import time
from wsgiref.handlers import _monthname     # Locale-independent, RFC-2616
from wsgiref.handlers import _weekdayname   # Locale-independent, RFC-2616
from zope.interface import implementer
@@ -13,11 +17,11 @@
from repoze.who._compat import STRING_TYPES
from repoze.who._compat import u
_NOW_TESTING = None  # unit tests can replace
def _now():  #pragma NO COVERAGE
    if _NOW_TESTING is not None:
        return _NOW_TESTING
    return datetime.datetime.now()
_UTCNOW = None  # unit tests can replace
def _utcnow():  #pragma NO COVERAGE
    if _UTCNOW is not None:
        return _UTCNOW
    return datetime.datetime.utcnow()
@implementer(IIdentifier, IAuthenticator)
@@ -166,9 +170,17 @@
    def _get_cookies(self, environ, value, max_age=None):
        if max_age is not None:
            max_age = int(max_age)
            later = _now() + datetime.timedelta(seconds=max_age)
            later = _utcnow() + datetime.timedelta(seconds=max_age)
            # Wdy, DD-Mon-YY HH:MM:SS GMT
            expires = later.strftime('%a, %d %b %Y %H:%M:%S')
            expires = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
                _weekdayname[later.weekday()],
                later.day,
                _monthname[later.month],
                later.year,
                later.hour,
                later.minute,
                later.second,
            )
            # the Expires header is *required* at least for IE7 (IE7 does
            # not respect Max-Age)
            max_age = "; Max-Age=%s; Expires=%s" % (max_age, expires)
repoze/who/plugins/tests/test_authtkt.py
@@ -58,7 +58,7 @@
    def _setNowTesting(self, value):
        from repoze.who.plugins import auth_tkt
        auth_tkt._NOW_TESTING, self._now_testing = value, auth_tkt._NOW_TESTING
        auth_tkt._UTCNOW, self._now_testing = value, auth_tkt._UTCNOW
    def test_class_conforms_to_IIdentifier(self):
        from zope.interface.verify import verifyClass
@@ -443,36 +443,64 @@
                         ('Set-Cookie',
                          'auth_tkt="%s"; Path=/' % new_val))
    def test_remember_max_age(self):
    def test_l10n_sane_cookie_date(self):
        from datetime import datetime
        now = datetime(2009, 11, 8, 16, 15, 22)
        self._setNowTesting(now)
        plugin = self._makeOne('secret')
        environ = {'HTTP_HOST':'example.com'}
        environ = {'HTTP_HOST': 'example.com'}
        tkt = self._makeTicket(userid='chris', userdata='')
        result = plugin.remember(environ, {'repoze.who.userid':'chris',
                                           'max_age':'500'})
        name,value = result.pop(0)
        result = plugin.remember(environ, {'repoze.who.userid': 'chris',
                                            'max_age': '500'})
        name, value = result.pop(0)
        self.assertEqual('Set-Cookie', name)
        self.failUnless(
            value.endswith('; Expires=Sun, 08 Nov 2009 16:23:42 GMT'))
    def test_remember_max_age(self):
        from datetime import datetime
        now = datetime(2009, 11, 8, 16, 15, 22)
        self._setNowTesting(now)
        plugin = self._makeOne('secret')
        environ = {'HTTP_HOST': 'example.com'}
        tkt = self._makeTicket(userid='chris', userdata='')
        result = plugin.remember(environ, {'repoze.who.userid': 'chris',
                                           'max_age': '500'})
        name, value = result.pop(0)
        self.assertEqual('Set-Cookie', name)
        self.failUnless(
            value.startswith('auth_tkt="%s"; Path=/; Max-Age=500' % tkt),
            value)
        self.failUnless('; Expires=' in value)
        name,value = result.pop(0)
        self.assertEqual('Set-Cookie', name)
        self.failUnless(
            value.startswith(
            'auth_tkt="%s"; Path=/; Domain=example.com; Max-Age=500'
            % tkt), value)
        self.failUnless('; Expires=' in value)
            value.endswith('; Expires=Sun, 08 Nov 2009 16:23:42 GMT'))
        name,value = result.pop(0)
        name, value = result.pop(0)
        self.assertEqual('Set-Cookie', name)
        self.failUnless(
            value.startswith(
            'auth_tkt="%s"; Path=/; Domain=.example.com; Max-Age=500' % tkt),
                'auth_tkt="%s"; Path=/; Domain=example.com; Max-Age=500'
                % tkt), value)
        self.failUnless(
            value.endswith('; Expires=Sun, 08 Nov 2009 16:23:42 GMT'))
        name, value = result.pop(0)
        self.assertEqual('Set-Cookie', name)
        self.failUnless(
            value.startswith(
                'auth_tkt="%s"; Path=/; Domain=.example.com; Max-Age=500' % tkt),
            value)
        self.failUnless('; Expires=' in value)
        self.failUnless(
            value.endswith('; Expires=Sun, 08 Nov 2009 16:23:42 GMT'))
    def test_forget(self):
        from datetime import datetime
@@ -487,21 +515,21 @@
        self.assertEqual(name, 'Set-Cookie')
        self.assertEqual(value,
                         'auth_tkt="INVALID"; Path=/; '
                         'Max-Age=0; Expires=Thu, 05 Nov 2009 16:15:22'
                         'Max-Age=0; Expires=Thu, 05 Nov 2009 16:15:22 GMT'
                         )
        header = headers[1]
        name, value = header
        self.assertEqual(name, 'Set-Cookie')
        self.assertEqual(value,
                         'auth_tkt="INVALID"; Path=/; Domain=localhost; '
                         'Max-Age=0; Expires=Thu, 05 Nov 2009 16:15:22'
                         'Max-Age=0; Expires=Thu, 05 Nov 2009 16:15:22 GMT'
                         )
        header = headers[2]
        name, value = header
        self.assertEqual(name, 'Set-Cookie')
        self.assertEqual(value,
                         'auth_tkt="INVALID"; Path=/; Domain=.localhost; '
                         'Max-Age=0; Expires=Thu, 05 Nov 2009 16:15:22'
                         'Max-Age=0; Expires=Thu, 05 Nov 2009 16:15:22 GMT'
                        )
    def test_authenticate_non_auth_tkt_credentials(self):