Merge "tempauth: Add .reseller_reader group"

This commit is contained in:
Zuul 2021-02-26 00:26:14 +00:00 committed by Gerrit Code Review
commit 0c2cc63b59
3 changed files with 75 additions and 8 deletions

View File

@ -388,8 +388,9 @@ use = egg:swift#tempauth
# 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
# .reseller_reader = can GET/HEAD anything in any account for this auth
# .admin = can do anything within the account
# If neither of these groups are specified, the user can only access containers
# If none of these groups are specified, the user can only access containers
# that have been explicitly allowed for them by a .admin or .reseller_admin.
# The trailing optional storage_url allows you to specify an alternate url to
# hand back to the user upon authentication. If not specified, this defaults to
@ -397,6 +398,7 @@ use = egg:swift#tempauth
# to what the requester would need to use to reach this host.
# Here are example entries, required for running the tests:
user_admin_admin = admin .admin .reseller_admin
user_admin_auditor = admin_ro .reseller_reader
user_test_tester = testing .admin
user_test_tester2 = testing2 .admin
user_test_tester3 = testing3

View File

@ -54,12 +54,13 @@ in a line like this::
user64_<account_b64>_<user_b64> = <key> [group] [...] [storage_url]
There are two special groups:
There are three special groups:
* ``.reseller_admin`` -- can do anything to any account for this auth
* ``.reseller_reader`` -- can GET/HEAD anything in any account for this auth
* ``.admin`` -- can do anything within the account
If neither of these groups are specified, the user can only access
If none of these groups are specified, the user can only access
containers that have been explicitly allowed for them by a ``.admin`` or
``.reseller_admin``.
@ -124,8 +125,8 @@ and ``X-Service-Token`` is from the ``glance`` user::
user_maryacct_mary = marypw .admin
user_glance_glance = glancepw .service
The name ``.service`` is an example. Unlike ``.admin`` and
``.reseller_admin`` it is not a reserved name.
The name ``.service`` is an example. Unlike ``.admin``, ``.reseller_admin``,
``.reseller_reader`` it is not a reserved name.
Please note that ACLs can be set on service accounts and are matched
against the identity validated by ``X-Auth-Token``. As such ACLs can grant
@ -569,6 +570,14 @@ class TempAuth(object):
% account_user)
return None
if '.reseller_reader' in user_groups and \
account not in self.reseller_prefixes and \
not self._dot_account(account) and \
req.method in ('GET', 'HEAD'):
self.logger.debug("User %s has reseller reader authorizing."
% account_user)
return None
if wsgi_to_str(account) in user_groups and \
(req.method not in ('DELETE', 'PUT') or container):
# The user is admin for the account and is not trying to do an

View File

@ -537,7 +537,7 @@ class TestAuth(unittest.TestCase):
def test_account_put_permissions(self):
self.test_auth = auth.filter_factory({})(
FakeApp(iter(NO_CONTENT_RESP * 4)))
FakeApp(iter(NO_CONTENT_RESP * 5)))
req = self._make_request('/v1/AUTH_new',
environ={'REQUEST_METHOD': 'PUT'})
req.remote_user = 'act:usr,act'
@ -563,6 +563,12 @@ class TestAuth(unittest.TestCase):
resp = self.test_auth.authorize(req)
self.assertIsNone(resp)
req = self._make_request('/v1/AUTH_new',
environ={'REQUEST_METHOD': 'PUT'})
req.remote_user = 'act:usr,act,.reseller_reader'
resp = self.test_auth.authorize(req)
self.assertEqual(resp.status_int, 403)
# .super_admin is not something the middleware should ever see or care
# about
req = self._make_request('/v1/AUTH_new',
@ -573,7 +579,7 @@ class TestAuth(unittest.TestCase):
def test_account_delete_permissions(self):
self.test_auth = auth.filter_factory({})(
FakeApp(iter(NO_CONTENT_RESP * 4)))
FakeApp(iter(NO_CONTENT_RESP * 5)))
req = self._make_request('/v1/AUTH_new',
environ={'REQUEST_METHOD': 'DELETE'})
req.remote_user = 'act:usr,act'
@ -599,6 +605,12 @@ class TestAuth(unittest.TestCase):
resp = self.test_auth.authorize(req)
self.assertIsNone(resp)
req = self._make_request('/v1/AUTH_new',
environ={'REQUEST_METHOD': 'DELETE'})
req.remote_user = 'act:usr,act,.reseller_reader'
resp = self.test_auth.authorize(req)
self.assertEqual(resp.status_int, 403)
# .super_admin is not something the middleware should ever see or care
# about
req = self._make_request('/v1/AUTH_new',
@ -824,9 +836,18 @@ class TestAuth(unittest.TestCase):
req = self._make_request('/v1/AUTH_cfa',
headers={'X-Auth-Token': 'AUTH_t'})
req.remote_user = '.reseller_admin'
self.test_auth.authorize(req)
resp = self.test_auth.authorize(req)
self.assertIsNone(resp)
self.assertEqual(owner_values, [True])
owner_values = []
req = self._make_request('/v1/AUTH_cfa',
headers={'X-Auth-Token': 'AUTH_t'})
req.remote_user = '.reseller_reader'
resp = self.test_auth.authorize(req)
self.assertIsNone(resp)
self.assertEqual(owner_values, [False])
def test_admin_is_owner(self):
orig_authorize = self.test_auth.authorize
owner_values = []
@ -1172,12 +1193,17 @@ class TestParseUserCreation(unittest.TestCase):
'user_test_tester3': 'testing',
'user_has_url': 'urlly .admin http://a.b/v1/DEF_has',
'user_admin_admin': 'admin .admin .reseller_admin',
'user_admin_auditor': 'admin_ro .reseller_reader',
})(FakeApp())
self.assertEqual(auth_filter.users, {
'admin:admin': {
'url': '$HOST/v1/ABC_admin',
'groups': ['.admin', '.reseller_admin'],
'key': 'admin'
}, 'admin:auditor': {
'url': '$HOST/v1/ABC_admin',
'groups': ['.reseller_reader'],
'key': 'admin_ro'
}, 'test:tester3': {
'url': '$HOST/v1/ABC_test',
'groups': [],
@ -1612,6 +1638,16 @@ class ServiceTokenFunctionality(unittest.TestCase):
{'reseller_prefix': 'AUTH'}, 'acct:joe,acct,AUTH_acct',
'/v1/AUTH_acct/c', method='PUT')
self.assertEqual(resp.status_int, 200)
resp = self._make_authed_request(
{'reseller_prefix': 'AUTH'},
'admin:mary,admin,AUTH_admin,.reseller_reader',
'/v1/AUTH_acct', method='GET')
self.assertEqual(resp.status_int, 200)
resp = self._make_authed_request(
{'reseller_prefix': 'AUTH'},
'admin:mary,admin,AUTH_admin,.reseller_reader',
'/v1/AUTH_acct/c', method='GET')
self.assertEqual(resp.status_int, 200)
resp = self._make_authed_request(
{'reseller_prefix': 'AUTH'},
'admin:mary,admin,AUTH_admin,.reseller_admin',
@ -1641,6 +1677,26 @@ class ServiceTokenFunctionality(unittest.TestCase):
'/v1/AUTH_acct',
method='DELETE')
self.assertEqual(resp.status_int, 403)
resp = self._make_authed_request(
{'reseller_prefix': 'AUTH'},
'admin:mary,admin,.admin,.reseller_reader',
'/v1/AUTH_acct', method='PUT')
self.assertEqual(resp.status_int, 403)
resp = self._make_authed_request(
{'reseller_prefix': 'AUTH'},
'admin:mary,admin,.admin,.reseller_reader',
'/v1/AUTH_acct', method='DELETE')
self.assertEqual(resp.status_int, 403)
resp = self._make_authed_request(
{'reseller_prefix': 'AUTH'},
'admin:mary,admin,.admin,.reseller_reader',
'/v1/AUTH_acct/c', method='PUT')
self.assertEqual(resp.status_int, 403)
resp = self._make_authed_request(
{'reseller_prefix': 'AUTH'},
'admin:mary,admin,.admin,.reseller_reader',
'/v1/AUTH_acct/c', method='DELETE')
self.assertEqual(resp.status_int, 403)
def test_authed_for_primary_path_multiple(self):
resp = self._make_authed_request(