diff options
| author | Aurélien Bompard | 2015-12-26 13:41:30 +0100 |
|---|---|---|
| committer | Aurélien Bompard | 2015-12-26 13:41:30 +0100 |
| commit | f739cda916ffdd64ed3b389c5c8b920578cc9475 (patch) | |
| tree | a551011ff301a220d26cc72084c28075f9dfa431 | |
| parent | a11e089cc1e0e5aff2502e584014295a414a43f9 (diff) | |
| download | mailman-f739cda916ffdd64ed3b389c5c8b920578cc9475.tar.gz mailman-f739cda916ffdd64ed3b389c5c8b920578cc9475.tar.zst mailman-f739cda916ffdd64ed3b389c5c8b920578cc9475.zip | |
Return predictable token IDs in testing mode.
This commits builds upon the mailman.utilities.uid.UniqueIDFactory to
generate predictable tokens when the testing mode is activated. This
will make VCR tapes more diffable between runs.
| -rw-r--r-- | src/mailman/model/pending.py | 18 | ||||
| -rw-r--r-- | src/mailman/utilities/uid.py | 32 |
2 files changed, 33 insertions, 17 deletions
diff --git a/src/mailman/model/pending.py b/src/mailman/model/pending.py index 1973820d0..b8dbb1932 100644 --- a/src/mailman/model/pending.py +++ b/src/mailman/model/pending.py @@ -24,9 +24,6 @@ __all__ = [ import json -import time -import random -import hashlib from lazr.config import as_timedelta from mailman.config import config @@ -35,12 +32,16 @@ from mailman.database.transaction import dbconnection from mailman.interfaces.pending import ( IPendable, IPended, IPendedKeyValue, IPendings) from mailman.utilities.datetime import now +from mailman.utilities.uid import TokenFactory from sqlalchemy import Column, DateTime, ForeignKey, Integer, Unicode, and_ from sqlalchemy.orm import aliased, relationship from zope.interface import implementer from zope.interface.verify import verifyObject +token_factory = TokenFactory() + + @implementer(IPendedKeyValue) class PendedKeyValue(Model): @@ -88,17 +89,8 @@ class Pendings: # Calculate the token and the lifetime. if lifetime is None: lifetime = as_timedelta(config.mailman.pending_request_life) - # Calculate a unique token. Algorithm vetted by the Timbot. time() - # has high resolution on Linux, clock() on Windows. random gives us - # about 45 bits in Python 2.2, 53 bits on Python 2.3. The time and - # clock values basically help obscure the random number generator, as - # does the hash calculation. The integral parts of the time values - # are discarded because they're the most predictable bits. for attempts in range(3): - right_now = time.time() - x = random.random() + right_now % 1.0 + time.clock() % 1.0 - # Use sha1 because it produces shorter strings. - token = hashlib.sha1(repr(x).encode('utf-8')).hexdigest() + token = token_factory.new_token() # In practice, we'll never get a duplicate, but we'll be anal # about checking anyway. if store.query(Pended).filter_by(token=token).count() == 0: diff --git a/src/mailman/utilities/uid.py b/src/mailman/utilities/uid.py index ec7948189..d20360ca0 100644 --- a/src/mailman/utilities/uid.py +++ b/src/mailman/utilities/uid.py @@ -28,8 +28,11 @@ __all__ = [ import os +import time import uuid import errno +import random +import hashlib from flufl.lock import Lock from mailman.config import config @@ -78,7 +81,7 @@ class UniqueIDFactory: # may still not be ideal due to race conditions, but I think the # tests will be serialized enough (and the ids reset between # tests) that it will not be a problem. Maybe. - return self._next_uid() + return uuid.UUID(int=self._next_uid()) while True: uid = uuid.uuid4() try: @@ -96,13 +99,13 @@ class UniqueIDFactory: next_uid = uid + 1 with open(self._uid_file, 'w') as fp: fp.write(str(next_uid)) - return uuid.UUID(int=uid) + return uid except IOError as error: if error.errno != errno.ENOENT: raise with open(self._uid_file, 'w') as fp: fp.write('2') - return uuid.UUID(int=1) + return 1 def reset(self): with self._lock: @@ -111,4 +114,25 @@ class UniqueIDFactory: -factory = UniqueIDFactory() +class TokenFactory(UniqueIDFactory): + + def __init__(self): + super(TokenFactory, self).__init__(context='token') + + def new_token(self): + """ + Calculate a unique token. Algorithm vetted by the Timbot. time() + has high resolution on Linux, clock() on Windows. random gives us + about 45 bits in Python 2.2, 53 bits on Python 2.3. The time and + clock values basically help obscure the random number generator, as + does the hash calculation. The integral parts of the time values + are discarded because they're the most predictable bits. + """ + if layers.is_testing(): + # When in testing mode we want to produce predictable tokens, see + # UniqueIDFactory for a similar use case. + return str(self._next_uid()).zfill(40) + right_now = time.time() + x = random.random() + right_now % 1.0 + time.clock() % 1.0 + # Use sha1 because it produces shorter strings. + return hashlib.sha1(repr(x).encode('utf-8')).hexdigest() |
