def _validate_user_rbac(auth_info, username, client=None): """Check if a user is active and/or superuser via RBAC.""" if client is None: from maasserver.rbac import RBACClient client = RBACClient() try: is_admin = bool( client.allowed_for_user("maas", username, "admin")["admin"] ) access_to_pools = any( client.allowed_for_user( "resource-pool", username, "view", "view-all", "deploy-machines", "admin-machines", ).values() ) user_details = client.get_user_details(username) except APIError: raise UserValidationFailed() return (is_admin or access_to_pools, is_admin, user_details)
def setUp(self): super().setUp() key = PrivateKey.deserialize( 'x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=') agent = Agent(url='https://auth.example.com', username='******') auth_info = AuthInfo(key=key, agents=[agent]) url = 'https://rbac.example.com/' self.mock_request = self.patch(requests, 'request') self.client = RBACClient(url=url, auth_info=auth_info)
def _validate_user_rbac(auth_info, username, client=None): """Check if a user is active and/or superuser via RBAC.""" if client is None: from maasserver.rbac import RBACClient client = RBACClient() try: is_admin = bool( client.allowed_for_user('maas', username, 'admin')['admin']) access_to_pools = any( client.allowed_for_user('resource-pool', username, 'view', 'view-all', 'deploy-machines', 'admin-machines').values()) except APIError: raise UserValidationFailed() return is_admin or access_to_pools, is_admin
def _validate_user_rbac(auth_info, username, client=None): """Check if a user is active and/or superuser via RBAC.""" if client is None: from maasserver.rbac import RBACClient client = RBACClient() try: admin_resource = client.allowed_for_user('maas', username, 'admin') pool_resources = client.allowed_for_user('maas', username, 'resource-pool') except APIError: raise UserValidationFailed() # and is active if it has access to any resource active = any([admin_resource, pool_resources]) # and is superuser if the admin resource is returned superuser = bool(admin_resource) return active, superuser
def test_default_config_from_settings(self): Config.objects.set_config('rbac_url', 'https://rbac.example.com') Config.objects.set_config('external_auth_url', 'https://auth.example.com') Config.objects.set_config('external_auth_user', 'user@candid') Config.objects.set_config( 'external_auth_key', 'x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=') client = RBACClient() self.assertEqual(client._url, 'https://rbac.example.com') self.assertEqual( client._auth_info.key, PrivateKey.deserialize( 'x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=')) [agent] = client._auth_info.agents self.assertEqual(agent.url, 'https://auth.example.com') self.assertEqual(agent.username, 'user@candid')
def _getRBACClient(self): """Return the `RBACClient`. This tries to use an already held client when initialized because the cookiejar will be updated with the already authenticated macaroon. """ url = Config.objects.get_config('rbac_url') if not url: # RBAC is not enabled (or no longer enabled). self.rbacClient = None return None auth_info = get_auth_info() if (self.rbacClient is None or self.rbacClient._url != url or self.rbacClient._auth_info != auth_info): self.rbacClient = RBACClient(url, auth_info) return self.rbacClient
def test_default_config_from_settings(self): Config.objects.set_config("rbac_url", "https://rbac.example.com") Config.objects.set_config("external_auth_url", "https://auth.example.com") Config.objects.set_config("external_auth_user", "user@candid") Config.objects.set_config( "external_auth_key", "x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=") client = RBACClient() self.assertEqual(client._url, "https://rbac.example.com") self.assertEqual( client._auth_info.key, PrivateKey.deserialize( "x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY="), ) [agent] = client._auth_info.agents self.assertEqual(agent.url, "https://auth.example.com") self.assertEqual(agent.username, "user@candid")
class TestRBACClient(MAASServerTestCase): def setUp(self): super().setUp() key = PrivateKey.deserialize( 'x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=') agent = Agent(url='https://auth.example.com', username='******') auth_info = AuthInfo(key=key, agents=[agent]) url = 'https://rbac.example.com/' self.mock_request = self.patch(requests, 'request') self.client = RBACClient(url=url, auth_info=auth_info) def test_default_config_from_settings(self): Config.objects.set_config('rbac_url', 'https://rbac.example.com') Config.objects.set_config('external_auth_url', 'https://auth.example.com') Config.objects.set_config('external_auth_user', 'user@candid') Config.objects.set_config( 'external_auth_key', 'x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=') client = RBACClient() self.assertEqual(client._url, 'https://rbac.example.com') self.assertEqual( client._auth_info.key, PrivateKey.deserialize( 'x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=')) [agent] = client._auth_info.agents self.assertEqual(agent.url, 'https://auth.example.com') self.assertEqual(agent.username, 'user@candid') def test_get_resources(self): resources = [ { 'identifier': '1', 'name': 'pool-1', }, { 'identifier': '2', 'name': 'pool-2', }, ] response = mock.MagicMock(status_code=200) response.json.return_value = resources self.mock_request.return_value = response self.assertCountEqual(self.client.get_resources('resource-pool'), [ Resource(identifier='1', name='pool-1'), Resource(identifier='2', name='pool-2'), ]) self.assertThat( self.mock_request, MockCalledOnceWith('GET', 'https://rbac.example.com/api/' 'service/1.0/resources/resource-pool', auth=mock.ANY, cookies=mock.ANY, json=None)) def test_update_resources(self): updates = [ Resource(identifier='1', name='pool-1'), Resource(identifier='2', name='pool-2'), ] removals = [11, 22, 33] json = { 'last-sync-id': 'a-b-c', 'updates': [ { 'identifier': '1', 'name': 'pool-1', }, { 'identifier': '2', 'name': 'pool-2', }, ], 'removals': ['11', '22', '33'] } response = mock.MagicMock(status_code=200) response.json.return_value = {'sync-id': 'x-y-z'} self.mock_request.return_value = response sync_id = self.client.update_resources('resource-pool', updates=updates, removals=removals, last_sync_id='a-b-c') self.assertEqual(sync_id, 'x-y-z') self.assertThat( self.mock_request, MockCalledOnceWith('POST', 'https://rbac.example.com/api/' 'service/1.0/resources/resource-pool', auth=mock.ANY, cookies=mock.ANY, json=json)) def test_update_resources_no_sync_id(self): updates = [ Resource(identifier='1', name='pool-1'), Resource(identifier='2', name='pool-2'), ] removals = [11, 22, 33] # removals are ignored json = { 'last-sync-id': None, 'updates': [ { 'identifier': '1', 'name': 'pool-1', }, { 'identifier': '2', 'name': 'pool-2', }, ], 'removals': [] } response = mock.MagicMock(status_code=200) response.json.return_value = {'sync-id': 'x-y-z'} self.mock_request.return_value = response sync_id = self.client.update_resources('resource-pool', updates=updates, removals=removals) self.assertEqual(sync_id, 'x-y-z') self.assertThat( self.mock_request, MockCalledOnceWith('POST', 'https://rbac.example.com/api/' 'service/1.0/resources/resource-pool', auth=mock.ANY, cookies=mock.ANY, json=json)) def test_update_resources_sync_conflict(self): updates = [ Resource(identifier='1', name='pool-1'), Resource(identifier='2', name='pool-2'), ] removals = [11, 22, 33] response = mock.MagicMock(status_code=int(http.client.CONFLICT)) response.json.return_value = {'sync-id': 'x-y-z'} self.mock_request.return_value = response self.assertRaises(SyncConflictError, self.client.update_resources, 'resource-pool', updates=updates, removals=removals, last_sync_id='a-b-c') def test_allowed_for_user_all_resources(self): response = mock.MagicMock(status_code=200) response.json.return_value = [""] self.mock_request.return_value = response user = factory.make_name('user') self.assertEqual(ALL_RESOURCES, self.client.allowed_for_user('maas', user, 'admin')) self.assertThat( self.mock_request, MockCalledOnceWith( 'GET', 'https://rbac.example.com/api/' 'service/1.0/resources/maas/' 'allowed-for-user?user={}&permission=admin'.format(user), auth=mock.ANY, cookies=mock.ANY, json=None)) def test_allowed_for_user_resource_ids(self): response = mock.MagicMock(status_code=200) response.json.return_value = ["1", "2", "3"] self.mock_request.return_value = response user = factory.make_name('user') self.assertEqual([1, 2, 3], self.client.allowed_for_user('maas', user, 'admin')) self.assertThat( self.mock_request, MockCalledOnceWith( 'GET', 'https://rbac.example.com/api/' 'service/1.0/resources/maas/' 'allowed-for-user?user={}&permission=admin'.format(user), auth=mock.ANY, cookies=mock.ANY, json=None))
class TestRBACClient(MAASServerTestCase): def setUp(self): super().setUp() key = PrivateKey.deserialize( "x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=") agent = Agent(url="https://auth.example.com", username="******") auth_info = AuthInfo(key=key, agents=[agent]) url = "https://rbac.example.com/" self.mock_request = self.patch(requests, "request") self.client = RBACClient(url=url, auth_info=auth_info) def test_default_config_from_settings(self): Config.objects.set_config("rbac_url", "https://rbac.example.com") Config.objects.set_config("external_auth_url", "https://auth.example.com") Config.objects.set_config("external_auth_user", "user@candid") Config.objects.set_config( "external_auth_key", "x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY=") client = RBACClient() self.assertEqual(client._url, "https://rbac.example.com") self.assertEqual( client._auth_info.key, PrivateKey.deserialize( "x0NeASLPFhOFfq3Q9M0joMveI4HjGwEuJ9dtX/HTSRY="), ) [agent] = client._auth_info.agents self.assertEqual(agent.url, "https://auth.example.com") self.assertEqual(agent.username, "user@candid") def test_get_user(self): response = mock.MagicMock(status_code=200) response.json.return_value = { "username": "******", "name": "A user", "email": "*****@*****.**", } self.mock_request.return_value = response details = self.client.get_user_details("user") self.assertEqual(details.username, "user") self.assertEqual(details.fullname, "A user") self.assertEqual(details.email, "*****@*****.**") self.mock_request.assert_called_once_with( "GET", "https://rbac.example.com/api/service/v1/user/user", auth=mock.ANY, cookies=mock.ANY, json=None, ) def test_get_resources(self): resources = [ { "identifier": "1", "name": "pool-1" }, { "identifier": "2", "name": "pool-2" }, ] response = mock.MagicMock(status_code=200) response.json.return_value = resources self.mock_request.return_value = response self.assertCountEqual( self.client.get_resources("resource-pool"), [ Resource(identifier="1", name="pool-1"), Resource(identifier="2", name="pool-2"), ], ) self.assertThat( self.mock_request, MockCalledOnceWith( "GET", "https://rbac.example.com/api/" "service/v1/resources/resource-pool", auth=mock.ANY, cookies=mock.ANY, json=None, ), ) def test_update_resources(self): updates = [ Resource(identifier="1", name="pool-1"), Resource(identifier="2", name="pool-2"), ] removals = [11, 22, 33] json = { "last-sync-id": "a-b-c", "updates": [ { "identifier": "1", "name": "pool-1" }, { "identifier": "2", "name": "pool-2" }, ], "removals": ["11", "22", "33"], } response = mock.MagicMock(status_code=200) response.json.return_value = {"sync-id": "x-y-z"} self.mock_request.return_value = response sync_id = self.client.update_resources( "resource-pool", updates=updates, removals=removals, last_sync_id="a-b-c", ) self.assertEqual(sync_id, "x-y-z") self.assertThat( self.mock_request, MockCalledOnceWith( "POST", "https://rbac.example.com/api/" "service/v1/resources/resource-pool", auth=mock.ANY, cookies=mock.ANY, json=json, ), ) def test_update_resources_no_sync_id(self): updates = [ Resource(identifier="1", name="pool-1"), Resource(identifier="2", name="pool-2"), ] removals = [11, 22, 33] # removals are ignored json = { "last-sync-id": None, "updates": [ { "identifier": "1", "name": "pool-1" }, { "identifier": "2", "name": "pool-2" }, ], "removals": [], } response = mock.MagicMock(status_code=200) response.json.return_value = {"sync-id": "x-y-z"} self.mock_request.return_value = response sync_id = self.client.update_resources("resource-pool", updates=updates, removals=removals) self.assertEqual(sync_id, "x-y-z") self.assertThat( self.mock_request, MockCalledOnceWith( "POST", "https://rbac.example.com/api/" "service/v1/resources/resource-pool", auth=mock.ANY, cookies=mock.ANY, json=json, ), ) def test_update_resources_sync_conflict(self): updates = [ Resource(identifier="1", name="pool-1"), Resource(identifier="2", name="pool-2"), ] removals = [11, 22, 33] response = mock.MagicMock(status_code=int(http.client.CONFLICT)) response.json.return_value = {"sync-id": "x-y-z"} self.mock_request.return_value = response self.assertRaises( SyncConflictError, self.client.update_resources, "resource-pool", updates=updates, removals=removals, last_sync_id="a-b-c", ) def test_allowed_for_user_all_resources(self): response = mock.MagicMock(status_code=200) response.json.return_value = {"admin": [""]} self.mock_request.return_value = response user = factory.make_name("user") self.assertEqual( {"admin": ALL_RESOURCES}, self.client.allowed_for_user("maas", user, "admin"), ) self.assertThat( self.mock_request, MockCalledOnceWith( "GET", "https://rbac.example.com/api/" "service/v1/resources/maas/" "allowed-for-user?u={}&p=admin".format(user), auth=mock.ANY, cookies=mock.ANY, json=None, ), ) def test_allowed_for_user_resource_ids(self): response = mock.MagicMock(status_code=200) response.json.return_value = {"admin": ["1", "2", "3"]} self.mock_request.return_value = response user = factory.make_name("user") self.assertEqual( {"admin": [1, 2, 3]}, self.client.allowed_for_user("maas", user, "admin"), ) self.assertThat( self.mock_request, MockCalledOnceWith( "GET", "https://rbac.example.com/api/" "service/v1/resources/maas/" "allowed-for-user?u={}&p=admin".format(user), auth=mock.ANY, cookies=mock.ANY, json=None, ), )