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:
parent
85b8d97086
commit
d2ec027e22
5
bin/st
5
bin/st
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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', '-'),
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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__':
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user