From e0b0292a27bdb404a6c6fe0b7dd52194630a35a7 Mon Sep 17 00:00:00 2001 From: Prashanth Pai Date: Fri, 26 Feb 2016 15:38:27 +0530 Subject: [PATCH] Allow configuring salt manually Older versions of swauth supported manually setting up a salt string in conf file. This change re-introduces it and makes it a tunable option. The current behavior of randomly generating salt for every password is NOT affected with this change. Change-Id: Ifdf6f806b954e4d41c083eeffa981cd7d0dd50b9 Signed-off-by: Prashanth Pai --- etc/proxy-server.conf-sample | 4 ++++ swauth/authtypes.py | 10 ++++++++-- swauth/middleware.py | 3 +++ test/unit/test_middleware.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index c0800b5..db44bcd 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -51,6 +51,10 @@ use = egg:swauth#swauth # An alternative is 'sha512' which stores only a one-way hash of the key leaving # it secure but unavailable for key-signing. # auth_type = plaintext +# Used if the auth_type is sha1 or sha512. Salt is data(text) that is used as +# an additional input to the one-way encoding function. If not set, a random +# salt will be generated for each password. +# auth_type_salt = # This allows middleware higher in the WSGI pipeline to override auth # processing, useful for middleware such as tempurl and formpost. If you know # you're not going to use such middleware and you want a bit of extra security, diff --git a/swauth/authtypes.py b/swauth/authtypes.py index 8a52801..7698f4a 100644 --- a/swauth/authtypes.py +++ b/swauth/authtypes.py @@ -89,10 +89,13 @@ class Sha1(object): """Encodes a user key into a particular format. The result of this method will be used by swauth for storing user credentials. + If salt is not manually set in conf file, a random salt will be + generated and used. + :param key: User's secret key :returns: A string representing user credentials """ - salt = os.urandom(32).encode('base64').rstrip() + salt = self.salt or os.urandom(32).encode('base64').rstrip() return self.encode_w_salt(salt, key) def match(self, key, creds): @@ -134,10 +137,13 @@ class Sha512(object): """Encodes a user key into a particular format. The result of this method will be used by swauth for storing user credentials. + If salt is not manually set in conf file, a random salt will be + generated and used. + :param key: User's secret key :returns: A string representing user credentials """ - salt = os.urandom(32).encode('base64').rstrip() + salt = self.salt or os.urandom(32).encode('base64').rstrip() return self.encode_w_salt(salt, key) def match(self, key, creds): diff --git a/swauth/middleware.py b/swauth/middleware.py index 00465b7..7936962 100644 --- a/swauth/middleware.py +++ b/swauth/middleware.py @@ -165,6 +165,9 @@ class Swauth(object): if self.auth_encoder is None: raise ValueError('Invalid auth_type in config file: %s' % self.auth_type) + # If auth_type_salt is not set in conf file, a random salt will be + # generated for each new password to be encoded. + self.auth_encoder.salt = conf.get('auth_type_salt', None) self.allow_overrides = \ conf.get('allow_overrides', 't').lower() in TRUE_VALUES self.agent = '%(orig)s Swauth' diff --git a/test/unit/test_middleware.py b/test/unit/test_middleware.py index 0e10eeb..d9a079f 100644 --- a/test/unit/test_middleware.py +++ b/test/unit/test_middleware.py @@ -119,6 +119,39 @@ class TestAuth(unittest.TestCase): 'token_life': str(DEFAULT_TOKEN_LIFE), 'max_token_life': str(MAX_TOKEN_LIFE)})(FakeApp()) + def test_salt(self): + for auth_type in ('sha1', 'sha512'): + # Salt not manually set + test_auth = \ + auth.filter_factory({ + 'super_admin_key': 'supertest', + 'token_life': str(DEFAULT_TOKEN_LIFE), + 'max_token_life': str(MAX_TOKEN_LIFE), + 'auth_type': auth_type})(FakeApp()) + self.assertEqual(test_auth.auth_encoder.salt, None) + mock_urandom = mock.Mock(return_value="abc") + with mock.patch("os.urandom", mock_urandom): + h_key = test_auth.auth_encoder().encode("key") + self.assertTrue(mock_urandom.called) + prefix = auth_type + ":" + "abc".encode('base64').rstrip() + '$' + self.assertTrue(h_key.startswith(prefix)) + + # Salt manually set + test_auth = \ + auth.filter_factory({ + 'super_admin_key': 'supertest', + 'token_life': str(DEFAULT_TOKEN_LIFE), + 'max_token_life': str(MAX_TOKEN_LIFE), + 'auth_type': auth_type, + 'auth_type_salt': "mysalt"})(FakeApp()) + self.assertEqual(test_auth.auth_encoder.salt, "mysalt") + mock_urandom = mock.Mock() + with mock.patch("os.urandom", mock_urandom): + h_key = test_auth.auth_encoder().encode("key") + self.assertFalse(mock_urandom.called) + prefix = auth_type + ":" + "mysalt" + '$' + self.assertTrue(h_key.startswith(prefix)) + def test_swift_version(self): app = FakeApp()