summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAurélien Bompard2015-12-26 13:41:30 +0100
committerAurélien Bompard2015-12-26 13:41:30 +0100
commitf739cda916ffdd64ed3b389c5c8b920578cc9475 (patch)
treea551011ff301a220d26cc72084c28075f9dfa431
parenta11e089cc1e0e5aff2502e584014295a414a43f9 (diff)
downloadmailman-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.py18
-rw-r--r--src/mailman/utilities/uid.py32
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()