Merge pull request #10 from sjmc7/rbac-tests

Rbac tests
This commit is contained in:
sjmc7 2015-06-15 14:27:05 -05:00
commit 55471cb2f1
7 changed files with 220 additions and 12 deletions

View File

@ -91,6 +91,11 @@ class ImageIndex(base.IndexBase):
'type': {
'value': self.get_document_type()
}
},
{
'index': {
'value': self.get_index_name()
}
}
]
}

View File

@ -112,6 +112,11 @@ class MetadefIndex(base.IndexBase):
'type': {
'value': self.get_document_type()
}
},
{
'index': {
'value': self.get_index_name()
}
}
]
}

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import datetime
import mock
@ -21,6 +22,7 @@ from oslo_utils import timeutils
from searchlight.elasticsearch.plugins.glance import images as images_plugin
from searchlight.elasticsearch.plugins import openstack_clients
import searchlight.tests.unit.utils as unit_test_utils
import searchlight.tests.utils as test_utils
@ -79,6 +81,7 @@ def _image_fixture(image_id, **kwargs):
class TestImageLoaderPlugin(test_utils.BaseTestCase):
def setUp(self):
super(TestImageLoaderPlugin, self).setUp()
self.set_property_protections()
self._create_images()
@ -236,10 +239,13 @@ class TestImageLoaderPlugin(test_utils.BaseTestCase):
image_member_mocks = [
[], [], [], self.members_image_members
]
member_calls = [
mock.call(i['id']) for i in self.images
]
with mock.patch('glanceclient.v2.images.Controller.list',
return_value=self.images) as mock_get:
return_value=self.images) as mock_list:
with mock.patch('glanceclient.v2.image_members.Controller.list',
side_effect=image_member_mocks):
side_effect=image_member_mocks) as mock_members:
# This is not testing the elasticsearch call, just
# that the documents being indexed are as expected
with mock.patch.object(
@ -247,7 +253,9 @@ class TestImageLoaderPlugin(test_utils.BaseTestCase):
'save_documents') as mock_save:
self.plugin.setup_data()
mock_get.assert_called_once_with()
mock_list.assert_called_once_with()
mock_members.assert_has_calls(member_calls)
mock_save.assert_called_once_with([
{
'status': 'active',
@ -328,3 +336,128 @@ class TestImageLoaderPlugin(test_utils.BaseTestCase):
'updated_at': DATE1
}
])
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 = [{
"and": [
{
"or": [
{"term": {"owner": TENANT1}},
{"term": {"visibility": "public"}},
{"term": {"members": TENANT1}}
]
},
{"type": {"value": "image"}},
{"index": {"value": "glance"}}
]
}]
self.assertEqual(expected_fragment, rbac_query_fragment)
def test_protected_properties(self):
extra_props = {
'x_foo_matcher': 'this is protected',
'x_foo_something_else': 'this is not protected',
'z_this_has_no_rules': 'this is protected too'
}
image_with_properties = _image_fixture(
UUID1, owner=TENANT1, checksum=CHECKSUM, name='simple', size=256,
is_public=True, status='active', extra_properties=extra_props
)
with mock.patch('glanceclient.v2.image_members.Controller.list',
return_value=[]):
serialized = self.plugin.serialize(image_with_properties)
elasticsearch_results = {
'hits': {
'hits': [{
'_source': copy.deepcopy(serialized),
'_type': self.plugin.get_document_type(),
'_index': self.plugin.get_index_name()
}]
}
}
# Admin context
fake_request = unit_test_utils.get_fake_request(
USER1, TENANT1, '/v1/search', is_admin=True
)
filtered_result = self.plugin.filter_result(
elasticsearch_results, fake_request.context
)
# This should contain the three properties we added
expected = {
'checksum': '93264c3edf5972c9f1cb309543d38a5c',
'container_format': None,
'disk_format': None,
'id': 'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d',
'members': [],
'min_disk': None,
'min_ram': None,
'name': 'simple',
'owner': '6838eb7b-6ded-434a-882c-b344c77fe8df',
'protected': False,
'size': 256,
'status': 'active',
'tags': [],
'virtual_size': None,
'visibility': 'public',
'created_at': DATE1,
'updated_at': DATE1,
'x_foo_matcher': 'this is protected',
'x_foo_something_else': 'this is not protected',
'z_this_has_no_rules': 'this is protected too'
}
self.assertEqual(expected,
filtered_result['hits']['hits'][0]['_source'])
# Non admin user. Recreate this because the filter operation modifies
# it in place and we want a fresh copy
elasticsearch_results = {
'hits': {
'hits': [{
'_source': copy.deepcopy(serialized),
'_type': self.plugin.get_document_type(),
'_index': self.plugin.get_index_name()
}]
}
}
# Non admin context should miss the x_foo property
fake_request = unit_test_utils.get_fake_request(
USER1, TENANT1, '/v1/search', is_admin=False
)
filtered_result = self.plugin.filter_result(
elasticsearch_results, fake_request.context
)
# Should be missing two of the properties
expected = {
'checksum': '93264c3edf5972c9f1cb309543d38a5c',
'container_format': None,
'disk_format': None,
'id': 'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d',
'members': [],
'min_disk': None,
'min_ram': None,
'name': 'simple',
'owner': '6838eb7b-6ded-434a-882c-b344c77fe8df',
'protected': False,
'size': 256,
'status': 'active',
'tags': [],
'virtual_size': None,
'visibility': 'public',
'created_at': DATE1,
'updated_at': DATE1,
'x_foo_something_else': 'this is not protected'
}
self.assertEqual(expected,
filtered_result['hits']['hits'][0]['_source'])

View File

@ -18,10 +18,13 @@ import mock
from searchlight.elasticsearch.plugins.glance import metadefs as md_plugin
from searchlight.elasticsearch.plugins import openstack_clients
import searchlight.tests.unit.utils as unit_test_utils
import searchlight.tests.utils as test_utils
# Metadefinitions
USER1 = '54492ba0-f4df-4e4e-be62-27f4d76b29cf'
TENANT1 = '6838eb7b-6ded-434a-882c-b344c77fe8df'
TENANT2 = '2c014f32-55eb-467d-8fcb-4bd706012f81'
@ -419,3 +422,23 @@ class TestMetadefLoaderPlugin(test_utils.BaseTestCase):
],
}
])
def test_metadef_rbac(self):
"""Test metadefs 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 = [{
"and": [
{
"or": [
{"term": {"owner": TENANT1}},
{"term": {"visibility": "public"}},
]
},
{"type": {"value": "metadef"}},
{"index": {"value": "glance"}}
]
}]
self.assertEqual(expected_fragment, rbac_query_fragment)

View File

@ -0,0 +1,43 @@
# Copyright 2015 Hewlett-Packard Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Common utilities used in testing"""
from searchlight.common import wsgi
import searchlight.context
SOMEUSER = '54492ba0-dead-beef-be62-27f4d76b29cf'
SOMETENANT = '6838eb7b-6ded-dead-beef-b344c77fe8df'
def get_fake_request(user=SOMEUSER, tenant=SOMETENANT, path='/v1/search',
method='GET', is_admin=False, roles=['member']):
req = wsgi.Request.blank(path)
req.method = method
if is_admin and 'admin' not in roles:
roles = roles[:]
roles.append('admin')
kwargs = {
'user': user,
'tenant': tenant,
'roles': roles,
'is_admin': is_admin,
}
req.context = searchlight.context.RequestContext(**kwargs)
return req

View File

View File

@ -22,7 +22,6 @@ from searchlight.common import exception
from searchlight.common import utils
import searchlight.elasticsearch
import searchlight.gateway
from searchlight.tests.unit import base
import searchlight.tests.unit.utils as unit_test_utils
import searchlight.tests.utils as test_utils
@ -54,7 +53,7 @@ def _image_fixture(op_type, _id=None, index='glance', doc_type='image',
return _action_fixture(op_type, image_data, index, doc_type, _id, **kwargs)
class TestSearchController(base.IsolatedUnitTest):
class TestSearchController(test_utils.BaseTestCase):
def setUp(self):
super(TestSearchController, self).setUp()
@ -155,7 +154,7 @@ class TestSearchController(base.IsolatedUnitTest):
request, query, index, doc_type, fields, offset, limit)
def test_index_complete(self):
request = unit_test_utils.get_fake_request()
request = unit_test_utils.get_fake_request(is_admin=True)
self.search_controller.index = mock.Mock(return_value="{}")
actions = [{'action': 'create', 'index': 'myindex', 'id': 10,
'type': 'MyTest', 'data': '{"name": "MyName"}'}]
@ -168,7 +167,7 @@ class TestSearchController(base.IsolatedUnitTest):
request, actions, default_index, default_type)
def test_index_repo_complete(self):
request = unit_test_utils.get_fake_request()
request = unit_test_utils.get_fake_request(is_admin=True)
repo = searchlight.elasticsearch.CatalogSearchRepo
repo.index = mock.Mock(return_value="{}")
actions = [{'action': 'create', 'index': 'myindex', 'id': 10,
@ -182,7 +181,7 @@ class TestSearchController(base.IsolatedUnitTest):
default_index, default_type, actions)
def test_index_repo_minimal(self):
request = unit_test_utils.get_fake_request()
request = unit_test_utils.get_fake_request(is_admin=True)
repo = searchlight.elasticsearch.CatalogSearchRepo
repo.index = mock.Mock(return_value="{}")
actions = [{'action': 'create', 'index': 'myindex', 'id': 10,
@ -203,7 +202,7 @@ class TestSearchController(base.IsolatedUnitTest):
request, actions)
def test_index_not_found(self):
request = unit_test_utils.get_fake_request()
request = unit_test_utils.get_fake_request(is_admin=True)
repo = searchlight.elasticsearch.CatalogSearchRepo
repo.index = mock.Mock(side_effect=exception.NotFound)
actions = [{'action': 'create', 'index': 'myindex', 'id': 10,
@ -214,7 +213,7 @@ class TestSearchController(base.IsolatedUnitTest):
request, actions)
def test_index_duplicate(self):
request = unit_test_utils.get_fake_request()
request = unit_test_utils.get_fake_request(is_admin=True)
repo = searchlight.elasticsearch.CatalogSearchRepo
repo.index = mock.Mock(side_effect=exception.Duplicate)
actions = [{'action': 'create', 'index': 'myindex', 'id': 10,
@ -225,7 +224,7 @@ class TestSearchController(base.IsolatedUnitTest):
request, actions)
def test_index_exception(self):
request = unit_test_utils.get_fake_request()
request = unit_test_utils.get_fake_request(is_admin=True)
repo = searchlight.elasticsearch.CatalogSearchRepo
repo.index = mock.Mock(side_effect=Exception)
actions = [{'action': 'create', 'index': 'myindex', 'id': 10,
@ -236,7 +235,7 @@ class TestSearchController(base.IsolatedUnitTest):
request, actions)
def test_plugins_info(self):
request = unit_test_utils.get_fake_request()
request = unit_test_utils.get_fake_request(is_admin=True)
self.search_controller.plugins_info = mock.Mock(return_value="{}")
self.search_controller.plugins_info(request)
self.search_controller.plugins_info.assert_called_once_with(request)