diff --git a/bin/keystone_data.sh b/bin/keystone_data.sh index 68dba2b3b..19ca43942 100755 --- a/bin/keystone_data.sh +++ b/bin/keystone_data.sh @@ -66,6 +66,19 @@ if [[ "$ENABLED_SERVICES" =~ "barbican" ]]; then --user_id="$BARBICAN_USER" \ --role_id="$ADMIN_ROLE" # + # Setup Default service-admin User + # + SERVICE_ADMIN=$(get_id keystone user-create \ + --name="service-admin" \ + --pass="$SERVICE_PASSWORD" \ + --email="service_admin@example.com") + SERVICE_ADMIN_ROLE=$(get_id keystone role-create \ + --name="key-manager:service-admin") + keystone user-role-add \ + --tenant_id="$SERVICE_TENANT" \ + --user_id="$SERVICE_ADMIN" \ + --role_id="$SERVICE_ADMIN_ROLE" + # # Setup RBAC User Projects and Roles # USER_PASSWORD="barbican" diff --git a/contrib/devstack/lib/barbican b/contrib/devstack/lib/barbican index cd864742e..d2f227d46 100755 --- a/contrib/devstack/lib/barbican +++ b/contrib/devstack/lib/barbican @@ -246,6 +246,19 @@ function create_barbican_accounts { --user-id $BARBICAN_USER \ --role-id $ADMIN_ROLE # + # Setup Default service-admin User + # + SERVICE_ADMIN=$(get_id keystone user-create \ + --name="service-admin" \ + --pass="$SERVICE_PASSWORD" \ + --email="service-admin@example.com") + SERVICE_ADMIN_ROLE=$(get_id keystone role-create \ + --name="key-manager:service-admin") + keystone user-role-add \ + --tenant_id="$SERVICE_TENANT" \ + --user_id="$SERVICE_ADMIN" \ + --role_id="$SERVICE_ADMIN_ROLE" + # # Setup RBAC User Projects and Roles # PASSWORD="barbican" diff --git a/etc/barbican/barbican-functional.conf b/etc/barbican/barbican-functional.conf index a2c36ee00..e524ff2fc 100644 --- a/etc/barbican/barbican-functional.conf +++ b/etc/barbican/barbican-functional.conf @@ -10,6 +10,10 @@ project_name=admin password=secretadmin domain_name=Default +service_admin=service-admin +service_admin_project=service +service_admin_password=secretservice + [rbac_users] # Replace these values that represent additional users for RBAC testing project_a=project_a diff --git a/etc/barbican/policy.json b/etc/barbican/policy.json index 8735dc6f2..9b09c2ce9 100644 --- a/etc/barbican/policy.json +++ b/etc/barbican/policy.json @@ -3,11 +3,12 @@ "observer": "role:observer", "creator_role": "role:creator", "audit": "role:audit", + "service_admin": "role:key-manager:service-admin", "admin_or_user_does_not_work": "project_id:%(project_id)s", "admin_or_user": "role:admin or project_id:%(project_id)s", "admin_or_creator_role": "role:admin or role:creator", "all_but_audit": "role:admin or role:observer or role:creator", - "all_users": "role:admin or role:observer or role:creator or role:audit", + "all_users": "role:admin or role:observer or role:creator or role:audit or rule:service_admin", "secret_project_match": "project:%(target.secret.project_id)s", "secret_acl_read": "'read':%(target.secret.read)s", "secret_private_read": "'False':%(target.secret.read_project_access)s", @@ -68,7 +69,7 @@ "container_acls:delete": "rule:container_project_admin or rule:container_project_creator", "container_acls:get": "rule:all_but_audit and rule:container_project_match", "quotas:get": "rule:all_users", - "project_quotas:get": "rule:admin", - "project_quotas:post": "rule:admin", - "project_quotas:delete": "rule:admin" + "project_quotas:get": "rule:service_admin", + "project_quotas:post": "rule:service_admin", + "project_quotas:delete": "rule:service_admin" } diff --git a/functionaltests/api/v1/functional/test_quotas.py b/functionaltests/api/v1/functional/test_quotas.py index 2c80dd250..318ea13dd 100644 --- a/functionaltests/api/v1/functional/test_quotas.py +++ b/functionaltests/api/v1/functional/test_quotas.py @@ -16,6 +16,11 @@ from functionaltests.api import base from functionaltests.api.v1.behaviors import quota_behaviors from functionaltests.api.v1.models import quota_models +from functionaltests.common import config + + +CONF = config.get_config() +service_admin = CONF.identity.service_admin def get_set_project_quotas_request(): @@ -50,7 +55,8 @@ class QuotasTestCase(base.TestCase): def test_get_project_quota_list(self): """Get list of all project quotas""" - resp, project_quotas_list = self.behaviors.get_project_quotas_list() + resp, project_quotas_list = self.behaviors.get_project_quotas_list( + user_name=service_admin) self.assertEqual(200, resp.status_code) for project_quotas in project_quotas_list: @@ -64,7 +70,8 @@ class QuotasTestCase(base.TestCase): def test_get_one_project_quotas(self): """Get project quota information for specific project""" - resp = self.behaviors.get_project_quotas(self.project_id) + resp = self.behaviors.get_project_quotas(self.project_id, + user_name=service_admin) self.assertEqual(200, resp.status_code) self.assertEqual(500, resp.model.project_quotas['secrets']) @@ -74,16 +81,18 @@ class QuotasTestCase(base.TestCase): self.assertEqual(100, resp.model.project_quotas['consumers']) def test_set_project_quotas(self): - """Get project quota information""" + """Set project quota information""" request_model = quota_models.ProjectQuotaRequestModel( **get_set_project_quotas_request()) resp = self.behaviors.set_project_quotas(self.project_id, - request_model) + request_model, + user_name=service_admin) self.assertEqual(200, resp.status_code) def test_delete_project_quotas(self): - """Get project quota information""" + """Delete project quota information""" - resp = self.behaviors.delete_project_quotas(self.project_id) + resp = self.behaviors.delete_project_quotas(self.project_id, + user_name=service_admin) self.assertEqual(204, resp.status_code) diff --git a/functionaltests/api/v1/functional/test_quotas_rbac.py b/functionaltests/api/v1/functional/test_quotas_rbac.py new file mode 100644 index 000000000..e8ec8eb86 --- /dev/null +++ b/functionaltests/api/v1/functional/test_quotas_rbac.py @@ -0,0 +1,162 @@ +# Copyright (c) 2015 Cisco Systems +# +# 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. + +from barbican.tests import utils +from functionaltests.api import base +from functionaltests.api.v1.behaviors import quota_behaviors +from functionaltests.api.v1.models import quota_models +from functionaltests.common import config + + +CONF = config.get_config() +admin_a = CONF.rbac_users.admin_a +creator_a = CONF.rbac_users.creator_a +observer_a = CONF.rbac_users.observer_a +auditor_a = CONF.rbac_users.auditor_a +service_admin = CONF.identity.service_admin + +test_data_rbac_get_quotas = { + 'with_service_admin': {'user': service_admin, 'admin': service_admin, + 'expected_return': 200}, + 'with_admin_a': {'user': admin_a, 'admin': admin_a, + 'expected_return': 200}, + 'with_creator_a': {'user': creator_a, 'admin': admin_a, + 'expected_return': 200}, + 'with_observer_a': {'user': observer_a, 'admin': admin_a, + 'expected_return': 200}, + 'with_auditor_a': {'user': auditor_a, 'admin': admin_a, + 'expected_return': 200}, +} + + +test_data_rbac_get_project_quotas = { + 'with_service_admin': {'user': service_admin, 'admin': service_admin, + 'expected_return': 200}, + 'with_admin_a': {'user': admin_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_creator_a': {'user': creator_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_observer_a': {'user': observer_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_auditor_a': {'user': auditor_a, 'admin': admin_a, + 'expected_return': 403}, +} + +test_data_rbac_set_project_quotas = { + 'with_service_admin': {'user': service_admin, 'admin': service_admin, + 'expected_return': 200}, + 'with_admin_a': {'user': admin_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_creator_a': {'user': creator_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_observer_a': {'user': observer_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_auditor_a': {'user': auditor_a, 'admin': admin_a, + 'expected_return': 403}, +} + + +test_data_rbac_delete_project_quotas = { + 'with_service_admin': {'user': service_admin, 'admin': service_admin, + 'expected_return': 204}, + 'with_admin_a': {'user': admin_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_creator_a': {'user': creator_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_observer_a': {'user': observer_a, 'admin': admin_a, + 'expected_return': 403}, + 'with_auditor_a': {'user': auditor_a, 'admin': admin_a, + 'expected_return': 403}, +} + + +def get_set_project_quotas_request(): + return {"project_quotas": + {"secrets": 50, + "orders": 10, + "containers": 20}} + + +@utils.parameterized_test_case +class RBACQuotasTestCase(base.TestCase): + """Functional tests exercising RBAC Policies""" + def setUp(self): + super(RBACQuotasTestCase, self).setUp() + self.behaviors = quota_behaviors.QuotaBehaviors(self.client) + self.project_id = self.behaviors.get_project_id_from_name('admin') + + def tearDown(self): + super(RBACQuotasTestCase, self).tearDown() + + @utils.parameterized_dataset(test_data_rbac_get_quotas) + def test_rbac_get_quotas(self, user, admin, expected_return): + """Test RBAC for get quotas + + Issue a get quotas and verify that that the correct + http return code comes back for the specified user. + + :param user: the user who will attempt to do the get + :param admin: the admin of the group owning quotas + :param expected_return: the expected http return code + """ + resp = self.behaviors.get_quotas(user_name=user) + self.assertEqual(expected_return, resp.status_code) + + @utils.parameterized_dataset(test_data_rbac_get_project_quotas) + def test_rbac_get_project_quotas(self, user, admin, expected_return): + """Test RBAC for get project quotas + + Issue a get quotas and verify that that the correct + http return code comes back for the specified user. + + :param user: the user who will attempt to do the get + :param admin: the admin of the group owning quotas + :param expected_return: the expected http return code + """ + resp, _ = self.behaviors.get_project_quotas_list(user_name=user) + self.assertEqual(expected_return, resp.status_code) + + @utils.parameterized_dataset(test_data_rbac_set_project_quotas) + def test_rbac_set_project_quotas(self, user, admin, expected_return): + """Test RBAC for set project quotas + + Issue a set project quotas and verify that that the correct + http return code comes back for the specified user. + + :param user: the user who will attempt to do the set + :param admin: the admin of the group owning quotas + :param expected_return: the expected http return code + """ + request_model = quota_models.ProjectQuotaRequestModel( + **get_set_project_quotas_request()) + resp = self.behaviors.set_project_quotas(self.project_id, + request_model, + user_name=user) + self.assertEqual(expected_return, resp.status_code) + + @utils.parameterized_dataset(test_data_rbac_delete_project_quotas) + def test_rbac_delete_project_quotas(self, user, admin, expected_return): + """Test RBAC for delete project quotas + + Issue a set project quotas and verify that that the correct + http return code comes back for the specified user. + + :param user: the user who will attempt to do the delete + :param admin: the admin of the group owning quotas + :param expected_return: the expected http return code + """ + resp = self.behaviors.delete_project_quotas(self.project_id, + user_name=user) + self.assertEqual(expected_return, resp.status_code) diff --git a/functionaltests/common/client.py b/functionaltests/common/client.py index 362737809..cf1d674bb 100644 --- a/functionaltests/common/client.py +++ b/functionaltests/common/client.py @@ -46,6 +46,12 @@ class BarbicanClient(object): username=CONF.identity.username, password=CONF.identity.password, project_name=CONF.identity.project_name) + self._auth[CONF.identity.service_admin] = auth.FunctionalTestAuth( + endpoint=CONF.identity.uri, + version=CONF.identity.version, + username=CONF.identity.service_admin, + password=CONF.identity.service_admin_password, + project_name=CONF.identity.service_admin_project) self._auth[CONF.rbac_users.admin_a] = auth.FunctionalTestAuth( endpoint=CONF.identity.uri, version=CONF.identity.version, diff --git a/functionaltests/common/config.py b/functionaltests/common/config.py index 748afa0b0..87dabd077 100644 --- a/functionaltests/common/config.py +++ b/functionaltests/common/config.py @@ -32,7 +32,10 @@ def setup_config(config_file=''): cfg.StrOpt('password', default='secretadmin'), cfg.StrOpt('project_name', default='admin'), cfg.StrOpt('domain_name', default='Default'), - cfg.StrOpt('region', default='RegionOne')] + cfg.StrOpt('region', default='RegionOne'), + cfg.StrOpt('service_admin', default='service-admin'), + cfg.StrOpt('service_admin_project', default='service'), + cfg.StrOpt('service_admin_password', default='secretservice')] TEST_CONF.register_group(identity_group) TEST_CONF.register_opts(identity_options, group=identity_group)