From cf4f320644b63b71d0147474455f76555218d1c0 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Mon, 8 Feb 2021 12:37:17 -0800 Subject: [PATCH] tempauth: Add .reseller_reader group Change-Id: I8c5197ed327fbb175c8a2c0e788b1ae14e6dfe23 --- etc/proxy-server.conf-sample | 4 +- swift/common/middleware/tempauth.py | 17 ++++-- test/unit/common/middleware/test_tempauth.py | 62 +++++++++++++++++++- 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 503ba0483a..b474b27a36 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -388,8 +388,9 @@ use = egg:swift#tempauth # user64__ = [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 diff --git a/swift/common/middleware/tempauth.py b/swift/common/middleware/tempauth.py index 4083a3d945..b2520f7ead 100644 --- a/swift/common/middleware/tempauth.py +++ b/swift/common/middleware/tempauth.py @@ -54,12 +54,13 @@ in a line like this:: user64__ = [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 diff --git a/test/unit/common/middleware/test_tempauth.py b/test/unit/common/middleware/test_tempauth.py index 11edf3bf48..f985453ca3 100644 --- a/test/unit/common/middleware/test_tempauth.py +++ b/test/unit/common/middleware/test_tempauth.py @@ -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(