From 0d0d70bd78f432397b91eee4d9743000686037a6 Mon Sep 17 00:00:00 2001 From: Jakub Stasiak Date: Sun, 14 Jun 2020 20:30:18 +0200 Subject: Add a way to pass current time to Fernet (#5256) * Add a way to pass current time to Fernet The motivation behind this is to be able to unit test code using Fernet easily without having to monkey patch global state. * Reformat to satisfy flake8 * Trigger a Fernet.encrypt() branch missing from coverage * Revert specifying explicit current time in MultiFernet.rotate() Message's timestamp is not verified anyway since ttl is None. * Change the Fernet's explicit current time API slightly This's been suggested in code review. * Fix a typo * Fix a typo * Restore full MultiFernet test coverage and fix a typo * Restore more coverage time.time() is not called by MultiFernet.rotate() anymore so the monkey patching and lambda need to go, because the patched function is not used and coverage calculation will rightfully notice it. * Remove an unused import * Document when the *_at_time Fernet methods were added --- tests/test_fernet.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/test_fernet.py b/tests/test_fernet.py index 75ecc356..da2096fb 100644 --- a/tests/test_fernet.py +++ b/tests/test_fernet.py @@ -6,7 +6,6 @@ from __future__ import absolute_import, division, print_function import base64 import calendar -import datetime import json import os import time @@ -70,6 +69,10 @@ class TestFernet(object): monkeypatch): f = Fernet(secret.encode("ascii"), backend=backend) current_time = calendar.timegm(iso8601.parse_date(now).utctimetuple()) + payload = f.decrypt_at_time( + token.encode("ascii"), ttl=ttl_sec, current_time=current_time, + ) + assert payload == src.encode("ascii") monkeypatch.setattr(time, "time", lambda: current_time) payload = f.decrypt(token.encode("ascii"), ttl=ttl_sec) assert payload == src.encode("ascii") @@ -78,6 +81,10 @@ class TestFernet(object): def test_invalid(self, secret, token, now, ttl_sec, backend, monkeypatch): f = Fernet(secret.encode("ascii"), backend=backend) current_time = calendar.timegm(iso8601.parse_date(now).utctimetuple()) + with pytest.raises(InvalidToken): + f.decrypt_at_time( + token.encode("ascii"), ttl=ttl_sec, current_time=current_time, + ) monkeypatch.setattr(time, "time", lambda: current_time) with pytest.raises(InvalidToken): f.decrypt(token.encode("ascii"), ttl=ttl_sec) @@ -110,6 +117,8 @@ class TestFernet(object): token = f.encrypt(pt) ts = "1985-10-26T01:20:01-07:00" current_time = calendar.timegm(iso8601.parse_date(ts).utctimetuple()) + assert f.decrypt_at_time( + token, ttl=None, current_time=current_time) == pt monkeypatch.setattr(time, "time", lambda: current_time) assert f.decrypt(token, ttl=None) == pt @@ -125,8 +134,7 @@ class TestFernet(object): def test_extract_timestamp(self, monkeypatch, backend): f = Fernet(base64.urlsafe_b64encode(b"\x00" * 32), backend=backend) current_time = 1526138327 - monkeypatch.setattr(time, "time", lambda: current_time) - token = f.encrypt(b'encrypt me') + token = f.encrypt_at_time(b'encrypt me', current_time) assert f.extract_timestamp(token) == current_time with pytest.raises(InvalidToken): f.extract_timestamp(b"nonsensetoken") @@ -195,18 +203,14 @@ class TestMultiFernet(object): mf2 = MultiFernet([f2, f1]) plaintext = b"abc" - mf1_ciphertext = mf1.encrypt(plaintext) - - later = datetime.datetime.now() + datetime.timedelta(minutes=5) - later_time = time.mktime(later.timetuple()) - monkeypatch.setattr(time, "time", lambda: later_time) + original_time = int(time.time()) - 5 * 60 + mf1_ciphertext = mf1.encrypt_at_time(plaintext, original_time) - original_time, _ = Fernet._get_unverified_token_data(mf1_ciphertext) rotated_time, _ = Fernet._get_unverified_token_data( mf2.rotate(mf1_ciphertext) ) - assert later_time != rotated_time + assert int(time.time()) != rotated_time assert original_time == rotated_time def test_rotate_decrypt_no_shared_keys(self, backend): -- cgit v1.2.3