diff --git a/barbican/tests/api/test_resources_policy.py b/barbican/tests/api/test_resources_policy.py index 67ecc4996..8243f058b 100644 --- a/barbican/tests/api/test_resources_policy.py +++ b/barbican/tests/api/test_resources_policy.py @@ -648,11 +648,24 @@ class WhenTestingSecretResource(BaseTestCase): project_id=self.external_project_id) def test_should_raise_delete_secret(self): - self._assert_fail_rbac([None, 'audit', 'observer', 'creator', 'bogus'], - self._invoke_on_delete) + """A non-admin user cannot delete other user's secret. + + User id is different from initial user who has created the secret. + """ + self._assert_fail_rbac([None, 'audit', 'observer', 'creator', 'bogus'], + self._invoke_on_delete, + user_id=self.user_id, + project_id=self.external_project_id) + + def test_should_pass_delete_secret_for_owner(self): + """Non-admin user can delete his/her own secret + + Secret creator_id should match with token user to establish ownership. + """ + self._assert_pass_rbac(['creator'], self._invoke_on_delete, + user_id=self.creator_user_id, + project_id=self.external_project_id) - # @mock.patch.object(secrets.SecretController, 'get_acl_tuple', - # return_value=(None, None)) def _invoke_on_get(self): self.resource.on_get(self.req, self.resp) @@ -883,8 +896,24 @@ class WhenTestingContainerResource(BaseTestCase): project_id=self.external_project_id) def test_should_raise_delete_container(self): + """A non-admin user cannot delete other user's container. + + User id is different from initial user who has created the container. + """ self._assert_fail_rbac([None, 'audit', 'observer', 'creator', 'bogus'], - self._invoke_on_delete) + self._invoke_on_delete, + user_id=self.user_id, + project_id=self.external_project_id) + + def test_should_pass_delete_container_for_owner(self): + """Non-admin user can delete his/her own container + + Container creator_id should match with token user to establish + ownership. + """ + self._assert_pass_rbac(['creator'], self._invoke_on_delete, + user_id=self.creator_user_id, + project_id=self.external_project_id) def _invoke_on_get(self): self.resource.on_get(self.req, self.resp) diff --git a/bin/keystone_data.sh b/bin/keystone_data.sh index ce67b037d..c01173f72 100755 --- a/bin/keystone_data.sh +++ b/bin/keystone_data.sh @@ -104,6 +104,15 @@ if [[ "$ENABLED_SERVICES" =~ "barbican" ]]; then --user "$USER_ID" \ --project "$PROJECT_A_ID" \ "$ROLE_CREATOR_ID" + # Adding second creator user in project_a + USER_ID=$(openstack user create \ + --password "$PASSWORD" \ + --email "creator2_a@example.net" \ + "project_a_creator_2" -f value -c id) + openstack role add \ + --user "$USER_ID" \ + --project "$PROJECT_A_ID" \ + "$ROLE_CREATOR_ID" # # Setup RBAC Observer of Project A # diff --git a/devstack/lib/barbican b/devstack/lib/barbican index f003572f9..9be55ef07 100644 --- a/devstack/lib/barbican +++ b/devstack/lib/barbican @@ -278,6 +278,15 @@ function create_barbican_accounts { --user "$USER_ID" \ --project "$PROJECT_A_ID" \ "$ROLE_CREATOR_ID" + # Adding second creator user in project_a + USER_ID=$(openstack user create \ + --password "$PASSWORD" \ + --email "creator2_a@example.net" \ + "project_a_creator_2" -f value -c id) + openstack role add \ + --user "$USER_ID" \ + --project "$PROJECT_A_ID" \ + "$ROLE_CREATOR_ID" # # Setup RBAC Observer of Project A # diff --git a/doc/source/admin-guide-cloud/access_control.rst b/doc/source/admin-guide-cloud/access_control.rst index 69a8d984e..2a85c312a 100644 --- a/doc/source/admin-guide-cloud/access_control.rst +++ b/doc/source/admin-guide-cloud/access_control.rst @@ -5,8 +5,8 @@ Access Control Role Based Access Control (RBAC) -------------------------------- -Like many other services, the Key Manager service supports the protection of -its APIs by enforcing policy rules defined in a policy file. The Key Manager +Like many other services, the Key Manager service supports the protection of its +APIs by enforcing policy rules defined in a policy file. The Key Manager service stores a reference to a policy JSON file in its configuration file, :file:`/etc/barbican/barbican.conf`. Typically this file is named ``policy.json`` and it is stored in :file:`/etc/barbican/policy.json`. @@ -58,9 +58,11 @@ admin by the project for which the admin role is scoped. creator - Users with this role are allowed to create new resources but are not - allowed to delete any existing resources. They are also allowed full - access to existing secrets owned by the project in scope. + Users with this role are allowed to create new resources and can only + delete resources which are originally created (owned) by them. Users with + this role cannot delete other user's resources managed within same project. + They are also allowed full access to existing secrets owned by the project + in scope. observer Users with this role are allowed to access to existing resources but are @@ -73,11 +75,11 @@ audit Access Control List API ----------------------- -There are some limitations that result from scoping ownership of a secret -at the project level. For example, there is no easy way for a user to upload -a secret for which only they have access. There is also no easy way to grant -a user access to only a single secret. +There are some limitations that result from scoping ownership of a secret at the +project level. For example, there is no easy way for a user to upload a secret +for which only they have access. There is also no easy way to grant a user +access to only a single secret. -To address this limitations the Key Manager service includes an Access -Control List (ACL) API. For full details see the +To address this limitations the Key Manager service includes an Access Control +List (ACL) API. For full details see the `ACL API User Guide `__ diff --git a/etc/barbican/barbican-functional.conf b/etc/barbican/barbican-functional.conf index f826f4216..214dfa75c 100644 --- a/etc/barbican/barbican-functional.conf +++ b/etc/barbican/barbican-functional.conf @@ -24,6 +24,8 @@ admin_a=project_a_admin admin_a_password=barbican creator_a=project_a_creator creator_a_password=barbican +creator_a_2=project_a_creator_2 +creator_a_2_password=barbican observer_a=project_a_observer observer_a_password=barbican auditor_a=project_a_auditor diff --git a/etc/barbican/policy.json b/etc/barbican/policy.json index 0e51957e8..06e0b06c9 100644 --- a/etc/barbican/policy.json +++ b/etc/barbican/policy.json @@ -30,7 +30,7 @@ "secret:decrypt": "rule:secret_decrypt_non_private_read or rule:secret_project_creator or rule:secret_project_admin or rule:secret_acl_read", "secret:get": "rule:secret_non_private_read or rule:secret_project_creator or rule:secret_project_admin or rule:secret_acl_read", "secret:put": "rule:admin_or_creator and rule:secret_project_match", - "secret:delete": "rule:admin and rule:secret_project_match", + "secret:delete": "rule:secret_project_admin or rule:secret_project_creator", "secrets:post": "rule:admin_or_creator", "secrets:get": "rule:all_but_audit", "orders:post": "rule:admin_or_creator", @@ -45,7 +45,7 @@ "containers:post": "rule:admin_or_creator", "containers:get": "rule:all_but_audit", "container:get": "rule:container_non_private_read or rule:container_project_creator or rule:container_project_admin or rule:container_acl_read", - "container:delete": "rule:admin", + "container:delete": "rule:container_project_admin or rule:container_project_creator", "container_secret:post": "rule:admin", "container_secret:delete": "rule:admin", "transport_key:get": "rule:all_users", diff --git a/functionaltests/api/v1/functional/test_containers_rbac.py b/functionaltests/api/v1/functional/test_containers_rbac.py index 77b93149d..b5495b5d7 100644 --- a/functionaltests/api/v1/functional/test_containers_rbac.py +++ b/functionaltests/api/v1/functional/test_containers_rbac.py @@ -24,6 +24,7 @@ from functionaltests.common import config CONF = config.get_config() admin_a = CONF.rbac_users.admin_a creator_a = CONF.rbac_users.creator_a +creator_a_2 = CONF.rbac_users.creator_a_2 observer_a = CONF.rbac_users.observer_a auditor_a = CONF.rbac_users.auditor_a @@ -55,8 +56,10 @@ test_data_rbac_update_container = { test_data_rbac_delete_container = { 'with_admin_a': {'user': admin_a, 'admin': admin_a, 'expected_return': 204}, - 'with_creator_a': {'user': creator_a, 'admin': admin_a, - 'expected_return': 403}, + 'with_creator_a': {'user': creator_a, 'admin': creator_a, + 'expected_return': 204}, + 'with_creator_a_2': {'user': creator_a_2, 'admin': creator_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, diff --git a/functionaltests/api/v1/functional/test_secrets_rbac.py b/functionaltests/api/v1/functional/test_secrets_rbac.py index 73486ee07..3b3da8ff1 100644 --- a/functionaltests/api/v1/functional/test_secrets_rbac.py +++ b/functionaltests/api/v1/functional/test_secrets_rbac.py @@ -24,6 +24,7 @@ from functionaltests.common import config CONF = config.get_config() admin_a = CONF.rbac_users.admin_a creator_a = CONF.rbac_users.creator_a +creator_a_2 = CONF.rbac_users.creator_a_2 observer_a = CONF.rbac_users.observer_a auditor_a = CONF.rbac_users.auditor_a @@ -86,8 +87,10 @@ test_data_rbac_get_list_of_secrets = { test_data_rbac_delete_secret = { 'with_admin_a': {'user': admin_a, 'admin': admin_a, 'expected_return': 204}, - 'with_creator_a': {'user': creator_a, 'admin': admin_a, - 'expected_return': 403}, + 'with_creator_a': {'user': creator_a, 'admin': creator_a, + 'expected_return': 204}, + 'with_creator_a_2': {'user': creator_a_2, 'admin': creator_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, diff --git a/functionaltests/common/client.py b/functionaltests/common/client.py index 4492891ed..596c07fd1 100644 --- a/functionaltests/common/client.py +++ b/functionaltests/common/client.py @@ -65,6 +65,12 @@ class BarbicanClient(object): username=CONF.rbac_users.creator_a, password=CONF.rbac_users.creator_a_password, project_name=CONF.rbac_users.project_a) + self._auth[CONF.rbac_users.creator_a_2] = auth.FunctionalTestAuth( + endpoint=CONF.identity.uri, + version=CONF.identity.version, + username=CONF.rbac_users.creator_a_2, + password=CONF.rbac_users.creator_a_2_password, + project_name=CONF.rbac_users.project_a) self._auth[CONF.rbac_users.observer_a] = auth.FunctionalTestAuth( endpoint=CONF.identity.uri, version=CONF.identity.version, diff --git a/functionaltests/common/config.py b/functionaltests/common/config.py index f5dd569ae..284e67d64 100644 --- a/functionaltests/common/config.py +++ b/functionaltests/common/config.py @@ -48,6 +48,8 @@ def setup_config(config_file=''): cfg.StrOpt('admin_a_password', default='barbican', secret=True), cfg.StrOpt('creator_a', default='project_a_creator'), cfg.StrOpt('creator_a_password', default='barbican', secret=True), + cfg.StrOpt('creator_a_2', default='project_a_creator_2'), + cfg.StrOpt('creator_a_2_password', default='barbican', secret=True), cfg.StrOpt('observer_a', default='project_a_observer'), cfg.StrOpt('observer_a_password', default='barbican', secret=True), cfg.StrOpt('auditor_a', default='project_a_auditor'),