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_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)
Example #4
0
    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)
Example #5
0
    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))
Example #6
0
    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)
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
    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']))
Example #10
0
    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_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",
        )
Example #12
0
    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)
Example #13
0
    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_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)
Example #15
0
    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_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_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_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)
Example #20
0
    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)
Example #21
0
    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', {})
Example #22
0
    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)
Example #23
0
 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)
Example #24
0
    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)
Example #25
0
    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)
Example #26
0
    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'])
Example #27
0
    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)
Example #28
0
    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)
Example #29
0
    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)
Example #30
0
    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)