Allow underscores in account/user in tempauth via encoding.

This patch introduces a new user format to tempauth which starts with
"user64_" and in which the account and user are base64 encoded (sans
padding equal signs):

user64_<account_b64>_<user_b64> = <key> [group] [group] [...] [storage_url]

This allows unambiguous parsing and adds the ability to have a user or
account name containing underscores.

The handling of tempauth configuration is fully backward compatible and
this only adds new, optional functionality.

Also beefed up unit tests to get full coverage of the user parsing code
in tempauth's __init__().

Change-Id: Id06304934ab8055a28921f2e1aa3c58a2036d8f8
This commit is contained in:
Darrell Bishop 2012-10-01 21:43:34 -07:00
parent f0bd91dd14
commit 5582f166c2
4 changed files with 87 additions and 7 deletions

View File

@ -644,6 +644,12 @@ is::
user_<account>_<user> = <key> [group] [group] [...] [storage_url]
or if you want to be able to include underscores in the ``<account>`` or
``<user>`` portions, you can base64 encode them (with *no* equal signs) in a
line like this::
user64_<account_b64>_<user_b64> = <key> [group] [group] [...] [storage_url]
There are special groups of::
.reseller_admin = can do anything to any account for this auth
@ -669,6 +675,9 @@ Here are example entries, required for running the tests::
user_test2_tester2 = testing2 .admin
user_test_tester3 = testing3
# account "test_y" and user "tester_y" (note the lack of padding = chars)
user64_dGVzdF95_dGVzdGVyX3k = testing4 .admin
------------------------
Memcached Considerations
------------------------

View File

@ -106,6 +106,9 @@ use = egg:swift#tempauth
# allow_overrides = true
# Lastly, you need to list all the accounts/users you want here. The format is:
# user_<account>_<user> = <key> [group] [group] [...] [storage_url]
# or if you want underscores in <account> or <user>, you can base64 encode them
# (with no equal signs) and use this format:
# user64_<account_b64>_<user_b64> = <key> [group] [group] [...] [storage_url]
# There are special groups of:
# .reseller_admin = can do anything to any account for this auth
# .admin = can do anything within the account

View File

@ -54,6 +54,11 @@ class TempAuth(object):
user_test_tester = testing .admin
user_test2_tester2 = testing2 .admin
user_test_tester3 = testing3
# To allow accounts/users with underscores you can base64 encode them.
# Here is the account "under_score" and username "a_b" (note the lack
# of padding equal signs):
user64_dW5kZXJfc2NvcmU_YV9i = testing4
See the proxy-server.conf-sample for more information.
@ -86,7 +91,15 @@ class TempAuth(object):
conf.get('allow_overrides', 't').lower() in TRUE_VALUES
self.users = {}
for conf_key in conf:
if conf_key.startswith('user_'):
if conf_key.startswith('user_') or conf_key.startswith('user64_'):
account, username = conf_key.split('_', 1)[1].split('_')
if conf_key.startswith('user64_'):
# Because trailing equal signs would screw up config file
# parsing, we auto-pad with '=' chars.
account += '=' * (len(account) % 4)
account = base64.b64decode(account)
username += '=' * (len(username) % 4)
username = base64.b64decode(username)
values = conf[conf_key].split()
if not values:
raise ValueError('%s has no key set' % conf_key)
@ -100,8 +113,8 @@ class TempAuth(object):
ip = '127.0.0.1'
url += ip
url += ':' + conf.get('bind_port', '8080') + '/v1/' + \
self.reseller_prefix + conf_key.split('_')[1]
self.users[conf_key.split('_', 1)[1].replace('_', ':')] = {
self.reseller_prefix + account
self.users[account + ':' + username] = {
'key': key, 'url': url, 'groups': values}
def __call__(self, env, start_response):

View File

@ -20,6 +20,7 @@ except ImportError:
import unittest
from contextlib import contextmanager
from time import time
from base64 import b64encode
from webob import Request, Response
@ -578,20 +579,74 @@ class TestAuth(unittest.TestCase):
class TestParseUserCreation(unittest.TestCase):
def test_parse_user_creation(self):
auth_filter = auth.filter_factory({
'reseller_prefix': 'ABC',
'bind_ip': '1.2.3.4',
'user_test_tester3': 'testing',
'user_has_url': 'urlly .admin http://a.b/v1/DEF_has',
'user_admin_admin': 'admin .admin .reseller_admin',
})(FakeApp())
self.assertEquals(auth_filter.users, {
'admin:admin': {
'url': 'http://127.0.0.1:8080/v1/AUTH_admin',
'groups': ['.admin', '.reseller_admin'],
'url': 'http://1.2.3.4:8080/v1/ABC_admin',
'groups': ['.admin', '.reseller_admin'],
'key': 'admin'
}, 'test:tester3': {
'url': 'http://127.0.0.1:8080/v1/AUTH_test',
'groups': [],
'url': 'http://1.2.3.4:8080/v1/ABC_test',
'groups': [],
'key': 'testing'
}, 'has:url': {
'url': 'http://a.b/v1/DEF_has',
'groups': ['.admin'],
'key': 'urlly'
},
})
def test_base64_encoding(self):
auth_filter = auth.filter_factory({
'reseller_prefix': 'ABC',
'bind_ip': '1.2.3.4',
'user64_%s_%s' % (
b64encode('test').rstrip('='),
b64encode('tester3').rstrip('=')):
'testing .reseller_admin',
'user64_%s_%s' % (
b64encode('user_foo').rstrip('='),
b64encode('ab').rstrip('=')):
'urlly .admin http://a.b/v1/DEF_has',
})(FakeApp())
self.assertEquals(auth_filter.users, {
'test:tester3': {
'url': 'http://1.2.3.4:8080/v1/ABC_test',
'groups': ['.reseller_admin'],
'key': 'testing'
}, 'user_foo:ab': {
'url': 'http://a.b/v1/DEF_has',
'groups': ['.admin'],
'key': 'urlly'
},
})
def test_bind_ip_all_zeroes(self):
auth_filter = auth.filter_factory({
'reseller_prefix': 'ABC',
'bind_ip': '0.0.0.0',
'user_admin_admin': 'admin .admin .reseller_admin',
})(FakeApp())
self.assertEquals(auth_filter.users, {
'admin:admin': {
'url': 'http://127.0.0.1:8080/v1/ABC_admin',
'groups': ['.admin', '.reseller_admin'],
'key': 'admin',
},
})
def test_key_with_no_value(self):
self.assertRaises(ValueError, auth.filter_factory({
'user_test_tester3': 'testing',
'user_bob_bobby': '',
'user_admin_admin': 'admin .admin .reseller_admin',
}), FakeApp())
if __name__ == '__main__':
unittest.main()