def test_image_member_list(self): with mock.patch('glanceclient.v2.image_members.Controller.list', return_value=self.members_image_members): serialized = self.plugin.serialize(self.members_image) # Admin context fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search', is_admin=True ) result_hit = {'_source': copy.deepcopy(serialized)} # Expect to see all three tenants self.plugin.filter_result(result_hit, fake_request.context) self.assertEqual(set([TENANT1, TENANT2, TENANT3]), set(result_hit['_source']['members'])) fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search', is_admin=False ) result_hit = {'_source': copy.deepcopy(serialized)} # Tenant1 can see the image but doesn't own it self.plugin.filter_result(result_hit, fake_request.context) self.assertEqual(set([TENANT1]), set(result_hit['_source']['members'])) fake_request = unit_test_utils.get_fake_request( USER1, TENANT2, '/v1/search', is_admin=False ) result_hit = {'_source': copy.deepcopy(serialized)} # Tenant2 owns the image and should see all three members self.plugin.filter_result(result_hit, fake_request.context) self.assertEqual(set([TENANT1, TENANT2, TENANT3]), set(result_hit['_source']['members']))
def test_facets_all_projects(self): # For non admins, all_projects should have no effect mock_engine = mock.Mock() self.plugin.engine = mock_engine # Don't really care about the return values mock_engine.search.return_value = {'aggregations': {}} fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, '/v1/search/facets', is_admin=False) self.plugin.get_facets(fake_request.context, all_projects=True) expected_agg_query_filter = { 'filtered': { 'filter': { 'and': [{ 'term': { ROLE_USER_FIELD: 'user' } }, { 'term': { 'tenant_id': TENANT1 } }] } } } # Call args are a tuple (name, posargs, kwargs) search_call = mock_engine.search.mock_calls[0] search_call_body = search_call[2]['body'] self.assertEqual(set(['aggs', 'query']), set(search_call_body.keys())) # The aggregation query's tested elsewhere, just check the query filter self.assertEqual(expected_agg_query_filter, search_call_body['query']) # Admins can request all_projects fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, '/v1/search/facets', is_admin=True) self.plugin.get_facets(fake_request.context, all_projects=True) # Test the SECOND call to the mock search_call = mock_engine.search.mock_calls[1] search_call_body = search_call[2]['body'] # We don't expect any filter query here, just the aggregations. self.assertEqual(set(['aggs', 'query']), set(search_call_body.keys()))
def test_facets_all_projects(self): # For non admins, all_projects should have no effect mock_engine = mock.Mock() self.plugin.engine = mock_engine # Don't really care about the return values mock_engine.search.return_value = { 'aggregations': {} } fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search/facets', is_admin=False ) self.plugin.get_facets(fake_request.context, all_projects=True) expected_agg_query_filter = { 'filtered': { 'filter': { 'and': [ {'term': {ROLE_USER_FIELD: 'user'}}, {'term': {'tenant_id': TENANT1}} ] } } } # Call args are a tuple (name, posargs, kwargs) search_call = mock_engine.search.mock_calls[0] search_call_body = search_call[2]['body'] self.assertEqual(set(['aggs', 'query']), set(search_call_body.keys())) # The aggregation query's tested elsewhere, just check the query filter self.assertEqual(expected_agg_query_filter, search_call_body['query']) # Admins can request all_projects fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search/facets', is_admin=True ) self.plugin.get_facets(fake_request.context, all_projects=True) # Test the SECOND call to the mock search_call = mock_engine.search.mock_calls[1] search_call_body = search_call[2]['body'] # We don't expect any filter query here, just the aggregations. self.assertEqual(set(['aggs', 'query']), set(search_call_body.keys()))
def test_protected_properties(self): with mock.patch(nova_server_getter, return_value=self.instance1): serialized = self.plugin.serialize(self.instance1.id) elasticsearch_results = { "hits": { "hits": [ { "_source": copy.deepcopy(serialized), "_type": self.plugin.get_document_type(), "_index": self.plugin.get_index_name(), } ] } } protected_fields = (u"OS-EXT-SRV-ATTR:host", u"OS-EXT-SRV-ATTR:hypervisor_hostname") fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, "/v1/search", is_admin=True) self.plugin.filter_result(elasticsearch_results["hits"]["hits"][0], fake_request.context) # Result should contain all the fields single_result = elasticsearch_results["hits"]["hits"][0]["_source"] self.assertEqual(serialized, single_result) for field in protected_fields: self.assertTrue(field in single_result) # Refresh the mock results since they can be modified in-place elasticsearch_results = { "hits": { "hits": [ { "_source": copy.deepcopy(serialized), "_type": self.plugin.get_document_type(), "_index": self.plugin.get_index_name(), } ] } } # Make the same request as a non-admin fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, "/v1/search", is_admin=False) self.plugin.filter_result(elasticsearch_results["hits"]["hits"][0], fake_request.context) single_result = elasticsearch_results["hits"]["hits"][0]["_source"] for field in protected_fields: self.assertFalse(field in single_result)
def test_facets(self): request = unit_test_utils.get_fake_request() doc_types = ["OS::Glance::Image", "OS::Nova::Server"] gf_path = 'searchlight.elasticsearch.plugins.base.IndexBase.get_facets' with mock.patch(gf_path) as mock_facets: mock_facets.return_value = [{"name": "fake", "type": "string"}], 0 default_response = self.search_controller.facets( request, doc_type=doc_types) expected = { "OS::Nova::Server": { "doc_count": 0, "facets": [{"name": "fake", "type": "string"}] }, "OS::Glance::Image": { "doc_count": 0, "facets": [{"name": "fake", "type": "string"}] } } self.assertEqual(expected, default_response) totals_only_response = self.search_controller.facets( request, doc_type=doc_types, include_fields=False) expected = {"OS::Nova::Server": {"doc_count": 0}, "OS::Glance::Image": {"doc_count": 0}} self.assertEqual(expected, totals_only_response)
def test_policy_all_disallowed(self): request = unit_test_utils.get_fake_request(is_admin=False) search_deserializer = search.RequestDeserializer( utils.get_search_plugins(), policy_enforcer=self.enforcer) types = ['OS::Glance::Image'] self.enforcer.add_rules( policy.policy.Rules.from_dict({ 'resource:OS::Glance::Image': '!', 'resource:OS::Nova::Server': '!', })) expected_message = ( "There are no resource types accessible to you to serve " "your request. You do not have access to the following " "resource types: OS::Glance::Image") self.assertRaisesRegex(webob.exc.HTTPForbidden, expected_message, search_deserializer._filter_types_by_policy, request.context, types) types = ['OS::Glance::Image', 'OS::Nova::Server'] expected_message = ( "There are no resource types accessible to you to serve " "your request. You do not have access to the following " "resource types: OS::Glance::Image, OS::Nova::Server") self.assertRaisesRegex(webob.exc.HTTPForbidden, expected_message, search_deserializer._filter_types_by_policy, request.context, types)
def test_facets_no_mapping(self): mock_engine = mock.Mock() self.plugin.engine = mock_engine fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, '/v1/search/facets', is_admin=True) mock_engine.search.return_value = { 'aggregations': { 'status': { 'buckets': [] }, 'image.id': { 'doc_count': 0 } } } facets = self.plugin.get_facets(fake_request.context) status_facet = list(filter(lambda f: f['name'] == 'status', facets))[0] image_facet = list(filter(lambda f: f['name'] == 'image.id', facets))[0] expected_status = {'name': 'status', 'options': [], 'type': 'string'} expected_image = { 'name': 'image.id', 'options': [], 'type': 'string', 'resource_type': 'OS::Glance::Image' } self.assertEqual(expected_status, status_facet) self.assertEqual(expected_image, image_facet)
def test_rbac_filter(self): fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, '/v1/search', is_admin=False) rbac_terms = self.plugin._get_rbac_field_filters(fake_request.context) self.assertEqual([{ 'bool': { 'should': [{ 'term': { 'tenant_id': TENANT1 } }, { 'terms': { 'members': [TENANT1, '*'] } }, { 'term': { 'router:external': True } }, { 'term': { 'shared': True } }] } }], rbac_terms)
def test_facets(self): fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, "/v1/search/facets", is_admin=False) mock_engine = mock.Mock() self.plugin.engine = mock_engine mock_engine.search.return_value = { "aggregations": { "status": {"buckets": [{"key": "pending", "doc_count": 2}]}, "type": {"buckets": [{"key": "PRIMARY", "doc_count": 2}]}, } } facets = self.plugin.get_facets(fake_request.context) status_facet = list(filter(lambda f: f["name"] == "status", facets))[0] expected_status = {"name": "status", "options": [{"key": "pending", "doc_count": 2}], "type": "string"} self.assertEqual(expected_status, status_facet) expected_agg_query = { "aggs": dict(unit_test_utils.simple_facet_field_agg(name) for name in ("status", "type")), "query": {"filtered": {"filter": {"and": [{"term": {"project_id": TENANT1}}]}}}, } mock_engine.search.assert_called_with( index=self.plugin.get_index_name(), doc_type=self.plugin.get_document_type(), body=expected_agg_query, ignore_unavailable=True, search_type="count", )
def test_policy_all_disallowed(self): request = unit_test_utils.get_fake_request(is_admin=False) search_deserializer = search.RequestDeserializer( utils.get_search_plugins(), policy_enforcer=self.enforcer) types = ['OS::Glance::Image'] self.enforcer.add_rules(policy.policy.Rules.from_dict({ 'resource:OS::Glance::Image': '!', 'resource:OS::Nova::Server': '!', })) expected_message = ( "There are no resource types accessible to you to serve " "your request. You do not have access to the following " "resource types: OS::Glance::Image") self.assertRaisesRegex( webob.exc.HTTPForbidden, expected_message, search_deserializer._filter_types_by_policy, request.context, types) types = ['OS::Glance::Image', 'OS::Nova::Server'] expected_message = ( "There are no resource types accessible to you to serve " "your request. You do not have access to the following " "resource types: OS::Glance::Image, OS::Nova::Server") self.assertRaisesRegex( webob.exc.HTTPForbidden, expected_message, search_deserializer._filter_types_by_policy, request.context, types)
def test_resource_policy_allows_admin(self): request = unit_test_utils.get_fake_request(is_admin=True) search_deserializer = search.RequestDeserializer( utils.get_search_plugins(), policy_enforcer=self.enforcer) types = ['OS::Glance::Image', 'OS::Nova::Server'] self.enforcer.add_rules(policy.policy.Rules.from_dict({ 'resource:OS::Glance::Image:allow': 'role:admin', 'resource:OS::Nova::Server:query': 'role:admin' })) request = unit_test_utils.get_fake_request(is_admin=True) filtered_types = search_deserializer._filter_types_by_policy( request.context, types, "query") self.assertEqual(types, filtered_types)
def test_empty_request(self): """Tests that ALL registered resource types are searched""" request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({})) output = self.deserializer.search(request) self.assertEqual(['searchlight-search'], output['index']) types = [ 'OS::Designate::RecordSet', 'OS::Designate::Zone', 'OS::Glance::Image', 'OS::Glance::Metadef', 'OS::Ironic::Chassis', 'OS::Ironic::Node', 'OS::Ironic::Port', 'OS::Nova::Flavor', 'OS::Nova::Server', 'OS::Nova::ServerGroup', 'OS::Nova::Hypervisor', 'OS::Neutron::FloatingIP', 'OS::Neutron::Net', 'OS::Neutron::Port', 'OS::Neutron::Subnet', 'OS::Neutron::Router', 'OS::Neutron::SecurityGroup', 'OS::Cinder::Volume', 'OS::Cinder::Snapshot' ] self.assertEqual(['searchlight-search'], output['index']) self.assertEqual(sorted(types), sorted(output['doc_type']))
def test_filter_result(self): """Verify that any highlighted query results will filter out the ROLE_USER_FIELD field. """ request = unit_test_utils.get_fake_request(is_admin=True) mock_engine = mock.Mock() simple_plugin = fake_plugins.FakeSimplePlugin(es_engine=mock_engine) # Verify with ROLE_USER_FIELD hit = {"_source": {"owner": "<em>admin</em>"}, "highlight": { "owner": "<em>admin</em>", "__searchlight-user-role": "<em>admin</em>"}} simple_plugin.filter_result(hit, request.context) self.assertNotIn('__searchlight-user-role', hit['highlight']) # Verify without ROLE_USER_FIELD hit = {"_source": {"owner": "<em>admin</em>"}, "highlight": { "owner": "<em>admin</em>"}} original_hit = copy.deepcopy(hit) simple_plugin.filter_result(hit, request.context) self.assertEqual(original_hit, hit)
def test_create_multiple(self): actions = [ _image_fixture('create', '1'), _image_fixture('create', '2', data={'name': 'image-2'}), ] request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'actions': actions, })) output = self.deserializer.index(request) expected = { 'actions': [ { '_id': '1', '_index': 'searchlight', '_op_type': 'create', '_source': {'disk_format': 'raw', 'name': 'image-1'}, '_type': 'OS::Glance::Image' }, { '_id': '2', '_index': 'searchlight', '_op_type': 'create', '_source': {'disk_format': 'raw', 'name': 'image-2'}, '_type': 'OS::Glance::Image' }, ], 'default_index': None, 'default_type': None } self.assertEqual(expected, output)
def test_multiple_sort(self): """Test multiple sort fields""" request = unit_test_utils.get_fake_request() request.body = six.b( jsonutils.dumps({ 'query': { 'match_all': {} }, 'sort': [ 'status', { 'created_at': 'desc' }, { 'members': { 'order': 'asc', 'mode': 'max' } } ] })) output = self.deserializer.search(request) expected = [ 'status', { 'created_at': 'desc' }, { 'members': { 'order': 'asc', 'mode': 'max' } } ] self.assertEqual(expected, output['query']['sort'])
def test_plugins_info_not_found(self): request = unit_test_utils.get_fake_request() repo = searchlight.elasticsearch.CatalogSearchRepo repo.plugins_info = mock.Mock(side_effect=exception.NotFound) self.assertRaises(webob.exc.HTTPNotFound, self.search_controller.plugins_info, request)
def test_plugins_info_internal_server_error(self): request = unit_test_utils.get_fake_request() repo = searchlight.elasticsearch.CatalogSearchRepo repo.plugins_info = mock.Mock(side_effect=Exception) self.assertRaises(webob.exc.HTTPInternalServerError, self.search_controller.plugins_info, request)
def test_global_aggregation_not_allowed(self): """The 'global' aggregation type bypasses the search query (thus bypassing RBAC). This is Bad, so we won't allow it. It can only occur as a top level aggregation. """ request = unit_test_utils.get_fake_request() aggs = { 'cheating_rbac': { 'global': {}, 'aggregations': { 'name': { 'terms': { 'field': 'name' } } } } } request.body = six.b( jsonutils.dumps({ 'type': ['OS::Glance::Metadef'], 'query': { 'match_all': {} }, 'aggregations': aggs })) self.assertRaises(webob.exc.HTTPForbidden, self.deserializer.search, request)
def test_delete_multiple(self): action_1 = _image_fixture('delete', '1') action_1.pop('data') action_2 = _image_fixture('delete', '2') action_2.pop('data') request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'actions': [action_1, action_2], })) output = self.deserializer.index(request) expected = { 'actions': [ { '_id': '1', '_index': 'searchlight', '_op_type': 'delete', '_source': {}, '_type': 'OS::Glance::Image' }, { '_id': '2', '_index': 'searchlight', '_op_type': 'delete', '_source': {}, '_type': 'OS::Glance::Image' }, ], 'default_index': None, 'default_type': None } self.assertEqual(expected, output)
def test_search_aggregations(self): request = unit_test_utils.get_fake_request() with mock.patch(REPO_SEARCH, return_value={}) as mock_search: query = { "query": { "match_all": {} }, "aggs": { "test": { "terms": { "field": "some_field" } } } } index_name = "searchlight" limit = 10 offset = 0 doc_type = "OS::Glance::Image" self.search_controller.search(request, query, index_name, doc_type, offset, limit, version=True) mock_search.assert_called_once_with(index_name, doc_type, query, offset, limit, ignore_unavailable=True, version=True)
def test_nested_object_facets(self): """Check 'nested' and 'object' types are treated the same for facets""" mock_engine = mock.Mock() plugin = fake_plugins.FakeSimplePlugin(es_engine=mock_engine) fake_request = unit_test_utils.get_fake_request( 'a', 'b', '/v1/search/facets', is_admin=True ) mock_engine.search.return_value = { "hits": {"total": 1} } fake_mapping = { "properties": { "nested": { "type": "nested", "properties": {"inner": {"type": "string"}} }, "object": { "type": "object", "properties": {"inner": {"type": "string"}} } } } with mock.patch.object(plugin, 'get_mapping', return_value=fake_mapping): facets, _ = plugin.get_facets(fake_request.context) facets = sorted(facets, key=operator.itemgetter("name")) expected = [ {"name": "nested.inner", "type": "string", "nested": True}, {"name": "object.inner", "type": "string", "nested": False}, ] self.assertEqual(expected, facets)
def test_rbac_non_admin(self): """Test that a non-admin request results in an RBACed query""" request = unit_test_utils.get_fake_request(is_admin=False) request.body = jsonutils.dumps({ 'query': { 'match_all': {} }, 'type': 'OS::Nova::Server', }).encode("latin-1") output = self.deserializer.search(request) tenant_id = '6838eb7b-6ded-dead-beef-b344c77fe8df' nova_rbac_filter = { 'indices': { 'query': { 'bool': { 'filter': { 'bool': { 'must': { 'type': { 'value': 'OS::Nova::Server' } }, 'should': [{ 'term': { 'tenant_id': tenant_id } }], 'minimum_should_match': 1 } } } }, 'index': 'searchlight-search', 'no_match_query': 'none' } } role_field = searchlight.elasticsearch.ROLE_USER_FIELD expected_query = { 'query': { 'bool': { 'filter': { 'bool': { 'must': { 'term': { role_field: 'user' } }, 'should': [nova_rbac_filter], 'minimum_should_match': 1 } }, 'must': { 'match_all': {} } } } } self.assertEqual(expected_query, output['query'])
def test_plugins_info(self): request = unit_test_utils.get_fake_request() expected = { "plugins": [ { "index": "searchlight", "type": "OS::Designate::RecordSet", "name": "OS::Designate::RecordSet" }, { "index": "searchlight", "type": "OS::Designate::Zone", "name": "OS::Designate::Zone" }, { "index": "searchlight", "type": "OS::Glance::Image", "name": "OS::Glance::Image" }, { "index": "searchlight", "type": "OS::Glance::Metadef", "name": "OS::Glance::Metadef" }, { "index": "searchlight", "type": "OS::Nova::Server", "name": "OS::Nova::Server" } ] } actual = self.search_controller.plugins_info(request) self.assertEqual(sorted(expected), sorted(actual))
def test_filter_result(self): """Verify that any highlighted query results will filter out the ROLE_USER_FIELD field. """ request = unit_test_utils.get_fake_request(is_admin=True) mock_engine = mock.Mock() simple_plugin = fake_plugins.FakeSimplePlugin(es_engine=mock_engine) # Verify with ROLE_USER_FIELD hit = { "_source": { "owner": "<em>admin</em>" }, "highlight": { "owner": "<em>admin</em>", "__searchlight-user-role": "<em>admin</em>" } } simple_plugin.filter_result(hit, request.context) self.assertNotIn('__searchlight-user-role', hit['highlight']) # Verify without ROLE_USER_FIELD hit = { "_source": { "owner": "<em>admin</em>" }, "highlight": { "owner": "<em>admin</em>" } } original_hit = copy.deepcopy(hit) simple_plugin.filter_result(hit, request.context) self.assertEqual(original_hit, hit)
def test_search_policy_check(self, _): request = unit_test_utils.get_fake_request() with mock.patch.object(self.search_controller.policy, 'enforce') as mock_enforce: self.search_controller.search(request, query={"match_all": {}}) mock_enforce.assert_called_with(request.context, 'query', {})
def test_metadef_rbac(self): """Test metadefs RBAC query terms""" fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search' ) rbac_filter = self.plugin.get_query_filters(fake_request.context) expected_fragment = { "indices": { "index": "searchlight-search", "no_match_query": "none", "query": { "bool": { "filter": { "bool": { "must": { "type": {"value": "OS::Glance::Metadef"} }, "should": [ {"term": {"owner": TENANT1}}, {"term": {"visibility": "public"}}, ], "minimum_should_match": 1 } } } } } } self.assertEqual(expected_fragment, rbac_filter)
def test_image_rbac(self): """Test the image plugin RBAC query terms""" fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search' ) rbac_query_fragment = self.plugin.get_query_filters( fake_request.context) expected_fragment = { "indices": { "index": "searchlight-search", "no_match_filter": "none", "filter": { "and": [ { "type": {"value": "OS::Glance::Image"} }, { "or": [ {"term": {"owner": TENANT1}}, {"term": {"visibility": "public"}}, {"term": {"members": TENANT1}} ] } ] }, } } self.assertEqual(expected_fragment, rbac_query_fragment)
def test_image_rbac(self): """Test the image plugin RBAC query terms""" fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search' ) rbac_query_fragment = self.plugin.get_rbac_filter(fake_request.context) expected_fragment = [{ "indices": { "index": "searchlight", "no_match_filter": "none", "filter": { "and": [ { "or": [ {"term": {"owner": TENANT1}}, {"term": {"visibility": "public"}}, {"term": {"members": TENANT1}} ] }, { "type": {"value": "OS::Glance::Image"} }, ] }, } }] self.assertEqual(expected_fragment, rbac_query_fragment)
def test_rbac_filters(self): fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search', is_admin=False ) self.assertEqual( [{"term": {"project_id": TENANT1}}], self.plugin._get_rbac_field_filters(fake_request.context))
def test_image_rbac(self): """Test the image plugin RBAC query terms""" fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search' ) rbac_query_fragment = self.plugin.get_query_filters( fake_request.context) expected_fragment = { "indices": { "index": "searchlight-search", "no_match_query": "none", "query": { "bool": { "filter": { "bool": { "must": { "type": {"value": "OS::Glance::Image"} }, "should": [ {"term": {"owner": TENANT1}}, {"term": {"visibility": "public"}}, {"term": {"members": TENANT1}} ], 'minimum_should_match': 1 } } } }, } } self.assertEqual(expected_fragment, rbac_query_fragment)
def test_facets_no_mapping(self): mock_engine = mock.Mock() self.plugin.engine = mock_engine fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search/facets', is_admin=True ) mock_engine.search.return_value = { 'aggregations': { 'status': {'buckets': []}, 'image.id': {'doc_count': 0} } } facets = self.plugin.get_facets(fake_request.context) status_facet = list(filter(lambda f: f['name'] == 'status', facets))[0] image_facet = list(filter(lambda f: f['name'] == 'image.id', facets))[0] expected_status = {'name': 'status', 'options': [], 'type': 'string'} expected_image = {'name': 'image.id', 'options': [], 'type': 'string'} self.assertEqual(expected_status, status_facet) self.assertEqual(expected_image, image_facet)
def test_rbac_filter(self): fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, '/v1/search', is_admin=False) rbac_terms = self.plugin._get_rbac_field_filters(fake_request.context) self.assertEqual([{"term": {"id": AUTH_PREFIX + TENANT1}}], rbac_terms)
def test_default_facet_options(self): request = unit_test_utils.get_fake_request(path='/v1/search/facets') output = self.deserializer.facets(request) expected = {'index_name': None, 'doc_type': None, 'all_projects': False, 'limit_terms': 0} self.assertEqual(expected, output)
def test_facets(self): """If you have a weak constitution, we may want to avert your eyes now. We want to verify that the "exclude_options" parameter for facets will not result in any aggregation in the Elasticsearch query. The actual ES query is buried in the bowels of Searchlight. Instead of trying to call it directly through the searchcontroller and mock mutliple levels of Servers/Requests/Catalogs/etc we will go directly to the IndexBase call. """ request = unit_test_utils.get_fake_request(is_admin=True) mock_engine = mock.Mock() simple_plugin = fake_plugins.FakeSimplePlugin(es_engine=mock_engine) mock_engine.search.return_value = {"hits": {"total": 0}} simple_plugin._get_facet_terms(fields={}, request_context=request.context, all_projects=False, limit_terms=False, exclude_options=True) # Verify there is no aggregation when searching Elasticsearch. body = {'query': { 'filtered': {'filter': {'and': [{'term': { '__searchlight-user-role': 'admin'}}]}}} } mock_engine.search.assert_called_with(body=body, doc_type='fake-simple', ignore_unavailable=True, index='searchlight-search', size=0)
def test_rbac_filter_admin_role(self): fake_request = unit_test_utils.get_fake_request( USER1, TENANT1, '/v1/search', is_admin=True ) rbac_terms = self.plugin._get_rbac_field_filters(fake_request.context) expected_rbac = [ { "or": [ { 'term': {'tenant_id': TENANT1} }, { 'has_parent': { 'type': self.plugin.parent_plugin_type(), 'query': { "bool": { "should": [ {'term': {'shared': True}}, {'term': {'router:external': True}} ] } } } } ] } ] self.assertEqual(expected_rbac, rbac_terms)
def test_plugins_info(self): request = unit_test_utils.get_fake_request() expected = { "plugins": [{ "index": "searchlight-search", "type": "OS::Designate::RecordSet", "name": "OS::Designate::RecordSet" }, { "index": "searchlight-search", "type": "OS::Designate::Zone", "name": "OS::Designate::Zone" }, { "index": "searchlight-search", "type": "OS::Glance::Image", "name": "OS::Glance::Image" }, { "index": "searchlight-search", "type": "OS::Glance::Metadef", "name": "OS::Glance::Metadef" }, { "index": "searchlight-search", "type": "OS::Nova::Server", "name": "OS::Nova::Server" }] } # Simulate policy filtering doc_types = [p['name'] for p in expected['plugins']] actual = self.search_controller.plugins_info(request, doc_types) self.assertEqual(['plugins'], list(actual.keys())) self.assertEqual( sorted(expected['plugins'], key=operator.itemgetter('name')), sorted(actual['plugins'], key=operator.itemgetter('name')))
def test_invalid_type(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'type': 'invalid', })) self.assertRaises(webob.exc.HTTPBadRequest, self.deserializer.index, request)
def test_facet_policy(self): request = unit_test_utils.get_fake_request() search_repo = mock.Mock() with mock.patch.object(self.enforcer, 'enforce') as mock_enforce: repo = policy.CatalogSearchRepoProxy(search_repo, request.context, self.enforcer) repo.facets() mock_enforce.assert_called_with(request.context, 'facets', {})
def test_forbidden_self(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'self': {}, })) self.assertRaises(webob.exc.HTTPForbidden, self.deserializer.search, request)
def test_forbidden_schema(self): request = unit_test_utils.get_fake_request() request.body = jsonutils.dumps({ 'schema': {}, }).encode("latin-1") self.assertRaises(webob.exc.HTTPForbidden, self.deserializer.search, request)
def test_empty_operation_type(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'actions': [_image_fixture('', '1')] })) self.assertRaises(webob.exc.HTTPBadRequest, self.deserializer.index, request)
def test_single_index(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'index': 'searchlight-search', })) output = self.deserializer.search(request) self.assertEqual(['searchlight-search'], output['index'])
def test_context_policy_target(self): request = unit_test_utils.get_fake_request() expected = { 'user_id': unit_test_utils.SOMEUSER, 'project_id': unit_test_utils.SOMETENANT, 'tenant_id': unit_test_utils.SOMETENANT } self.assertEqual(expected, request.context.policy_target)
def test_create_invalid_index(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'actions': [_image_fixture('create', index='invalid')] })) self.assertRaises(webob.exc.HTTPBadRequest, self.deserializer.index, request)
def test_create_missing_doc_type(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'actions': [_image_fixture('create', '1', doc_type=None)] })) self.assertRaises(webob.exc.HTTPBadRequest, self.deserializer.index, request)
def test_missing_actions(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'default_type': 'OS::Glance::Image' })) self.assertRaises(webob.exc.HTTPBadRequest, self.deserializer.index, request)
def test_search_version(self): request = unit_test_utils.get_fake_request(is_admin=True) request.body = six.b(jsonutils.dumps({ 'query': {'match_all': {}}, 'version': True })) output = self.deserializer.search(request) self.assertEqual(True, output['version'])
def test_rbac_filter(self): fake_request = unit_test_utils.get_fake_request(USER1, TENANT1, '/v1/search', is_admin=False) rbac_terms = self.plugin._get_rbac_field_filters(fake_request.context) expected = [{"term": {"tenant_id": TENANT1}}] self.assertEqual(expected, rbac_terms)
def test_single_type(self): request = unit_test_utils.get_fake_request() request.body = six.b(jsonutils.dumps({ 'type': 'OS::Glance::Image', })) output = self.deserializer.search(request) self.assertEqual(['searchlight-search'], output['index']) self.assertEqual(['OS::Glance::Image'], output['doc_type'])
def test_admin_or_owner(self): """Since this a commonly used rule, check that it works""" self.enforcer.add_rules( policy.policy.Rules.from_dict({ 'admin_or_owner': 'role:admin or project_id:%(project_id)s', 'test_rule': 'rule:admin_or_owner' })) request = unit_test_utils.get_fake_request(is_admin=False) self.assertTrue( self.enforcer.check(request.context, 'test_rule', request.context.policy_target)) request = unit_test_utils.get_fake_request(is_admin=False) self.assertFalse(self.enforcer.check(request.context, 'test_rule', {})) request = unit_test_utils.get_fake_request(is_admin=True) self.assertTrue(self.enforcer.check(request.context, 'test_rule', {}))
def test_fields_exclude_rbac(self): """Test various forms for source_exclude""" role_field = searchlight.elasticsearch.ROLE_USER_FIELD request = unit_test_utils.get_fake_request() request.body = six.b( jsonutils.dumps({ 'type': ['OS::Glance::Metadef'], 'query': { 'match_all': {} }, '_source': { 'exclude': ['something', 'other thing'] } })) output = self.deserializer.search(request) self.assertEqual([role_field, 'something', 'other thing'], output['_source_exclude']) # Test with a single field request.body = six.b( jsonutils.dumps({ 'type': ['OS::Glance::Metadef'], 'query': { 'match_all': {} }, '_source': { 'exclude': "something" } })) output = self.deserializer.search(request) self.assertEqual([role_field, 'something'], output['_source_exclude']) # Test with a single field request.body = six.b( jsonutils.dumps({ 'type': ['OS::Glance::Metadef'], 'query': { 'match_all': {} }, '_source': "includeme" })) output = self.deserializer.search(request) self.assertEqual([role_field], output['_source_exclude']) self.assertEqual("includeme", output['_source_include']) # Test with a single field request.body = six.b( jsonutils.dumps({ 'type': ['OS::Glance::Metadef'], 'query': { 'match_all': {} }, '_source': ["includeme", "andme"] })) output = self.deserializer.search(request) self.assertEqual([role_field], output['_source_exclude']) self.assertEqual(["includeme", "andme"], output['_source_include'])