From 5e3876f0c61b05a1a64514b1594e8418f7a5170f Mon Sep 17 00:00:00 2001 From: Tuomas Suutari Date: Wed, 7 Dec 2016 13:15:47 +0200 Subject: [PATCH 1/2] Revert "Fix timestamps computing in tokens" This reverts commit 975eb0163fce63d47f4556accc4f0e6ba79b9811. --- oidc_provider/lib/utils/common.py | 20 -------------------- oidc_provider/lib/utils/token.py | 9 +++++---- oidc_provider/tests/test_utils.py | 16 +--------------- 3 files changed, 6 insertions(+), 39 deletions(-) diff --git a/oidc_provider/lib/utils/common.py b/oidc_provider/lib/utils/common.py index 16673de..b7a6676 100644 --- a/oidc_provider/lib/utils/common.py +++ b/oidc_provider/lib/utils/common.py @@ -1,9 +1,5 @@ -import datetime -import time - from django.core.urlresolvers import reverse from django.http import HttpResponse -from django.utils import timezone from oidc_provider import settings @@ -102,19 +98,3 @@ def default_idtoken_processing_hook(id_token, user): :rtype dict """ return id_token - -def to_timestamp(dt): - """ - Convert a datetime to an integer timestamp. - - Inspired from Py3 code, can be replaced by ``int(dt.timestamp())`` - when Py2 is not supported anymore. - - Note: we assume the timezone of naive datetimes is the one of the - system, not settings.TIME_ZONE as this setting may not have been - set by the user. - """ - if timezone.is_aware(dt): - return int((dt - datetime.datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds()) - else: - return int(time.mktime(dt.timetuple())) diff --git a/oidc_provider/lib/utils/token.py b/oidc_provider/lib/utils/token.py index df3befa..7c06c00 100644 --- a/oidc_provider/lib/utils/token.py +++ b/oidc_provider/lib/utils/token.py @@ -1,4 +1,5 @@ from datetime import timedelta +import time import uuid from Cryptodome.PublicKey.RSA import importKey @@ -8,7 +9,7 @@ from jwkest.jwk import SYMKey from jwkest.jws import JWS from jwkest.jwt import JWT -from oidc_provider.lib.utils.common import get_issuer, to_timestamp +from oidc_provider.lib.utils.common import get_issuer from oidc_provider.models import ( Code, RSAKey, @@ -29,10 +30,10 @@ def create_id_token(user, aud, nonce='', at_hash='', request=None, scope=[]): # Convert datetimes into timestamps. now = timezone.now() - iat_time = to_timestamp(now) - exp_time = to_timestamp(now + timedelta(seconds=expires_in)) + iat_time = int(time.mktime(now.timetuple())) + exp_time = int(time.mktime((now + timedelta(seconds=expires_in)).timetuple())) user_auth_time = user.last_login or user.date_joined - auth_time = to_timestamp(user_auth_time) + auth_time = int(time.mktime(user_auth_time.timetuple())) dic = { 'iss': get_issuer(request=request), diff --git a/oidc_provider/tests/test_utils.py b/oidc_provider/tests/test_utils.py index dea523a..32bdf8d 100644 --- a/oidc_provider/tests/test_utils.py +++ b/oidc_provider/tests/test_utils.py @@ -1,10 +1,6 @@ -import datetime - from django.test import TestCase -from django.utils import timezone -from django.utils import six -from oidc_provider.lib.utils.common import get_issuer, to_timestamp +from oidc_provider.lib.utils.common import get_issuer class Request(object): @@ -46,13 +42,3 @@ class CommonTest(TestCase): self.assertEqual(get_issuer(site_url='http://127.0.0.1:9000', request=request), 'http://127.0.0.1:9000/openid') - - def test_to_timestamp(self): - if not six.PY2: - naive_dt = datetime.datetime.now() - self.assertEqual(to_timestamp(naive_dt), int(naive_dt.timestamp())) - - aware_dt = datetime.datetime(2016, 3, 2, 14, 2, 6, 123, timezone.utc) - self.assertEqual(to_timestamp(aware_dt), 1456927326) - if not six.PY2: - self.assertEqual(to_timestamp(aware_dt), int(aware_dt.timestamp())) From 65538b0f7d94c846f111ff1097b243df629bdf56 Mon Sep 17 00:00:00 2001 From: Tuomas Suutari Date: Wed, 7 Dec 2016 14:14:37 +0200 Subject: [PATCH 2/2] utils.token: Use time.time to generate the timestamps Use `time.time()` rather than `timezone.now()` for generating the unix timestamps. This avoids conversion between year-month-day-hh-mm-ss formatted timestamp vs. unix timestamp and is therefore simpler and more robust. Add a test case for this too and amend test_token_endpoint, since it used to mock timezone.now, but now it needs to mock time.time. --- oidc_provider/lib/utils/token.py | 10 +++---- oidc_provider/tests/test_token_endpoint.py | 15 +++++----- oidc_provider/tests/test_utils.py | 34 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/oidc_provider/lib/utils/token.py b/oidc_provider/lib/utils/token.py index 7c06c00..3f0d2e9 100644 --- a/oidc_provider/lib/utils/token.py +++ b/oidc_provider/lib/utils/token.py @@ -3,7 +3,7 @@ import time import uuid from Cryptodome.PublicKey.RSA import importKey -from django.utils import timezone +from django.utils import dateformat, timezone from jwkest.jwk import RSAKey as jwk_RSAKey from jwkest.jwk import SYMKey from jwkest.jws import JWS @@ -29,11 +29,11 @@ def create_id_token(user, aud, nonce='', at_hash='', request=None, scope=[]): expires_in = settings.get('OIDC_IDTOKEN_EXPIRE') # Convert datetimes into timestamps. - now = timezone.now() - iat_time = int(time.mktime(now.timetuple())) - exp_time = int(time.mktime((now + timedelta(seconds=expires_in)).timetuple())) + now = int(time.time()) + iat_time = now + exp_time = int(now + expires_in) user_auth_time = user.last_login or user.date_joined - auth_time = int(time.mktime(user_auth_time.timetuple())) + auth_time = int(dateformat.format(user_auth_time, 'U')) dic = { 'iss': get_issuer(request=request), diff --git a/oidc_provider/tests/test_token_endpoint.py b/oidc_provider/tests/test_token_endpoint.py index de7972e..7660240 100644 --- a/oidc_provider/tests/test_token_endpoint.py +++ b/oidc_provider/tests/test_token_endpoint.py @@ -1,5 +1,5 @@ -from datetime import timedelta import json +import time import uuid from base64 import b64encode @@ -15,7 +15,6 @@ from django.test import ( override_settings, ) from django.test import TestCase -from django.utils import timezone from jwkest.jwk import KEYS from jwkest.jws import JWS from jwkest.jwt import JWT @@ -164,9 +163,9 @@ class TokenTestCase(TestCase): # Retrieve refresh token code = self._create_code() post_data = self._auth_code_post_data(code=code.code) - real_now = timezone.now - with patch('oidc_provider.lib.utils.token.timezone.now') as now: - now.return_value = real_now() + start_time = time.time() + with patch('oidc_provider.lib.utils.token.time.time') as time_func: + time_func.return_value = start_time response = self._post_request(post_data) response_dic1 = json.loads(response.content.decode('utf-8')) @@ -174,8 +173,8 @@ class TokenTestCase(TestCase): # Use refresh token to obtain new token post_data = self._refresh_token_post_data(response_dic1['refresh_token']) - with patch('oidc_provider.lib.utils.token.timezone.now') as now: - now.return_value = real_now() + timedelta(minutes=10) + with patch('oidc_provider.lib.utils.token.time.time') as time_func: + time_func.return_value = start_time + 600 response = self._post_request(post_data) response_dic2 = json.loads(response.content.decode('utf-8')) @@ -189,6 +188,8 @@ class TokenTestCase(TestCase): self.assertEqual(id_token1['iss'], id_token2['iss']) self.assertEqual(id_token1['sub'], id_token2['sub']) self.assertNotEqual(id_token1['iat'], id_token2['iat']) + self.assertEqual(id_token1['iat'], int(start_time)) + self.assertEqual(id_token2['iat'], int(start_time + 600)) self.assertEqual(id_token1['aud'], id_token2['aud']) self.assertEqual(id_token1['auth_time'], id_token2['auth_time']) self.assertEqual(id_token1.get('azp'), id_token2.get('azp')) diff --git a/oidc_provider/tests/test_utils.py b/oidc_provider/tests/test_utils.py index 32bdf8d..fd09c40 100644 --- a/oidc_provider/tests/test_utils.py +++ b/oidc_provider/tests/test_utils.py @@ -1,6 +1,12 @@ +import time +from datetime import datetime + from django.test import TestCase +from django.utils import timezone from oidc_provider.lib.utils.common import get_issuer +from oidc_provider.lib.utils.token import create_id_token +from oidc_provider.tests.app.utils import create_fake_user class Request(object): @@ -42,3 +48,31 @@ class CommonTest(TestCase): self.assertEqual(get_issuer(site_url='http://127.0.0.1:9000', request=request), 'http://127.0.0.1:9000/openid') + + +def timestamp_to_datetime(timestamp): + tz = timezone.get_current_timezone() + return datetime.fromtimestamp(timestamp, tz=tz) + + +class TokenTest(TestCase): + def setUp(self): + self.user = create_fake_user() + + def test_create_id_token(self): + start_time = int(time.time()) + login_timestamp = start_time - 1234 + self.user.last_login = timestamp_to_datetime(login_timestamp) + id_token_data = create_id_token(self.user, aud='test-aud') + iat = id_token_data['iat'] + self.assertEqual(type(iat), int) + self.assertGreaterEqual(iat, start_time) + self.assertLessEqual(iat - start_time, 5) # Can't take more than 5 s + self.assertEqual(id_token_data, { + 'aud': 'test-aud', + 'auth_time': login_timestamp, + 'exp': iat + 600, + 'iat': iat, + 'iss': 'http://localhost:8000/openid', + 'sub': str(self.user.id), + })