From f5c7b152b0ede008d81c5da0b4e3fbd583552125 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 28 Apr 2016 21:07:30 -0400 Subject: [PATCH 1/4] Fix the Makefile for Sphinx It was depending on swaddle, which is gone in favor of honcho. --- docs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Makefile b/docs/Makefile index 95ddd23b7c..c1e0125cf3 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -3,7 +3,7 @@ # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = ../env/bin/swaddle ../tests/env ../env/bin/sphinx-build +SPHINXBUILD = ../env/bin/honcho -e ../tests/env run ../env/bin/sphinx-build BUILDDIR = _build # User-friendly check for sphinx-build From de796f99b8ffa2a23ff11a75338bd8781df4dc8a Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 28 Apr 2016 21:05:34 -0400 Subject: [PATCH 2/4] Make way for refactoring participant Moves from a single file to a submodule directory. This file is so big, I want to refactor it. --- gratipay/models/{participant.py => participant/__init__.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gratipay/models/{participant.py => participant/__init__.py} (100%) diff --git a/gratipay/models/participant.py b/gratipay/models/participant/__init__.py similarity index 100% rename from gratipay/models/participant.py rename to gratipay/models/participant/__init__.py From 4c115d5e28fa615c67f8e145685ae877bdc86f7b Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Sat, 30 Apr 2016 17:38:21 -0400 Subject: [PATCH 3/4] Start a docstring for the add_event function --- gratipay/models/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gratipay/models/__init__.py b/gratipay/models/__init__.py index e21edbe2d4..85fb8723fb 100644 --- a/gratipay/models/__init__.py +++ b/gratipay/models/__init__.py @@ -193,6 +193,18 @@ def _check_orphans_no_tips(cursor): def add_event(c, type, payload): + """Log an event. + + This is the function we use to capture interesting events that happen + across the system in one place, the ``events`` table. + + :param c: a :py:class:`Postres` or :py:class:`Cursor` instance + :param unicode type: an indicator of what type of event it is--either ``participant``, ``team`` + or ``payday`` + :param payload: an arbitrary JSON-serializable data structure; for ``participant`` type, ``id`` + must be the id of the participant in question + + """ SQL = """ INSERT INTO events (type, payload) VALUES (%s, %s) From 11b12c5fa1dc98e33c6fdb54e423f6f3d8508bb5 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Sat, 30 Apr 2016 17:43:45 -0400 Subject: [PATCH 4/4] prune code we borrowed from Django but don't use --- gratipay/security/crypto.py | 166 +++--------------------------------- 1 file changed, 10 insertions(+), 156 deletions(-) diff --git a/gratipay/security/crypto.py b/gratipay/security/crypto.py index 85cb218424..e4adf0fe78 100644 --- a/gratipay/security/crypto.py +++ b/gratipay/security/crypto.py @@ -1,102 +1,28 @@ -""" -Django's standard crypto functions and utilities. -""" -from __future__ import unicode_literals +from __future__ import absolute_import, division, print_function, unicode_literals -import hmac -import struct import hashlib -import binascii -import operator +import random +import string import time -from functools import reduce -# Use the system PRNG if possible -import random -try: + +# utils +# ===== +# borrowed from Django + +try: # use the system PRNG if possible random = random.SystemRandom() using_sysrandom = True -except NotImplementedError: +except NotImplementedError: # fall back import warnings warnings.warn('A secure pseudo-random number generator is not available ' 'on your system. Falling back to Mersenne Twister.') using_sysrandom = False -#from django.conf import settings -SECRET_KEY = "" -import string pool = string.digits + string.letters + string.punctuation UNSECURE_RANDOM_STRING = b"".join([random.choice(pool) for i in range(64)]) -# I get wet. - -#from django.utils.functional import Promise -class Promise(object): - """ - This is just a base class for the proxy class created in - the closure of the lazy function. It can be used to recognize - promises in code. - """ - pass - -#from django.utils.encoding import smart_str -def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): - """ - Returns a bytestring version of 's', encoded as specified in 'encoding'. - - If strings_only is True, don't convert (some) non-string-like objects. - """ - if strings_only and (s is None or isinstance(s, int)): - return s - if isinstance(s, Promise): - return unicode(s).encode(encoding, errors) - elif not isinstance(s, basestring): - try: - return str(s) - except UnicodeEncodeError: - if isinstance(s, Exception): - # An Exception subclass containing non-ASCII data that doesn't - # know how to print itself properly. We shouldn't raise a - # further exception. - return ' '.join([smart_str(arg, encoding, strings_only, - errors) for arg in s]) - return unicode(s).encode(encoding, errors) - elif isinstance(s, unicode): - return s.encode(encoding, errors) - elif s and encoding != 'utf-8': - return s.decode('utf-8', errors).encode(encoding, errors) - else: - return s - - -_trans_5c = b"".join([chr(x ^ 0x5C) for x in xrange(256)]) -_trans_36 = b"".join([chr(x ^ 0x36) for x in xrange(256)]) - - -def salted_hmac(key_salt, value, secret=None): - """ - Returns the HMAC-SHA1 of 'value', using a key generated from key_salt and a - secret (which defaults to settings.SECRET_KEY). - - A different key_salt should be passed in for every application of HMAC. - """ - if secret is None: - raise NotImplementedError - #secret = settings.SECRET_KEY - - # We need to generate a derived key from our base key. We can do this by - # passing the key_salt and our base key through a pseudo-random function and - # SHA1 works nicely. - key = hashlib.sha1((key_salt + secret).encode('utf-8')).digest() - - # If len(key_salt + secret) > sha_constructor().block_size, the above - # line is redundant and could be replaced by key = key_salt + secret, since - # the hmac module does the same thing for keys longer than the block size. - # However, we need to ensure that we *always* do this. - return hmac.new(key, msg=value, digestmod=hashlib.sha1) - - def get_random_string(length=12, allowed_chars='abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'): @@ -135,75 +61,3 @@ def constant_time_compare(val1, val2): for x, y in zip(val1, val2): result |= ord(x) ^ ord(y) return result == 0 - - -def _bin_to_long(x): - """ - Convert a binary string into a long integer - - This is a clever optimization for fast xor vector math - """ - return long(x.encode('hex'), 16) - - -def _long_to_bin(x, hex_format_string): - """ - Convert a long integer into a binary string. - hex_format_string is like "%020x" for padding 10 characters. - """ - return binascii.unhexlify((hex_format_string % x).encode('ascii')) - - -def _fast_hmac(key, msg, digest): - """ - A trimmed down version of Python's HMAC implementation - """ - dig1, dig2 = digest(), digest() - key = smart_str(key) - if len(key) > dig1.block_size: - key = digest(key).digest() - key += chr(0) * (dig1.block_size - len(key)) - dig1.update(key.translate(_trans_36)) - dig1.update(msg) - dig2.update(key.translate(_trans_5c)) - dig2.update(dig1.digest()) - return dig2 - - -def pbkdf2(password, salt, iterations, dklen=0, digest=None): - """ - Implements PBKDF2 as defined in RFC 2898, section 5.2 - - HMAC+SHA256 is used as the default pseudo random function. - - Right now 10,000 iterations is the recommended default which takes - 100ms on a 2.2Ghz Core 2 Duo. This is probably the bare minimum - for security given 1000 iterations was recommended in 2001. This - code is very well optimized for CPython and is only four times - slower than openssl's implementation. - """ - assert iterations > 0 - if not digest: - digest = hashlib.sha256 - password = smart_str(password) - salt = smart_str(salt) - hlen = digest().digest_size - if not dklen: - dklen = hlen - if dklen > (2 ** 32 - 1) * hlen: - raise OverflowError('dklen too big') - l = -(-dklen // hlen) - r = dklen - (l - 1) * hlen - - hex_format_string = "%%0%ix" % (hlen * 2) - - def F(i): - def U(): - u = salt + struct.pack(b'>I', i) - for j in xrange(int(iterations)): - u = _fast_hmac(password, u, digest).digest() - yield _bin_to_long(u) - return _long_to_bin(reduce(operator.xor, U()), hex_format_string) - - T = [F(x) for x in range(1, l + 1)] - return b''.join(T[:-1]) + T[-1][:r]