For ACL strings: Shortened .ref to just .r, though .ref, .referer, and .referrer are all accepted. Updated 'Creating Your Own Auth Middleware' to describe how the DevAuth server works and suggestions for creating one's own. Added reseller_prefix (optional) implementation. Used urlparse in referrer_allowed. Fixed bug where group names would get lowercased by clean_acl. Changed .r:any to .r:*. Allowed .r:*.example.com to mean .r:.example.com. Made proxy log just the first authenticated group (the user) alongside the token. Moved proxy callback to clean_acl before the length check of the metadata. Cleaned up redundant logic in first proxy swift.authorize callback. Bit better docs. More and updated tests.

This commit is contained in:
gholt 2010-09-08 22:37:27 -07:00
parent 85b8d97086
commit d2ec027e22
15 changed files with 209 additions and 134 deletions

5
bin/st
View File

@ -1417,9 +1417,8 @@ Example:
'general documentation for what this means).')
parser.add_option('-r', '--read-acl', dest='read_acl',
help='Sets the Read ACL with post container commands. '
'Quick summary of ACL syntax: .ref:any, '
'.ref:-.example.com, .ref:www.example.com, account1, '
'account2:user2')
'Quick summary of ACL syntax: .r:*, .r:-.example.com, '
'.r:www.example.com, account1, account2:user2')
parser.add_option('-w', '--write-acl', dest='write_acl',
help='Sets the Write ACL with post container commands. '
'Quick summary of ACL syntax: account1, account2:user2')

View File

@ -1,10 +1,10 @@
===============
Auth Middleware
===============
==========================
Auth Server and Middleware
==========================
---------------------------------
Creating Your Own Auth Middleware
---------------------------------
--------------------------------------------
Creating Your Own Auth Server and Middleware
--------------------------------------------
The included swift/common/middleware/auth.py is a good minimal example of how
to create auth middleware. The main points are that the auth middleware can
@ -26,6 +26,39 @@ specific information, it just passes it along. Convention has
environ['REMOTE_USER'] set to the authenticated user string but often more
information is needed than just that.
The included DevAuth will set the REMOTE_USER to a comma separated list of
groups the user belongs to. The first group will be the "user's group", a group
that only the user belongs to. The second group will be the "account's group",
a group that includes all users for that auth account (different than the
storage account). The third group is optional and is the storage account
string. If the user does not have admin access to the account, the third group
will be omitted.
It is highly recommended that authentication server implementers prefix their
group names and tokens with a configurable reseller prefix (`AUTH_` by default
with the included DevAuth). This prefix will allow deconflicting with other
authentication servers that might be using the same Swift cluster.
The only other restriction is that no group name should begin with a period '.'
as that is reserved for internal Swift use (such as the .r for referrer
designations as you'll see later). This shouldn't be an issue if a reseller
prefix is in use and does not begin with a period.
Example Authentication with DevAuth:
* Token AUTH_tkabcd is given to the DevAuth middleware in a request's
X-Auth-Token header.
* The DevAuth middleware makes a validate token AUTH_tkabcd call to the
external DevAuth server.
* The external DevAuth server validates the token AUTH_tkabcd and discovers
it matches the "tester" user within the "test" account for the storage
account "AUTH_storage_xyz".
* The external DevAuth server responds with "X-Auth-Groups:
AUTH_test:tester,AUTH_test,AUTH_storage_xyz"
* Now this user will have full access (via authorization procedures later)
to the AUTH_storage_xyz Swift storage account and access to anything with
ACLs specifying at least one of those three groups returned.
Authorization is performed through callbacks by the Swift Proxy server to the
WSGI environment's swift.authorize value, if one is set. The swift.authorize
value should simply be a function that takes a webob.Request as an argument and

View File

@ -37,6 +37,10 @@ use = egg:swift#proxy
[filter:auth]
use = egg:swift#auth
# The reseller prefix, if set, will verify a token begins with this prefix
# before even attempting to validate it with the external reseller. Usefull if
# multiple auth systems are in use for one Swift cluster.
# reseller_prefix =
# ip = 127.0.0.1
# port = 11000
# ssl = false

View File

@ -94,6 +94,9 @@ class AuthController(object):
def __init__(self, conf, ring=None):
self.logger = get_logger(conf)
self.swift_dir = conf.get('swift_dir', '/etc/swift')
self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip()
if self.reseller_prefix and self.reseller_prefix[-1] != '_':
self.reseller_prefix += '_'
self.default_cluster_url = \
conf.get('default_cluster_url', 'http://127.0.0.1:8080/v1')
self.token_life = int(conf.get('token_life', 86400))
@ -147,7 +150,7 @@ class AuthController(object):
begin = time()
orig_account_name = account_name
if not account_name:
account_name = str(uuid4())
account_name = '%s%s' % (self.reseller_prefix, uuid4().hex)
partition, nodes = self.account_ring.get_nodes(account_name)
headers = {'X-Timestamp': normalize_timestamp(time()),
'x-cf-trans-id': 'tx' + str(uuid4())}
@ -358,7 +361,9 @@ class AuthController(object):
validation = self.validate_token(token)
if not validation:
return HTTPNotFound()
groups = ['%s:%s' % (validation[1], validation[2]), validation[1]]
groups = [
'%s%s:%s' % (self.reseller_prefix, validation[1], validation[2]),
'%s%s' % (self.reseller_prefix, validation[1])]
if validation[3]: # admin access to a cfaccount
groups.append(validation[3])
return HTTPNoContent(headers={'X-Auth-TTL': validation[0],
@ -482,7 +487,7 @@ class AuthController(object):
if row:
token = row[0]
else:
token = 'tk' + str(uuid4())
token = '%stk%s' % (self.reseller_prefix, uuid4().hex)
conn.execute('''
INSERT INTO token
(token, created, account, user, cfaccount)

View File

@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from urlparse import urlparse
def clean_acl(name, value):
"""
Returns a cleaned ACL header value, validating that it meets the formatting
@ -27,30 +30,35 @@ def clean_acl(name, value):
The referrer designation format is::
.ref:[-]value
.r:[-]value
The value can be "any" to specify any referrer host is allowed access, a
specific host name like "www.example.com", or if it has a leading period
"." it is a domain name specification, like ".example.com". The leading
minus sign "-" indicates referrer hosts that should be denied access.
The ``.r`` can also be ``.ref``, ``.referer``, or ``.referrer``; though it
will be shortened to just ``.r`` for decreased character count usage.
The value can be ``*`` to specify any referrer host is allowed access, a
specific host name like ``www.example.com``, or if it has a leading period
``.`` or leading ``*.`` it is a domain name specification, like
``.example.com`` or ``*.example.com``. The leading minus sign ``-``
indicates referrer hosts that should be denied access.
Referrer access is applied in the order they are specified. For example,
.ref:.example.com,.ref:-thief.example.com would allow all hosts ending with
.r:.example.com,.r:-thief.example.com would allow all hosts ending with
.example.com except for the specific host thief.example.com.
Example valid ACLs::
.ref:any
.ref:any,.ref:-.thief.com
.ref:any,.ref:-.thief.com,bobs_account,sues_account:sue
.r:*
.r:*,.r:-.thief.com
.r:*,.r:.example.com,.r:-thief.example.com
.r:*,.r:-.thief.com,bobs_account,sues_account:sue
bobs_account,sues_account:sue
Example invalid ACLs::
.ref:
.ref:-
.r:
.r:-
Also, .ref designations aren't allowed in headers whose names include the
Also, .r designations aren't allowed in headers whose names include the
word 'write'.
ACLs that are "messy" will be cleaned up. Examples:
@ -58,10 +66,11 @@ def clean_acl(name, value):
====================== ======================
Original Cleaned
---------------------- ----------------------
bob, sue bob,sue
bob , sue bob,sue
bob,,,sue bob,sue
.ref : any .ref:any
``bob, sue`` ``bob,sue``
``bob , sue`` ``bob,sue``
``bob,,,sue`` ``bob,sue``
``.referrer : *`` ``.r:*``
``.ref:*.example.com`` ``.r:.example.com``
====================== ======================
:param name: The name of the header being cleaned, such as X-Container-Read
@ -71,30 +80,34 @@ def clean_acl(name, value):
:raises ValueError: If the value does not meet the ACL formatting
requirements; the error message will indicate why.
"""
name = name.lower()
values = []
for raw_value in value.lower().split(','):
for raw_value in value.split(','):
raw_value = raw_value.strip()
if raw_value:
if ':' not in raw_value:
values.append(raw_value)
else:
first, second = (v.strip() for v in raw_value.split(':', 1))
if first != '.ref':
if not first or first[0] != '.':
values.append(raw_value)
elif 'write' in name:
raise ValueError('Referrers not allowed in write ACLs: %s'
% repr(raw_value))
elif not second:
raise ValueError('No value after referrer designation in '
'%s' % repr(raw_value))
else:
if second[0] == '-':
elif first in ('.r', '.ref', '.referer', '.referrer'):
if 'write' in name:
raise ValueError('Referrers not allowed in write ACL: '
'%s' % repr(raw_value))
negate = False
if second and second[0] == '-':
negate = True
second = second[1:].strip()
if not second:
raise ValueError('No value after referrer deny '
'designation in %s' % repr(raw_value))
second = '-' + second
values.append('%s:%s' % (first, second))
if second and second != '*' and second[0] == '*':
second = second[1:].strip()
if not second or second == '.':
raise ValueError('No host/domain value after referrer '
'designation in ACL: %s' % repr(raw_value))
values.append('.r:%s%s' % (negate and '-' or '', second))
else:
raise ValueError('Unknown designator %s in ACL: %s' %
(repr(first), repr(raw_value)))
return ','.join(values)
@ -106,15 +119,15 @@ def parse_acl(acl_string):
:param acl_string: The standard Swift ACL string to parse.
:returns: A tuple of (referrers, groups) where referrers is a list of
referrer designations (without the leading .ref:) and groups is a
referrer designations (without the leading .r:) and groups is a
list of groups to allow access.
"""
referrers = []
groups = []
if acl_string:
for value in acl_string.split(','):
if value.startswith('.ref:'):
referrers.append(value[len('.ref:'):])
if value.startswith('.r:'):
referrers.append(value[len('.r:'):])
else:
groups.append(value)
return referrers, groups
@ -134,24 +147,14 @@ def referrer_allowed(referrer, referrer_acl):
"""
allow = False
if referrer_acl:
if not referrer:
rhost = 'unknown'
else:
parts = referrer.split('//', 1)
if len(parts) == 2:
rhost = parts[1].split('/', 1)[0]
if '@' in rhost:
rhost = rhost.rsplit('@', 1)[1]
rhost = rhost.split(':', 1)[0].lower()
else:
rhost = 'unknown'
rhost = urlparse(referrer or '').hostname or 'unknown'
for mhost in referrer_acl:
if mhost[0] == '-':
mhost = mhost[1:]
if mhost == rhost or \
(mhost[0] == '.' and rhost.endswith(mhost)):
allow = False
elif mhost == 'any' or mhost == rhost or \
elif mhost == '*' or mhost == rhost or \
(mhost[0] == '.' and rhost.endswith(mhost)):
allow = True
return allow

View File

@ -29,6 +29,9 @@ class DevAuth(object):
def __init__(self, app, conf):
self.app = app
self.conf = conf
self.reseller_prefix = conf.get('reseller_prefix', '').strip()
if self.reseller_prefix and self.reseller_prefix[-1] != '_':
self.reseller_prefix += '_'
self.auth_host = conf.get('ip', '127.0.0.1')
self.auth_port = int(conf.get('port', 11000))
self.ssl = \
@ -44,7 +47,7 @@ class DevAuth(object):
"""
groups = None
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
if token:
if token and token.startswith(self.reseller_prefix):
memcache_client = cache_from_env(env)
key = 'devauth/%s' % token
cached_auth_data = memcache_client.get(key)
@ -68,6 +71,10 @@ class DevAuth(object):
env['REMOTE_USER'] = groups
env['swift.authorize'] = self.authorize
env['swift.clean_acl'] = clean_acl
# We know the proxy logs the token, so we augment it just a bit to also
# log the authenticated user.
user = groups and groups.split(',', 1)[0] or ''
env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token)
return self.app(env, start_response)
def authorize(self, req):

View File

@ -882,6 +882,7 @@ class ContainerController(Controller):
@public
def PUT(self, req):
"""HTTP PUT request handler."""
self.clean_acls(req)
error_response = check_metadata(req, 'container')
if error_response:
return error_response
@ -899,7 +900,6 @@ class ContainerController(Controller):
self.account_name, self.container_name)
headers = {'X-Timestamp': normalize_timestamp(time.time()),
'x-cf-trans-id': self.trans_id}
self.clean_acls(req)
headers.update(value for value in req.headers.iteritems()
if value[0].lower() in self.pass_through_headers or
value[0].lower().startswith('x-container-meta-'))
@ -948,6 +948,7 @@ class ContainerController(Controller):
@public
def POST(self, req):
"""HTTP POST request handler."""
self.clean_acls(req)
error_response = check_metadata(req, 'container')
if error_response:
return error_response
@ -958,7 +959,6 @@ class ContainerController(Controller):
self.account_name, self.container_name)
headers = {'X-Timestamp': normalize_timestamp(time.time()),
'x-cf-trans-id': self.trans_id}
self.clean_acls(req)
headers.update(value for value in req.headers.iteritems()
if value[0].lower() in self.pass_through_headers or
value[0].lower().startswith('x-container-meta-'))
@ -1273,12 +1273,14 @@ class BaseApplication(object):
# controller's method indicates it'd like to gather more
# information and try again later.
resp = req.environ['swift.authorize'](req)
if resp:
if not getattr(handler, 'delay_denial', None) and \
'swift.authorize' in req.environ:
return resp
else:
if not resp:
# No resp means authorized, no delayed recheck required.
del req.environ['swift.authorize']
else:
# Response indicates denial, but we might delay the denial
# and recheck later. If not delayed, return the error now.
if not getattr(handler, 'delay_denial', None):
return resp
return handler(req)
except Exception:
self.logger.exception('ERROR Unhandled exception in request')
@ -1331,8 +1333,7 @@ class Application(BaseApplication):
status_int,
req.referer or '-',
req.user_agent or '-',
'%s:%s' % (req.remote_user or '',
req.headers.get('x-auth-token', '-')),
req.headers.get('x-auth-token', '-'),
getattr(req, 'bytes_transferred', 0) or '-',
getattr(response, 'bytes_transferred', 0) or '-',
req.headers.get('etag', '-'),

View File

@ -2,6 +2,7 @@
auth_host = 127.0.0.1
auth_port = 11000
auth_ssl = no
auth_prefix = AUTH
# Primary functional test account (needs admin access to the account)
account = test

View File

@ -10,6 +10,9 @@ from swift.common.client import get_auth, http_connection
swift_test_auth = os.environ.get('SWIFT_TEST_AUTH')
swift_test_auth_prefix = os.environ.get('SWIFT_TEST_AUTH_PREFIX')
if swift_test_auth_prefix and swift_test_auth_prefix[-1] != '_':
swift_test_auth_prefix += '_'
swift_test_user = [os.environ.get('SWIFT_TEST_USER'), None, None]
swift_test_key = [os.environ.get('SWIFT_TEST_KEY'), None, None]
@ -32,6 +35,9 @@ if not all([swift_test_auth, swift_test_user[0], swift_test_key[0]]):
if conf.get('auth_ssl', 'no').lower() in ('yes', 'true', 'on', '1'):
swift_test_auth = 'https'
swift_test_auth += '://%(auth_host)s:%(auth_port)s/v1.0' % conf
swift_test_auth_prefix = conf.get('auth_prefix', 'AUTH')
if swift_test_auth_prefix and swift_test_auth_prefix[-1] != '_':
swift_test_auth_prefix += '_'
swift_test_user[0] = '%(account)s:%(username)s' % conf
swift_test_key[0] = conf['password']
try:

View File

@ -9,7 +9,7 @@ from swift.common.constraints import MAX_META_COUNT, MAX_META_NAME_LENGTH, \
MAX_META_OVERALL_SIZE, MAX_META_VALUE_LENGTH
from swift_testing import check_response, retry, skip, skip2, skip3, \
swift_test_user
swift_test_auth_prefix, swift_test_user
class TestContainer(unittest.TestCase):
@ -334,7 +334,7 @@ class TestContainer(unittest.TestCase):
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.name, '',
{'X-Auth-Token': token,
'X-Container-Read': '.ref:any'})
'X-Container-Read': '.r:*'})
return check_response(conn)
resp = retry(post)
resp.read()
@ -377,10 +377,10 @@ class TestContainer(unittest.TestCase):
self.assertEquals(resp.status, 403)
# Make the container accessible by the second account
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.name, '',
{'X-Auth-Token': token,
'X-Container-Read': 'test2',
'X-Container-Write': 'test2'})
conn.request('POST', parsed.path + '/' + self.name, '', {
'X-Auth-Token': token,
'X-Container-Read': '%stest2' % swift_test_auth_prefix,
'X-Container-Write': '%stest2' % swift_test_auth_prefix})
return check_response(conn)
resp = retry(post)
resp.read()
@ -427,7 +427,7 @@ class TestContainer(unittest.TestCase):
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.name, '',
{'X-Auth-Token': token,
'X-Container-Read': '.ref:any'})
'X-Container-Read': '.r:*'})
return check_response(conn)
resp = retry(post)
resp.read()
@ -446,9 +446,9 @@ class TestContainer(unittest.TestCase):
self.assertEquals(resp.status, 403)
# Now make the container also writeable by the second account
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.name, '',
{'X-Auth-Token': token,
'X-Container-Write': 'test2'})
conn.request('POST', parsed.path + '/' + self.name, '', {
'X-Auth-Token': token,
'X-Container-Write': '%stest2' % swift_test_auth_prefix})
return check_response(conn)
resp = retry(post)
resp.read()
@ -485,7 +485,9 @@ class TestContainer(unittest.TestCase):
# Make the container accessible by the third account
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.name, '',
{'X-Auth-Token': token, 'X-Container-Read': swift_test_user[2]})
{'X-Auth-Token': token,
'X-Container-Read': '%s%s' %
(swift_test_auth_prefix, swift_test_user[2])})
return check_response(conn)
resp = retry(post)
resp.read()
@ -506,7 +508,8 @@ class TestContainer(unittest.TestCase):
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.name, '',
{'X-Auth-Token': token,
'X-Container-Write': swift_test_user[2]})
'X-Container-Write': '%s%s' %
(swift_test_auth_prefix, swift_test_user[2])})
return check_response(conn)
resp = retry(post)
resp.read()

View File

@ -65,7 +65,7 @@ class TestObject(unittest.TestCase):
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.container, '',
{'X-Auth-Token': token,
'X-Container-Read': '.ref:any'})
'X-Container-Read': '.r:*'})
return check_response(conn)
resp = retry(post)
resp.read()

View File

@ -21,56 +21,71 @@ from swift.common.middleware import acl
class TestACL(unittest.TestCase):
def test_clean_acl(self):
value = acl.clean_acl('header', '.ref:any')
self.assertEquals(value, '.ref:any')
value = acl.clean_acl('header', '.ref:specific.host')
self.assertEquals(value, '.ref:specific.host')
value = acl.clean_acl('header', '.ref:.ending.with')
self.assertEquals(value, '.ref:.ending.with')
value = acl.clean_acl('header', '.ref:one,.ref:two')
self.assertEquals(value, '.ref:one,.ref:two')
value = acl.clean_acl('header', '.ref:any,.ref:-specific.host')
self.assertEquals(value, '.ref:any,.ref:-specific.host')
value = acl.clean_acl('header', '.ref:any,.ref:-.ending.with')
self.assertEquals(value, '.ref:any,.ref:-.ending.with')
value = acl.clean_acl('header', '.ref:one,.ref:-two')
self.assertEquals(value, '.ref:one,.ref:-two')
value = acl.clean_acl('header',
'.ref:one,.ref:-two,account,account:user')
self.assertEquals(value, '.ref:one,.ref:-two,account,account:user')
value = acl.clean_acl('header', '.r:*')
self.assertEquals(value, '.r:*')
value = acl.clean_acl('header', '.r:specific.host')
self.assertEquals(value, '.r:specific.host')
value = acl.clean_acl('header', '.r:.ending.with')
self.assertEquals(value, '.r:.ending.with')
value = acl.clean_acl('header', '.r:*.ending.with')
self.assertEquals(value, '.r:.ending.with')
value = acl.clean_acl('header', '.r:-*.ending.with')
self.assertEquals(value, '.r:-.ending.with')
value = acl.clean_acl('header', '.r:one,.r:two')
self.assertEquals(value, '.r:one,.r:two')
value = acl.clean_acl('header', '.r:*,.r:-specific.host')
self.assertEquals(value, '.r:*,.r:-specific.host')
value = acl.clean_acl('header', '.r:*,.r:-.ending.with')
self.assertEquals(value, '.r:*,.r:-.ending.with')
value = acl.clean_acl('header', '.r:one,.r:-two')
self.assertEquals(value, '.r:one,.r:-two')
value = acl.clean_acl('header', '.r:one,.r:-two,account,account:user')
self.assertEquals(value, '.r:one,.r:-two,account,account:user')
value = acl.clean_acl('header', 'TEST_account')
self.assertEquals(value, 'TEST_account')
value = acl.clean_acl('header', '.ref:*')
self.assertEquals(value, '.r:*')
value = acl.clean_acl('header', '.referer:*')
self.assertEquals(value, '.r:*')
value = acl.clean_acl('header', '.referrer:*')
self.assertEquals(value, '.r:*')
value = acl.clean_acl('header',
' .ref : one , ,, .ref:two , .ref : - three ')
self.assertEquals(value, '.ref:one,.ref:two,.ref:-three')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.ref:')
self.assertRaises(ValueError, acl.clean_acl, 'header', ' .ref : ')
' .r : one , ,, .r:two , .r : - three ')
self.assertEquals(value, '.r:one,.r:two,.r:-three')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.unknown:test')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.r:')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.r:*.')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.r : * . ')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.r:-*.')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.r : - * . ')
self.assertRaises(ValueError, acl.clean_acl, 'header', ' .r : ')
self.assertRaises(ValueError, acl.clean_acl, 'header', 'user , .r : ')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.r:-')
self.assertRaises(ValueError, acl.clean_acl, 'header', ' .r : - ')
self.assertRaises(ValueError, acl.clean_acl, 'header',
'user , .ref : ')
self.assertRaises(ValueError, acl.clean_acl, 'header', '.ref:-')
self.assertRaises(ValueError, acl.clean_acl, 'header', ' .ref : - ')
self.assertRaises(ValueError, acl.clean_acl, 'header',
'user , .ref : - ')
self.assertRaises(ValueError, acl.clean_acl, 'write-header', '.ref:r')
'user , .r : - ')
self.assertRaises(ValueError, acl.clean_acl, 'write-header', '.r:r')
def test_parse_acl(self):
self.assertEquals(acl.parse_acl(None), ([], []))
self.assertEquals(acl.parse_acl(''), ([], []))
self.assertEquals(acl.parse_acl('.ref:ref1'), (['ref1'], []))
self.assertEquals(acl.parse_acl('.ref:-ref1'), (['-ref1'], []))
self.assertEquals(acl.parse_acl('.r:ref1'), (['ref1'], []))
self.assertEquals(acl.parse_acl('.r:-ref1'), (['-ref1'], []))
self.assertEquals(acl.parse_acl('account:user'),
([], ['account:user']))
self.assertEquals(acl.parse_acl('account'), ([], ['account']))
self.assertEquals(acl.parse_acl('acc1,acc2:usr2,.ref:ref3,.ref:-ref4'),
self.assertEquals(acl.parse_acl('acc1,acc2:usr2,.r:ref3,.r:-ref4'),
(['ref3', '-ref4'], ['acc1', 'acc2:usr2']))
self.assertEquals(acl.parse_acl(
'acc1,acc2:usr2,.ref:ref3,acc3,acc4:usr4,.ref:ref5,.ref:-ref6'),
'acc1,acc2:usr2,.r:ref3,acc3,acc4:usr4,.r:ref5,.r:-ref6'),
(['ref3', 'ref5', '-ref6'],
['acc1', 'acc2:usr2', 'acc3', 'acc4:usr4']))
def test_referrer_allowed(self):
self.assert_(not acl.referrer_allowed('host', None))
self.assert_(not acl.referrer_allowed('host', []))
self.assert_(acl.referrer_allowed(None, ['any']))
self.assert_(acl.referrer_allowed('', ['any']))
self.assert_(acl.referrer_allowed(None, ['*']))
self.assert_(acl.referrer_allowed('', ['*']))
self.assert_(not acl.referrer_allowed(None, ['specific.host']))
self.assert_(not acl.referrer_allowed('', ['specific.host']))
self.assert_(acl.referrer_allowed('http://www.example.com/index.html',
@ -93,7 +108,7 @@ class TestACL(unittest.TestCase):
self.assert_(not acl.referrer_allowed('http://thief.example.com',
['.example.com', '-thief.example.com']))
self.assert_(not acl.referrer_allowed('http://thief.example.com',
['any', '-thief.example.com']))
['*', '-thief.example.com']))
self.assert_(acl.referrer_allowed('http://www.example.com',
['.other.com', 'www.example.com']))
self.assert_(acl.referrer_allowed('http://www.example.com',
@ -104,7 +119,7 @@ class TestACL(unittest.TestCase):
['.example.com']))
self.assert_(not acl.referrer_allowed('../index.html',
['.example.com']))
self.assert_(acl.referrer_allowed('www.example.com', ['any']))
self.assert_(acl.referrer_allowed('www.example.com', ['*']))
if __name__ == '__main__':

View File

@ -253,31 +253,31 @@ class TestAuth(unittest.TestCase):
self.assert_(resp.startswith('403'), resp)
req = Request.blank('/v1/cfa')
req.remote_user = 'act:usr,act'
req.acl = '.ref:any'
req.acl = '.r:*'
self.assertEquals(self.test_auth.authorize(req), None)
req = Request.blank('/v1/cfa')
req.remote_user = 'act:usr,act'
req.acl = '.ref:.example.com'
req.acl = '.r:.example.com'
resp = str(self.test_auth.authorize(req))
self.assert_(resp.startswith('403'), resp)
req = Request.blank('/v1/cfa')
req.remote_user = 'act:usr,act'
req.referrer = 'http://www.example.com/index.html'
req.acl = '.ref:.example.com'
req.referer = 'http://www.example.com/index.html'
req.acl = '.r:.example.com'
self.assertEquals(self.test_auth.authorize(req), None)
req = Request.blank('/v1/cfa')
resp = str(self.test_auth.authorize(req))
self.assert_(resp.startswith('401'), resp)
req = Request.blank('/v1/cfa')
req.acl = '.ref:any'
req.acl = '.r:*'
self.assertEquals(self.test_auth.authorize(req), None)
req = Request.blank('/v1/cfa')
req.acl = '.ref:.example.com'
req.acl = '.r:.example.com'
resp = str(self.test_auth.authorize(req))
self.assert_(resp.startswith('401'), resp)
req = Request.blank('/v1/cfa')
req.referrer = 'http://www.example.com/index.html'
req.acl = '.ref:.example.com'
req.referer = 'http://www.example.com/index.html'
req.acl = '.r:.example.com'
self.assertEquals(self.test_auth.authorize(req), None)

View File

@ -67,14 +67,13 @@ class TestContainerController(unittest.TestCase):
self.assert_('x-container-write' not in response.headers)
# Ensure POSTing acls works
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
headers={'X-Timestamp': '1', 'X-Container-Read': '.ref:any',
headers={'X-Timestamp': '1', 'X-Container-Read': '.r:*',
'X-Container-Write': 'account:user'})
self.controller.POST(req)
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
response = self.controller.HEAD(req)
self.assert_(response.status.startswith('204'))
self.assertEquals(response.headers.get('x-container-read'),
'.ref:any')
self.assertEquals(response.headers.get('x-container-read'), '.r:*')
self.assertEquals(response.headers.get('x-container-write'),
'account:user')
# Ensure we can clear acls on POST
@ -89,14 +88,13 @@ class TestContainerController(unittest.TestCase):
self.assert_('x-container-write' not in response.headers)
# Ensure PUTing acls works
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
headers={'X-Timestamp': '4', 'X-Container-Read': '.ref:any',
headers={'X-Timestamp': '4', 'X-Container-Read': '.r:*',
'X-Container-Write': 'account:user'})
self.controller.PUT(req)
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'HEAD'})
response = self.controller.HEAD(req)
self.assert_(response.status.startswith('204'))
self.assertEquals(response.headers.get('x-container-read'),
'.ref:any')
self.assertEquals(response.headers.get('x-container-read'), '.r:*')
self.assertEquals(response.headers.get('x-container-write'),
'account:user')

View File

@ -1962,7 +1962,7 @@ class TestContainerController(unittest.TestCase):
controller = proxy_server.ContainerController(self.app, 'account',
'container')
req = Request.blank('/a/c', environ={'REQUEST_METHOD': 'POST'},
headers={'X-Container-Read': '.ref:any'})
headers={'X-Container-Read': '.r:*'})
req.environ['swift.clean_acl'] = clean_acl
self.app.update_request(req)
res = controller.POST(req)
@ -1973,7 +1973,7 @@ class TestContainerController(unittest.TestCase):
controller = proxy_server.ContainerController(self.app, 'account',
'container')
req = Request.blank('/a/c', environ={'REQUEST_METHOD': 'POST'},
headers={'X-Container-Write': '.ref:any'})
headers={'X-Container-Write': '.r:*'})
req.environ['swift.clean_acl'] = clean_acl
self.app.update_request(req)
res = controller.POST(req)
@ -1989,7 +1989,7 @@ class TestContainerController(unittest.TestCase):
controller = proxy_server.ContainerController(self.app, 'account',
'container')
req = Request.blank('/a/c', environ={'REQUEST_METHOD': 'PUT'},
headers={'X-Container-Read': '.ref:any'})
headers={'X-Container-Read': '.r:*'})
req.environ['swift.clean_acl'] = clean_acl
self.app.update_request(req)
res = controller.PUT(req)
@ -2000,7 +2000,7 @@ class TestContainerController(unittest.TestCase):
controller = proxy_server.ContainerController(self.app, 'account',
'container')
req = Request.blank('/a/c', environ={'REQUEST_METHOD': 'PUT'},
headers={'X-Container-Write': '.ref:any'})
headers={'X-Container-Write': '.r:*'})
req.environ['swift.clean_acl'] = clean_acl
self.app.update_request(req)
res = controller.PUT(req)