Merge "Replace assertItemsEqual with assertCountEqual"
diff --git a/.zuul.yaml b/.zuul.yaml
index b827bce..ba6fc8c 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,3 +1,39 @@
+- job:
+    name: keystone-protection-functional
+    parent: keystone-dsvm-py3-functional
+    vars:
+      tempest_test_regex: 'keystone_tempest_plugin.tests.rbac'
+      devstack_localrc:
+        KEYSTONE_ENFORCE_SCOPE: True
+      devstack_plugins:
+        keystone: https://opendev.org/openstack/keystone
+      devstack_services:
+        g-api: false
+        g-reg: false
+        n-api: false
+        n-api-meta: false
+        n-cond: false
+        n-cpu: false
+        n-novnc: false
+        n-sch: false
+        placement-api: false
+        q-agt: false
+        q-dhcp: false
+        q-l3: false
+        q-meta: false
+        q-metering: false
+        s-account: false
+        s-container: false
+        s-object: false
+        s-proxy: false
+        c-api: false
+        c-bak: false
+        c-sch: false
+        c-vol: false
+        cinder: false
+      devstack_local_conf:
+        post-config: {}
+
 - project:
     templates:
       - check-requirements
@@ -5,28 +41,38 @@
     check:
       jobs:
         - keystone-dsvm-py3-functional
-        - keystone-dsvm-py3-functional-federation-opensuse15:
+        - keystone-dsvm-py3-functional-federation-ubuntu-focal:
             voting: false
-        - keystone-dsvm-py3-functional-federation-opensuse15-k2k
+        - keystone-dsvm-py3-functional-federation-ubuntu-focal-k2k
+        - keystone-dsvm-py3-functional-wallaby
+        - keystone-dsvm-py3-functional-victoria
         - keystone-dsvm-py3-functional-ussuri
         - keystone-dsvm-py3-functional-train
-        - keystone-dsvm-py3-functional-stein
+        - keystone-protection-functional
     gate:
       jobs:
         - keystone-dsvm-py3-functional
-        - keystone-dsvm-py3-functional-federation-opensuse15-k2k
+        - keystone-dsvm-py3-functional-federation-ubuntu-focal-k2k
+        - keystone-protection-functional
+
+- job:
+    name: keystone-dsvm-py3-functional-wallaby
+    parent: keystone-dsvm-py3-functional
+    override-checkout: stable/wallaby
+
+- job:
+    name: keystone-dsvm-py3-functional-victoria
+    parent: keystone-dsvm-py3-functional
+    override-checkout: stable/victoria
 
 - job:
     name: keystone-dsvm-py3-functional-ussuri
     parent: keystone-dsvm-py3-functional
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
 
 - job:
     name: keystone-dsvm-py3-functional-train
     parent: keystone-dsvm-py3-functional
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
-
-- job:
-    name: keystone-dsvm-py3-functional-stein
-    parent: keystone-dsvm-py3-functional
-    override-checkout: stable/stein
diff --git a/keystone_tempest_plugin/clients.py b/keystone_tempest_plugin/clients.py
index 97b5b14..93c0753 100644
--- a/keystone_tempest_plugin/clients.py
+++ b/keystone_tempest_plugin/clients.py
@@ -19,6 +19,7 @@
 from keystone_tempest_plugin.services.identity.v3 import (
     service_providers_client)
 from keystone_tempest_plugin.services.identity.v3 import auth_client
+from keystone_tempest_plugin.services.identity.v3 import limits_client
 from keystone_tempest_plugin.services.identity.v3 import saml2_client
 
 from tempest import clients
@@ -29,7 +30,10 @@
     def __init__(self, credentials):
         super(Manager, self).__init__(credentials)
 
+        # keystone auth client
         self.auth_client = auth_client.AuthClient(self.auth_provider)
+
+        # federation clients
         self.identity_providers_client = (
             identity_providers_client.IdentityProvidersClient(
                 self.auth_provider))
@@ -40,3 +44,8 @@
         self.service_providers_client = (
             service_providers_client.ServiceProvidersClient(
                 self.auth_provider))
+
+        # unified limits clients
+        self.registered_limits_client = limits_client.RegisteredLimitsClient(
+            self.auth_provider)
+        self.limits_client = limits_client.LimitsClient(self.auth_provider)
diff --git a/keystone_tempest_plugin/config.py b/keystone_tempest_plugin/config.py
index 25964a8..2d4d189 100644
--- a/keystone_tempest_plugin/config.py
+++ b/keystone_tempest_plugin/config.py
@@ -15,7 +15,7 @@
 
 from oslo_config import cfg
 
-identity_feature_option = [
+identity_feature_options = [
     cfg.BoolOpt('federation',
                 default=False,
                 help='Does the environment support the Federated Identity '
@@ -25,6 +25,10 @@
                 help='Whether to test federated scenarios against an external '
                      'identity provider. If disabled, only '
                      'Keystone-to-Keystone tests will be enabled.'),
+    cfg.BoolOpt('enforce_scope',
+                default=False,
+                help='Does the keystone service enforce scope and use '
+                     'scope-aware policies?'),
 ]
 
 fed_scenario_group = cfg.OptGroup(name='fed_scenario',
diff --git a/keystone_tempest_plugin/plugin.py b/keystone_tempest_plugin/plugin.py
index 76f7a9c..72db604 100644
--- a/keystone_tempest_plugin/plugin.py
+++ b/keystone_tempest_plugin/plugin.py
@@ -32,12 +32,12 @@
 
     def register_opts(self, conf):
         config.register_opt_group(conf, config.identity_feature_group,
-                                  project_config.identity_feature_option)
+                                  project_config.identity_feature_options)
         config.register_opt_group(conf, project_config.fed_scenario_group,
                                   project_config.FedScenarioGroup)
 
     def get_opt_lists(self):
         return [(config.identity_feature_group.name,
-                 project_config.identity_feature_option),
+                 project_config.identity_feature_options),
                 (project_config.fed_scenario_group.name,
                  project_config.FedScenarioGroup)]
diff --git a/keystone_tempest_plugin/services/identity/v3/limits_client.py b/keystone_tempest_plugin/services/identity/v3/limits_client.py
new file mode 100644
index 0000000..6e95b9d
--- /dev/null
+++ b/keystone_tempest_plugin/services/identity/v3/limits_client.py
@@ -0,0 +1,156 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import json
+
+import http.client
+from tempest.lib.common import rest_client
+
+from keystone_tempest_plugin.services.identity import clients
+
+
+class RegisteredLimitsClient(clients.Identity):
+
+    subpath_prefix = 'registered_limits'
+    collection_url = subpath_prefix
+    entity_url = subpath_prefix + '/%s'
+
+    def create_registered_limits(self, payload):
+        """Create a list of registered limits.
+
+        :param body: A list of registered limits objects.
+        """
+        post_body = json.dumps({'registered_limits': payload})
+        resp, body = super(RegisteredLimitsClient, self).post(
+            self.collection_url, post_body)
+        self.expected_success(http.client.CREATED, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def list_registered_limits(self, **kwargs):
+        """List registered limits.
+
+        :param kwargs: Filter by service_id, region_id, or resource_name
+        """
+        resp, body = super(RegisteredLimitsClient, self).get(
+            self.collection_url, **kwargs)
+        self.expected_success(http.client.OK, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def update_registered_limit(self, registered_limit_id, registered_limit):
+        """Update a registered limit.
+
+        :param registered_limit_id: ID of registered limit to update
+        :param registered_limit: new registered limit object
+        """
+        patch_body = json.dumps({'registered_limit': registered_limit})
+        resp, body = super(RegisteredLimitsClient, self).patch(
+            self.entity_url % registered_limit_id, patch_body)
+        self.expected_success(http.client.OK, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def show_registered_limit(self, registered_limit_id):
+        """Get a registered limit.
+
+        :param registered_limit_id: ID of registered limit to show
+        """
+        resp, body = super(RegisteredLimitsClient, self).get(
+            self.entity_url % registered_limit_id)
+        self.expected_success(http.client.OK, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_registered_limit(self, registered_limit_id):
+        """Delete a registered limit.
+
+        :param registered_limit_id: ID of registered limit to delete.
+        """
+        resp, body = super(RegisteredLimitsClient, self).delete(
+            self.entity_url % registered_limit_id)
+        self.expected_success(http.client.NO_CONTENT, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+
+class LimitsClient(clients.Identity):
+
+    subpath_prefix = 'limits'
+    collection_url = subpath_prefix
+    entity_url = subpath_prefix + '/%s'
+
+    def limits_model(self):
+        """Get limits model from server."""
+        url = self.entity_url % 'model'
+        resp, body = super(LimitsClient, self).get(url)
+        self.expected_success(http.client.OK, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def create_limits(self, payload):
+        """Create a list of project limits.
+
+        :param body: A list of project limits objects.
+        """
+        post_body = json.dumps({'limits': payload})
+        resp, body = super(LimitsClient, self).post(
+            self.collection_url, post_body)
+        self.expected_success(http.client.CREATED, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def list_limits(self, **kwargs):
+        """List project limits.
+
+        :param kwargs: Filter by service_id, region_id, resource_name,
+            or project/domain ID
+        """
+        resp, body = super(LimitsClient, self).get(
+            self.collection_url, **kwargs)
+        self.expected_success(http.client.OK, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def update_limit(self, limit_id, limit):
+        """Update a project limit.
+
+        :param limit_id: ID of project limit to update
+        :param limit: new project limit object
+        """
+        patch_body = json.dumps({'limit': limit})
+        resp, body = super(LimitsClient, self).patch(
+            self.entity_url % limit_id, patch_body)
+        self.expected_success(http.client.OK, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def show_limit(self, limit_id):
+        """Get a project limit.
+
+        :param limit_id: ID of project limit to show
+        """
+        resp, body = super(LimitsClient, self).get(self.entity_url % limit_id)
+        self.expected_success(http.client.OK, resp.status)
+        body = json.loads(body.decode('utf-8'))
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_limit(self, limit_id):
+        """Delete a project limit.
+
+        :param limit_id: ID of project limit to delete.
+        """
+        resp, body = super(LimitsClient, self).delete(
+            self.entity_url % limit_id)
+        self.expected_success(http.client.NO_CONTENT, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/keystone_tempest_plugin/tests/base.py b/keystone_tempest_plugin/tests/base.py
index 16f0092..77dcafe 100644
--- a/keystone_tempest_plugin/tests/base.py
+++ b/keystone_tempest_plugin/tests/base.py
@@ -42,3 +42,6 @@
         cls.tokens_client = cls.keystone_manager.token_v3_client
         cls.consumers_client = cls.keystone_manager.oauth_consumers_client
         cls.oauth_token_client = cls.keystone_manager.oauth_token_client
+        cls.registered_limits_client = (
+            cls.keystone_manager.registered_limits_client)
+        cls.limits_client = cls.keystone_manager.limits_client
diff --git a/keystone_tempest_plugin/tests/rbac/__init__.py b/keystone_tempest_plugin/tests/rbac/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/__init__.py
diff --git a/keystone_tempest_plugin/tests/rbac/v3/__init__.py b/keystone_tempest_plugin/tests/rbac/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/__init__.py
diff --git a/keystone_tempest_plugin/tests/rbac/v3/base.py b/keystone_tempest_plugin/tests/rbac/v3/base.py
new file mode 100644
index 0000000..d260b7b
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/base.py
@@ -0,0 +1,41 @@
+# Copyright 2020 SUSE LLC
+#
+# 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 tempest import config
+
+CONF = config.CONF
+
+
+class IdentityV3RbacBaseTests(object):
+
+    identity_version = 'v3'
+
+    @classmethod
+    def skip_checks(cls):
+        super(IdentityV3RbacBaseTests, cls).skip_checks()
+        if not CONF.identity_feature_enabled.enforce_scope:
+            raise cls.skipException("enforce_scope is not enabled for "
+                                    "keystone, skipping RBAC tests")
+
+    def do_request(self, method, expected_status=200, client=None, **payload):
+        if not client:
+            client = self.client
+        if isinstance(expected_status, type(Exception)):
+            self.assertRaises(expected_status,
+                              getattr(client, method),
+                              **payload)
+        else:
+            response = getattr(client, method)(**payload)
+            self.assertEqual(response.response.status, expected_status)
+            return response
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_access_rule.py b/keystone_tempest_plugin/tests/rbac/v3/test_access_rule.py
new file mode 100644
index 0000000..25767fc
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_access_rule.py
@@ -0,0 +1,487 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest import clients
+from tempest.lib import auth
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacAccessRuleTest(rbac_base.IdentityV3RbacBaseTests,
+                                   metaclass=abc.ABCMeta):
+
+    identity_version = 'v3'
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacAccessRuleTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.access_rules_client
+        cls.admin_client = cls.os_system_admin
+
+    def user(self):
+        user = {}
+        name = data_utils.rand_name('user')
+        user['name'] = name
+        user['password'] = data_utils.rand_password()
+        return user
+
+    def app_cred(self):
+        app_cred = {}
+        app_cred['name'] = data_utils.rand_name('app_cred')
+        app_cred['access_rules'] = [
+            {
+                'path': '/servers',
+                'method': 'GET',
+                'service': 'compute',
+            }
+        ]
+        return app_cred
+
+    @classmethod
+    def setup_user_client(cls, domain_id=None):
+        """Set up project user with its own client.
+
+        This is to enable the project user to create its own app cred.
+
+        Returns a client object and the user's ID.
+        """
+        user_dict = {
+            'name': data_utils.rand_name('user'),
+            'password': data_utils.rand_password(),
+        }
+        if domain_id:
+            user_dict['domain_id'] = domain_id
+        user_id = cls.admin_client.users_v3_client.create_user(
+            **user_dict)['user']['id']
+
+        def try_delete_user():
+            # delete user if not deleted by domain deletion
+            try:
+                cls.admin_client.users_v3_client.delete_user(user_id)
+            except exceptions.NotFound:
+                pass
+
+        cls.addClassResourceCleanup(try_delete_user)
+        project_id = cls.admin_client.projects_client.create_project(
+            data_utils.rand_name())['project']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project, project_id)
+        member_role_id = cls.admin_client.roles_v3_client.list_roles(
+            name='member')['roles'][0]['id']
+        cls.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, member_role_id)
+        creds = auth.KeystoneV3Credentials(
+            user_id=user_id,
+            password=user_dict['password'],
+            project_id=project_id)
+        auth_provider = clients.get_auth_provider(creds)
+        creds = auth_provider.fill_credentials()
+        client = clients.Manager(credentials=creds)
+        return client, user_id
+
+    @abc.abstractmethod
+    def test_identity_get_access_rule(self):
+        """Test identity:get_access_rule policy
+
+        This test must check:
+          * whether the persona can retrieve an access rule they own
+          * whether the persona can retrieve an access rule they do not own
+          * whether the persona can retrieve an access rule that does not exist
+          * whether the persona can retrieve an access rule for a user in their
+            own domain (if applicable)
+          * whether the persona can retrieve an access rule for a user in
+            another domain (if applicable)
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_access_rules(self):
+        """Test identity:list_access_rules policy
+
+        This test must check:
+          * whether the persona can list their own access rules
+          * whether the persona can list the access rules for another user
+          * whether the persona can list the access rules for a user in their
+            own domain
+          * whether the persona can list the access rules for a user in another
+            domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_access_rule(self):
+        """Test identity:delete_access_rule policy.
+
+        This test must check
+          * whether the persona can delete an access rule they own
+          * whether the persona can delete an access rule for an arbitrary user
+          * whether the persona can delete an access rule that does not exist
+          * whether the persona can delete an access rule for a user in another
+            domain (if applicable)
+          * whether the persona can delete an access rule for a user in their
+            own domain (if applicable)
+          * whether the persona can delete an access rule that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacAccessRuleTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(SystemAdminTests, cls).setup_clients()
+        cls.test_user_client, cls.test_user_id = cls.setup_user_client()
+
+    def setUp(self):
+        # create app cred for other user
+        super(SystemAdminTests, self).setUp()
+        app_cred_client = self.test_user_client.application_credentials_client
+        app_cred = app_cred_client.create_application_credential(
+            user_id=self.test_user_id, **self.app_cred()
+        )['application_credential']
+        self.app_cred_id = app_cred['id']
+        self.access_rule_id = app_cred['access_rules'][0]['id']
+
+        def try_delete_app_cred(id):
+            app_cred_client = self.admin_client.application_credentials_client
+            try:
+                app_cred_client.delete_application_credential(
+                    user_id=self.test_user_id,
+                    application_credential_id=id)
+            except exceptions.NotFound:
+                pass
+
+        def try_delete_access_rule(id):
+            try:
+                self.admin_client.access_rules_client.delete_access_rule(
+                    user_id=self.test_user_id,
+                    access_rule_id=id)
+            except exceptions.NotFound:
+                pass
+        self.addCleanup(try_delete_access_rule, self.access_rule_id)
+        self.addCleanup(try_delete_app_cred, self.app_cred_id)
+
+    def test_identity_get_access_rule(self):
+        # system admin cannot create app creds and therefore cannot create
+        # access rules, so skip retrieval of own access rule
+
+        # retrieve other user's access rules
+        self.do_request(
+            'show_access_rule',
+            user_id=self.test_user_id, access_rule_id=self.access_rule_id)
+
+        # retrieving a non-existent access rule should return a 404
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.NotFound,
+            user_id=self.test_user_id,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_access_rules(self):
+        # system admin cannot create app creds and therefore cannot create
+        # access rules, so skip listing of own access rule
+
+        # list other user's access rules
+        self.do_request('list_access_rules', user_id=self.test_user_id)
+
+    def test_identity_delete_access_rule(self):
+        # system admin cannot create app creds and therefore cannot create
+        # access rules, so skip deletion of own access rule
+
+        # delete other user's access rules
+        app_cred_client = self.admin_client.application_credentials_client
+        app_cred_client.delete_application_credential(
+            user_id=self.test_user_id,
+            application_credential_id=self.app_cred_id)
+        self.do_request(
+            'delete_access_rule', expected_status=204,
+            user_id=self.test_user_id, access_rule_id=self.access_rule_id)
+
+        # deleting a non-existent access rule should return a 404
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.NotFound,
+            user_id=self.test_user_id,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_delete_access_rule(self):
+        app_cred_client = self.admin_client.application_credentials_client
+        app_cred_client.delete_application_credential(
+            user_id=self.test_user_id,
+            application_credential_id=self.app_cred_id)
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id, access_rule_id=self.access_rule_id)
+
+        # retrieving a non-existent access rule should return a 404
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.NotFound,
+            user_id=self.test_user_id,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacAccessRuleTest, base.BaseIdentityTest):
+
+    # Domain admins cannot create their own app creds (app creds can only be
+    # scoped to projects) and domain admins have no special privileges over the
+    # app creds own by users in their domains.
+
+    credentials = ['domain_admin', 'system_admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(DomainAdminTests, cls).setup_clients()
+        own_domain_id = cls.persona.credentials.domain_id
+        cls.test_client_1, cls.test_user_1 = cls.setup_user_client(
+            domain_id=own_domain_id)
+
+    def setUp(self):
+        super(DomainAdminTests, self).setUp()
+        self.other_domain_id = self.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_client.domains_client.delete_domain,
+                        self.other_domain_id)
+        self.addCleanup(self.admin_client.domains_client.update_domain,
+                        domain_id=self.other_domain_id, enabled=False)
+        self.test_client_2, self.test_user_2 = self.setup_user_client(
+            domain_id=self.other_domain_id)
+        client = self.test_client_1.application_credentials_client
+        app_cred_1 = client.create_application_credential(
+            user_id=self.test_user_1, **self.app_cred()
+        )['application_credential']
+        self.access_rule_1 = app_cred_1['access_rules'][0]['id']
+        self.addCleanup(
+            self.test_client_1.access_rules_client.delete_access_rule,
+            self.test_user_1,
+            self.access_rule_1)
+        self.addCleanup(
+            client.delete_application_credential,
+            self.test_user_1,
+            app_cred_1['id'])
+        client = self.test_client_2.application_credentials_client
+        app_cred_2 = client.create_application_credential(
+            user_id=self.test_user_2, **self.app_cred()
+        )['application_credential']
+        self.access_rule_2 = app_cred_2['access_rules'][0]['id']
+        self.addCleanup(
+            self.test_client_2.access_rules_client.delete_access_rule,
+            self.test_user_2,
+            self.access_rule_2)
+        self.addCleanup(
+            client.delete_application_credential,
+            self.test_user_2,
+            app_cred_2['id'])
+
+    def test_identity_get_access_rule(self):
+        # accessing access rules should be forbidden no matter whether the
+        # owner is in the domain or outside of it
+
+        # retrieve access rule from user in own domain
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1, access_rule_id=self.access_rule_1)
+
+        # retrieve access rule from user in other domain
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2, access_rule_id=self.access_rule_2)
+
+        # retrieving a non-existent access rule should return a 403
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1,
+            access_rule_id=data_utils.rand_uuid_hex())
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_access_rules(self):
+        # listing access rules should be forbidden no matter whether the
+        # owner is in the domain or outside of it
+        self.do_request(
+            'list_access_rules', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1)
+        self.do_request(
+            'list_access_rules', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2)
+
+    def test_identity_delete_access_rule(self):
+        # deleting access rules should be forbidden no matter whether the
+        # owner is in the domain or outside of it
+
+        # delete access rule from user in own domain
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1, access_rule_id=self.access_rule_1)
+
+        # delete access rule from user in other domain
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2, access_rule_id=self.access_rule_2)
+
+        # deleting a non-existent access rule should return a 403
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1,
+            access_rule_id=data_utils.rand_uuid_hex())
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainAdminTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacAccessRuleTest, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(ProjectAdminTests, cls).setup_clients()
+        cls.test_user_client, cls.test_user_id = cls.setup_user_client()
+
+    def setUp(self):
+        super(ProjectAdminTests, self).setUp()
+        app_cred_client = self.persona.application_credentials_client
+        user_id = self.persona.credentials.user_id
+        self.app_cred_1 = app_cred_client.create_application_credential(
+            user_id, **self.app_cred())['application_credential']
+        self.access_rule_1 = self.app_cred_1['access_rules'][0]['id']
+
+        def try_delete_own_app_cred(id):
+            app_cred_client = self.persona.application_credentials_client
+            try:
+                app_cred_client.delete_application_credential(
+                    self.persona.credentials.user_id, id)
+            except exceptions.NotFound:
+                pass
+
+        def try_delete_own_access_rule(id):
+            try:
+                self.persona.access_rules_client.delete_access_rule(
+                    self.persona.credentials.user_id, id)
+            except exceptions.NotFound:
+                pass
+
+        self.addCleanup(try_delete_own_access_rule, self.access_rule_1)
+        self.addCleanup(try_delete_own_app_cred, self.app_cred_1['id'])
+
+        app_cred_client = self.test_user_client.application_credentials_client
+        self.app_cred_2 = app_cred_client.create_application_credential(
+            self.test_user_id, **self.app_cred())['application_credential']
+        self.access_rule_2 = self.app_cred_2['access_rules'][0]['id']
+        self.addCleanup(
+            self.test_user_client.access_rules_client.delete_access_rule,
+            self.test_user_id, self.access_rule_2)
+        self.addCleanup(
+            app_cred_client.delete_application_credential,
+            self.test_user_id, self.app_cred_2['id'])
+
+    def test_identity_get_access_rule(self):
+        # should be able to access own credential
+        self.do_request(
+            'show_access_rule',
+            user_id=self.persona.credentials.user_id,
+            access_rule_id=self.access_rule_1)
+
+        # retrieving non-existent access rule for self should return 404
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.NotFound,
+            user_id=self.persona.credentials.user_id,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+        # should not be able to access another user's credential
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id, access_rule_id=self.access_rule_2)
+
+        # retrieving non-existent access rule for other user should return 403
+        self.do_request(
+            'show_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_access_rules(self):
+        # should be able to list own credentials
+        self.do_request(
+            'list_access_rules', user_id=self.persona.credentials.user_id)
+
+        # should not be able to list another user's credentials
+        self.do_request(
+            'list_access_rules', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id)
+
+    def test_identity_delete_access_rule(self):
+        # should be able to delete own credential
+        app_cred_client = self.persona.application_credentials_client
+        app_cred_client.delete_application_credential(
+            user_id=self.persona.credentials.user_id,
+            application_credential_id=self.app_cred_1['id'])
+        self.do_request(
+            'delete_access_rule', expected_status=204,
+            user_id=self.persona.credentials.user_id,
+            access_rule_id=self.access_rule_1)
+
+        # deleting non-existent access rule for self should return 404
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.NotFound,
+            user_id=self.persona.credentials.user_id,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+        # should not be able to delete another user's credential
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id, access_rule_id=self.access_rule_2)
+
+        # deleting non-existent access rule for other user should return 403
+        self.do_request(
+            'delete_access_rule', expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id,
+            access_rule_id=data_utils.rand_uuid_hex())
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_application_credential.py b/keystone_tempest_plugin/tests/rbac/v3/test_application_credential.py
new file mode 100644
index 0000000..3ca1680
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_application_credential.py
@@ -0,0 +1,565 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest import clients
+from tempest.lib import auth
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacApplicationCredentialTest(
+    rbac_base.IdentityV3RbacBaseTests, metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacApplicationCredentialTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.application_credentials_client
+        cls.admin_client = cls.os_system_admin
+
+    @classmethod
+    def setup_user_client(cls, domain_id=None):
+        """Set up project user with its own client.
+
+        This is to enable the project user to create its own app cred.
+
+        Returns a client object and the user's ID.
+        """
+        user_dict = {
+            'name': data_utils.rand_name('user'),
+            'password': data_utils.rand_password(),
+        }
+        if domain_id:
+            user_dict['domain_id'] = domain_id
+        user_id = cls.admin_client.users_v3_client.create_user(
+            **user_dict)['user']['id']
+
+        def try_cleanup_user():
+            # if domain is cleaned up first, user will already be deleted
+            try:
+                cls.admin_client.users_v3_client.delete_user(user_id)
+            except exceptions.NotFound:
+                pass
+
+        cls.addClassResourceCleanup(try_cleanup_user)
+        project_id = cls.admin_client.projects_client.create_project(
+            data_utils.rand_name())['project']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project, project_id)
+        member_role_id = cls.admin_client.roles_v3_client.list_roles(
+            name='member')['roles'][0]['id']
+        cls.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, member_role_id)
+        creds = auth.KeystoneV3Credentials(
+            user_id=user_id,
+            password=user_dict['password'],
+            project_id=project_id)
+        auth_provider = clients.get_auth_provider(creds)
+        creds = auth_provider.fill_credentials()
+        client = clients.Manager(credentials=creds)
+        return client, user_id
+
+    def app_cred(self):
+        app_cred = {}
+        app_cred['name'] = data_utils.rand_name('app_cred')
+        return app_cred
+
+    @abc.abstractmethod
+    def test_identity_create_application_credential(self):
+        """Test identity:create_application_credential policy.
+
+        This test must check:
+          * whether the persona can create an application credential for
+            themself
+          * whether the persona can create an application credential for
+            another user
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_application_credential(self):
+        """Test identity:get_application_credential policy.
+
+        This test must check:
+          * whether the persona can get their own application credential
+          * whether the persona can get an application credential for another
+            user
+          * whether the persona can get an application credential for a user in
+            another domain (if applicable)
+          * whether the persona can get an application credential for a user in
+            their own domain (if applicable)
+          * whether the persona can get an application credential that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_application_credentials(self):
+        """Test identity:list_application_credentials policy.
+
+        This test must check:
+          * whether the persona can list all application credentials for
+            themself
+          * whether the persona can list all application credentials for
+            another user
+          * whether the persona can list application credentials for a user in
+            their own domain
+          * whether the persona can list application credentials for a user in
+            another domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_application_credential(self):
+        """Test identity:delete_application_credential policy.
+
+        This test must check
+          * whether the persona can delete their own application credential
+          * whether the persona can delete an application credential for
+            another user
+          * whether the persona can delete an application credential for a user
+            in another domain (if applicable)
+          * whether the persona can delete an application credential for a user
+            in their own domain (if applicable)
+          * whether the persona can delete an application credential that does
+            not exist
+        """
+        pass
+
+
+class SystemAdminTests(
+    IdentityV3RbacApplicationCredentialTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(SystemAdminTests, cls).setup_clients()
+        cls.test_user_client, cls.test_user_id = cls.setup_user_client()
+
+    def test_identity_create_application_credential(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore system-scoped users cannot create app creds.
+        raise self.skipException(
+            "Skipping identity:create_application_credential test for "
+            "system user")
+
+    def test_identity_get_application_credential(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore system-scoped users cannot create app creds, so skip
+        # check for showing user's own app creds
+
+        # retrieve other user's app cred
+        user_app_cred_client = \
+            self.test_user_client.application_credentials_client
+        app_cred = user_app_cred_client.create_application_credential(
+            user_id=self.test_user_id, **self.app_cred()
+        )['application_credential']
+        self.addCleanup(
+            user_app_cred_client.delete_application_credential,
+            self.test_user_id,
+            app_cred['id'])
+        self.do_request(
+            'show_application_credential',
+            user_id=self.test_user_id,
+            application_credential_id=app_cred['id'])
+
+        # retrieve app cred that does not exist
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_id,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_application_credentials(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore system-scoped users cannot create app creds, so skip
+        # check for listing user's own app creds
+
+        # list other user's app creds
+        user_app_cred_client = \
+            self.test_user_client.application_credentials_client
+        app_cred = user_app_cred_client.create_application_credential(
+            user_id=self.test_user_id, **self.app_cred()
+        )['application_credential']
+        self.addCleanup(
+            user_app_cred_client.delete_application_credential,
+            self.test_user_id,
+            app_cred['id'])
+        resp = self.do_request(
+            'list_application_credentials',
+            user_id=self.test_user_id)
+        self.assertEqual(
+            resp['application_credentials'][0]['id'],
+            app_cred['id'])
+
+    def test_identity_delete_application_credential(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore system-scoped users cannot create app creds, so skip
+        # check for deleting user's own app creds
+
+        # delete other user's app cred
+        user_app_cred_client = \
+            self.test_user_client.application_credentials_client
+        app_cred = user_app_cred_client.create_application_credential(
+            user_id=self.test_user_id, **self.app_cred()
+        )['application_credential']
+        self.do_request(
+            'delete_application_credential',
+            expected_status=204,
+            user_id=self.test_user_id,
+            application_credential_id=app_cred['id'])
+
+        # delete app cred that does not exist
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_id,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_delete_application_credential(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore system-scoped users cannot create app creds, so skip
+        # check for deleting user's own app creds
+
+        # delete other user's app cred
+        user_app_cred_client = \
+            self.test_user_client.application_credentials_client
+        app_cred = user_app_cred_client.create_application_credential(
+            user_id=self.test_user_id, **self.app_cred()
+        )['application_credential']
+        self.addCleanup(
+            user_app_cred_client.delete_application_credential,
+            self.test_user_id,
+            app_cred['id'])
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id,
+            application_credential_id=app_cred['id'])
+
+        # delete app cred that does not exist
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_id,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(
+    IdentityV3RbacApplicationCredentialTest, base.BaseIdentityTest):
+
+    # Domain admins cannot create their own app creds (app creds can only be
+    # scoped to projects) and domain admins have no special privileges over the
+    # app creds own by users in their domains.
+
+    credentials = ['domain_admin', 'system_admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(DomainAdminTests, cls).setup_clients()
+        own_domain_id = cls.persona.credentials.domain_id
+        cls.test_client_1, cls.test_user_1 = cls.setup_user_client(
+            domain_id=own_domain_id)
+
+    def setUp(self):
+        super(DomainAdminTests, self).setUp()
+        self.other_domain_id = self.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_client.domains_client.delete_domain,
+                        self.other_domain_id)
+        self.addCleanup(self.admin_client.domains_client.update_domain,
+                        domain_id=self.other_domain_id, enabled=False)
+        self.test_client_2, self.test_user_2 = self.setup_user_client(
+            domain_id=self.other_domain_id)
+        client = self.test_client_1.application_credentials_client
+        self.app_cred_1 = client.create_application_credential(
+            user_id=self.test_user_1, **self.app_cred()
+        )['application_credential']
+        self.addCleanup(
+            client.delete_application_credential,
+            self.test_user_1,
+            self.app_cred_1['id'])
+        client = self.test_client_2.application_credentials_client
+        self.app_cred_2 = client.create_application_credential(
+            user_id=self.test_user_2, **self.app_cred()
+        )['application_credential']
+        self.addCleanup(
+            client.delete_application_credential,
+            self.test_user_2,
+            self.app_cred_2['id'])
+
+    def test_identity_create_application_credential(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore system-scoped users cannot create app creds.
+        raise self.skipException(
+            "Skipping identity:create_application_credential test for "
+            "domain user")
+
+    def test_identity_get_application_credential(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore domain-scoped users cannot create app creds, so skip
+        # check for showing user's own app creds
+
+        # accessing application credentials should be forbidden no matter
+        # whether the owner is in the domain or outside of it
+
+        # retrieve app cred from user in own domain
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1,
+            application_credential_id=self.app_cred_1['id'])
+
+        # retrieve app cred from user in other domain
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2,
+            application_credential_id=self.app_cred_2['id'])
+
+        # retrieve app cred that does not exist
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_application_credentials(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore domain-scoped users cannot create app creds, so skip
+        # check for listing user's own app creds
+
+        # listing application credentials should be forbidden no matter
+        # whether the owner is in the domain or outside of it
+
+        # list app creds from user in own domain
+        self.do_request(
+            'list_application_credentials',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1)
+
+        # list app creds from user in other domain
+        self.do_request(
+            'list_application_credentials',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2)
+
+    def test_identity_delete_application_credential(self):
+        # Creating an application credential requires a project ID in the
+        # token, therefore domain-scoped users cannot create app creds, so skip
+        # check for deleting user's own app creds
+
+        # deleting application credentials should be forbidden no matter
+        # whether the owner is in the domain or outside of it
+
+        # delete app cred from user in own domain
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1,
+            application_credential_id=self.app_cred_1['id'])
+
+        # delete app cred from user in other domain
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2,
+            application_credential_id=self.app_cred_2['id'])
+
+        # delete app cred that does not exist
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainAdminTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacApplicationCredentialTest,
+                        base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(ProjectAdminTests, cls).setup_clients()
+        cls.test_user_client, cls.test_user_id = cls.setup_user_client()
+
+    def test_identity_create_application_credential(self):
+        # user can create their own app cred
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request(
+            'create_application_credential',
+            expected_status=201,
+            user_id=user_id,
+            **self.app_cred())['application_credential']
+        self.addCleanup(
+            self.client.delete_application_credential,
+            user_id, resp['id'])
+
+        # user cannot create app cred for another user
+        user_id = self.test_user_id
+        self.do_request(
+            'create_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=user_id,
+            **self.app_cred())
+
+    def test_identity_get_application_credential(self):
+        # user can retrieve their own app cred
+        user_id = self.persona.credentials.user_id
+        app_cred = self.client.create_application_credential(
+            user_id=user_id, **self.app_cred())['application_credential']
+        self.addCleanup(
+            self.client.delete_application_credential,
+            user_id=user_id, application_credential_id=app_cred['id'])
+        self.do_request(
+            'show_application_credential',
+            user_id=user_id, application_credential_id=app_cred['id'])
+
+        # retrieving non-existent app cred for self should return 404
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.NotFound,
+            user_id=user_id,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+        # user cannot retrieve another user's app cred by using the victim's
+        # user ID in the request or by trying to bypass the user ownership
+        # check by crafting a path the the attacker's user ID
+        user_id = self.test_user_id
+        client = self.test_user_client.application_credentials_client
+        app_cred = client.create_application_credential(
+            user_id=user_id, **self.app_cred())['application_credential']
+        self.addCleanup(
+            client.delete_application_credential,
+            user_id=user_id, application_credential_id=app_cred['id'])
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.persona.credentials.user_id,
+            application_credential_id=app_cred['id'])
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=user_id, application_credential_id=app_cred['id'])
+
+        # retrieving non-existent app cred for another user should return 403
+        self.do_request(
+            'show_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=user_id,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_application_credentials(self):
+        # user can list their own app creds
+        user_id = self.persona.credentials.user_id
+        app_cred = self.client.create_application_credential(
+            user_id=user_id, **self.app_cred())['application_credential']
+        self.addCleanup(
+            self.client.delete_application_credential,
+            user_id=user_id, application_credential_id=app_cred['id'])
+        self.do_request(
+            'list_application_credentials', user_id=user_id)
+
+        # user cannot list another user's app creds
+        user_id = self.test_user_id
+        client = self.test_user_client.application_credentials_client
+        app_cred = client.create_application_credential(
+            user_id=user_id, **self.app_cred())['application_credential']
+        self.addCleanup(
+            client.delete_application_credential,
+            user_id=user_id, application_credential_id=app_cred['id'])
+        self.do_request(
+            'list_application_credentials',
+            expected_status=exceptions.Forbidden, user_id=user_id)
+
+    def test_identity_delete_application_credential(self):
+        # user can delete their own app cred
+        user_id = self.persona.credentials.user_id
+        app_cred = self.client.create_application_credential(
+            user_id=user_id, **self.app_cred())['application_credential']
+        self.do_request(
+            'delete_application_credential',
+            expected_status=204,
+            user_id=user_id, application_credential_id=app_cred['id'])
+
+        # deleting non-existent app cred for self should return 404
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.NotFound,
+            user_id=user_id,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+        # user cannot delete another user's app cred by using the victim's
+        # user ID in the request or by trying to bypass the user ownership
+        # check by crafting a path the the attacker's user ID
+        user_id = self.test_user_id
+        client = self.test_user_client.application_credentials_client
+        app_cred = client.create_application_credential(
+            user_id=user_id, **self.app_cred())['application_credential']
+        self.addCleanup(
+            client.delete_application_credential,
+            user_id=user_id, application_credential_id=app_cred['id'])
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.persona.credentials.user_id,
+            application_credential_id=app_cred['id'])
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=user_id, application_credential_id=app_cred['id'])
+
+        # deleting non-existent app cred for another user should return 403
+        self.do_request(
+            'delete_application_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=user_id,
+            application_credential_id=data_utils.rand_uuid_hex())
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_consumer.py b/keystone_tempest_plugin/tests/rbac/v3/test_consumer.py
new file mode 100644
index 0000000..b7dbb95
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_consumer.py
@@ -0,0 +1,196 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacOauth1ConsumerTest(rbac_base.IdentityV3RbacBaseTests,
+                                       metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacOauth1ConsumerTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.oauth_consumers_client
+        cls.admin_client = cls.os_system_admin.oauth_consumers_client
+
+    def consumer(self):
+        return {"description": data_utils.arbitrary_string()}
+
+    @abc.abstractmethod
+    def test_identity_create_consumer(self):
+        """Test identity:create_consumer policy.
+
+        This test must check:
+          * whether the persona can create a consumer
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_consumer(self):
+        """Test identity:get_consumer policy.
+
+        This test must check:
+          * whether the persona can get a consumer
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_consumers(self):
+        """Test identity:list_consumers policy.
+
+        This test must check:
+          * whether the persona can list all consumers
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_consumer(self):
+        """Test identity:update_consumer policy.
+
+        This test must check:
+          * whether the persona can update a
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_consumer(self):
+        """Test identity:delete_consumer policy.
+
+        This test must check
+          * whether the persona can delete a consumer
+        """
+        pass
+
+
+class SystemAdminTests(
+    IdentityV3RbacOauth1ConsumerTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_consumer(self):
+        resp = self.do_request('create_consumer',
+                               expected_status=201,
+                               **self.consumer())
+        self.addCleanup(self.client.delete_consumer,
+                        resp['consumer']['id'])
+
+    def test_identity_get_consumer(self):
+        consumer = self.admin_client.create_consumer(
+            **self.consumer())['consumer']
+        self.addCleanup(self.admin_client.delete_consumer, consumer['id'])
+        resp = self.do_request('show_consumer', consumer_id=consumer['id'])
+        self.assertEqual(resp['consumer']['id'], consumer['id'])
+
+    def test_identity_list_consumers(self):
+        consumer = self.admin_client.create_consumer(
+            **self.consumer())['consumer']
+        self.addCleanup(self.admin_client.delete_consumer, consumer['id'])
+        resp = self.do_request('list_consumers')
+        self.assertIn(consumer['id'], set(c['id'] for c in resp['consumers']))
+
+    def test_identity_update_consumer(self):
+        consumer = self.client.create_consumer(**self.consumer())['consumer']
+        self.addCleanup(self.client.delete_consumer, consumer['id'])
+        self.do_request('update_consumer',
+                        consumer_id=consumer['id'],
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_consumer(self):
+        consumer = self.client.create_consumer(**self.consumer())['consumer']
+        self.do_request('delete_consumer',
+                        expected_status=204,
+                        consumer_id=consumer['id'])
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_consumer(self):
+        self.do_request('create_consumer',
+                        expected_status=exceptions.Forbidden,
+                        **self.consumer())
+
+    def test_identity_update_consumer(self):
+        consumer = self.admin_client.create_consumer(
+            **self.consumer())['consumer']
+        self.addCleanup(self.admin_client.delete_consumer, consumer['id'])
+        self.do_request('update_consumer',
+                        expected_status=exceptions.Forbidden,
+                        consumer_id=consumer['id'],
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_consumer(self):
+        consumer = self.admin_client.create_consumer(
+            **self.consumer())['consumer']
+        self.do_request('delete_consumer',
+                        expected_status=exceptions.Forbidden,
+                        consumer_id=consumer['id'])
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemMemberTests):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_consumer(self):
+        consumer = self.admin_client.create_consumer(
+            **self.consumer())['consumer']
+        self.addCleanup(self.admin_client.delete_consumer, consumer['id'])
+        self.do_request('show_consumer',
+                        expected_status=exceptions.Forbidden,
+                        consumer_id=consumer['id'])
+
+    def test_identity_list_consumers(self):
+        consumer = self.admin_client.create_consumer(
+            **self.consumer())['consumer']
+        self.addCleanup(self.admin_client.delete_consumer, consumer['id'])
+        self.do_request('list_consumers',
+                        expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_credential.py b/keystone_tempest_plugin/tests/rbac/v3/test_credential.py
new file mode 100644
index 0000000..5b1bee2
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_credential.py
@@ -0,0 +1,490 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest import clients
+from tempest.lib import auth
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacCredentialTest(rbac_base.IdentityV3RbacBaseTests,
+                                   metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacCredentialTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.credentials_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_credentials_client = cls.admin_client.credentials_client
+
+        # personas in own or other domains
+        own_domain_id = cls.persona.credentials.domain_id
+        cls.test_client_1, cls.test_user_1 = cls.setup_user_client(
+            domain_id=own_domain_id)
+        cls.other_domain_id = cls.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.delete_domain, cls.other_domain_id)
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.update_domain,
+            domain_id=cls.other_domain_id, enabled=False)
+        cls.test_client_2, cls.test_user_2 = cls.setup_user_client(
+            domain_id=cls.other_domain_id)
+
+    @classmethod
+    def setup_user_client(cls, domain_id=None):
+        """Set up project user with its own client.
+
+        This is to enable the project user to create its own credential.
+
+        Returns a client object and the user's ID.
+        """
+        user_dict = {
+            'name': data_utils.rand_name('user'),
+            'password': data_utils.rand_password(),
+        }
+        if domain_id:
+            user_dict['domain_id'] = domain_id
+        user_id = cls.admin_client.users_v3_client.create_user(
+            **user_dict)['user']['id']
+
+        def try_cleanup_user():
+            # if domain is cleaned up first, user will already be deleted
+            try:
+                cls.admin_client.users_v3_client.delete_user(user_id)
+            except exceptions.NotFound:
+                pass
+
+        cls.addClassResourceCleanup(try_cleanup_user)
+        project_id = cls.admin_client.projects_client.create_project(
+            data_utils.rand_name())['project']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project, project_id)
+        member_role_id = cls.admin_client.roles_v3_client.list_roles(
+            name='member')['roles'][0]['id']
+        cls.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, member_role_id)
+        creds = auth.KeystoneV3Credentials(
+            user_id=user_id,
+            password=user_dict['password'],
+            project_id=project_id)
+        auth_provider = clients.get_auth_provider(creds)
+        creds = auth_provider.fill_credentials()
+        client = clients.Manager(credentials=creds)
+        return client, user_id
+
+    def credential(self, user_id):
+        cred = {
+            'blob': data_utils.rand_uuid_hex(),
+            'type': data_utils.rand_uuid_hex(),
+            'user_id': user_id,
+        }
+        return cred
+
+    @abc.abstractmethod
+    def test_identity_create_credential(self):
+        """Test identity:create_credential policy.
+
+        This test must check:
+          * whether the persona can create a credential for themself
+          * whether the persona can create acredential for another user in
+            their own domain
+          * whether the persona can create acredential for another user in
+            another domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_credential(self):
+        """Test identity:get_credential policy.
+
+        This test must check:
+          * whether the persona can get their own credential
+          * whether the persona can get a credential for a user in another
+            domain
+          * whether the persona can get a credential for a user in their own
+            domain
+          * whether the persona can get a credential that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_credentials(self):
+        """Test identity:list_credentials policy.
+
+        This test must check:
+          * whether the persona can list all credentials for themself
+          * whether the persona can list credentials for a user in their own
+            domain
+          * whether the persona can list credentials for a user in another
+            domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_credential(self):
+        """Test identity:update_credential policy.
+
+        This test must check:
+          * whether the persona can update their own credential
+          * whether the persona can update a credential for a user in another
+            domain
+          * whether the persona can update a credential for a user in their own
+            domain
+          * whether the persona can update a credential that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_credential(self):
+        """Test identity:delete_credential policy.
+
+        This test must check
+          * whether the persona can delete their own credential
+          * whether the persona can delete a credential for a user in another
+            domain
+          * whether the persona can delete a credential for a user in their own
+            domain
+          * whether the persona can delete a credential that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacCredentialTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_credential(self):
+        # user can create their own credential
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request(
+            'create_credential',
+            expected_status=201,
+            **self.credential(user_id=user_id))['credential']
+        self.addCleanup(self.client.delete_credential, resp['id'])
+        # user can create credential for other user in own domain
+        resp = self.do_request(
+            'create_credential',
+            expected_status=201,
+            **self.credential(user_id=self.test_user_1))['credential']
+        self.addCleanup(self.client.delete_credential, resp['id'])
+        # user can create credential for other user in other domain
+        resp = self.do_request(
+            'create_credential',
+            expected_status=201,
+            **self.credential(user_id=self.test_user_2))['credential']
+        self.addCleanup(self.client.delete_credential, resp['id'])
+
+    def test_identity_get_credential(self):
+        # user can get their own credential, credential for user in own domain,
+        # or credential for user in other domain
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.admin_credentials_client.create_credential(
+                **self.credential(user_id=u))['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, cred['id'])
+            self.do_request('show_credential', credential_id=cred['id'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'show_credential',
+            expected_status=exceptions.NotFound,
+            credential_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_credentials(self):
+        # user can list their own credentials, credentials for user in own
+        # domain, or credentials for user in other domain
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.admin_credentials_client.create_credential(
+                **self.credential(user_id=u))['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, cred['id'])
+            resp = self.do_request('list_credentials')['credentials']
+            self.assertIn(cred['id'], [c['id'] for c in resp])
+
+    def test_identity_update_credential(self):
+        # user can update their own credential, credential for user in own
+        # domain, or credential for user in other domain
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.credential(user_id=u)
+            resp = self.client.create_credential(**cred)['credential']
+            self.addCleanup(self.client.delete_credential, resp['id'])
+            cred['blob'] = data_utils.rand_uuid_hex()
+            self.do_request(
+                'update_credential', credential_id=resp['id'], **cred)
+        # non-existent credential is Not Found
+        self.do_request(
+            'update_credential',
+            expected_status=exceptions.NotFound,
+            credential_id=data_utils.rand_uuid_hex(),
+            **self.credential(user_id=self.test_user_2))
+
+    def test_identity_delete_credential(self):
+        # user can delete their own credential, credential for user in own
+        # domain, or credential for user in other domain
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.credential(user_id=u)
+            resp = self.client.create_credential(**cred)['credential']
+            self.do_request(
+                'delete_credential',
+                expected_status=204,
+                credential_id=resp['id'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'delete_credential',
+            expected_status=exceptions.NotFound,
+            credential_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_credential(self):
+        # user can create their own credential
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request(
+            'create_credential',
+            expected_status=201,
+            **self.credential(user_id=user_id))['credential']
+        self.addCleanup(self.client.delete_credential, resp['id'])
+        # user cannot create credential for other user
+        for u in [self.test_user_1, self.test_user_2]:
+            self.do_request(
+                'create_credential',
+                expected_status=exceptions.Forbidden,
+                **self.credential(user_id=u))
+
+    def test_identity_update_credential(self):
+        # user can update their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.credential(user_id=user_id)
+        resp = self.admin_credentials_client.create_credential(
+            **cred)['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_credential, resp['id'])
+        cred['blob'] = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_credential',
+            credential_id=resp['id'], **cred)
+        # user cannot update credential for other user
+        for u in [self.test_user_1, self.test_user_2]:
+            cred = self.credential(user_id=u)
+            resp = self.admin_credentials_client.create_credential(
+                **cred)['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, resp['id'])
+            cred['blob'] = data_utils.rand_uuid_hex()
+            self.do_request(
+                'update_credential',
+                expected_status=exceptions.Forbidden,
+                credential_id=resp['id'], **cred)
+        # non-existent credential is Forbidden
+        self.do_request(
+            'update_credential',
+            expected_status=exceptions.Forbidden,
+            credential_id=data_utils.rand_uuid_hex(),
+            **self.credential(user_id=self.test_user_2))
+
+    def test_identity_delete_credential(self):
+        # user can delete their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.credential(user_id=user_id)
+        resp = self.admin_credentials_client.create_credential(
+            **cred)['credential']
+        self.do_request(
+            'delete_credential',
+            expected_status=204,
+            credential_id=resp['id'])
+        # user cannot delete credential for other user
+        for u in [self.test_user_1, self.test_user_2]:
+            cred = self.credential(user_id=u)
+            resp = self.admin_credentials_client.create_credential(
+                **cred)['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, resp['id'])
+            self.do_request(
+                'delete_credential',
+                expected_status=exceptions.Forbidden,
+                credential_id=resp['id'])
+        # non-existent credential is Forbidden
+        self.do_request(
+            'delete_credential',
+            expected_status=exceptions.Forbidden,
+            credential_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacCredentialTest, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_create_credential(self):
+        # domain admins cannot create credentials
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            self.do_request(
+                'create_credential',
+                expected_status=exceptions.Forbidden,
+                **self.credential(user_id=u))
+
+    def test_identity_get_credential(self):
+        # domain admins cannot get credentials
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.admin_credentials_client.create_credential(
+                **self.credential(user_id=u))['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, cred['id'])
+            self.do_request(
+                'show_credential',
+                expected_status=exceptions.Forbidden,
+                credential_id=cred['id'])
+        # non-existent credential is Forbidden
+        self.do_request(
+            'show_credential',
+            expected_status=exceptions.Forbidden,
+            credential_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_credentials(self):
+        # domain admins cannot list credentials
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.admin_credentials_client.create_credential(
+                **self.credential(user_id=u))['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, cred['id'])
+            self.do_request(
+                'list_credentials',
+                expected_status=exceptions.Forbidden)
+
+    def test_identity_update_credential(self):
+        # domain admins cannot update credentials
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.credential(user_id=u)
+            resp = self.admin_credentials_client.create_credential(
+                **cred)['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, resp['id'])
+            cred['blob'] = data_utils.rand_uuid_hex()
+            self.do_request(
+                'update_credential',
+                expected_status=exceptions.Forbidden,
+                credential_id=resp['id'], **cred)
+        # non-existent credential is Forbidden
+        self.do_request(
+            'update_credential',
+            expected_status=exceptions.Forbidden,
+            credential_id=data_utils.rand_uuid_hex(),
+            **self.credential(user_id=user_id))
+
+    def test_identity_delete_credential(self):
+        # domain admins cannot delete credentials
+        user_id = self.persona.credentials.user_id
+        for u in [user_id, self.test_user_1, self.test_user_2]:
+            cred = self.credential(user_id=u)
+            resp = self.admin_credentials_client.create_credential(
+                **cred)['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, resp['id'])
+            self.do_request(
+                'delete_credential',
+                expected_status=exceptions.Forbidden,
+                credential_id=resp['id'])
+        # non-existent credential is Forbidden
+        self.do_request(
+            'delete_credential',
+            expected_status=exceptions.Forbidden,
+            credential_id=data_utils.rand_uuid_hex())
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainAdminTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(SystemReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_identity_get_credential(self):
+        # user can get their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_credential(
+            **self.credential(user_id=user_id))['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_credential, cred['id'])
+        self.do_request('show_credential', credential_id=cred['id'])
+        # user cannot get credential for another user
+        for u in [self.test_user_1, self.test_user_2]:
+            cred = self.admin_credentials_client.create_credential(
+                **self.credential(user_id=u))['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, cred['id'])
+            self.do_request(
+                'show_credential',
+                expected_status=exceptions.Forbidden,
+                credential_id=cred['id'])
+        # non-existent credential is Forbidden
+        self.do_request(
+            'show_credential',
+            expected_status=exceptions.Forbidden,
+            credential_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_credentials(self):
+        # user can list their own credentials
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_credential(
+            **self.credential(user_id=user_id))['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_credential, cred['id'])
+        resp = self.do_request('list_credentials')['credentials']
+        self.assertIn(cred['id'], [c['id'] for c in resp])
+        # user cannot list credentials for other users
+        for u in [self.test_user_1, self.test_user_2]:
+            cred = self.admin_credentials_client.create_credential(
+                **self.credential(user_id=u))['credential']
+            self.addCleanup(
+                self.admin_credentials_client.delete_credential, cred['id'])
+            resp = self.do_request('list_credentials')['credentials']
+            self.assertNotIn(cred['id'], [c['id'] for c in resp])
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_domain.py b/keystone_tempest_plugin/tests/rbac/v3/test_domain.py
new file mode 100644
index 0000000..13f0b71
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_domain.py
@@ -0,0 +1,223 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacDomainTests(rbac_base.IdentityV3RbacBaseTests,
+                                metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacDomainTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.domains_client
+        admin_client = cls.os_system_admin
+        cls.admin_domains_client = admin_client.domains_client
+
+    @abc.abstractmethod
+    def test_identity_create_domain(self):
+        """Test identity:create_domain policy.
+
+        This test must check:
+          * whether the persona can create a domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_domain(self):
+        """Test identity:get_domain policy.
+
+        This test must check:
+          * whether the persona can get a domain
+          * whether the persona can get a domain that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_domains(self):
+        """Test identity:list_domains policy.
+
+        This test must check:
+          * whether the persona can list all domains
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_domain(self):
+        """Test identity:update_domain policy.
+
+        This test must check:
+          * whether the persona can update a domain
+          * whether the persona can update a domain that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_domain(self):
+        """Test identity:delete_domain policy.
+
+        This test must check
+          * whether the persona can delete a domain
+          * whether the persona can delete a domain that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacDomainTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_domain(self):
+        domain_id = self.do_request(
+            'create_domain', expected_status=201, name=data_utils.rand_name()
+        )['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+
+    def test_identity_get_domain(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+        self.do_request('show_domain', domain_id=domain_id)
+        # user gets a 404 for nonexistent domain
+        self.do_request('show_domain', expected_status=exceptions.NotFound,
+                        domain_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_domains(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+        resp = self.do_request('list_domains')
+        self.assertIn(domain_id, [d['id'] for d in resp['domains']])
+
+    def test_identity_update_domain(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+        self.do_request('update_domain',
+                        domain_id=domain_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent domain
+        self.do_request('update_domain', expected_status=exceptions.NotFound,
+                        domain_id=data_utils.rand_uuid_hex())
+
+    def test_identity_delete_domain(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.do_request('update_domain',
+                        domain_id=domain_id,
+                        enabled=False)
+        self.do_request('delete_domain', expected_status=204,
+                        domain_id=domain_id)
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_domain(self):
+        self.do_request('create_domain', expected_status=exceptions.Forbidden,
+                        name=data_utils.rand_name())
+
+    def test_identity_update_domain(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+        self.do_request('update_domain', expected_status=exceptions.Forbidden,
+                        domain_id=domain_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent domain
+        self.do_request('update_domain', expected_status=exceptions.NotFound,
+                        domain_id=data_utils.rand_uuid_hex())
+
+    def test_identity_delete_domain(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+        self.do_request('delete_domain', expected_status=exceptions.Forbidden,
+                        domain_id=domain_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_domain(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+        self.do_request('show_domain', expected_status=exceptions.Forbidden,
+                        domain_id=domain_id)
+        # user gets a 403 for nonexistent domain
+        self.do_request('show_domain', expected_status=exceptions.Forbidden,
+                        domain_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_domains(self):
+        domain_id = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain, domain_id)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=domain_id, enabled=False)
+        self.do_request('list_domains',
+                        expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_domain_config.py b/keystone_tempest_plugin/tests/rbac/v3/test_domain_config.py
new file mode 100644
index 0000000..8f1fdbf
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_domain_config.py
@@ -0,0 +1,381 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacDomainConfigTest(rbac_base.IdentityV3RbacBaseTests,
+                                     metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacDomainConfigTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.domain_config_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_domain_config_client = cls.admin_client.domain_config_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3RbacDomainConfigTest, cls).resource_setup()
+        cls.domain_id = cls.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name('domain'))['domain']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.delete_domain,
+            cls.domain_id)
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.update_domain,
+            cls.domain_id,
+            enabled=False)
+
+    def domain_config(self, **kwargs):
+        ref = {
+            "identity": {
+                "driver": "ldap"
+            },
+            "ldap": {
+                "url": "ldap://myldap.com:389/",
+                "user_tree_dn": "ou=Users,dc=my_new_root,dc=org"
+            }
+        }
+        ref.update(kwargs)
+        return ref
+
+    @abc.abstractmethod
+    def test_identity_create_domain_config(self):
+        """Test identity:create_domain_config policy.
+
+        This test must check:
+          * whether the persona can create a domain config for a valid domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_domain_config(self):
+        """Test identity:get_domain_config policy.
+
+        This test must check:
+          * whether the persona can get a domain config
+          * whether the persona can get an option group for a domain config
+          * whether the persona can get an option from a group in a domain
+            config
+          * whether the persona can get a config for an invalid domain
+          * whether the persona can get a config that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_domain_config_default(self):
+        """Test identity:get_domain_config_default policy.
+
+          * whether the persona can get the default config
+          * whether the persona can get the default config for an option group
+          * whether the persona can get the default value for an option
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_security_compliance_domain_config(self):
+        """Test identity:get_security_compliance_domain_config policy.
+
+        This test must check:
+          * whether the persona can get the security compliance configuration
+            for the default domain
+          * whether the persona can get an option from the security compliance
+            configuration for the default domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_domain_config(self):
+        """Test identity:update_domain_config policy.
+
+        This test must check:
+          * whether the persona can update the config for a domain
+          * whether the persona can update an option group config for a domain
+          * whether the persona can update an option in a domain config
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_domain_config(self):
+        """Test identity:delete_domain_config policy.
+
+        This test must check
+          * whether the persona can delete a domain config
+          * whether the persona can delete an option group within a domain
+            config
+          * whether the persona can delete an option within a domain config
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacDomainConfigTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_domain_config(self):
+        self.do_request(
+            'create_domain_config',
+            expected_status=201,
+            domain_id=self.domain_id,
+            **self.domain_config())
+        self.addCleanup(
+            self.admin_domain_config_client.delete_domain_config,
+            self.domain_id)
+
+    def test_identity_get_domain_config(self):
+        # should be able to get domain config, group and individual options
+        self.admin_domain_config_client.create_domain_config(
+            self.domain_id, **self.domain_config())
+        self.addCleanup(
+            self.admin_domain_config_client.delete_domain_config,
+            self.domain_id)
+        self.do_request(
+            'show_domain_config',
+            domain_id=self.domain_id)
+        self.do_request(
+            'show_domain_group_config',
+            domain_id=self.domain_id,
+            group='ldap')
+        self.do_request(
+            'show_domain_group_option_config',
+            domain_id=self.domain_id,
+            group='ldap',
+            option='url')
+        # should get Not Found for invalid domain
+        self.do_request(
+            'show_domain_config',
+            expected_status=exceptions.NotFound,
+            domain_id=data_utils.rand_uuid_hex())
+        # should get Not Found for nonexistent config for valid domain
+        domain = self.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name('domain'))['domain']['id']
+        self.addCleanup(self.admin_client.domains_client.delete_domain, domain)
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            domain, enabled=False)
+        self.do_request(
+            'show_domain_config',
+            expected_status=exceptions.NotFound,
+            domain_id=domain)
+
+    def test_identity_get_domain_config_default(self):
+        self.do_request('show_default_config_settings')
+        self.do_request('show_default_group_config', group='ldap')
+        self.do_request(
+            'show_default_group_option', group='ldap', option='url')
+
+    def test_identity_get_security_compliance_domain_config(self):
+        self.do_request(
+            'show_domain_group_config',
+            domain_id='default',
+            group='security_compliance')
+        self.do_request(
+            'show_domain_group_option_config',
+            domain_id='default',
+            group='security_compliance',
+            option='password_regex_description')
+
+    def test_identity_update_domain_config(self):
+        self.admin_domain_config_client.create_domain_config(
+            self.domain_id, **self.domain_config())
+        self.addCleanup(
+            self.admin_domain_config_client.delete_domain_config,
+            self.domain_id)
+        self.do_request(
+            'update_domain_group_config',
+            domain_id=self.domain_id,
+            group='ldap',
+            ldap={'url': 'ldaps://myldap.com:636/',
+                  'user_tree_dn': 'ou=People,dc=my_new_root,dc=org'})
+        self.do_request(
+            'update_domain_group_option_config',
+            domain_id=self.domain_id,
+            group='ldap',
+            option='user_tree_dn',
+            user_tree_dn='ou=Aliens,dc=my_new_root,dc=org')
+        # test changing the entire config last
+        self.do_request(
+            'update_domain_config',
+            domain_id=self.domain_id,
+            identity={"driver": "sql"})
+
+    def test_identity_delete_domain_config(self):
+        self.admin_domain_config_client.create_domain_config(
+            self.domain_id, **self.domain_config())
+        self.do_request(
+            'delete_domain_group_option_config',
+            expected_status=204,
+            domain_id=self.domain_id,
+            group='ldap',
+            option='user_tree_dn')
+        self.do_request(
+            'delete_domain_group_config',
+            expected_status=204,
+            domain_id=self.domain_id,
+            group='ldap')
+        self.do_request(
+            'delete_domain_config',
+            expected_status=204,
+            domain_id=self.domain_id)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_domain_config(self):
+        self.do_request(
+            'create_domain_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            **self.domain_config())
+
+    def test_identity_update_domain_config(self):
+        self.admin_domain_config_client.create_domain_config(
+            self.domain_id, **self.domain_config())
+        self.addCleanup(
+            self.admin_domain_config_client.delete_domain_config,
+            self.domain_id)
+        self.do_request(
+            'update_domain_group_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            group='ldap',
+            ldap={'url': 'ldaps://myldap.com:636/',
+                  'user_tree_dn': 'ou=People,dc=my_new_root,dc=org'})
+        self.do_request(
+            'update_domain_group_option_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            group='ldap',
+            option='user_tree_dn',
+            user_tree_dn='ou=Aliens,dc=my_new_root,dc=org')
+        # test changing the entire config last
+        self.do_request(
+            'update_domain_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            identity={"driver": "sql"})
+
+    def test_identity_delete_domain_config(self):
+        self.admin_domain_config_client.create_domain_config(
+            self.domain_id, **self.domain_config())
+        self.do_request(
+            'delete_domain_group_option_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            group='ldap',
+            option='user_tree_dn')
+        self.do_request(
+            'delete_domain_group_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            group='ldap')
+        self.do_request(
+            'delete_domain_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_domain_config(self):
+        # should not be able to get domain config, group and individual options
+        self.admin_domain_config_client.create_domain_config(
+            self.domain_id, **self.domain_config())
+        self.addCleanup(
+            self.admin_domain_config_client.delete_domain_config,
+            self.domain_id)
+        self.do_request(
+            'show_domain_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id)
+        self.do_request(
+            'show_domain_group_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            group='ldap')
+        self.do_request(
+            'show_domain_group_option_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.domain_id,
+            group='ldap',
+            option='url')
+        # should get Forbidden for invalid domain
+        self.do_request(
+            'show_domain_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=data_utils.rand_uuid_hex())
+        # should get Forbidden for nonexistent config for valid domain
+        domain = self.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name('domain'))['domain']['id']
+        self.addCleanup(self.admin_client.domains_client.delete_domain, domain)
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            domain, enabled=False)
+        self.do_request(
+            'show_domain_config',
+            expected_status=exceptions.Forbidden,
+            domain_id=domain)
+
+    def test_identity_get_domain_config_default(self):
+        self.do_request(
+            'show_default_config_settings',
+            expected_status=exceptions.Forbidden)
+        self.do_request(
+            'show_default_group_config',
+            expected_status=exceptions.Forbidden, group='ldap')
+        self.do_request(
+            'show_default_group_option',
+            expected_status=exceptions.Forbidden, group='ldap', option='url')
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainAdminTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_ec2_credential.py b/keystone_tempest_plugin/tests/rbac/v3/test_ec2_credential.py
new file mode 100644
index 0000000..6c7d19b
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_ec2_credential.py
@@ -0,0 +1,544 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest import clients
+from tempest.lib import auth
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacEc2CredentialTest(rbac_base.IdentityV3RbacBaseTests,
+                                      metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacEc2CredentialTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.users_v3_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_credentials_client = cls.admin_client.users_v3_client
+
+        # personas in own or other domains
+        own_domain_id = cls.persona.credentials.domain_id
+        cls.test_client_1, cls.test_user_1, cls.test_project_1 = (
+            cls.setup_user_client(domain_id=own_domain_id))
+        cls.other_domain_id = cls.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.delete_domain, cls.other_domain_id)
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.update_domain,
+            domain_id=cls.other_domain_id, enabled=False)
+        cls.test_client_2, cls.test_user_2, cls.test_project_2 = (
+            cls.setup_user_client(domain_id=cls.other_domain_id))
+
+    @classmethod
+    def setup_user_client(cls, domain_id=None):
+        """Set up project user with its own client.
+
+        This is to enable the project user to create its own credential.
+
+        Returns a client object and the user's ID.
+        """
+        user_dict = {
+            'name': data_utils.rand_name('user'),
+            'password': data_utils.rand_password(),
+        }
+        if domain_id:
+            user_dict['domain_id'] = domain_id
+        user_id = cls.admin_client.users_v3_client.create_user(
+            **user_dict)['user']['id']
+
+        def try_cleanup_user():
+            # if domain is cleaned up first, user will already be deleted
+            try:
+                cls.admin_client.users_v3_client.delete_user(user_id)
+            except exceptions.NotFound:
+                pass
+
+        cls.addClassResourceCleanup(try_cleanup_user)
+        project_id = cls.admin_client.projects_client.create_project(
+            data_utils.rand_name())['project']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project, project_id)
+        member_role_id = cls.admin_client.roles_v3_client.list_roles(
+            name='member')['roles'][0]['id']
+        cls.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, member_role_id)
+        creds = auth.KeystoneV3Credentials(
+            user_id=user_id,
+            password=user_dict['password'],
+            project_id=project_id)
+        auth_provider = clients.get_auth_provider(creds)
+        creds = auth_provider.fill_credentials()
+        client = clients.Manager(credentials=creds)
+        return client, user_id, project_id
+
+    def ec2_credential(self, project_id=None):
+        return {
+            'tenant_id': project_id or self.project_id
+        }
+
+    @abc.abstractmethod
+    def test_identity_ec2_create_credential(self):
+        """Test identity:ec2_create_credential policy.
+
+        This test must check:
+          * whether the persona can create a credential for themself
+          * whether the persona can create acredential for another user in
+            their own domain
+          * whether the persona can create acredential for another user in
+            another domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_ec2_get_credential(self):
+        """Test identity:ec2_get_credential policy.
+
+        This test must check:
+          * whether the persona can get their own credential
+          * whether the persona can get a credential for a user in another
+            domain
+          * whether the persona can get a credential for a user in their own
+            domain
+          * whether the persona can get a credential that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_ec2_list_credentials(self):
+        """Test identity:list_credentials policy.
+
+        This test must check:
+          * whether the persona can list all credentials for themself
+          * whether the persona can list credentials for a user in their own
+            domain
+          * whether the persona can list credentials for a user in another
+            domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_ec2_delete_credential(self):
+        """Test identity:ec2_delete_credential policy.
+
+        This test must check
+          * whether the persona can delete their own credential
+          * whether the persona can delete a credential for a user in another
+            domain
+          * whether the persona can delete a credential for a user in their own
+            domain
+          * whether the persona can delete a credential that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacEc2CredentialTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_ec2_create_credential(self):
+        # user can create their own credential
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request(
+            'create_user_ec2_credential',
+            expected_status=201,
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(self.client.delete_user_ec2_credential,
+                        user_id=user_id, access=resp['access'])
+        # user can create credential for other user
+        resp = self.do_request(
+            'create_user_ec2_credential',
+            expected_status=201,
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(self.client.delete_user_ec2_credential,
+                        user_id=user_id, access=resp['access'])
+
+    def test_identity_ec2_get_credential(self):
+        # user can get their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=user_id, access=cred['access'])
+        self.do_request('show_user_ec2_credential',
+                        user_id=user_id, access=cred['access'])
+        # user can get credential for other user
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        self.do_request('show_user_ec2_credential',
+                        user_id=self.test_user_2, access=cred['access'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'show_user_ec2_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_2,
+            access=data_utils.rand_uuid_hex())
+
+    def test_identity_ec2_list_credentials(self):
+        # user can list their own credentials
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=user_id, access=cred['access'])
+        resp = self.do_request('list_user_ec2_credentials',
+                               user_id=user_id)['credentials']
+        self.assertIn(cred['access'], [c['access'] for c in resp])
+        # user can list credentials for other user
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        resp = self.do_request('list_user_ec2_credentials',
+                               user_id=self.test_user_2)['credentials']
+        self.assertIn(cred['access'], [c['access'] for c in resp])
+
+    def test_identity_ec2_delete_credential(self):
+        # user can delete their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=204,
+            user_id=user_id, access=cred['access'])
+        # user can delete another user's credential
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=204,
+            user_id=self.test_user_2, access=cred['access'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_2,
+            access=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_ec2_create_credential(self):
+        # user can create their own credential
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request(
+            'create_user_ec2_credential',
+            expected_status=201,
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(self.client.delete_user_ec2_credential,
+                        user_id=user_id, access=resp['access'])
+        # user cannot create credential for other user
+        self.do_request(
+            'create_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2))
+
+    def test_identity_ec2_delete_credential(self):
+        # user can delete their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=204,
+            user_id=user_id, access=cred['access'])
+        # user cannot delete another user's credential
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2, access=cred['access'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_2,
+            access=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacEc2CredentialTest, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_ec2_create_credential(self):
+        # user cannot create their own credential
+        user_id = self.persona.credentials.user_id
+        self.do_request(
+            'create_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1))
+        # user cannot create credential for user in own domain
+        self.do_request(
+            'create_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1,
+            **self.ec2_credential(project_id=self.test_project_1))
+        # user cannot create credential for other user
+        self.do_request(
+            'create_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2))
+
+    def test_identity_ec2_get_credential(self):
+        # user cannot get their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=user_id, access=cred['access'])
+        self.do_request('show_user_ec2_credential',
+                        expected_status=exceptions.Forbidden,
+                        user_id=user_id, access=cred['access'])
+        # user cannot get credential for user in own domain
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_1,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_1, access=cred['access'])
+        self.do_request('show_user_ec2_credential',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.test_user_2, access=cred['access'])
+        # user cannot get credential for other user
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+            )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        self.do_request('show_user_ec2_credential',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.test_user_2, access=cred['access'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'show_user_ec2_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_2,
+            access=data_utils.rand_uuid_hex())
+
+    def test_identity_ec2_list_credentials(self):
+        # user cannot list their own credentials
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=user_id, access=cred['access'])
+        self.do_request('list_user_ec2_credentials',
+                        expected_status=exceptions.Forbidden,
+                        user_id=user_id)
+        # user cannot list credentials for user in own domain
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_1,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_1, access=cred['access'])
+        self.do_request('list_user_ec2_credentials',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.test_user_1)
+        # user cannot list credentials for other user
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        self.do_request('list_user_ec2_credentials',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.test_user_2)
+
+    def test_identity_ec2_delete_credential(self):
+        # user cannot delete their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=user_id, access=cred['access'])
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=user_id, access=cred['access'])
+        # user cannot delete credential for user in own domain
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_1,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_1, access=cred['access'])
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_1, access=cred['access'])
+        # user cannot delete another user's credential
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=exceptions.Forbidden,
+            user_id=self.test_user_2, access=cred['access'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'delete_user_ec2_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_2,
+            access=data_utils.rand_uuid_hex())
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainAdminTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(SystemReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_identity_ec2_get_credential(self):
+        # user can get their own credential
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=user_id, access=cred['access'])
+        self.do_request('show_user_ec2_credential',
+                        user_id=user_id, access=cred['access'])
+        # user cannot get credential for other user
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        self.do_request('show_user_ec2_credential',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.test_user_2, access=cred['access'])
+        # non-existent credential is Not Found
+        self.do_request(
+            'show_user_ec2_credential',
+            expected_status=exceptions.NotFound,
+            user_id=self.test_user_2,
+            access=data_utils.rand_uuid_hex())
+
+    def test_identity_ec2_list_credentials(self):
+        # user can list their own credentials
+        user_id = self.persona.credentials.user_id
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=user_id,
+            **self.ec2_credential(project_id=self.test_project_1)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=user_id, access=cred['access'])
+        resp = self.do_request('list_user_ec2_credentials',
+                               user_id=user_id)['credentials']
+        self.assertIn(cred['access'], [c['access'] for c in resp])
+        # user cannot list credentials for other user
+        cred = self.admin_credentials_client.create_user_ec2_credential(
+            user_id=self.test_user_2,
+            **self.ec2_credential(project_id=self.test_project_2)
+        )['credential']
+        self.addCleanup(
+            self.admin_credentials_client.delete_user_ec2_credential,
+            user_id=self.test_user_2, access=cred['access'])
+        self.do_request('list_user_ec2_credentials',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.test_user_2)
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_endpoint.py b/keystone_tempest_plugin/tests/rbac/v3/test_endpoint.py
new file mode 100644
index 0000000..48dfb19
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_endpoint.py
@@ -0,0 +1,245 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacEndpointTests(rbac_base.IdentityV3RbacBaseTests,
+                                  metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacEndpointTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.endpoints_v3_client
+        admin_client = cls.os_system_admin
+        cls.services_client = admin_client.identity_services_v3_client
+        cls.admin_endpoints_client = admin_client.endpoints_v3_client
+
+    @classmethod
+    def setUpClass(cls):
+        super(IdentityV3RbacEndpointTests, cls).setUpClass()
+        cls.service_id = cls.services_client.create_service(
+            type=data_utils.rand_name(),
+            name=data_utils.rand_name())['service']['id']
+        cls.addClassResourceCleanup(
+            cls.services_client.delete_service,
+            cls.service_id)
+
+    def endpoint(self):
+        return {
+            'interface': 'public',
+            'service_id': self.service_id,
+            'url': 'http://localhost/service'
+        }
+
+    @abc.abstractmethod
+    def test_identity_create_endpoint(self):
+        """Test identity:create_endpoint policy.
+
+        This test must check:
+          * whether the persona can create an endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_endpoint(self):
+        """Test identity:get_endpoint policy.
+
+        This test must check:
+          * whether the persona can get an endpoint
+          * whether the persona can get an endpoint that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_endpoints(self):
+        """Test identity:list_endpoints policy.
+
+        This test must check:
+          * whether the persona can list all endpoints
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_endpoint(self):
+        """Test identity:update_endpoint policy.
+
+        This test must check:
+          * whether the persona can update an endpoint
+          * whether the persona can update an endpoint that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_endpoint(self):
+        """Test identity:delete_endpoint policy.
+
+        This test must check
+          * whether the persona can delete an endpoint
+          * whether the persona can delete an endpoint that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacEndpointTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_endpoint(self):
+        endpoint_id = self.do_request(
+            'create_endpoint', expected_status=201,
+            **self.endpoint())['endpoint']['id']
+        self.addCleanup(
+            self.admin_endpoints_client.delete_endpoint,
+            endpoint_id=endpoint_id)
+
+    def test_identity_get_endpoint(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.addCleanup(
+            self.admin_endpoints_client.delete_endpoint,
+            endpoint_id=endpoint_id)
+        self.do_request('show_endpoint', endpoint_id=endpoint_id)
+        # user gets a 404 for nonexistent endpoint
+        self.do_request('show_endpoint', expected_status=exceptions.NotFound,
+                        endpoint_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_endpoints(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.addCleanup(
+            self.admin_endpoints_client.delete_endpoint,
+            endpoint_id=endpoint_id)
+        resp = self.do_request('list_endpoints')
+        self.assertIn(endpoint_id, [e['id'] for e in resp['endpoints']])
+
+    def test_identity_update_endpoint(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.addCleanup(
+            self.admin_endpoints_client.delete_endpoint,
+            endpoint_id=endpoint_id)
+        self.do_request('update_endpoint',
+                        endpoint_id=endpoint_id,
+                        interface='internal')
+        # user gets a 404 for nonexistent endpoint
+        self.do_request('update_endpoint', expected_status=exceptions.NotFound,
+                        endpoint_id=data_utils.rand_uuid_hex(),
+                        interface='internal')
+
+    def test_identity_delete_endpoint(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.do_request('delete_endpoint', expected_status=204,
+                        endpoint_id=endpoint_id)
+        # user gets a 404 for nonexistent endpoint
+        self.do_request('delete_endpoint', expected_status=exceptions.NotFound,
+                        endpoint_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_endpoint(self):
+        self.do_request(
+            'create_endpoint', expected_status=exceptions.Forbidden,
+            **self.endpoint())
+
+    def test_identity_update_endpoint(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.addCleanup(
+            self.admin_endpoints_client.delete_endpoint,
+            endpoint_id=endpoint_id)
+        self.do_request('update_endpoint',
+                        expected_status=exceptions.Forbidden,
+                        endpoint_id=endpoint_id,
+                        interface='internal')
+        # user gets a 404 for nonexistent endpoint
+        self.do_request('update_endpoint', expected_status=exceptions.NotFound,
+                        endpoint_id=data_utils.rand_uuid_hex(),
+                        interface='internal')
+
+    def test_identity_delete_endpoint(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.do_request('delete_endpoint',
+                        expected_status=exceptions.Forbidden,
+                        endpoint_id=endpoint_id)
+        # user gets a 404 for nonexistent endpoint
+        self.do_request('delete_endpoint', expected_status=exceptions.NotFound,
+                        endpoint_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_endpoint(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.addCleanup(
+            self.admin_endpoints_client.delete_endpoint,
+            endpoint_id=endpoint_id)
+        self.do_request('show_endpoint', expected_status=exceptions.Forbidden,
+                        endpoint_id=endpoint_id)
+        # user gets a 404 for nonexistent endpoint
+        self.do_request('show_endpoint', expected_status=exceptions.NotFound,
+                        endpoint_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_endpoints(self):
+        endpoint_id = self.admin_endpoints_client.create_endpoint(
+            **self.endpoint())['endpoint']['id']
+        self.addCleanup(
+            self.admin_endpoints_client.delete_endpoint,
+            endpoint_id=endpoint_id)
+        self.do_request('list_endpoints', expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_endpoint_group.py b/keystone_tempest_plugin/tests/rbac/v3/test_endpoint_group.py
new file mode 100644
index 0000000..6cca108
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_endpoint_group.py
@@ -0,0 +1,521 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacEndpointGroupTests(rbac_base.IdentityV3RbacBaseTests,
+                                       metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacEndpointGroupTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.eg_client = cls.persona.endpoint_groups_client
+        cls.ef_client = cls.persona.endpoint_filter_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_eg_client = cls.admin_client.endpoint_groups_client
+        cls.admin_ef_client = cls.admin_client.endpoint_filter_client
+
+    def endpoint_group(self):
+        return {
+            'name': data_utils.rand_name('endpoint_group'),
+            'filters': {'interface': 'public'},
+        }
+
+    @abc.abstractmethod
+    def test_identity_create_endpoint_group(self):
+        """Test identity:create_endpoint_group policy.
+
+        This test must check:
+          * whether the persona can create an endpoint group
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_endpoint_group(self):
+        """Test identity:get_endpoint_group policy.
+
+        This test must check:
+          * whether the persona can get an endpoint group
+          * whether the persona can get an endpoint group that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_endpoint_groups(self):
+        """Test identity:list_endpoint_groups policy.
+
+        This test must check:
+          * whether the persona can list all endpoint groups
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_endpoint_group(self):
+        """Test identity:update_endpoint_group policy.
+
+        This test must check:
+          * whether the persona can update an endpoint group
+          * whether the persona can update an endpoint group that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_endpoint_group(self):
+        """Test identity:delete_endpoint_group policy.
+
+        This test must check
+          * whether the persona can delete an endpoint group
+          * whether the persona can delete an endpoint group that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_projects_associated_with_endpoint_group(self):
+        """Test identity:list_projects_associated_with_endpoint_group policy.
+
+        This test must check
+          * whether the persona can list projects associated with an endpoint
+            group
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_endpoints_associated_with_endpoint_group(self):
+        """Test identity:list_endpoints_associated_with_endpoint_group
+
+        This test must check
+          * whether the persona can list endpoints associated with an endpoint
+            group
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_endpoint_group_in_project(self):
+        """Test identity:get_endpoint_group_in_project
+
+        This test must check
+          * whether the persona can check if an endpoint group is associated
+            with a project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_endpoint_groups_for_project(self):
+        """Test identity:list_endpoint_groups_for_project
+
+        This test must check
+          * whether the persona can list endpoint groups associated with a
+            project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_add_endpoint_group_to_project(self):
+        """Test identity:add_endpoint_group_to_project
+
+        This test must check
+          * whether the persona can allow a project to access an endpoint group
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_remove_endpoint_group_from_project(self):
+        """Test identity:remove_endpoint_group_from_project
+
+        This test must check
+          * whether the persona can remove an endpoint group from a project
+        """
+        pass
+
+
+class SystemAdminTests(
+    IdentityV3RbacEndpointGroupTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_endpoint_group(self):
+        eg = self.do_request('create_endpoint_group', expected_status=201,
+                             client=self.eg_client,
+                             **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+
+    def test_identity_get_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        # user can get an endpoint group
+        self.do_request('show_endpoint_group', client=self.eg_client,
+                        endpoint_group_id=eg)
+        # nonexistent endpoint group gives a 404
+        self.do_request('show_endpoint_group',
+                        expected_status=exceptions.NotFound,
+                        client=self.eg_client,
+                        endpoint_group_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_endpoint_groups(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        resp = self.do_request('list_endpoint_groups',
+                               client=self.eg_client)['endpoint_groups']
+        self.assertIn(eg, [e['id'] for e in resp])
+
+    def test_identity_update_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        # user can update an endpoint group
+        self.do_request('update_endpoint_group', client=self.eg_client,
+                        endpoint_group_id=eg,
+                        description=data_utils.arbitrary_string())
+        # nonexistent endpoint group gives a 404
+        self.do_request('update_endpoint_group', client=self.eg_client,
+                        expected_status=exceptions.NotFound,
+                        endpoint_group_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        # user can delete an endpoint group
+        self.do_request('delete_endpoint_group',
+                        expected_status=204,
+                        client=self.eg_client,
+                        endpoint_group_id=eg)
+        # nonexistent endpoint group gives a 404
+        self.do_request('delete_endpoint_group',
+                        expected_status=exceptions.NotFound,
+                        client=self.eg_client,
+                        endpoint_group_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_projects_associated_with_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+        resp = self.do_request('list_projects_for_endpoint_group',
+                               client=self.ef_client,
+                               endpoint_group_id=eg)['projects']
+        self.assertIn(project, [p['id'] for p in resp])
+
+    def test_identity_list_endpoints_associated_with_endpoint_group(self):
+        service = self.admin_client.identity_services_v3_client.create_service(
+            type=data_utils.rand_name('service'))['service']['id']
+        self.addCleanup(
+            self.admin_client.identity_services_v3_client.delete_service,
+            service)
+        endpoint = self.admin_client.endpoints_v3_client.create_endpoint(
+            interface='public',
+            url='http://localhost/foo',
+            service_id=service)['endpoint']['id']
+        self.addCleanup(self.admin_client.endpoints_v3_client.delete_endpoint,
+                        endpoint)
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        resp = self.do_request('list_endpoints_for_endpoint_group',
+                               client=self.ef_client,
+                               endpoint_group_id=eg)['endpoints']
+        self.assertIn(endpoint, [e['id'] for e in resp])
+
+    def test_identity_get_endpoint_group_in_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+        self.do_request('show_endpoint_group_for_project',
+                        client=self.ef_client,
+                        endpoint_group_id=eg,
+                        project_id=project)
+
+    def test_identity_list_endpoint_groups_for_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+        resp = self.do_request('list_endpoint_groups_for_project',
+                               client=self.ef_client,
+                               project_id=project)
+        self.assertIn(eg, [e['id'] for e in resp['endpoint_groups']])
+
+    def test_identity_add_endpoint_group_to_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.do_request('add_endpoint_group_to_project',
+                        client=self.ef_client,
+                        expected_status=204,
+                        endpoint_group_id=eg,
+                        project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+
+    def test_identity_remove_endpoint_group_from_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.do_request('delete_endpoint_group_from_project',
+                        client=self.ef_client,
+                        expected_status=204,
+                        endpoint_group_id=eg, project_id=project)
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_endpoint_group(self):
+        self.do_request('create_endpoint_group',
+                        expected_status=exceptions.Forbidden,
+                        client=self.eg_client,
+                        **self.endpoint_group())
+
+    def test_identity_update_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        # user can update an endpoint group
+        self.do_request('update_endpoint_group', client=self.eg_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=eg,
+                        description=data_utils.arbitrary_string())
+        # nonexistent endpoint group gives a 403
+        self.do_request('update_endpoint_group', client=self.eg_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        # user cannot delete an endpoint group
+        self.do_request('delete_endpoint_group',
+                        expected_status=exceptions.Forbidden,
+                        client=self.eg_client,
+                        endpoint_group_id=eg)
+        # nonexistent endpoint group gives a 403
+        self.do_request('delete_endpoint_group',
+                        expected_status=exceptions.Forbidden,
+                        client=self.eg_client,
+                        endpoint_group_id=data_utils.rand_uuid_hex())
+
+    def test_identity_add_endpoint_group_to_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.do_request('add_endpoint_group_to_project',
+                        client=self.ef_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=eg,
+                        project_id=project)
+
+    def test_identity_remove_endpoint_group_from_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+        self.do_request('delete_endpoint_group_from_project',
+                        client=self.ef_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=eg, project_id=project)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        # user cannot get an endpoint group
+        self.do_request('show_endpoint_group', client=self.eg_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=eg)
+        # nonexistent endpoint group gives a 403
+        self.do_request('show_endpoint_group',
+                        expected_status=exceptions.Forbidden,
+                        client=self.eg_client,
+                        endpoint_group_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_endpoint_groups(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        self.do_request('list_endpoint_groups',
+                        expected_status=exceptions.Forbidden,
+                        client=self.eg_client)
+
+    def test_identity_list_projects_associated_with_endpoint_group(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+        self.do_request('list_projects_for_endpoint_group',
+                        client=self.ef_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=eg)
+
+    def test_identity_list_endpoints_associated_with_endpoint_group(self):
+        service = self.admin_client.identity_services_v3_client.create_service(
+            type=data_utils.rand_name('service'))['service']['id']
+        self.addCleanup(
+            self.admin_client.identity_services_v3_client.delete_service,
+            service)
+        endpoint = self.admin_client.endpoints_v3_client.create_endpoint(
+            interface='public',
+            url='http://localhost/foo',
+            service_id=service)['endpoint']['id']
+        self.addCleanup(self.admin_client.endpoints_v3_client.delete_endpoint,
+                        endpoint)
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        self.do_request('list_endpoints_for_endpoint_group',
+                        client=self.ef_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=eg)
+
+    def test_identity_get_endpoint_group_in_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+        self.do_request('show_endpoint_group_for_project',
+                        client=self.ef_client,
+                        expected_status=exceptions.Forbidden,
+                        endpoint_group_id=eg,
+                        project_id=project)
+
+    def test_identity_list_endpoint_groups_for_project(self):
+        eg = self.admin_eg_client.create_endpoint_group(
+            **self.endpoint_group())['endpoint_group']['id']
+        self.addCleanup(self.admin_eg_client.delete_endpoint_group, eg)
+        project = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'))['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        project)
+        self.admin_ef_client.add_endpoint_group_to_project(
+            endpoint_group_id=eg, project_id=project)
+        self.addCleanup(
+            self.admin_ef_client.delete_endpoint_group_from_project,
+            endpoint_group_id=eg, project_id=project)
+        self.do_request('list_endpoint_groups_for_project',
+                        client=self.ef_client,
+                        expected_status=exceptions.Forbidden,
+                        project_id=project)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_grant.py b/keystone_tempest_plugin/tests/rbac/v3/test_grant.py
new file mode 100644
index 0000000..e88b679
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_grant.py
@@ -0,0 +1,3799 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacGrantTest(rbac_base.IdentityV3RbacBaseTests,
+                              metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacGrantTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.roles_v3_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_roles_client = cls.admin_client.roles_v3_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3RbacGrantTest, cls).resource_setup()
+        cls._setup_assignments()
+
+    @classmethod
+    def _setup_assignments(cls):
+        # global role
+        cls.role_id = cls.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name('role'))['role']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.roles_v3_client.delete_role, cls.role_id)
+
+        # own domain - if system or project user, this will be the user's
+        # namespace and isn't applicable for RBAC testing
+        # if domain user, this will be the domain on which the user has a role
+        # assignment
+        cls.own_domain = cls.persona.credentials.domain_id
+
+        # domain-specific role in own domain
+        cls.role_own_domain = cls.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name('role'),
+            domain_id=cls.own_domain)['role']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.roles_v3_client.delete_role, cls.role_own_domain)
+
+        # arbitrary domain
+        cls.other_domain = cls.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name('domain'))['domain']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.delete_domain,
+            cls.other_domain)
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.update_domain,
+            cls.other_domain,
+            enabled=False)
+
+        # domain-specific role in another domain
+        cls.role_other_domain = cls.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name('role'),
+            domain_id=cls.other_domain)['role']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.roles_v3_client.delete_role,
+            cls.role_other_domain)
+
+        # user in own domain
+        cls.user_in_domain = cls.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=cls.own_domain)['user']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.users_v3_client.delete_user,
+            cls.user_in_domain)
+
+        # group in own domain
+        cls.group_in_domain = cls.admin_client.groups_client.create_group(
+            name=data_utils.rand_name('group'),
+            domain_id=cls.own_domain)['group']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.groups_client.delete_group,
+            cls.group_in_domain)
+
+        # project in own domain
+        cls.project_in_domain = (
+            cls.admin_client.projects_client.create_project(
+                name=data_utils.rand_name('project'),
+                domain_id=cls.own_domain)['project']['id'])
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project,
+            cls.project_in_domain)
+
+        # stuff in arbitrary domain, useful for testing system users' access to
+        # arbitrary domain and domain users non-access to domains they don't
+        # belong to
+        # user in other domain
+        cls.user_other_domain = cls.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=cls.other_domain)['user']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.users_v3_client.delete_user,
+            cls.user_other_domain)
+
+        # group in other domain
+        cls.group_other_domain = cls.admin_client.groups_client.create_group(
+            name=data_utils.rand_name('group'),
+            domain_id=cls.other_domain)['group']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.groups_client.delete_group,
+            cls.group_other_domain)
+
+        # project in other domain
+        cls.project_other_domain = (
+            cls.admin_client.projects_client.create_project(
+                name=data_utils.rand_name('project'),
+                domain_id=cls.other_domain)['project']['id'])
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project,
+            cls.project_other_domain)
+
+        # assignments
+        roles_client = cls.admin_client.roles_v3_client
+        roles_client.create_user_role_on_project(
+            cls.project_in_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_project(
+            cls.project_in_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_project(
+            cls.project_other_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_project(
+            cls.project_other_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.own_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.own_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.other_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.other_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_system(
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_system(
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_project(
+            cls.project_in_domain,
+            cls.user_in_domain,
+            cls.role_own_domain)
+        roles_client.create_user_role_on_project(
+            cls.project_in_domain,
+            cls.user_other_domain,
+            cls.role_own_domain)
+        roles_client.create_user_role_on_project(
+            cls.project_other_domain,
+            cls.user_in_domain,
+            cls.role_other_domain)
+        roles_client.create_user_role_on_project(
+            cls.project_other_domain,
+            cls.user_other_domain,
+            cls.role_other_domain)
+        roles_client.create_user_role_on_domain(
+            cls.own_domain,
+            cls.user_in_domain,
+            cls.role_own_domain)
+        roles_client.create_user_role_on_domain(
+            cls.own_domain,
+            cls.user_other_domain,
+            cls.role_own_domain)
+        roles_client.create_user_role_on_domain(
+            cls.other_domain,
+            cls.user_in_domain,
+            cls.role_other_domain)
+        roles_client.create_user_role_on_domain(
+            cls.other_domain,
+            cls.user_other_domain,
+            cls.role_other_domain)
+        roles_client.create_group_role_on_project(
+            cls.project_in_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_in_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_other_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_other_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.own_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.own_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.other_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.other_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_system(
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_system(
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_in_domain,
+            cls.group_in_domain,
+            cls.role_own_domain)
+        roles_client.create_group_role_on_project(
+            cls.project_in_domain,
+            cls.group_other_domain,
+            cls.role_own_domain)
+        roles_client.create_group_role_on_project(
+            cls.project_other_domain,
+            cls.group_in_domain,
+            cls.role_other_domain)
+        roles_client.create_group_role_on_project(
+            cls.project_other_domain,
+            cls.group_other_domain,
+            cls.role_other_domain)
+        roles_client.create_group_role_on_domain(
+            cls.own_domain,
+            cls.group_in_domain,
+            cls.role_own_domain)
+        roles_client.create_group_role_on_domain(
+            cls.own_domain,
+            cls.group_other_domain,
+            cls.role_own_domain)
+        roles_client.create_group_role_on_domain(
+            cls.other_domain,
+            cls.group_in_domain,
+            cls.role_other_domain)
+        roles_client.create_group_role_on_domain(
+            cls.other_domain,
+            cls.group_other_domain,
+            cls.role_other_domain)
+
+    @abc.abstractmethod
+    def test_identity_check_grant(self):
+        """Test identity:check_grant policy.
+
+        This test must check:
+          * whether the persona can check a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_grants(self):
+        """Test identity:list_grants policy.
+
+        This test must check:
+          * whether the persona can list grants for
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |      |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |      |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_create_grant(self):
+        """Test identity:create_grant policy.
+
+        This test must check:
+          * whether the persona can create a grant of
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_revoke_grant(self):
+        """Test identity:revoke_grant policy.
+
+        This test must check:
+          * whether the persona can revoke a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |  X   |  X    |   X     |   X    |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_system_grants_for_user(self):
+        """Test identity:list_system_grants_for_user policy.
+
+        This test must check:
+          * whether the persona can list grants for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |       |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |      |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |      |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_system_grant_for_user(self):
+        """Test identity:check_system_grant_for_user policy.
+
+        This test must check:
+          * whether the persona can check a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |       |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_create_system_grant_for_user(self):
+        """Test identity:create_system_grant_for_user policy.
+
+        This test must check:
+          * whether the persona can create a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |       |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_revoke_system_grant_for_user(self):
+        """Test identity:revoke_system_grant_for_user policy.
+
+        This test must check:
+          * whether the persona can revoke a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |  X   |       |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |  X   |       |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_system_grants_for_group(self):
+        """Test identity:list_system_grants_for_group policy.
+
+        This test must check:
+          * whether the persona can list grants for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |      |  X    |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |      |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |      |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_system_grant_for_group(self):
+        """Test identity:check_system_grant_for_group policy.
+
+        This test must check:
+          * whether the persona can check a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |      |  X    |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_create_system_grant_for_group(self):
+        """Test identity:create_system_grant_for_group policy.
+
+        This test must check:
+          * whether the persona can create a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |      |  X    |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_revoke_system_grant_for_group(self):
+        """Test identity:revoke_system_grant_for_group policy.
+
+        This test must check:
+          * whether the persona can revoke a grant for
+
+                         +------+------+-------+---------+--------+--------+
+                         | Role | User | Group | Project | Domain | System |
+          +--------------+------+------+-------+---------+--------+--------+
+          | global       |  X   |      |  X    |         |        |  X     |
+          +--------------+------+------+-------+---------+--------+--------+
+          | own domain   |  X   |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+          | other domain |  X   |      |  X    |         |        |        |
+          +--------------+------+------+-------+---------+--------+--------+
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacGrantTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_check_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=204,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+        # domain-specific role not matching arbitrary project, arbitrary group
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.NotFound,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # domain-specific role not matching arbitrary project, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.NotFound,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # domain-specific role not matching arbitrary domain, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.NotFound,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # domain-specific role not matching arbitrary domain, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.NotFound,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+
+        # domain-specific role, arbitrary project, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary project, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=204,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+    def test_identity_list_grants(self):
+        # arbitrary project, arbitrary user
+        self.do_request(
+            'list_user_roles_on_project',
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain)
+        # arbitrary project, arbitrary group
+        self.do_request(
+            'list_group_roles_on_project',
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain)
+        # arbitrary domain, arbitrary user
+        self.do_request(
+            'list_user_roles_on_domain',
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain)
+        # arbitrary domain, arbitrary group
+        self.do_request(
+            'list_group_roles_on_domain',
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_create_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_project,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_project,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_domain,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_domain,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # domain-specific, arbitrary project, arbitrary user
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_project,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary project, arbitrary group
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_project,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary domain, arbitrary user
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_domain,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary domain, arbitrary group
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_domain,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_revoke_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # domain-specific role, arbitrary project, arbitrary user
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary project, arbitrary group
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=204,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary user
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary group
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=204,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_list_system_grants_for_user(self):
+        self.do_request('list_user_roles_on_system',
+                        user_id=self.user_other_domain)
+
+    def test_identity_check_system_grant_for_user(self):
+        self.do_request('check_user_role_existence_on_system',
+                        expected_status=204,
+                        user_id=self.user_other_domain,
+                        role_id=self.role_id)
+
+    def test_identity_create_system_grant_for_user(self):
+        self.do_request(
+            'create_user_role_on_system',
+            expected_status=204,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_system,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_user(self):
+        self.admin_roles_client.create_user_role_on_system(
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_system',
+            expected_status=204,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_list_system_grants_for_group(self):
+        self.do_request('list_group_roles_on_system',
+                        group_id=self.group_other_domain)
+
+    def test_identity_check_system_grant_for_group(self):
+        self.do_request('check_role_from_group_on_system_existence',
+                        expected_status=204,
+                        group_id=self.group_other_domain,
+                        role_id=self.role_id)
+
+    def test_identity_create_system_grant_for_group(self):
+        self.do_request(
+            'create_group_role_on_system',
+            expected_status=204,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_system,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_group(self):
+        self.admin_roles_client.create_group_role_on_system(
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_system',
+            expected_status=204,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_project,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_project,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_domain,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_domain,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # domain-specific, arbitrary project, arbitrary user
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_project,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary project, arbitrary group
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_project,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary domain, arbitrary user
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_domain,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary domain, arbitrary group
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_domain,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_revoke_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # domain-specific role, arbitrary project, arbitrary user
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary project, arbitrary group
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary user
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary group
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_create_system_grant_for_user(self):
+        self.do_request(
+            'create_user_role_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_system,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_user(self):
+        self.admin_roles_client.create_user_role_on_system(
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_system,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_create_system_grant_for_group(self):
+        self.do_request(
+            'create_group_role_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_group(self):
+        self.admin_roles_client.create_group_role_on_system(
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_system,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacGrantTest, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_check_grant(self):
+        ###################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OWN DOMAIN #
+        ###################################################
+        # global role, project in own domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=204,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, domain in own domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, domain in own domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=204,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, domain in own domain, user in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, domain in own domain, group in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #####################################################
+        # global role, project in own domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, domain in own domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, domain in own domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, domain in own domain, user in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, domain in own domain, group in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+        #####################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OWN DOMAIN #
+        #####################################################
+        # global role, project in other domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in own domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in own domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in own domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #######################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #######################################################
+        # global role, project in other domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in other domain
+        # (none created, should 403)
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in other domain
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in other domain
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+    def test_identity_list_grants(self):
+        ###################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OWN DOMAIN #
+        ###################################################
+        # project in other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_project',
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain)
+        # project in other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_project',
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain)
+        # other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_domain',
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain)
+        # other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_domain',
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain)
+        #####################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #####################################################
+        # project in other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain)
+        # project in other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain)
+        # other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain)
+        # other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain)
+        #####################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OWN DOMAIN #
+        #####################################################
+        # project in other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain)
+        # project in other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain)
+        # other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain)
+        # other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain)
+        #######################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #######################################################
+        # project in other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain)
+        # project in other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain)
+        # other domain, user in other domain
+        self.do_request(
+            'list_user_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain)
+        # other domain, group in other domain
+        self.do_request(
+            'list_group_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain)
+
+    def test_identity_create_grant(self):
+        ###################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OWN DOMAIN #
+        ###################################################
+        # global role, project in own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #####################################################
+        # global role, project in own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OWN DOMAIN #
+        #####################################################
+        # global role, project in other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #######################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #######################################################
+        # global role, project in other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+    def test_identity_revoke_grant(self):
+        ###################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OWN DOMAIN #
+        ###################################################
+        # global role, project in own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=204,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=204,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #####################################################
+        # global role, project in own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OWN DOMAIN #
+        #####################################################
+        # global role, project in other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #######################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #######################################################
+        # global role, project in other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+    def test_identity_list_system_grants_for_user(self):
+        self.do_request('list_user_roles_on_system',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.user_other_domain)
+        self.do_request('list_user_roles_on_system',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.user_other_domain)
+
+    def test_identity_check_system_grant_for_user(self):
+        self.do_request('check_user_role_existence_on_system',
+                        exceptions.Forbidden,
+                        user_id=self.user_other_domain,
+                        role_id=self.role_id)
+        self.do_request('check_user_role_existence_on_system',
+                        exceptions.Forbidden,
+                        user_id=self.user_other_domain,
+                        role_id=self.role_id)
+
+    def test_identity_create_system_grant_for_user(self):
+        self.do_request(
+            'create_user_role_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'create_user_role_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_user(self):
+        # user in own domain
+        self.admin_roles_client.create_user_role_on_system(
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_system,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # user in other domain
+        self.admin_roles_client.create_user_role_on_system(
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_system,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_list_system_grants_for_group(self):
+        self.do_request('list_group_roles_on_system',
+                        expected_status=exceptions.Forbidden,
+                        group_id=self.group_in_domain)
+        self.do_request('list_group_roles_on_system',
+                        expected_status=exceptions.Forbidden,
+                        group_id=self.group_other_domain)
+
+    def test_identity_check_system_grant_for_group(self):
+        self.do_request('check_role_from_group_on_system_existence',
+                        exceptions.Forbidden,
+                        group_id=self.group_other_domain,
+                        role_id=self.role_id)
+        self.do_request('check_role_from_group_on_system_existence',
+                        exceptions.Forbidden,
+                        group_id=self.group_other_domain,
+                        role_id=self.role_id)
+
+    def test_identity_create_system_grant_for_group(self):
+        self.do_request(
+            'create_group_role_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'create_group_role_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_group(self):
+        # group in own domain
+        self.admin_roles_client.create_group_role_on_system(
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_system,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # group in other domain
+        self.admin_roles_client.create_group_role_on_system(
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_system,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+    def test_identity_create_grant(self):
+        ###################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OWN DOMAIN #
+        ###################################################
+        # global role, project in own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #####################################################
+        # global role, project in own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OWN DOMAIN #
+        #####################################################
+        # global role, project in other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in own domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in own domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #######################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #######################################################
+        # global role, project in other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in other domain
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in other domain
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+    def test_identity_revoke_grant(self):
+        ###################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OWN DOMAIN #
+        ###################################################
+        # global role, project in own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OWN DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #####################################################
+        # global role, project in own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, own domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in own domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in own domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_in_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, own domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.own_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        #####################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OWN DOMAIN #
+        #####################################################
+        # global role, project in other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in own domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in own domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_in_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in own domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_in_domain,
+            role_id=self.role_other_domain)
+        #######################################################
+        # RESOURCE IN OTHER DOMAIN - IDENTITY IN OTHER DOMAIN #
+        #######################################################
+        # global role, project in other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, project in other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # role in own domain, project in other domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, project in other domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, user in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # role in own domain, other domain, group in other domain
+        # role assignment does not exist, should 403
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # role in other domain, project in other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, project in other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, user in other domain
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # role in other domain, other domain, group in other domain
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+    def test_identity_create_system_grant_for_user(self):
+        self.do_request(
+            'create_user_role_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'create_user_role_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_user(self):
+        # group in own domain
+        self.admin_roles_client.create_group_role_on_system(
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_system,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_in_domain,
+            role_id=self.role_id)
+        # group in other domain
+        self.admin_roles_client.create_group_role_on_system(
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_system,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacGrantTest, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_identity_check_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+        # domain-specific role not matching arbitrary project, arbitrary group
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # domain-specific role not matching arbitrary project, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+        # domain-specific role not matching arbitrary domain, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_own_domain)
+        # domain-specific role not matching arbitrary domain, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_own_domain)
+
+        # domain-specific role, arbitrary project, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary project, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_project_existence',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary user
+        self.do_request(
+            'check_user_role_existence_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary group
+        self.do_request(
+            'check_role_from_group_on_domain_existence',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+
+    def test_identity_list_grants(self):
+        # arbitrary project, arbitrary user
+        self.do_request(
+            'list_user_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain)
+        # arbitrary project, arbitrary group
+        self.do_request(
+            'list_group_roles_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain)
+        # arbitrary domain, arbitrary user
+        self.do_request(
+            'list_user_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain)
+        # arbitrary domain, arbitrary group
+        self.do_request(
+            'list_group_roles_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_create_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # domain-specific, arbitrary project, arbitrary user
+        self.do_request(
+            'create_user_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_project,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary project, arbitrary group
+        self.do_request(
+            'create_group_role_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_group_on_project,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary domain, arbitrary user
+        self.do_request(
+            'create_user_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.addCleanup(
+            self.admin_roles_client.delete_role_from_user_on_domain,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific, arbitrary domain, arbitrary group
+        self.do_request(
+            'create_group_role_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_revoke_grant(self):
+        # global role, arbitrary project, arbitrary user
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary project, arbitrary group
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary user
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        # global role, arbitrary domain, arbitrary group
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        # domain-specific role, arbitrary project, arbitrary user
+        self.admin_roles_client.create_user_role_on_project(
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary project, arbitrary group
+        self.admin_roles_client.create_group_role_on_project(
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_project',
+            expected_status=exceptions.Forbidden,
+            project_id=self.project_other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary user
+        self.admin_roles_client.create_user_role_on_domain(
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_user_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            user_id=self.user_other_domain,
+            role_id=self.role_other_domain)
+        # domain-specific role, arbitrary domain, arbitrary group
+        self.admin_roles_client.create_group_role_on_domain(
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        self.do_request(
+            'delete_role_from_group_on_domain',
+            expected_status=exceptions.Forbidden,
+            domain_id=self.other_domain,
+            group_id=self.group_other_domain,
+            role_id=self.role_other_domain)
+        # other domain-specific tests not applicable to system user
+
+    def test_identity_list_system_grants_for_user(self):
+        self.do_request('list_user_roles_on_system',
+                        expected_status=exceptions.Forbidden,
+                        user_id=self.user_other_domain)
+
+    def test_identity_check_system_grant_for_user(self):
+        self.do_request('check_user_role_existence_on_system',
+                        exceptions.Forbidden,
+                        user_id=self.user_other_domain,
+                        role_id=self.role_id)
+
+    def test_identity_create_system_grant_for_user(self):
+        self.do_request(
+            'create_user_role_on_system',
+            expected_status=exceptions.Forbidden,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_user(self):
+        self.admin_roles_client.create_user_role_on_system(
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_user_on_system',
+            exceptions.Forbidden,
+            user_id=self.user_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_list_system_grants_for_group(self):
+        self.do_request('list_group_roles_on_system',
+                        exceptions.Forbidden,
+                        group_id=self.group_other_domain)
+
+    def test_identity_check_system_grant_for_group(self):
+        self.do_request('check_role_from_group_on_system_existence',
+                        exceptions.Forbidden,
+                        group_id=self.group_other_domain,
+                        role_id=self.role_id)
+
+    def test_identity_create_system_grant_for_group(self):
+        self.do_request(
+            'create_group_role_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+    def test_identity_revoke_system_grant_for_group(self):
+        self.admin_roles_client.create_group_role_on_system(
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+        self.do_request(
+            'delete_role_from_group_on_system',
+            expected_status=exceptions.Forbidden,
+            group_id=self.group_other_domain,
+            role_id=self.role_id)
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectMemberTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_group.py b/keystone_tempest_plugin/tests/rbac/v3/test_group.py
new file mode 100644
index 0000000..c3ce1d9
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_group.py
@@ -0,0 +1,1152 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacGroupTest(rbac_base.IdentityV3RbacBaseTests,
+                              metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacGroupTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.groups_client
+        cls.users_client = cls.persona.users_v3_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_groups_client = cls.admin_client.groups_client
+        cls.admin_users_client = cls.admin_client.users_v3_client
+        cls.admin_domains_client = cls.admin_client.domains_client
+
+    def setUp(self):
+        super(IdentityV3RbacGroupTest, self).setUp()
+        self.own_domain = self.persona.credentials.domain_id
+        self.other_domain = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_domains_client.delete_domain,
+                        self.other_domain)
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=self.other_domain, enabled=False)
+
+    def group(self, domain_id=None):
+        group = {}
+        name = data_utils.rand_name('group')
+        group['name'] = name
+        if domain_id:
+            group['domain_id'] = domain_id
+        return group
+
+    @abc.abstractmethod
+    def test_identity_create_group(self):
+        """Test identity:create_group policy.
+
+        This test must check:
+          * whether the persona can create an arbitrary group
+          * whether the persona can create a group in another domain
+          * whether the persona can create a group in their own domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_group(self):
+        """Test identity:get_group policy.
+
+        This test must check:
+          * whether the persona can get an arbitrary group
+          * whether the persona can get a group in another domain
+          * whether the persona can get a group in their own domain
+          * whether the persona can get a group that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_groups(self):
+        """Test identity:list_groups policy.
+
+        This test must check:
+          * whether the persona can list all groups
+          * whether the result list is appropriately filtered to domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_groups_for_user(self):
+        """Test identity:list_groups_for_user policy.
+
+        This test must check:
+          * whether the persona can list groups for an arbitrary user
+          * whether the persona can see groups in their own domain for user in
+            their own domain
+          * whether the persona can see groups in another domain for user in
+            their own domain
+          * whether the persona can see groups in their own domain for user in
+            another domain
+          * whether the persona can see groups in another domain for user in
+            another domain
+          * whether the persona can list groups for a nonexistent user
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_group(self):
+        """Test identity:update_groups policy.
+
+        This test must check:
+          * whether the persona can update an arbitrary group
+          * whether the persona can update a group in another domain
+          * whether the persona can update a group in their own domain
+          * whether the persona can update a group that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_group(self):
+        """Test identity:delete_group policy.
+
+        This test must check
+          * whether the persona can delete an arbitrary group
+          * whether the persona can delete a group in another domain
+          * whether the persona can delete a group in their own domain
+          * whether the persona can delete a group that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_users_in_group(self):
+        """Test identity:list_users_in_group policy.
+
+        This test must check
+          * whether the persona can list users in an arbitrary group
+          * whether the persona can see users in their own domain for group in
+            their own domain
+          * whether the persona can see users in another domain for group in
+            their own domain
+          * whether the persona can see users in their own domain for group in
+            another domain
+          * whether the persona can see users in another domain for group in
+            another domain
+          * whether the persona can list users for a nonexistent group
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_add_user_to_group(self):
+        """Test identity:add_user_to_group policy.
+
+        This test must check
+          * whether the persona can add an arbitrary user to an arbitrary group
+          * whether the persona can add a user in their own domain to a group
+            in their own domain
+          * whether the persona can add a user in another domain to a group in
+            their own domain
+          * whether the persona can add a user in their own domain to a group
+            in another domain
+          * whether the persona can add a user in another domain to a group in
+            their own domain
+          * whether the persona can add a nonexistent user to a group
+          * whether the persona can add a user to a nonexistent group
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_remove_user_from_group(self):
+        """Test identity:remove_user_from_group policy.
+
+        This test must check
+          * whether the persona can remove an arbitrary user from an arbitrary
+            group
+          * whether the persona can remove a user in their own domain from a
+            group in their own domain
+          * whether the persona can remove a user in another domain from a
+            group in their own domain
+          * whether the persona can remove a user in their own domain from a
+            group in another domain
+          * whether the persona can remove a user in another domain from a
+            group in their own domain
+          * whether the persona can remove a nonexistent user from a group
+          * whether the persona can remove a user from a nonexistent group
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_user_in_group(self):
+        """Test identity:check_user_in_group policy.
+
+        This test must check
+          * whether the persona can check if an arbitrary user is in an
+            arbitrary group
+          * whether the persona can check if a user in their own domain is in a
+            group in their own domain
+          * whether the persona can check if a user in another domain is in a
+            group in their own domain
+          * whether the persona can check if a user in their own domain is in a
+            group in another domain
+          * whether the persona can check if a user in another domain is in a
+            group in another domain
+          * whether the persona can check if a nonexistent user is in a group
+          * whether the persona can check if a user is in a nonexistent group
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacGroupTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_group(self):
+        resp = self.do_request('create_group', expected_status=201,
+                               **self.group())
+        self.addCleanup(self.admin_groups_client.delete_group,
+                        resp['group']['id'])
+
+    def test_identity_get_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        # user can get an arbitrary group
+        self.do_request('show_group', group_id=group['id'])
+        # user gets a 404 for nonexistent group
+        self.do_request('show_group', expected_status=exceptions.NotFound,
+                        group_id='fakegroup')
+
+    def test_identity_list_groups(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        resp = self.do_request('list_groups')
+        self.assertIn(group['id'], set(g['id'] for g in resp['groups']))
+
+    def test_identity_list_groups_for_user(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        user = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'))['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user['id'])
+        self.admin_groups_client.add_group_user(group['id'], user['id'])
+        resp = self.do_request('list_user_groups', client=self.users_client,
+                               user_id=user['id'])
+        self.assertIn(group['id'], set(g['id'] for g in resp['groups']))
+        self.do_request('list_user_groups', client=self.users_client,
+                        expected_status=exceptions.NotFound,
+                        user_id='fakeuser')
+
+    def test_identity_update_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        # user can update an arbitrary group
+        group_update = {
+            'group_id': group['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', **group_update)
+        # user gets a 404 for nonexistent group
+        group_update = {
+            'group_id': 'fakegroup',
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.NotFound,
+                        **group_update)
+
+    def test_identity_delete_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        # user can delete an arbitrary group
+        self.do_request('delete_group', expected_status=204,
+                        group_id=group['id'])
+        # user gets a 404 for nonexistent group
+        self.do_request('delete_group', expected_status=exceptions.NotFound,
+                        group_id='fakegroup')
+
+    def test_identity_list_users_in_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        user = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'))['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user['id'])
+        self.admin_groups_client.add_group_user(group['id'], user['id'])
+        resp = self.do_request('list_group_users', group_id=group['id'])
+        user_ids = set(u['id'] for u in resp['users'])
+        self.assertEqual(1, len(user_ids))
+        self.assertIn(user['id'], user_ids)
+
+    def test_identity_add_user_to_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        user = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'))['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user['id'])
+        # user can add a user to a group
+        self.do_request('add_group_user', expected_status=204,
+                        group_id=group['id'], user_id=user['id'])
+        # user gets a 404 for nonexistent group
+        self.do_request('add_group_user', expected_status=exceptions.NotFound,
+                        group_id='fakegroup', user_id=user['id'])
+        # user gets a 404 for nonexistent user
+        self.do_request('add_group_user', expected_status=exceptions.NotFound,
+                        group_id=group['id'], user_id='fakeuser')
+
+    def test_identity_remove_user_from_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        user = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'))['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user['id'])
+        self.admin_groups_client.add_group_user(group['id'], user['id'])
+        # user can remove a user from a group
+        self.do_request('delete_group_user', expected_status=204,
+                        group_id=group['id'], user_id=user['id'])
+        # user gets a 404 for nonexistent group
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.NotFound,
+                        group_id='fakegroup', user_id=user['id'])
+        # user gets a 404 for nonexistent user
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.NotFound,
+                        group_id=group['id'], user_id='fakeuser')
+
+    def test_identity_check_user_in_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        user = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'))['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user['id'])
+        self.admin_groups_client.add_group_user(group['id'], user['id'])
+        # user can check if a user is in a group
+        self.do_request('check_group_user_existence', expected_status=204,
+                        group_id=group['id'], user_id=user['id'])
+        # user gets a 404 for nonexistent group
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.NotFound,
+                        group_id='fakegroup', user_id=user['id'])
+        # user gets a 404 for nonexistent user
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.NotFound,
+                        group_id=group['id'], user_id='fakeuser')
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_group(self):
+        self.do_request('create_group', expected_status=exceptions.Forbidden,
+                        **self.group())
+
+    def test_identity_update_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        # user cannot update an arbitrary group
+        group_update = {
+            'group_id': group['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+        # user gets a 403 for nonexistent group
+        group_update = {
+            'group_id': 'fakegroup',
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+
+    def test_identity_delete_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        # user cannot delete an arbitrary group
+        self.do_request('delete_group', expected_status=exceptions.Forbidden,
+                        group_id=group['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('delete_group', expected_status=exceptions.Forbidden,
+                        group_id=group['id'])
+
+    def test_identity_add_user_to_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        user = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'))['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user['id'])
+        # user cannot add a user to a group
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group['id'], user_id=user['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group['id'], user_id='fakeuser')
+
+    def test_identity_remove_user_from_group(self):
+        group = self.admin_groups_client.create_group(**self.group())['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        user = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'))['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user['id'])
+        self.admin_groups_client.add_group_user(group['id'], user['id'])
+        # user cannot remove a user from a group
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group['id'], user_id=user['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group['id'], user_id='fakeuser')
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacGroupTest, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_create_group(self):
+        # user can create group in own domain
+        resp = self.do_request('create_group', expected_status=201,
+                               **self.group(domain_id=self.own_domain))
+        self.addCleanup(self.admin_groups_client.delete_group,
+                        resp['group']['id'])
+        # user cannot create group in another domain
+        resp = self.do_request('create_group',
+                               expected_status=exceptions.Forbidden,
+                               **self.group(domain_id=self.other_domain))
+
+    def test_identity_get_group(self):
+        group = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        # user can get group in own domain
+        self.do_request('show_group', group_id=group['id'])
+        # user cannot get group in other domain
+        group = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        self.do_request('show_group', expected_status=exceptions.Forbidden,
+                        group_id=group['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('show_group', expected_status=exceptions.Forbidden,
+                        group_id='fakegroup')
+
+    def test_identity_list_groups(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        resp = self.do_request('list_groups')
+        # user can get groups in own domain
+        self.assertIn(group1['id'], set(g['id'] for g in resp['groups']))
+        # user cannot get groups in other domain
+        self.assertNotIn(group2['id'], set(g['id'] for g in resp['groups']))
+
+    def test_identity_list_groups_for_user(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.other_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        resp = self.do_request('list_user_groups', client=self.users_client,
+                               user_id=user1['id'])
+        # user can list groups in own domain for user in own domain
+        self.assertIn(group1['id'], set(g['id'] for g in resp['groups']))
+        # user cannot list groups in other domain for user in own domain
+        self.assertNotIn(group2['id'], set(g['id'] for g in resp['groups']))
+        # user cannot list groups for user in other domain
+        resp = self.do_request('list_user_groups', client=self.users_client,
+                               expected_status=exceptions.Forbidden,
+                               user_id=user2['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('list_user_groups', client=self.users_client,
+                        expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+    def test_identity_update_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        # user can update a group in own domain
+        group_update = {
+            'group_id': group1['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', **group_update)
+        # user cannot update a group in other domain
+        group_update = {
+            'group_id': group2['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+        # user gets a 403 for nonexistent group
+        group_update = {
+            'group_id': 'fakegroup',
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+
+    def test_identity_delete_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        # user can delete a group in own domain
+        self.do_request('delete_group', expected_status=204,
+                        group_id=group1['id'])
+        # user cannot delete a group in other domain
+        self.do_request('delete_group', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'])
+        # user gets a 404 for nonexistent group
+        self.do_request('delete_group', expected_status=exceptions.NotFound,
+                        group_id='fakegroup')
+
+    def test_identity_list_users_in_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.other_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        resp = self.do_request('list_group_users', group_id=group1['id'])
+        # user can list users in own domain for group in own domain
+        self.assertIn(user1['id'], set(u['id'] for u in resp['users']))
+        # user cannot list users in another domain for group in own domain
+        self.assertNotIn(user2['id'], set(u['id'] for u in resp['users']))
+        # user cannot list users for group in another domain
+        self.do_request('list_group_users',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'])
+
+    def test_identity_add_user_to_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        # user can add a user in own domain to a group in own domain
+        self.do_request('add_group_user', expected_status=204,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user can add a user in another domain to a group in own domain
+        self.do_request('add_group_user', expected_status=204,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot add a user in own domain to a group in another domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot add a user in another domain to a group in another domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+    def test_identity_remove_user_from_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        # user can remove a user in own domain from a group in own domain
+        self.do_request('delete_group_user', expected_status=204,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user can remove a user in another domain from a group in own
+        # domain
+        self.do_request('delete_group_user', expected_status=204,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot remove a user in own domain from a group in another
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot remove a user in another domain from a group in another
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+    def test_identity_check_user_in_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        # user can check if a user in own domain is in a group in own domain
+        self.do_request('check_group_user_existence', expected_status=204,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user can check if a user in another domain is in a group in own
+        # domain
+        self.do_request('check_group_user_existence',
+                        expected_status=204,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot check if a user in own domain is in a group in another
+        # domain
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot check if a user in another domain is in a group in
+        # another domain
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+    def test_identity_create_group(self):
+        # user cannot create group in own domain
+        self.do_request('create_group',
+                        expected_status=exceptions.Forbidden,
+                        **self.group(domain_id=self.own_domain))
+        # user cannot create group in another domain
+        self.do_request('create_group',
+                        expected_status=exceptions.Forbidden,
+                        **self.group(domain_id=self.other_domain))
+
+    def test_identity_update_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        # user cannot update a group in own domain
+        group_update = {
+            'group_id': group1['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+        # user cannot update a group in other domain
+        group_update = {
+            'group_id': group2['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+        # user gets a 403 for nonexistent group
+        group_update = {
+            'group_id': 'fakegroup',
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+
+    def test_identity_delete_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        # user cannot delete a group in own domain
+        self.do_request('delete_group', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'])
+        # user cannot delete a group in other domain
+        self.do_request('delete_group', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'])
+        # user gets a 404 for nonexistent group
+        self.do_request('delete_group', expected_status=exceptions.NotFound,
+                        group_id='fakegroup')
+
+    def test_identity_add_user_to_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        # user cannot add a user in own domain to a group in own domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user cannot add a user in another domain to a group in own domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot add a user in own domain to a group in another domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot add a user in another domain to a group in another domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+    def test_identity_remove_user_from_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        # user cannot remove a user in own domain from a group in own domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user cannot remove a user in another domain from a group in own
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot remove a user in own domain from a group in another
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot remove a user in another domain from a group in another
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacGroupTest, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_identity_create_group(self):
+        # user cannot create group in own domain
+        self.do_request('create_group', expected_status=exceptions.Forbidden,
+                        **self.group(domain_id=self.own_domain))
+        # user cannot create group in another domain
+        self.do_request('create_group',
+                        expected_status=exceptions.Forbidden,
+                        **self.group(domain_id=self.other_domain))
+
+    def test_identity_get_group(self):
+        group = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        # user cannot get group in own domain
+        self.do_request('show_group', expected_status=exceptions.Forbidden,
+                        group_id=group['id'])
+        # user cannot get group in other domain
+        group = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group['id'])
+        self.do_request('show_group', expected_status=exceptions.Forbidden,
+                        group_id=group['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('show_group', expected_status=exceptions.Forbidden,
+                        group_id='fakegroup')
+
+    def test_identity_list_groups(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        self.do_request('list_groups', expected_status=exceptions.Forbidden)
+
+    def test_identity_list_groups_for_user(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.other_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        # user cannot list groups for user in own domain
+        self.do_request('list_user_groups', client=self.users_client,
+                        expected_status=exceptions.Forbidden,
+                        user_id=user1['id'])
+        # user cannot list groups for user in other domain
+        self.do_request('list_user_groups', client=self.users_client,
+                        expected_status=exceptions.Forbidden,
+                        user_id=user2['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('list_user_groups', client=self.users_client,
+                        expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+    def test_identity_update_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        # user cannot update a group in own domain
+        group_update = {
+            'group_id': group1['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+        # user cannot update a group in other domain
+        group_update = {
+            'group_id': group2['id'],
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+        # user gets a 403 for nonexistent group
+        group_update = {
+            'group_id': 'fakegroup',
+            'description': data_utils.arbitrary_string
+        }
+        self.do_request('update_group', expected_status=exceptions.Forbidden,
+                        **group_update)
+
+    def test_identity_delete_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        # user cannot delete a group in own domain
+        self.do_request('delete_group', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'])
+        # user cannot delete a group in other domain
+        self.do_request('delete_group', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('delete_group', expected_status=exceptions.NotFound,
+                        group_id='fakegroup')
+
+    def test_identity_list_users_in_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(domain_id=self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.other_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        # user cannot list users for group in own domain
+        self.do_request('list_group_users',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'])
+        # user cannot list users for group in another domain
+        self.do_request('list_group_users',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'])
+
+    def test_identity_add_user_to_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        # user cannot add a user in own domain to a group in own domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user cannot add a user in another domain to a group in own domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot add a user in own domain to a group in another domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot add a user in another domain to a group in another domain
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('add_group_user', expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+    def test_identity_remove_user_from_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        # user cannot remove a user in own domain from a group in own domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user cannot remove a user in another domain from a group in own
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot remove a user in own domain from a group in another
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot remove a user in another domain from a group in another
+        # domain
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_group_user',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+    def test_identity_check_user_in_group(self):
+        group1 = self.admin_groups_client.create_group(
+            **self.group(self.own_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group1['id'])
+        group2 = self.admin_groups_client.create_group(
+            **self.group(self.other_domain))['group']
+        self.addCleanup(self.admin_groups_client.delete_group, group2['id'])
+        user1 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user1['id'])
+        user2 = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=self.own_domain)['user']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user,
+                        user2['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group1['id'], user2['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user1['id'])
+        self.admin_groups_client.add_group_user(group2['id'], user2['id'])
+        # user cannot check if a user in own domain is in a group in own domain
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user1['id'])
+        # user cannot check if a user in another domain is in a group in own
+        # domain
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id=user2['id'])
+        # user cannot check if a user in own domain is in a group in another
+        # domain
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user1['id'])
+        # user cannot check if a user in another domain is in a group in
+        # another domain
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group2['id'], user_id=user2['id'])
+        # user gets a 403 for nonexistent group
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id='fakegroup', user_id=user1['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('check_group_user_existence',
+                        expected_status=exceptions.Forbidden,
+                        group_id=group1['id'], user_id='fakeuser')
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_identity_provider.py b/keystone_tempest_plugin/tests/rbac/v3/test_identity_provider.py
new file mode 100644
index 0000000..819508c
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_identity_provider.py
@@ -0,0 +1,283 @@
+# Copyright 2020 SUSE LLC #
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin import clients
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacIdentityProviderTests(rbac_base.IdentityV3RbacBaseTests,
+                                          metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacIdentityProviderTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.keystone_manager = clients.Manager(cls.persona.credentials)
+        persona_mgr = clients.Manager(cls.persona.credentials)
+        cls.client = persona_mgr.identity_providers_client
+        cls.admin_client = cls.os_system_admin
+        admin_mgr = clients.Manager(cls.admin_client.credentials)
+        cls.admin_idp_client = admin_mgr.identity_providers_client
+
+    @abc.abstractmethod
+    def test_identity_create_identity_provider(self):
+        """Test identity:create_identity_provider policy.
+
+        This test must check:
+          * whether the persona can create an identity provider
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_identity_provider(self):
+        """Test identity:get_identity_provider policy.
+
+        This test must check:
+          * whether the persona can get an identity provider
+          * whether the persona can get an identity provider that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_identity_providers(self):
+        """Test identity:list_identity_providers policy.
+
+        This test must check:
+          * whether the persona can list all identity providers
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_identity_provider(self):
+        """Test identity:update_identity_provider policy.
+
+        This test must check:
+          * whether the persona can update an identity provider
+          * whether the persona can update an identity provider that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_identity_provider(self):
+        """Test identity:delete_identity_provider policy.
+
+        This test must check
+          * whether the persona can delete an identity provider
+          * whether the persona can delete an identity provider that does not
+            exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacIdentityProviderTests,
+                       base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_identity_provider(self):
+        idp = self.do_request(
+            'create_identity_provider', expected_status=201,
+            idp_id=data_utils.rand_name()
+        )['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+
+    def test_identity_get_identity_provider(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+        self.do_request('show_identity_provider', idp_id=idp['id'])
+        # user gets a 404 for nonexistent idp
+        self.do_request('show_identity_provider',
+                        expected_status=exceptions.NotFound,
+                        idp_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_identity_providers(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+        resp = self.do_request('list_identity_providers')
+        self.assertIn(idp['id'], [i['id'] for i in resp['identity_providers']])
+
+    def test_identity_update_identity_provider(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+        self.do_request('update_identity_provider',
+                        idp_id=idp['id'],
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent idp
+        self.do_request('update_identity_provider',
+                        expected_status=exceptions.NotFound,
+                        idp_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_identity_provider(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.do_request('delete_identity_provider', expected_status=204,
+                        idp_id=idp['id'])
+        # user gets a 404 for nonexistent idp
+        self.do_request('delete_identity_provider',
+                        expected_status=exceptions.NotFound,
+                        idp_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_identity_provider(self):
+        self.do_request('create_identity_provider',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=data_utils.rand_name())
+
+    def test_identity_update_identity_provider(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+        self.do_request('update_identity_provider',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=idp['id'],
+                        description=data_utils.arbitrary_string())
+        # user gets a 403 for nonexistent idp
+        self.do_request('update_identity_provider',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_identity_provider(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+        self.do_request('delete_identity_provider',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=idp['id'])
+        # user gets a 403 for nonexistent idp
+        self.do_request('delete_identity_provider',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_identity_provider(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+        self.do_request('show_identity_provider',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=idp['id'])
+        # user gets a 403 for nonexistent idp
+        self.do_request('show_identity_provider',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_identity_providers(self):
+        idp = self.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']
+        self.addCleanup(
+            self.admin_client.domains_client.delete_domain, idp['domain_id'])
+        self.addCleanup(
+            self.admin_client.domains_client.update_domain,
+            idp['domain_id'], enabled=False)
+        self.addCleanup(
+            self.admin_idp_client.delete_identity_provider, idp['id'])
+        self.do_request('list_identity_providers',
+                        expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_implied_role.py b/keystone_tempest_plugin/tests/rbac/v3/test_implied_role.py
new file mode 100644
index 0000000..73765e4
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_implied_role.py
@@ -0,0 +1,257 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacImpliedRoleTest(rbac_base.IdentityV3RbacBaseTests,
+                                    metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacImpliedRoleTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.roles_v3_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_roles_client = cls.admin_client.roles_v3_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3RbacImpliedRoleTest, cls).resource_setup()
+        cls.prior_role = cls.admin_roles_client.create_role(
+            name=data_utils.rand_name('prior_role'))['role']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_roles_client.delete_role, cls.prior_role)
+        cls.implied_role = cls.admin_roles_client.create_role(
+            name=data_utils.rand_name('implied_role'))['role']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_roles_client.delete_role, cls.implied_role)
+
+    @abc.abstractmethod
+    def test_identity_create_implied_role(self):
+        """Test identity:create_implied_role policy.
+
+        This test must check:
+          * whether the persona can create an implied role
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_implied_role(self):
+        """Test identity:get_implied_role policy.
+
+        This test must check:
+          * whether the persona can get an implied role
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_implied_roles(self):
+        """Test identity:list_implied_roles policy.
+
+        This test must check:
+          * whether the persona can list implied roles
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_role_inference_rules(self):
+        """Test identity:list_role_inference_rules policy.
+
+        This test must check:
+          * whether the persona can list role inference rules
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_implied_role(self):
+        """Test identity:delete_implied_role policy.
+
+        This test must check
+          * whether the persona can delete an implied role
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_implied_role(self):
+        """Test identity:check_implied_role policy.
+
+        This test must check:
+          * whether the persona can check an association between two roles
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacImpliedRoleTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_implied_role(self):
+        self.do_request('create_role_inference_rule',
+                        expected_status=201,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+    def test_identity_get_implied_role(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('show_role_inference_rule',
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+    def test_identity_list_implied_roles(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('list_role_inferences_rules',
+                        prior_role=self.prior_role)
+
+    def test_identity_list_role_inference_rules(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('list_all_role_inference_rules')
+
+    def test_identity_delete_implied_role(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.do_request('delete_role_inference_rule',
+                        expected_status=204,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+    def test_identity_check_implied_role(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('check_role_inference_rule',
+                        expected_status=204,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_implied_role(self):
+        self.do_request('create_role_inference_rule',
+                        expected_status=exceptions.Forbidden,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+    def test_identity_delete_implied_role(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('delete_role_inference_rule',
+                        expected_status=exceptions.Forbidden,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_implied_role(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('show_role_inference_rule',
+                        expected_status=exceptions.Forbidden,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+    def test_identity_list_implied_roles(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('list_role_inferences_rules',
+                        expected_status=exceptions.Forbidden,
+                        prior_role=self.prior_role)
+
+    def test_identity_list_role_inference_rules(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('list_all_role_inference_rules',
+                        expected_status=exceptions.Forbidden)
+
+    def test_identity_check_implied_role(self):
+        self.admin_roles_client.create_role_inference_rule(
+            prior_role=self.prior_role, implies_role=self.implied_role)
+        self.addCleanup(self.admin_roles_client.delete_role_inference_rule,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+        self.do_request('check_role_inference_rule',
+                        expected_status=exceptions.Forbidden,
+                        prior_role=self.prior_role,
+                        implies_role=self.implied_role)
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_limit.py b/keystone_tempest_plugin/tests/rbac/v3/test_limit.py
new file mode 100644
index 0000000..d2efc6c
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_limit.py
@@ -0,0 +1,391 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin import clients
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacLimitTests(rbac_base.IdentityV3RbacBaseTests,
+                               metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacLimitTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        persona_mgr = clients.Manager(cls.persona.credentials)
+        cls.client = persona_mgr.limits_client
+        cls.admin_client = cls.os_system_admin
+        admin_mgr = clients.Manager(cls.admin_client.credentials)
+        cls.admin_reglim_client = admin_mgr.registered_limits_client
+        cls.admin_limits_client = admin_mgr.limits_client
+
+    @classmethod
+    def resource_setup(cls):
+        cls.region_id = cls.admin_client.regions_client.create_region(
+            region_id=data_utils.rand_name())['region']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.regions_client.delete_region,
+            cls.region_id)
+        svc_client = cls.admin_client.identity_services_v3_client
+        cls.service_id = svc_client.create_service(
+            type=data_utils.rand_name())['service']['id']
+        cls.addClassResourceCleanup(svc_client.delete_service, cls.service_id)
+        cls.own_domain = cls.persona.credentials.domain_id
+        cls.other_domain = cls.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.delete_domain, cls.other_domain)
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.update_domain,
+            domain_id=cls.other_domain,
+            enabled=False)
+        cls.own_project = cls.persona.credentials.project_id
+        # if project-scoped, use existing project
+        # else create project in domain
+        if not cls.own_project:
+            cls.own_project = cls.admin_client.projects_client.create_project(
+                name=data_utils.rand_name(),
+                domain_id=cls.own_domain)['project']['id']
+            cls.addClassResourceCleanup(
+                cls.admin_client.projects_client.delete_project,
+                cls.own_project)
+        cls.other_project = cls.admin_client.projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=cls.other_domain)['project']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project, cls.other_project)
+        cls.reg_limit = cls.admin_reglim_client.create_registered_limits(
+            payload=[{
+                "service_id": cls.service_id,
+                "region_id": cls.region_id,
+                "resource_name": data_utils.rand_name(),
+                "default_limit": 5
+            }])['registered_limits'][0]
+        cls.addClassResourceCleanup(
+            cls.admin_reglim_client.delete_registered_limit,
+            registered_limit_id=cls.reg_limit['id'])
+
+    def limits(self, project_id=None):
+        return [
+            {
+                "service_id": self.service_id,
+                "region_id": self.region_id,
+                "project_id": project_id or self.other_project,
+                "resource_name": self.reg_limit['resource_name'],
+                "resource_limit": 10
+            }
+        ]
+
+    @abc.abstractmethod
+    def test_identity_get_limit_model(self):
+        """Test identity:get_limit_model policy.
+
+        This test must check:
+          * whether the persona can get the limit model
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_create_limits(self):
+        """Test identity:create_limits policy.
+
+        This test must check:
+          * whether the persona can create a project limit for a project in any
+            domain
+          * whether the persona can create a project limit for a project in own
+            domain
+          * whether the persona can create a project limit for own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_limits(self):
+        """Test identity:list_limits policy.
+
+        This test must check:
+          * whether the persona can list limits for any project
+          * whether the persona can list limits for projects in own domain
+          * whether the persona can list limits for own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_limit(self):
+        """Test identity:get_limit policy.
+
+        This test must check:
+          * whether the persona can get a project limit for a project in any
+            domain
+          * whether the persona can get a project limit for a project in own
+            domain
+          * whether the persona can get a project limit for own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_limit(self):
+        """Test identity:update_limit policy.
+
+        This test must check:
+          * whether the persona can update a project limit for a project in any
+            domain
+          * whether the persona can update a project limit for a project in own
+            domain
+          * whether the persona can update a project limit for own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_limit(self):
+        """Test identity:delete_limit policy.
+
+        This test must check:
+          * whether the persona can delete a project limit for a project limit
+            in any domain
+          * whether the persona can delete a project limit for a project limit
+            in own domain
+          * whether the persona can delete a project limit for a project limit
+            own project
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacLimitTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_get_limit_model(self):
+        self.do_request('limits_model')
+
+    def test_identity_create_limits(self):
+        resp = self.do_request('create_limits',
+                               expected_status=201,
+                               payload=self.limits())
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=resp['limits'][0]['id'])
+
+    def test_identity_list_limits(self):
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        resp = self.do_request('list_limits')
+        self.assertIn(
+            reg_limit_id, [rl['id'] for rl in resp['limits']])
+
+    def test_identity_get_limit(self):
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        self.do_request('show_limit',
+                        limit_id=reg_limit_id)
+
+    def test_identity_update_limit(self):
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        updated = {'description': data_utils.arbitrary_string()}
+        self.do_request('update_limit',
+                        limit_id=reg_limit_id,
+                        limit=updated)
+
+    def test_identity_delete_limit(self):
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.do_request('delete_limit',
+                        expected_status=204,
+                        limit_id=reg_limit_id)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_limits(self):
+        self.do_request('create_limits',
+                        expected_status=exceptions.Forbidden,
+                        payload=self.limits())
+
+    def test_identity_update_limit(self):
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        updated = {'description': data_utils.arbitrary_string()}
+        self.do_request('update_limit',
+                        expected_status=exceptions.Forbidden,
+                        limit_id=reg_limit_id,
+                        limit=updated)
+
+    def test_identity_delete_limit(self):
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        self.do_request('delete_limit',
+                        expected_status=exceptions.Forbidden,
+                        limit_id=reg_limit_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacLimitTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_limit_model(self):
+        self.do_request('limits_model')
+
+    def test_identity_create_limits(self):
+        # cannot create limit in arbitrary project
+        self.do_request('create_limits',
+                        expected_status=exceptions.Forbidden,
+                        payload=self.limits())
+        # cannot create limit in project in own domain
+        self.do_request('create_limits',
+                        expected_status=exceptions.Forbidden,
+                        payload=self.limits(project_id=self.own_project))
+
+    def test_identity_list_limits(self):
+        # random project
+        reg_limit_1 = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_1)
+        # project in own domain
+        reg_limit_2 = self.admin_limits_client.create_limits(
+            payload=self.limits(project_id=self.own_project)
+        )['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_2)
+        resp = self.do_request('list_limits')
+        # should not see limit for other project
+        self.assertNotIn(
+            reg_limit_1, [rl['id'] for rl in resp['limits']])
+        # should see limit for project in own domain
+        self.assertIn(
+            reg_limit_2, [rl['id'] for rl in resp['limits']])
+
+    def test_identity_get_limit(self):
+        # random project
+        reg_limit_1 = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_1)
+        # project in own domain
+        reg_limit_2 = self.admin_limits_client.create_limits(
+            payload=self.limits(project_id=self.own_project)
+        )['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_2)
+        # cannot get limit for other project
+        self.do_request('show_limit',
+                        expected_status=exceptions.Forbidden,
+                        limit_id=reg_limit_1)
+        # can get limit for project in own domain
+        self.do_request('show_limit',
+                        limit_id=reg_limit_2)
+
+    def test_identity_update_limit(self):
+        # cannot update limit for arbitrary project
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        updated = {'description': data_utils.arbitrary_string()}
+        self.do_request('update_limit',
+                        expected_status=exceptions.Forbidden,
+                        limit_id=reg_limit_id,
+                        limit=updated)
+        # cannot update limit for project in own domain
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits(project_id=self.own_project)
+        )['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        updated = {'description': data_utils.arbitrary_string()}
+        self.do_request('update_limit',
+                        expected_status=exceptions.Forbidden,
+                        limit_id=reg_limit_id,
+                        limit=updated)
+
+    def test_identity_delete_limit(self):
+        # cannot delete limit for arbitrary project
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits())['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        self.do_request('delete_limit',
+                        expected_status=exceptions.Forbidden,
+                        limit_id=reg_limit_id)
+
+        # cannot delete limit for project in own domain
+        reg_limit_id = self.admin_limits_client.create_limits(
+            payload=self.limits(project_id=self.own_project)
+        )['limits'][0]['id']
+        self.addCleanup(
+            self.admin_limits_client.delete_limit,
+            limit_id=reg_limit_id)
+        self.do_request('delete_limit',
+                        expected_status=exceptions.Forbidden,
+                        limit_id=reg_limit_id)
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectMemberTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_mapping.py b/keystone_tempest_plugin/tests/rbac/v3/test_mapping.py
new file mode 100644
index 0000000..00b2e6b
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_mapping.py
@@ -0,0 +1,255 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin import clients
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacMappingTests(rbac_base.IdentityV3RbacBaseTests,
+                                 metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacMappingTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.keystone_manager = clients.Manager(cls.persona.credentials)
+        persona_mgr = clients.Manager(cls.persona.credentials)
+        cls.client = persona_mgr.mapping_rules_client
+        admin_client = cls.os_system_admin
+        admin_mgr = clients.Manager(admin_client.credentials)
+        cls.admin_mapping_client = admin_mgr.mapping_rules_client
+
+    @abc.abstractmethod
+    def test_identity_create_mapping(self):
+        """Test identity:create_mapping policy.
+
+        This test must check:
+          * whether the persona can create a mapping
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_mapping(self):
+        """Test identity:get_mapping policy.
+
+        This test must check:
+          * whether the persona can get a mapping
+          * whether the persona can get a mapping that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_mappings(self):
+        """Test identity:list_mappings policy.
+
+        This test must check:
+          * whether the persona can list all mappings
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_mapping(self):
+        """Test identity:update_mapping policy.
+
+        This test must check:
+          * whether the persona can update a mapping
+          * whether the persona can update a mapping that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_mapping(self):
+        """Test identity:delete_mapping policy.
+
+        This test must check
+          * whether the persona can delete a mapping
+          * whether the persona can delete a mapping that does not
+            exist
+        """
+        pass
+
+
+_RULES = {
+    "rules":
+        [{
+            "local": [],
+            "remote": [{"type": data_utils.rand_name()}]
+        }]
+}
+
+
+class SystemAdminTests(IdentityV3RbacMappingTests, base.BaseIdentityTest):
+    credentials = ['system_admin']
+
+    def test_identity_create_mapping(self):
+        mapping_id = self.do_request(
+            'create_mapping_rule', expected_status=201,
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES
+        )['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+
+    def test_identity_get_mapping(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+        self.do_request('show_mapping_rule', mapping_id=mapping_id)
+        # user gets a 404 for nonexistent mapping
+        self.do_request('show_mapping_rule',
+                        expected_status=exceptions.NotFound,
+                        mapping_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_mappings(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+        resp = self.do_request('list_mapping_rules')
+        self.assertIn(mapping_id, [i['id'] for i in resp['mappings']])
+
+    def test_identity_update_mapping(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+        self.do_request('update_mapping_rule',
+                        mapping_id=mapping_id,
+                        rules=_RULES)
+        # user gets a 404 for nonexistent mapping
+        self.do_request('update_mapping_rule',
+                        expected_status=exceptions.NotFound,
+                        mapping_id=data_utils.rand_uuid_hex(),
+                        rules=_RULES)
+
+    def test_identity_delete_mapping(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.do_request('delete_mapping_rule', expected_status=204,
+                        mapping_id=mapping_id)
+        # user gets a 404 for nonexistent mapping
+        self.do_request('delete_mapping_rule',
+                        expected_status=exceptions.NotFound,
+                        mapping_id=mapping_id)
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_mapping(self):
+        self.do_request('create_mapping_rule',
+                        expected_status=exceptions.Forbidden,
+                        mapping_id=data_utils.rand_name(),
+                        rules=_RULES)
+
+    def test_identity_update_mapping(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+        self.do_request('update_mapping_rule',
+                        expected_status=exceptions.Forbidden,
+                        mapping_id=mapping_id,
+                        rules=_RULES)
+        # user gets a 403 for nonexistent mapping
+        self.do_request('update_mapping_rule',
+                        expected_status=exceptions.Forbidden,
+                        mapping_id=data_utils.rand_uuid_hex(),
+                        rules=_RULES)
+
+    def test_identity_delete_mapping(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+        self.do_request('delete_mapping_rule',
+                        expected_status=exceptions.Forbidden,
+                        mapping_id=mapping_id)
+        # user gets a 403 for nonexistent mapping
+        self.do_request('delete_mapping_rule',
+                        expected_status=exceptions.Forbidden,
+                        mapping_id=mapping_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_mapping(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+        self.do_request('show_mapping_rule',
+                        expected_status=exceptions.Forbidden,
+                        mapping_id=mapping_id)
+        # user gets a 403 for nonexistent mapping
+        self.do_request('show_mapping_rule',
+                        expected_status=exceptions.Forbidden,
+                        mapping_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_mappings(self):
+        mapping_id = self.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(),
+            rules=_RULES)['mapping']['id']
+        self.addCleanup(self.admin_mapping_client.delete_mapping_rule,
+                        mapping_id)
+        self.do_request('list_mapping_rules',
+                        expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_policy.py b/keystone_tempest_plugin/tests/rbac/v3/test_policy.py
new file mode 100644
index 0000000..53cdeb5
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_policy.py
@@ -0,0 +1,232 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacPolicyTests(rbac_base.IdentityV3RbacBaseTests,
+                                metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacPolicyTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.policies_client
+        admin_client = cls.os_system_admin
+        cls.admin_policies_client = admin_client.policies_client
+
+    def policy(self):
+        return {
+            'blob': data_utils.rand_uuid_hex(),
+            'type': data_utils.rand_uuid_hex()
+        }
+
+    @abc.abstractmethod
+    def test_identity_create_policy(self):
+        """Test identity:create_policy policy.
+
+        This test must check:
+          * whether the persona can create a policy
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_policy(self):
+        """Test identity:get_policy policy.
+
+        This test must check:
+          * whether the persona can get a policy
+          * whether the persona can get a policy that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_policies(self):
+        """Test identity:list_policies policy.
+
+        This test must check:
+          * whether the persona can list all policies
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_policy(self):
+        """Test identity:update_policy policy.
+
+        This test must check:
+          * whether the persona can update a policy
+          * whether the persona can update a policy that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_policy(self):
+        """Test identity:delete_policy policy.
+
+        This test must check
+          * whether the persona can delete a policy
+          * whether the persona can delete a policy that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacPolicyTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_policy(self):
+        policy_id = self.do_request(
+            'create_policy', expected_status=201,
+            **self.policy())['policy']['id']
+        self.addCleanup(
+            self.admin_policies_client.delete_policy,
+            policy_id=policy_id)
+
+    def test_identity_get_policy(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.addCleanup(
+            self.admin_policies_client.delete_policy,
+            policy_id=policy_id)
+        self.do_request('show_policy', policy_id=policy_id)
+        # user gets a 404 for nonexistent policy
+        self.do_request('show_policy', expected_status=exceptions.NotFound,
+                        policy_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_policies(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.addCleanup(
+            self.admin_policies_client.delete_policy,
+            policy_id=policy_id)
+        resp = self.do_request('list_policies')
+        self.assertIn(policy_id, [e['id'] for e in resp['policies']])
+
+    def test_identity_update_policy(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.addCleanup(
+            self.admin_policies_client.delete_policy,
+            policy_id=policy_id)
+        self.do_request('update_policy',
+                        policy_id=policy_id,
+                        blob=data_utils.rand_uuid_hex())
+        # user gets a 404 for nonexistent policy
+        self.do_request('update_policy', expected_status=exceptions.NotFound,
+                        policy_id=data_utils.rand_uuid_hex(),
+                        blob=data_utils.rand_uuid_hex())
+
+    def test_identity_delete_policy(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.do_request('delete_policy', expected_status=204,
+                        policy_id=policy_id)
+        # user gets a 404 for nonexistent policy
+        self.do_request('delete_policy', expected_status=exceptions.NotFound,
+                        policy_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_policy(self):
+        self.do_request(
+            'create_policy', expected_status=exceptions.Forbidden,
+            **self.policy())
+
+    def test_identity_update_policy(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.addCleanup(
+            self.admin_policies_client.delete_policy,
+            policy_id=policy_id)
+        self.do_request('update_policy', expected_status=exceptions.Forbidden,
+                        policy_id=policy_id,
+                        blob=data_utils.rand_uuid_hex())
+        # user gets a 403 for nonexistent policy
+        self.do_request('update_policy', expected_status=exceptions.Forbidden,
+                        policy_id=data_utils.rand_uuid_hex(),
+                        blob=data_utils.rand_uuid_hex())
+
+    def test_identity_delete_policy(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.do_request('delete_policy',
+                        expected_status=exceptions.Forbidden,
+                        policy_id=policy_id)
+        # user gets a 403 for nonexistent policy
+        self.do_request('delete_policy', expected_status=exceptions.Forbidden,
+                        policy_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_policy(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.addCleanup(
+            self.admin_policies_client.delete_policy,
+            policy_id=policy_id)
+        self.do_request('show_policy', expected_status=exceptions.Forbidden,
+                        policy_id=policy_id)
+        # user gets a 403 for nonexistent policy
+        self.do_request('show_policy', expected_status=exceptions.Forbidden,
+                        policy_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_policies(self):
+        policy_id = self.admin_policies_client.create_policy(
+            **self.policy())['policy']['id']
+        self.addCleanup(
+            self.admin_policies_client.delete_policy,
+            policy_id=policy_id)
+        self.do_request('list_policies', expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_policy_association.py b/keystone_tempest_plugin/tests/rbac/v3/test_policy_association.py
new file mode 100644
index 0000000..5c3f514
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_policy_association.py
@@ -0,0 +1,469 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacPolicyAssociationTests(rbac_base.IdentityV3RbacBaseTests,
+                                           metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacPolicyAssociationTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.policies_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_policies_client = cls.admin_client.policies_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3RbacPolicyAssociationTests, cls).resource_setup()
+        cls.policy_id = cls.admin_policies_client.create_policy(
+            blob=data_utils.rand_uuid_hex(),
+            type=data_utils.rand_uuid_hex())['policy']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_policies_client.delete_policy,
+            policy_id=cls.policy_id)
+        cls.region_id = cls.admin_client.regions_client.create_region(
+            region_id=data_utils.rand_name())['region']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.regions_client.delete_region,
+            cls.region_id)
+        svc_client = cls.admin_client.identity_services_v3_client
+        cls.service_id = svc_client.create_service(
+            type=data_utils.rand_name())['service']['id']
+        cls.addClassResourceCleanup(svc_client.delete_service, cls.service_id)
+        cls.endpoint_id = cls.admin_client.endpoints_v3_client.create_endpoint(
+            interface='public',
+            url='http://localhost/foo',
+            service_id=cls.service_id)['endpoint']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.endpoints_v3_client.delete_endpoint,
+            endpoint_id=cls.endpoint_id)
+
+    @abc.abstractmethod
+    def test_identity_create_policy_association_for_endpoint(self):
+        """Test identity:create_policy_association_for_endpoint policy.
+
+        This test must check:
+          * whether the persona can associate a policy with an endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_create_policy_association_for_service(self):
+        """Test identity:create_policy_association_for_service policy.
+
+        This test must check:
+          * whether the persona can associate a policy with a service
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_create_policy_association_for_region_and_service(self):
+        """Test identity:create_policy_association_for_region_and_service.
+
+        This test must check:
+          * whether the persona can associate a policy with a region and
+            service
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_policy_association_for_endpoint(self):
+        """Test identity:check_policy_association_for_endpoint policy.
+
+        This test must check:
+          * whether the persona can check a policy association for an endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_policy_association_for_service(self):
+        """Test identity:check_policy_association_for_service policy.
+
+        This test must check:
+          * whether the persona can check a policy association for a service
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_policy_association_for_region_and_service(self):
+        """Test identity:check_policy_association_for_region_and_service.
+
+        This test must check:
+          * whether the persona can check a policy association for a region and
+            service
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_policy_for_endpoint(self):
+        """Test identity:get_policy_for_endpoint policy.
+
+        This test must check:
+          * whether the persona can get a policy for an endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_endpoints_for_policy(self):
+        """Test identity:list_endpoints_for_policy policy.
+
+        This test must check:
+          * whether the persona can list endpoints for a policy
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_policy_association_for_endpoint(self):
+        """Test identity:delete_policy_association_for_endpoint policy.
+
+        This test must check
+          * whether the persona can delete a policy association for an endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_policy_association_for_service(self):
+        """Test identity:delete_policy_association_for_service policy.
+
+        This test must check
+          * whether the persona can delete a policy association for a service
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_policy_association_for_region_and_service(self):
+        """Test identity:delete_policy_association_for_region_and_service policy.
+
+        This test must check
+          * whether the persona can delete a policy association for a region
+            and service
+        """
+        pass
+
+
+class SystemAdminTests(
+    IdentityV3RbacPolicyAssociationTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_policy_association_for_endpoint(self):
+        self.do_request(
+            'update_policy_association_for_endpoint',
+            expected_status=204,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+
+    def test_identity_create_policy_association_for_service(self):
+        self.do_request(
+            'update_policy_association_for_service',
+            expected_status=204,
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_service,
+            policy_id=self.policy_id, service_id=self.service_id)
+
+    def test_identity_create_policy_association_for_region_and_service(self):
+        self.do_request(
+            'update_policy_association_for_region_and_service',
+            expected_status=204,
+            policy_id=self.policy_id, service_id=self.service_id,
+            region_id=self.region_id)
+        delete_fn = getattr(
+            self.admin_policies_client,
+            'delete_policy_association_for_region_and_service'
+        )
+        self.addCleanup(delete_fn,
+                        policy_id=self.policy_id,
+                        service_id=self.service_id,
+                        region_id=self.region_id)
+
+    def test_identity_check_policy_association_for_endpoint(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'show_policy_association_for_endpoint',
+            expected_status=204,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+
+    def test_identity_check_policy_association_for_service(self):
+        self.admin_policies_client.update_policy_association_for_service(
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_service,
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.do_request(
+            'show_policy_association_for_service',
+            expected_status=204,
+            policy_id=self.policy_id, service_id=self.service_id)
+
+    def test_identity_check_policy_association_for_region_and_service(self):
+        update_fn = getattr(
+            self.admin_policies_client,
+            'update_policy_association_for_region_and_service'
+        )
+        update_fn(policy_id=self.policy_id,
+                  service_id=self.service_id,
+                  region_id=self.region_id)
+        delete_fn = getattr(
+            self.admin_policies_client,
+            'delete_policy_association_for_region_and_service'
+        )
+        self.addCleanup(delete_fn,
+                        policy_id=self.policy_id,
+                        service_id=self.service_id,
+                        region_id=self.region_id)
+        self.do_request(
+            'show_policy_association_for_region_and_service',
+            expected_status=204,
+            policy_id=self.policy_id,
+            service_id=self.service_id,
+            region_id=self.region_id)
+
+    def test_identity_get_policy_for_endpoint(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'show_policy_for_endpoint',
+            expected_status=200,
+            endpoint_id=self.endpoint_id)
+
+    def test_identity_list_endpoints_for_policy(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'list_endpoints_for_policy',
+            expected_status=200,
+            policy_id=self.policy_id)
+
+    def test_identity_delete_policy_association_for_endpoint(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'delete_policy_association_for_endpoint',
+            expected_status=204,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+
+    def test_identity_delete_policy_association_for_service(self):
+        self.admin_policies_client.update_policy_association_for_service(
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.do_request(
+            'delete_policy_association_for_service',
+            expected_status=204,
+            policy_id=self.policy_id, service_id=self.service_id)
+
+    def test_identity_delete_policy_association_for_region_and_service(self):
+        update_fn = getattr(
+            self.admin_policies_client,
+            'update_policy_association_for_region_and_service'
+        )
+        update_fn(policy_id=self.policy_id,
+                  service_id=self.service_id,
+                  region_id=self.region_id)
+        self.do_request(
+            'delete_policy_association_for_region_and_service',
+            expected_status=204,
+            policy_id=self.policy_id,
+            service_id=self.service_id,
+            region_id=self.region_id)
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_policy_association_for_endpoint(self):
+        self.do_request(
+            'update_policy_association_for_endpoint',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+
+    def test_identity_create_policy_association_for_service(self):
+        self.do_request(
+            'update_policy_association_for_service',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id, service_id=self.service_id)
+
+    def test_identity_create_policy_association_for_region_and_service(self):
+        self.do_request(
+            'update_policy_association_for_region_and_service',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id, service_id=self.service_id,
+            region_id=self.region_id)
+
+    def test_identity_delete_policy_association_for_endpoint(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'delete_policy_association_for_endpoint',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+
+    def test_identity_delete_policy_association_for_service(self):
+        self.admin_policies_client.update_policy_association_for_service(
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_service,
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.do_request(
+            'delete_policy_association_for_service',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id, service_id=self.service_id)
+
+    def test_identity_delete_policy_association_for_region_and_service(self):
+        update_fn = getattr(
+            self.admin_policies_client,
+            'update_policy_association_for_region_and_service'
+        )
+        update_fn(policy_id=self.policy_id,
+                  service_id=self.service_id,
+                  region_id=self.region_id)
+        delete_fn = getattr(
+            self.admin_policies_client,
+            'delete_policy_association_for_region_and_service'
+        )
+        self.addCleanup(delete_fn,
+                        policy_id=self.policy_id,
+                        service_id=self.service_id,
+                        region_id=self.region_id)
+        self.do_request(
+            'delete_policy_association_for_region_and_service',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id,
+            service_id=self.service_id,
+            region_id=self.region_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_check_policy_association_for_endpoint(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'show_policy_association_for_endpoint',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+
+    def test_identity_check_policy_association_for_service(self):
+        self.admin_policies_client.update_policy_association_for_service(
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_service,
+            policy_id=self.policy_id, service_id=self.service_id)
+        self.do_request(
+            'show_policy_association_for_service',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id, service_id=self.service_id)
+
+    def test_identity_check_policy_association_for_region_and_service(self):
+        update_fn = getattr(
+            self.admin_policies_client,
+            'update_policy_association_for_region_and_service'
+        )
+        update_fn(policy_id=self.policy_id,
+                  service_id=self.service_id,
+                  region_id=self.region_id)
+        delete_fn = getattr(
+            self.admin_policies_client,
+            'delete_policy_association_for_region_and_service'
+        )
+        self.addCleanup(delete_fn,
+                        policy_id=self.policy_id,
+                        service_id=self.service_id,
+                        region_id=self.region_id)
+        self.do_request(
+            'show_policy_association_for_region_and_service',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id,
+            service_id=self.service_id,
+            region_id=self.region_id)
+
+    def test_identity_get_policy_for_endpoint(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'show_policy_for_endpoint',
+            expected_status=exceptions.Forbidden,
+            endpoint_id=self.endpoint_id)
+
+    def test_identity_list_endpoints_for_policy(self):
+        self.admin_policies_client.update_policy_association_for_endpoint(
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.addCleanup(
+            self.admin_policies_client.delete_policy_association_for_endpoint,
+            policy_id=self.policy_id, endpoint_id=self.endpoint_id)
+        self.do_request(
+            'list_endpoints_for_policy',
+            expected_status=exceptions.Forbidden,
+            policy_id=self.policy_id)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_project.py b/keystone_tempest_plugin/tests/rbac/v3/test_project.py
new file mode 100644
index 0000000..81b64e6
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_project.py
@@ -0,0 +1,467 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacProjectsTests(rbac_base.IdentityV3RbacBaseTests,
+                                  metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacProjectsTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.projects_client
+        cls.users_client = cls.persona.users_v3_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_projects_client = cls.admin_client.projects_client
+
+    @abc.abstractmethod
+    def test_identity_create_project(self):
+        """Test identity:create_project policy.
+
+        This test must check:
+          * whether the persona can create a project
+          * whether the persona can create a project in their own domain
+          * whether the persona can create a project in another domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_project(self):
+        """Test identity:get_project policy.
+
+        This test must check:
+          * whether the persona can get a project
+          * whether the persona can get a project in their own domain
+          * whether the persona can get a project in another domain
+          * whether the persona can get a project that does not exist
+          * whether the persona can get their own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_projects(self):
+        """Test identity:list_projects policy.
+
+        This test must check:
+          * whether the persona can list all projects
+          * whether the persona can list all projects in their own domain
+          * whether the persona can list all projects in another domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_user_projects(self):
+        """Test identity:list_user_projects policy.
+
+        This test must check:
+          * whether the persona can list projects of a user
+          * whether the persona can list projects of a user in their own domain
+          * whether the persona can list projects of a user in another domain
+          * whether the persona can list projects for themself
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_project(self):
+        """Test identity:update_project policy.
+
+        This test must check:
+          * whether the persona can update a project
+          * whether the persona can update a project in their own domain
+          * whether the persona can update a project in another domain
+          * whether the persona can update a project that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_project(self):
+        """Test identity:delete_project policy.
+
+        This test must check
+          * whether the persona can delete a project
+          * whether the persona can delete a project in their own domain
+          * whether the persona can delete a project in another domain
+          * whether the persona can delete a project that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacProjectsTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_project(self):
+        project_id = self.do_request(
+            'create_project', expected_status=201, name=data_utils.rand_name()
+        )['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+
+    def test_identity_get_project(self):
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('show_project', project_id=project_id)
+        # user gets a 404 for nonexistent project
+        self.do_request('show_project', expected_status=exceptions.NotFound,
+                        project_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_projects(self):
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        resp = self.do_request('list_projects')
+        self.assertIn(project_id, [p['id'] for p in resp['projects']])
+
+    def test_identity_list_user_projects(self):
+        user_id = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name())['user']['id']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user, user_id)
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        role_id = self.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name())['role']['id']
+        self.addCleanup(self.admin_client.roles_v3_client.delete_role,
+                        role_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, role_id)
+        # user can list projects for arbitrary user
+        resp = self.do_request('list_user_projects', client=self.users_client,
+                               user_id=user_id)
+        self.assertIn(project_id, [p['id'] for p in resp['projects']])
+        # user can list projects for self
+        resp = self.do_request('list_user_projects', client=self.users_client,
+                               user_id=self.persona.credentials.user_id)
+        self.assertEqual(0, len([p['id'] for p in resp['projects']]))
+
+    def test_identity_update_project(self):
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('update_project',
+                        project_id=project_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent domain
+        self.do_request('update_project', expected_status=exceptions.NotFound,
+                        project_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_project(self):
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.do_request('delete_project', expected_status=204,
+                        project_id=project_id)
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_project(self):
+        self.do_request('create_project', expected_status=exceptions.Forbidden,
+                        name=data_utils.rand_name())
+
+    def test_identity_update_project(self):
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('update_project', expected_status=exceptions.Forbidden,
+                        project_id=project_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 403 for nonexistent domain
+        self.do_request('update_project', expected_status=exceptions.Forbidden,
+                        project_id=data_utils.rand_uuid_hex())
+
+    def test_identity_delete_project(self):
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('delete_project', expected_status=exceptions.Forbidden,
+                        project_id=project_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacProjectsTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def setUp(self):
+        super(DomainAdminTests, self).setUp()
+        self.own_domain = self.persona.credentials.domain_id
+        self.other_domain = self.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_client.domains_client.delete_domain,
+                        self.other_domain)
+        self.addCleanup(self.admin_client.domains_client.update_domain,
+                        domain_id=self.other_domain, enabled=False)
+
+    def test_identity_create_project(self):
+        # user can create project in own domain
+        project_id = self.do_request(
+            'create_project', expected_status=201, name=data_utils.rand_name(),
+            domain_id=self.own_domain
+        )['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        # user cannot create project in other domain
+        self.do_request(
+            'create_project', expected_status=exceptions.Forbidden,
+            name=data_utils.rand_name(), domain_id=self.other_domain
+        )
+
+    def test_identity_get_project(self):
+        # user can get project in own domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('show_project', project_id=project_id)
+        # user cannot get project in other domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('show_project', expected_status=exceptions.Forbidden,
+                        project_id=project_id)
+        # user gets a 403 for nonexistent project
+        self.do_request('show_project', expected_status=exceptions.Forbidden,
+                        project_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_projects(self):
+        # user can list projects but cannot see project in other domain
+        own_project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project,
+                        own_project_id)
+        other_project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project,
+                        other_project_id)
+        resp = self.do_request('list_projects')
+        self.assertIn(own_project_id, [d['id'] for d in resp['projects']])
+        self.assertNotIn(other_project_id, [d['id'] for d in resp['projects']])
+
+    def test_identity_list_user_projects(self):
+        # user can list projects for user in own domain
+        user_id = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['user']['id']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user, user_id)
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        role_id = self.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name())['role']['id']
+        self.addCleanup(self.admin_client.roles_v3_client.delete_role,
+                        role_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, role_id)
+        resp = self.do_request('list_user_projects', client=self.users_client,
+                               user_id=user_id)
+        self.assertIn(project_id, [p['id'] for p in resp['projects']])
+        # user cannot list projects for user in other domain
+        user_id = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['user']['id']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user, user_id)
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        role_id = self.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name())['role']['id']
+        self.addCleanup(self.admin_client.roles_v3_client.delete_role,
+                        role_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, role_id)
+        self.do_request('list_user_projects', client=self.users_client,
+                        expected_status=exceptions.Forbidden,
+                        user_id=user_id)
+        # user can list projects for self
+        resp = self.do_request('list_user_projects', client=self.users_client,
+                               user_id=self.persona.credentials.user_id)
+        self.assertEqual(0, len([p['id'] for p in resp['projects']]))
+
+    def test_identity_update_project(self):
+        # user can update project in own domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('update_project',
+                        project_id=project_id,
+                        description=data_utils.arbitrary_string())
+        # user cannot update project in other domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('update_project',
+                        expected_status=exceptions.Forbidden,
+                        project_id=project_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 403 for nonexistent domain
+        self.do_request('update_project', expected_status=exceptions.Forbidden,
+                        project_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_project(self):
+        # user can delete project in own domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.do_request('delete_project', expected_status=204,
+                        project_id=project_id)
+        # user cannot delete project in other domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('delete_project', expected_status=exceptions.Forbidden,
+                        project_id=project_id)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+    def test_identity_create_project(self):
+        # user cannot create project in own domain
+        self.do_request(
+            'create_project', expected_status=exceptions.Forbidden,
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain
+        )
+        # user cannot create project in other domain
+        self.do_request(
+            'create_project', expected_status=exceptions.Forbidden,
+            name=data_utils.rand_name(), domain_id=self.other_domain
+        )
+
+    def test_identity_update_project(self):
+        # user cannot update project in own domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('update_project',
+                        expected_status=exceptions.Forbidden,
+                        project_id=project_id,
+                        description=data_utils.arbitrary_string())
+        # user cannot update project in other domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('update_project',
+                        expected_status=exceptions.Forbidden,
+                        project_id=project_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 403 for nonexistent domain
+        self.do_request('update_project', expected_status=exceptions.Forbidden,
+                        project_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_project(self):
+        # user cannot delete project in own domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.do_request('delete_project', expected_status=exceptions.Forbidden,
+                        project_id=project_id)
+        # user cannot delete project in other domain
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('delete_project', expected_status=exceptions.Forbidden,
+                        project_id=project_id)
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_identity_get_project(self):
+        # user cannot get arbitrary project
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        self.do_request('show_project', expected_status=exceptions.Forbidden,
+                        project_id=project_id)
+        # user gets a 403 for nonexistent project
+        self.do_request('show_project', expected_status=exceptions.Forbidden,
+                        project_id=data_utils.rand_uuid_hex())
+        # user can get own project
+        self.do_request('show_project',
+                        project_id=self.persona.credentials.project_id)
+
+    def test_identity_list_projects(self):
+        # user cannot list projects
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project,
+                        project_id)
+        self.do_request('list_projects', expected_status=exceptions.Forbidden)
+
+    def test_identity_list_user_projects(self):
+        # user can list projects for other user
+        user_id = self.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name())['user']['id']
+        self.addCleanup(self.admin_client.users_v3_client.delete_user, user_id)
+        project_id = self.admin_projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(self.admin_projects_client.delete_project, project_id)
+        role_id = self.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name())['role']['id']
+        self.addCleanup(self.admin_client.roles_v3_client.delete_role,
+                        role_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id, user_id, role_id)
+        self.do_request('list_user_projects', client=self.users_client,
+                        expected_status=exceptions.Forbidden,
+                        user_id=user_id)
+        # user can list projects for self
+        resp = self.do_request('list_user_projects', client=self.users_client,
+                               user_id=self.persona.credentials.user_id)
+        self.assertIn(self.persona.credentials.project_id,
+                      [p['id'] for p in resp['projects']])
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_project_endpoint.py b/keystone_tempest_plugin/tests/rbac/v3/test_project_endpoint.py
new file mode 100644
index 0000000..442ca9e
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_project_endpoint.py
@@ -0,0 +1,251 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacProjectEndpointsTests(rbac_base.IdentityV3RbacBaseTests,
+                                          metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacProjectEndpointsTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.endpoint_filter_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_ef_client = cls.admin_client.endpoint_filter_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3RbacProjectEndpointsTests, cls).resource_setup()
+        cls.project_id = cls.admin_client.projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project,
+            project_id=cls.project_id)
+        service = cls.admin_client.identity_services_v3_client.create_service(
+            type=data_utils.rand_name())['service']
+        cls.addClassResourceCleanup(
+            cls.admin_client.identity_services_v3_client.delete_service,
+            service['id'])
+        cls.endpoint_id = cls.admin_client.endpoints_v3_client.create_endpoint(
+            interface='public',
+            url='http://localhost/foo',
+            service_id=service['id'])['endpoint']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.endpoints_v3_client.delete_endpoint,
+            endpoint_id=cls.endpoint_id)
+
+    @abc.abstractmethod
+    def test_identity_add_endpoint_to_project(self):
+        """Test identity:add_endpoint_to_project policy.
+
+        This test must check:
+          * whether the persona can allow a project to access an endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_check_endpoint_in_project(self):
+        """Test identity:check_endpoint_in_project policy.
+
+        This test must check:
+          * whether the persona can check if a project has access to an
+            endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_projects_for_endpoint(self):
+        """Test identity:list_projects_for_endpoint policy.
+
+        This test must check:
+          * whether the persona can list all projects that have access to an
+            endpoint
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_endpoints_for_project(self):
+        """Test identity:list_endpoints_for_project policy.
+
+        This test must check:
+          * whether the persona can list all endpoints to which a project has
+            access
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_remove_endpoint_from_project(self):
+        """Test identity:remove_endpoint_from_project policy.
+
+        This test must check
+          * whether the persona can remove a project's access to an endpoint
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacProjectEndpointsTests):
+
+    credentials = ['system_admin']
+
+    def test_identity_add_endpoint_to_project(self):
+        self.do_request('add_endpoint_to_project',
+                        expected_status=204,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+
+    def test_identity_check_endpoint_in_project(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        self.do_request('check_endpoint_in_project',
+                        expected_status=204,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+
+    def test_identity_list_projects_for_endpoint(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        resp = self.do_request('list_projects_for_endpoint',
+                               endpoint_id=self.endpoint_id)
+        self.assertIn(self.project_id, [p['id'] for p in resp['projects']])
+
+    def test_identity_list_endpoints_for_project(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        resp = self.do_request('list_endpoints_in_project',
+                               project_id=self.project_id)
+        self.assertIn(self.endpoint_id, [e['id'] for e in resp['endpoints']])
+
+    def test_identity_remove_endpoint_from_project(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.do_request('delete_endpoint_from_project',
+                        expected_status=204,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_add_endpoint_to_project(self):
+        self.do_request('add_endpoint_to_project',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+
+    def test_identity_remove_endpoint_from_project(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        self.do_request('delete_endpoint_from_project',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_check_endpoint_in_project(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        self.do_request('check_endpoint_in_project',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+
+    def test_identity_list_projects_for_endpoint(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        self.do_request('list_projects_for_endpoint',
+                        expected_status=exceptions.Forbidden,
+                        endpoint_id=self.endpoint_id)
+
+    def test_identity_list_endpoints_for_project(self):
+        self.admin_ef_client.add_endpoint_to_project(
+            project_id=self.project_id,
+            endpoint_id=self.endpoint_id)
+        self.addCleanup(self.admin_ef_client.delete_endpoint_from_project,
+                        project_id=self.project_id,
+                        endpoint_id=self.endpoint_id)
+        self.do_request('list_endpoints_in_project',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.project_id)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_project_tag.py b/keystone_tempest_plugin/tests/rbac/v3/test_project_tag.py
new file mode 100644
index 0000000..3e6c4a6
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_project_tag.py
@@ -0,0 +1,599 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacProjectTagTests(rbac_base.IdentityV3RbacBaseTests,
+                                    metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacProjectTagTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.project_tags_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_project_tags_client = cls.admin_client.project_tags_client
+
+    @abc.abstractmethod
+    def test_identity_create_project_tag(self):
+        """Test identity:create_project_tag policy.
+
+        This test must check:
+          * whether the persona can create a tag for an arbitrary project
+          * whether the persona can create a tag for a project in their own
+            domain
+          * whether the persona can create a tag for a project in another
+            domain
+          * whether the persona can create a tag for their own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_project_tag(self):
+        """Test identity:get_project_tag policy.
+
+        This test must check:
+          * whether the persona can get a tag for an arbitrary project
+          * whether the persona can get a tag for a project in their own domain
+          * whether the persona can get a tag for a project in another domain
+          * whether the persona can get tag for their own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_project_tags(self):
+        """Test identity:list_project_tags policy.
+
+        This test must check:
+          * whether the persona can list tags for an arbitrary project
+          * whether the persona can list tags for a project in their own domain
+          * whether the persona can list tags for a project in another domain
+          * whether the persona can list tags for their own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_project_tags(self):
+        """Test identity:update_project_tags policy.
+
+        This test must check:
+          * whether the persona can update all tags for an project
+          * whether the persona can update all tags for a project in their own
+            domain
+          * whether the persona can update all tags for a project in another
+            domain
+          * whether the persona can update all tags for their own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_project_tag(self):
+        """Test identity:delete_project_tag policy.
+
+        This test must check
+          * whether the persona can delete a single tag for an arbitrary
+            project
+          * whether the persona can delete a single tag for a project in their
+            own domain
+          * whether the persona can delete a single tag for a project in
+            another domain
+          * whether the persona can delete a single tag for their own project
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_project_tags(self):
+        """Test identity:delete_project_tag policy.
+
+        This test must check
+          * whether the persona can delete all tags for an arbitrary project
+          * whether the persona can delete all tags for a project in their own
+            domain
+          * whether the persona can delete all tags for a project in another
+            domain
+          * whether the persona can delete all tags for their own project
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacProjectTagTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def setUp(self):
+        super(SystemAdminTests, self).setUp()
+        self.project_id = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(
+            self.admin_client.projects_client.delete_project, self.project_id)
+
+    def test_identity_create_project_tag(self):
+        self.do_request(
+            'update_project_tag', expected_status=201,
+            project_id=self.project_id,
+            tag=data_utils.rand_uuid_hex()
+        )
+
+    def test_identity_get_project_tag(self):
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.project_id, tag=tag)
+        self.do_request('check_project_tag_existence',
+                        expected_status=204,
+                        project_id=self.project_id, tag=tag)
+
+    def test_identity_list_project_tags(self):
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.project_id, tag=tag)
+        resp = self.do_request('list_project_tags', project_id=self.project_id)
+        self.assertIn(tag, resp['tags'])
+
+    def test_identity_update_project_tags(self):
+        self.do_request('update_all_project_tags',
+                        project_id=self.project_id,
+                        tags=[data_utils.rand_uuid_hex()])
+
+    def test_identity_delete_project_tag(self):
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.project_id, tag=tag)
+        self.do_request('delete_project_tag', expected_status=204,
+                        project_id=self.project_id,
+                        tag=tag)
+
+    def test_identity_delete_project_tags(self):
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.project_id, tag=tag)
+        self.do_request('delete_all_project_tags', expected_status=204,
+                        project_id=self.project_id)
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_project_tag(self):
+        self.do_request(
+            'update_project_tag', expected_status=exceptions.Forbidden,
+            project_id=self.project_id,
+            tag=data_utils.rand_uuid_hex()
+        )
+
+    def test_identity_update_project_tags(self):
+        self.do_request('update_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.project_id,
+                        tags=[data_utils.rand_uuid_hex()])
+
+    def test_identity_delete_project_tag(self):
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.project_id, tag=tag)
+        self.do_request('delete_project_tag',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.project_id,
+                        tag=tag)
+
+    def test_identity_delete_project_tags(self):
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.project_id, tag=tag)
+        self.do_request('delete_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.project_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacProjectTagTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def setUp(self):
+        super(DomainAdminTests, self).setUp()
+        self.own_domain = self.persona.credentials.domain_id
+        self.other_domain = self.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name())['domain']['id']
+        self.addCleanup(self.admin_client.domains_client.delete_domain,
+                        self.other_domain)
+        self.addCleanup(self.admin_client.domains_client.update_domain,
+                        domain_id=self.other_domain, enabled=False)
+        project_client = self.admin_client.projects_client
+        self.own_project_id = project_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.own_domain)['project']['id']
+        self.addCleanup(
+            project_client.delete_project,
+            self.own_project_id)
+        self.other_project_id = project_client.create_project(
+            name=data_utils.rand_name(),
+            domain_id=self.other_domain)['project']['id']
+        self.addCleanup(project_client.delete_project, self.other_project_id)
+
+    def test_identity_create_project_tag(self):
+        # user can add tags to project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=201,
+            project_id=self.own_project_id,
+            tag=tag
+        )
+        # user cannot add tags to project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=exceptions.Forbidden,
+            project_id=self.other_project_id,
+            tag=tag
+        )
+
+    def test_identity_get_project_tag(self):
+        # user can get tag for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.do_request('check_project_tag_existence',
+                        expected_status=204,
+                        project_id=self.own_project_id, tag=tag)
+        # user cannot get tag for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('check_project_tag_existence',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id, tag=tag)
+
+    def test_identity_list_project_tags(self):
+        # user can list tags for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        resp = self.do_request('list_project_tags',
+                               project_id=self.own_project_id)
+        self.assertIn(tag, resp['tags'])
+        # user cannot list tags for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('list_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id)
+
+    def test_identity_update_project_tags(self):
+        # user can update tags for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        project_id=self.own_project_id,
+                        tags=[tag])
+        # user cannot update tags for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tags=[tag])
+
+    def test_identity_delete_project_tag(self):
+        # user can delete tag for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.do_request('delete_project_tag', expected_status=204,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        # user cannot delete tag for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_project_tag',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tag=tag)
+
+    def test_identity_delete_project_tags(self):
+        # user can delete tags for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.do_request('delete_all_project_tags', expected_status=204,
+                        project_id=self.own_project_id)
+        # user cannot delete tags for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+    def test_identity_create_project_tag(self):
+        # user cannot add tags to project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=exceptions.Forbidden,
+            project_id=self.own_project_id,
+            tag=tag
+        )
+        # user cannot add tags to project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=exceptions.Forbidden,
+            project_id=self.other_project_id,
+            tag=tag
+        )
+
+    def test_identity_update_project_tags(self):
+        # user cannot update tags for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.own_project_id,
+                        tags=[tag])
+        # user cannot update tags for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tags=[tag])
+
+    def test_identity_delete_project_tag(self):
+        # user cannot delete tag for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.do_request('delete_project_tag',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        # user cannot delete tag for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_project_tag',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tag=tag)
+
+    def test_identity_delete_project_tags(self):
+        # user cannot delete tags for project in own domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.do_request('delete_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.own_project_id)
+        # user cannot delete tags for project in other domain
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id)
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacProjectTagTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def setUp(self):
+        super(ProjectAdminTests, self).setUp()
+        self.own_project_id = self.persona.credentials.project_id
+        project_client = self.admin_client.projects_client
+        self.other_project_id = project_client.create_project(
+            name=data_utils.rand_name())['project']['id']
+        self.addCleanup(project_client.delete_project, self.other_project_id)
+
+    def test_identity_create_project_tag(self):
+        # user can add tags to own project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=201,
+            project_id=self.own_project_id,
+            tag=tag
+        )
+        self.addCleanup(self.admin_project_tags_client.delete_project_tag,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        # user cannot add tags to arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=exceptions.Forbidden,
+            project_id=self.other_project_id,
+            tag=tag
+        )
+
+    def test_identity_get_project_tag(self):
+        # user can get tag for own project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.addCleanup(self.admin_project_tags_client.delete_project_tag,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        self.do_request('check_project_tag_existence',
+                        expected_status=204,
+                        project_id=self.own_project_id, tag=tag)
+        # user cannot get tag for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('check_project_tag_existence',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id, tag=tag)
+
+    def test_identity_list_project_tags(self):
+        # user can list tags for own project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.addCleanup(self.admin_project_tags_client.delete_project_tag,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        resp = self.do_request('list_project_tags',
+                               project_id=self.own_project_id)
+        self.assertIn(tag, resp['tags'])
+        # user cannot list tags for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('list_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id)
+
+    def test_identity_update_project_tags(self):
+        # user can update tags for own project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        project_id=self.own_project_id,
+                        tags=[tag])
+        self.addCleanup(self.admin_project_tags_client.delete_project_tag,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        # user cannot update tags for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tags=[tag])
+
+    def test_identity_delete_project_tag(self):
+        # user can delete tag for own project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.do_request('delete_project_tag', expected_status=204,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        # user cannot delete tag for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_project_tag',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tag=tag)
+
+    def test_identity_delete_project_tags(self):
+        # user can delete tags for own project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.do_request('delete_all_project_tags', expected_status=204,
+                        project_id=self.own_project_id)
+        # user cannot delete tags for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id)
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+    def test_identity_create_project_tag(self):
+        # user cannot add tags to own project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=exceptions.Forbidden,
+            project_id=self.own_project_id,
+            tag=tag
+        )
+        # user cannot add tags to arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request(
+            'update_project_tag', expected_status=exceptions.Forbidden,
+            project_id=self.other_project_id,
+            tag=tag
+        )
+
+    def test_identity_update_project_tags(self):
+        # user cannot update tags for own project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.own_project_id,
+                        tags=[tag])
+        # user cannot update tags for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.do_request('update_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tags=[tag])
+
+    def test_identity_delete_project_tag(self):
+        # user cannot delete tag for own project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.addCleanup(self.admin_project_tags_client.delete_project_tag,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        self.do_request('delete_project_tag',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        # user cannot delete tag for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_project_tag',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id,
+                        tag=tag)
+
+    def test_identity_delete_project_tags(self):
+        # user cannot delete tags for own project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.own_project_id, tag=tag)
+        self.addCleanup(self.admin_project_tags_client.delete_project_tag,
+                        project_id=self.own_project_id,
+                        tag=tag)
+        self.do_request('delete_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.own_project_id)
+        # user cannot delete tags for arbitrary project
+        tag = data_utils.rand_uuid_hex()
+        self.admin_project_tags_client.update_project_tag(
+            project_id=self.other_project_id, tag=tag)
+        self.do_request('delete_all_project_tags',
+                        expected_status=exceptions.Forbidden,
+                        project_id=self.other_project_id)
+
+
+class ProjectReaderTests(ProjectMemberTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_protocol.py b/keystone_tempest_plugin/tests/rbac/v3/test_protocol.py
new file mode 100644
index 0000000..5f9d9a2
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_protocol.py
@@ -0,0 +1,302 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin import clients
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacProtocolTests(rbac_base.IdentityV3RbacBaseTests,
+                                  metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacProtocolTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.keystone_manager = clients.Manager(cls.persona.credentials)
+        persona_mgr = clients.Manager(cls.persona.credentials)
+        cls.client = persona_mgr.identity_providers_client
+        admin_client = cls.os_system_admin
+        admin_mgr = clients.Manager(admin_client.credentials)
+        cls.admin_idp_client = admin_mgr.identity_providers_client
+        cls.admin_mapping_client = admin_mgr.mapping_rules_client
+
+    @classmethod
+    def setUpClass(cls):
+        super(IdentityV3RbacProtocolTests, cls).setUpClass()
+        cls.idp_id = cls.admin_idp_client.create_identity_provider(
+            idp_id=data_utils.rand_name())['identity_provider']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_idp_client.delete_identity_provider, cls.idp_id)
+        rules = {
+            "rules":
+                [{
+                    "local": [],
+                    "remote": [{"type": data_utils.rand_name()}]
+                }]
+        }
+        cls.mapping_id = cls.admin_mapping_client.create_mapping_rule(
+            mapping_id=data_utils.rand_name(), rules=rules)['mapping']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_mapping_client.delete_mapping_rule,
+            mapping_id=cls.mapping_id)
+
+    @abc.abstractmethod
+    def test_identity_create_protocol(self):
+        """Test identity:create_protocol policy.
+
+        This test must check:
+          * whether the persona can create a protocol
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_protocol(self):
+        """Test identity:get_protocol policy.
+
+        This test must check:
+          * whether the persona can get a protocol
+          * whether the persona can get a protocol that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_protocols(self):
+        """Test identity:list_protocols policy.
+
+        This test must check:
+          * whether the persona can list all identity providers
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_protocol(self):
+        """Test identity:update_protocol policy.
+
+        This test must check:
+          * whether the persona can update a protocol
+          * whether the persona can update a protocol that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_protocol(self):
+        """Test identity:delete_protocol policy.
+
+        This test must check
+          * whether the persona can delete a protocol
+          * whether the persona can delete a protocol that does not
+            exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacProtocolTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_protocol(self):
+        protocol_id = self.do_request(
+            'add_protocol_and_mapping', expected_status=201,
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id
+        )['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+
+    def test_identity_get_protocol(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        self.do_request('get_protocol_and_mapping',
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        # user gets a 404 for nonexistent idp
+        self.do_request('get_protocol_and_mapping',
+                        expected_status=exceptions.NotFound,
+                        idp_id=self.idp_id,
+                        protocol_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_protocols(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        resp = self.do_request('list_protocols_and_mappings',
+                               idp_id=self.idp_id)
+        self.assertIn(protocol_id, [p['id'] for p in resp['protocols']])
+
+    def test_identity_update_protocol(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        self.do_request('update_protocol_mapping',
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id,
+                        mapping_id=self.mapping_id)
+        # user gets a 404 for nonexistent protocol
+        self.do_request('update_protocol_mapping',
+                        expected_status=exceptions.NotFound,
+                        idp_id=self.idp_id,
+                        protocol_id=data_utils.rand_uuid_hex(),
+                        mapping_id=self.mapping_id)
+
+    def test_identity_delete_protocol(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.do_request('delete_protocol_and_mapping', expected_status=204,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        # user gets a 404 for nonexistent idp
+        self.do_request('delete_protocol_and_mapping',
+                        expected_status=exceptions.NotFound,
+                        idp_id=self.idp_id,
+                        protocol_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_protocol(self):
+        self.do_request('add_protocol_and_mapping',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id,
+                        protocol_id=data_utils.rand_name(),
+                        mapping_id=self.mapping_id)
+
+    def test_identity_update_protocol(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        self.do_request('update_protocol_mapping',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id,
+                        mapping_id=self.mapping_id)
+        # user gets a 403 for nonexistent protocol
+        self.do_request('update_protocol_mapping',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id,
+                        protocol_id=data_utils.rand_uuid_hex(),
+                        mapping_id=self.mapping_id)
+
+    def test_identity_delete_protocol(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        self.do_request('delete_protocol_and_mapping',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        # user gets a 403 for nonexistent protocol
+        self.do_request('delete_protocol_and_mapping',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id,
+                        protocol_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_protocol(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        self.do_request('get_protocol_and_mapping',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        # user gets a 403 for nonexistent idp
+        self.do_request('get_protocol_and_mapping',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id,
+                        protocol_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_protocols(self):
+        protocol_id = self.admin_idp_client.add_protocol_and_mapping(
+            idp_id=self.idp_id,
+            protocol_id=data_utils.rand_name(),
+            mapping_id=self.mapping_id)['protocol']['id']
+        self.addCleanup(self.admin_idp_client.delete_protocol_and_mapping,
+                        idp_id=self.idp_id,
+                        protocol_id=protocol_id)
+        self.do_request('list_protocols_and_mappings',
+                        expected_status=exceptions.Forbidden,
+                        idp_id=self.idp_id)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_region.py b/keystone_tempest_plugin/tests/rbac/v3/test_region.py
new file mode 100644
index 0000000..e58206a
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_region.py
@@ -0,0 +1,209 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacRegionTests(rbac_base.IdentityV3RbacBaseTests,
+                                metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacRegionTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.regions_client
+        admin_client = cls.os_system_admin
+        cls.admin_regions_client = admin_client.regions_client
+
+    def region(self):
+        return {'region_id': data_utils.rand_uuid_hex()}
+
+    @abc.abstractmethod
+    def test_identity_create_region(self):
+        """Test identity:create_region policy.
+
+        This test must check:
+          * whether the persona can create a region
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_region(self):
+        """Test identity:get_region policy.
+
+        This test must check:
+          * whether the persona can get a region
+          * whether the persona can get a region that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_regions(self):
+        """Test identity:list_regions policy.
+
+        This test must check:
+          * whether the persona can list all regions
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_region(self):
+        """Test identity:update_region policy.
+
+        This test must check:
+          * whether the persona can update a region
+          * whether the persona can update a region that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_region(self):
+        """Test identity:delete_region policy.
+
+        This test must check
+          * whether the persona can delete a region
+          * whether the persona can delete a region that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacRegionTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_region(self):
+        region_id = self.do_request(
+            'create_region', expected_status=201,
+            **self.region())['region']['id']
+        self.addCleanup(
+            self.admin_regions_client.delete_region,
+            region_id=region_id)
+
+    def test_identity_get_region(self):
+        region_id = self.admin_regions_client.create_region(
+            **self.region())['region']['id']
+        self.addCleanup(
+            self.admin_regions_client.delete_region,
+            region_id=region_id)
+        self.do_request('show_region', region_id=region_id)
+        # user gets a 404 for nonexistent region
+        self.do_request('show_region', expected_status=exceptions.NotFound,
+                        region_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_regions(self):
+        region_id = self.admin_regions_client.create_region(
+            **self.region())['region']['id']
+        self.addCleanup(
+            self.admin_regions_client.delete_region,
+            region_id=region_id)
+        resp = self.do_request('list_regions')
+        self.assertIn(region_id, [e['id'] for e in resp['regions']])
+
+    def test_identity_update_region(self):
+        region_id = self.admin_regions_client.create_region(
+            **self.region())['region']['id']
+        self.addCleanup(
+            self.admin_regions_client.delete_region,
+            region_id=region_id)
+        self.do_request('update_region',
+                        region_id=region_id,
+                        description=data_utils.rand_uuid_hex())
+        # user gets a 404 for nonexistent region
+        self.do_request('update_region', expected_status=exceptions.NotFound,
+                        region_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.rand_uuid_hex())
+
+    def test_identity_delete_region(self):
+        region_id = self.admin_regions_client.create_region(
+            **self.region())['region']['id']
+        self.do_request('delete_region', expected_status=204,
+                        region_id=region_id)
+        # user gets a 404 for nonexistent region
+        self.do_request('delete_region', expected_status=exceptions.NotFound,
+                        region_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_region(self):
+        self.do_request(
+            'create_region', expected_status=exceptions.Forbidden,
+            **self.region())
+
+    def test_identity_update_region(self):
+        region_id = self.admin_regions_client.create_region(
+            **self.region())['region']['id']
+        self.addCleanup(
+            self.admin_regions_client.delete_region,
+            region_id=region_id)
+        self.do_request('update_region', expected_status=exceptions.Forbidden,
+                        region_id=region_id,
+                        description=data_utils.rand_uuid_hex())
+        # user gets a 403 for nonexistent region
+        self.do_request('update_region', expected_status=exceptions.Forbidden,
+                        region_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.rand_uuid_hex())
+
+    def test_identity_delete_region(self):
+        region_id = self.admin_regions_client.create_region(
+            **self.region())['region']['id']
+        self.do_request('delete_region',
+                        expected_status=exceptions.Forbidden,
+                        region_id=region_id)
+        # user gets a 403 for nonexistent region
+        self.do_request('delete_region', expected_status=exceptions.Forbidden,
+                        region_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_registered_limit.py b/keystone_tempest_plugin/tests/rbac/v3/test_registered_limit.py
new file mode 100644
index 0000000..c18ed01
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_registered_limit.py
@@ -0,0 +1,222 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin import clients
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacRegisteredLimitTests(
+    rbac_base.IdentityV3RbacBaseTests, metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacRegisteredLimitTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        persona_mgr = clients.Manager(cls.persona.credentials)
+        cls.client = persona_mgr.registered_limits_client
+        cls.admin_client = cls.os_system_admin
+        admin_mgr = clients.Manager(cls.admin_client.credentials)
+        cls.admin_reglim_client = admin_mgr.registered_limits_client
+
+    @classmethod
+    def resource_setup(cls):
+        cls.region_id = cls.admin_client.regions_client.create_region(
+            region_id=data_utils.rand_name())['region']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.regions_client.delete_region,
+            cls.region_id)
+        svc_client = cls.admin_client.identity_services_v3_client
+        cls.service_id = svc_client.create_service(
+            type=data_utils.rand_name())['service']['id']
+        cls.addClassResourceCleanup(svc_client.delete_service, cls.service_id)
+
+    def registered_limits(self):
+        return [
+            {
+                "service_id": self.service_id,
+                "region_id": self.region_id,
+                "resource_name": data_utils.rand_name(),
+                "default_limit": 5,
+                "description": data_utils.arbitrary_string()
+            }
+        ]
+
+    @abc.abstractmethod
+    def test_identity_create_registered_limits(self):
+        """Test identity:create_registered_limits policy.
+
+        This test must check:
+          * whether the persona can create a registered limit
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_registered_limits(self):
+        """Test identity:list_registered_limits policy.
+
+        This test must check:
+          * whether the persona can list registered limits
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_registered_limit(self):
+        """Test identity:get_registered_limit policy.
+
+        This test must check:
+          * whether the persona can get a registered limit
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_registered_limit(self):
+        """Test identity:update_registered_limit policy.
+
+        This test must check:
+          * whether the persona can update a registered limit
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_registered_limit(self):
+        """Test identity:delete_registered_limit policy.
+
+        This test must check:
+          * whether the persona can delete a registered limit
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacRegisteredLimitTests,
+                       base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_registered_limits(self):
+        resp = self.do_request('create_registered_limits',
+                               expected_status=201,
+                               payload=self.registered_limits())
+        self.addCleanup(
+            self.admin_reglim_client.delete_registered_limit,
+            registered_limit_id=resp['registered_limits'][0]['id'])
+
+    def test_identity_list_registered_limits(self):
+        reg_limit_id = self.admin_reglim_client.create_registered_limits(
+            payload=self.registered_limits())['registered_limits'][0]['id']
+        self.addCleanup(
+            self.admin_reglim_client.delete_registered_limit,
+            registered_limit_id=reg_limit_id)
+        resp = self.do_request('list_registered_limits')
+        self.assertIn(
+            reg_limit_id, [rl['id'] for rl in resp['registered_limits']])
+
+    def test_identity_get_registered_limit(self):
+        reg_limit_id = self.admin_reglim_client.create_registered_limits(
+            payload=self.registered_limits())['registered_limits'][0]['id']
+        self.addCleanup(
+            self.admin_reglim_client.delete_registered_limit,
+            registered_limit_id=reg_limit_id)
+        self.do_request('show_registered_limit',
+                        registered_limit_id=reg_limit_id)
+
+    def test_identity_update_registered_limit(self):
+        reg_limit_id = self.admin_reglim_client.create_registered_limits(
+            payload=self.registered_limits())['registered_limits'][0]['id']
+        self.addCleanup(
+            self.admin_reglim_client.delete_registered_limit,
+            registered_limit_id=reg_limit_id)
+        updated = {'description': data_utils.arbitrary_string()}
+        self.do_request('update_registered_limit',
+                        registered_limit_id=reg_limit_id,
+                        registered_limit=updated)
+
+    def test_identity_delete_registered_limit(self):
+        reg_limit_id = self.admin_reglim_client.create_registered_limits(
+            payload=self.registered_limits())['registered_limits'][0]['id']
+        self.do_request('delete_registered_limit',
+                        expected_status=204,
+                        registered_limit_id=reg_limit_id)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_registered_limits(self):
+        self.do_request('create_registered_limits',
+                        expected_status=exceptions.Forbidden,
+                        payload=self.registered_limits())
+
+    def test_identity_update_registered_limit(self):
+        reg_limit_id = self.admin_reglim_client.create_registered_limits(
+            payload=self.registered_limits())['registered_limits'][0]['id']
+        self.addCleanup(
+            self.admin_reglim_client.delete_registered_limit,
+            registered_limit_id=reg_limit_id)
+        updated = {'description': data_utils.arbitrary_string()}
+        self.do_request('update_registered_limit',
+                        expected_status=exceptions.Forbidden,
+                        registered_limit_id=reg_limit_id,
+                        registered_limit=updated)
+
+    def test_identity_delete_registered_limit(self):
+        reg_limit_id = self.admin_reglim_client.create_registered_limits(
+            payload=self.registered_limits())['registered_limits'][0]['id']
+        self.addCleanup(
+            self.admin_reglim_client.delete_registered_limit,
+            registered_limit_id=reg_limit_id)
+        self.do_request('delete_registered_limit',
+                        expected_status=exceptions.Forbidden,
+                        registered_limit_id=reg_limit_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests):
+
+    credentials = ['domain_admin', 'system_admin']
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectMemberTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_role.py b/keystone_tempest_plugin/tests/rbac/v3/test_role.py
new file mode 100644
index 0000000..a9815e8
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_role.py
@@ -0,0 +1,390 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacRoleTest(rbac_base.IdentityV3RbacBaseTests,
+                             metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacRoleTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.roles_v3_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_roles_client = cls.admin_client.roles_v3_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3RbacRoleTest, cls).resource_setup()
+        cls.own_domain = cls.persona.credentials.domain_id
+        cls.domain_id = cls.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name('domain'))['domain']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.delete_domain,
+            cls.domain_id)
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.update_domain,
+            cls.domain_id,
+            enabled=False)
+
+    def role(self, domain_id=None):
+        role = {}
+        name = data_utils.rand_name('role')
+        role['name'] = name
+        if domain_id:
+            role['domain_id'] = domain_id
+        return role
+
+    @abc.abstractmethod
+    def test_identity_create_role(self):
+        """Test identity:create_role policy.
+
+        This test must check:
+          * whether the persona can create a role
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_role(self):
+        """Test identity:get_role policy.
+
+        This test must check:
+          * whether the persona can get a role
+          * whether the persona can get a role that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_roles(self):
+        """Test identity:list_roles policy.
+
+        This test must check:
+          * whether the persona can list roles
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_role(self):
+        """Test identity:update_role policy.
+
+        This test must check:
+          * whether the persona can update a role
+          * whether the persona can update a role that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_role(self):
+        """Test identity:delete_role policy.
+
+        This test must check
+          * whether the persona can delete a role
+          * whether the persona can delete a role that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_create_domain_role(self):
+        """Test identity:create_domain_role policy.
+
+        This test must check:
+          * whether the persona can create a domain role in their own domain
+          * whether the persona can create a domain role in another domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_domain_role(self):
+        """Test identity:get_domain_role policy.
+
+        This test must check:
+          * whether the persona can get a domain role in their own domain
+          * whether the persona can get a domain role in another domain
+          * whether the persona can get a domain role that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_domain_roles(self):
+        """Test identity:list_domain_roles policy.
+
+        This test must check:
+          * whether the persona can list domain roles for their own domain
+          * whether the persona can list domain roles for another domain
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_domain_role(self):
+        """Test identity:update_domain_role policy.
+
+        This test must check:
+          * whether the persona can update a domain role for their own domain
+          * whether the persona can update a domain role for another domain
+          * whether the persona can update a domain role that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_domain_role(self):
+        """Test identity:delete_domain_role policy.
+
+        This test must check
+          * whether the persona can delete a domain role for their own domain
+          * whether the persona can delete a domain role for another domain
+          * whether the persona can delete a domain role that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacRoleTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_role(self):
+        # user can create role
+        resp = self.do_request('create_role',
+                               expected_status=201,
+                               **self.role())
+        self.addCleanup(self.admin_roles_client.delete_role,
+                        resp['role']['id'])
+
+    def test_identity_get_role(self):
+        # user can get role
+        role = self.admin_roles_client.create_role(**self.role())['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('show_role', role_id=role['id'])
+        # user gets a 404 for nonexistent role
+        self.do_request('show_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_roles(self):
+        # user can list roles
+        role = self.admin_roles_client.create_role(**self.role())['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('list_roles')
+
+    def test_identity_update_role(self):
+        # user can update role
+        role = self.admin_roles_client.create_role(**self.role())['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('update_role',
+                        role_id=role['id'],
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent role
+        self.do_request('update_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_role(self):
+        # user can delete role
+        role = self.admin_roles_client.create_role(**self.role())['role']
+        self.do_request('delete_role', expected_status=204, role_id=role['id'])
+        # user gets a 404 for nonexistent role
+        self.do_request('delete_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+    def test_identity_create_domain_role(self):
+        # user can create domain role
+        resp = self.do_request('create_role',
+                               expected_status=201,
+                               **self.role(domain_id=self.domain_id))
+        self.addCleanup(self.admin_roles_client.delete_role,
+                        resp['role']['id'])
+
+    def test_identity_get_domain_role(self):
+        # user can get domain role
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.domain_id))['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('show_role', role_id=role['id'])
+        # user gets a 404 for nonexistent domain role
+        self.do_request('show_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_domain_roles(self):
+        # user can list domain roles
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.domain_id))['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('list_roles', domain_id=self.domain_id)
+
+    def test_identity_update_domain_role(self):
+        # user can update domain role
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.domain_id))['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('update_role',
+                        role_id=role['id'],
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent domain role
+        self.do_request('update_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_domain_role(self):
+        # user can delete role in other domain
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.domain_id))['role']
+        self.do_request('delete_role', expected_status=204, role_id=role['id'])
+        # user gets a 404 for nonexistent role
+        self.do_request('delete_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_role(self):
+        # user cannot create role
+        self.do_request('create_role',
+                        expected_status=exceptions.Forbidden,
+                        **self.role())
+
+    def test_identity_update_role(self):
+        # user cannot update role
+        role = self.admin_roles_client.create_role(
+            **self.role())['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('update_role', expected_status=exceptions.Forbidden,
+                        role_id=role['id'],
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent role
+        self.do_request('update_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_role(self):
+        # user can delete role
+        role = self.admin_roles_client.create_role(
+            **self.role())['role']
+        self.do_request('delete_role', expected_status=exceptions.Forbidden,
+                        role_id=role['id'])
+        # user gets a 404 for nonexistent domain role
+        self.do_request('delete_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+    def test_identity_create_domain_role(self):
+        # user cannot create domain role
+        self.do_request('create_role',
+                        expected_status=exceptions.Forbidden,
+                        **self.role(domain_id=self.domain_id))
+
+    def test_identity_update_domain_role(self):
+        # user cannot update domain role
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.domain_id))['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('update_role', expected_status=exceptions.Forbidden,
+                        role_id=role['id'],
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent domain role
+        self.do_request('update_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_domain_role(self):
+        # user can delete domain role
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.domain_id))['role']
+        self.do_request('delete_role', expected_status=exceptions.Forbidden,
+                        role_id=role['id'])
+        # user gets a 404 for nonexistent domain role
+        self.do_request('delete_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_role(self):
+        # user cannot get role
+        role = self.admin_roles_client.create_role(
+            **self.role())['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('show_role', expected_status=exceptions.Forbidden,
+                        role_id=role['id'])
+        # user gets a 404 for nonexistent role
+        self.do_request('show_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_roles(self):
+        # user cannot list roles
+        role = self.admin_roles_client.create_role(**self.role())['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('list_roles', expected_status=exceptions.Forbidden)
+
+    def test_identity_get_domain_role(self):
+        # user cannot get domain role in own domain
+        role = self.admin_roles_client.create_role(**self.role())['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('show_role', expected_status=exceptions.Forbidden,
+                        role_id=role['id'])
+        # user gets a 404 for nonexistent domain role
+        self.do_request('show_role', expected_status=exceptions.NotFound,
+                        role_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_domain_roles(self):
+        # user cannot list domain roles in own domain
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.own_domain))['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('list_roles', expected_status=exceptions.Forbidden,
+                        domain_id=self.persona.credentials.domain_id)
+        # user cannot get domain role in other domain
+        role = self.admin_roles_client.create_role(
+            **self.role(domain_id=self.domain_id))['role']
+        self.addCleanup(self.admin_roles_client.delete_role, role['id'])
+        self.do_request('list_roles', expected_status=exceptions.Forbidden,
+                        domain_id=self.domain_id)
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_role_assignment.py b/keystone_tempest_plugin/tests/rbac/v3/test_role_assignment.py
new file mode 100644
index 0000000..7b27294
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_role_assignment.py
@@ -0,0 +1,1130 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacAssignmentTest(rbac_base.IdentityV3RbacBaseTests,
+                                   metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacAssignmentTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.role_assignments_client
+        cls.admin_client = cls.os_system_admin
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3RbacAssignmentTest, cls).resource_setup()
+        cls._setup_assignments()
+
+    @classmethod
+    def _setup_assignments(cls):
+        cls.own_domain = cls.persona.credentials.domain_id
+        cls.role_id = cls.admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name('role'))['role']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.roles_v3_client.delete_role, cls.role_id)
+        cls.user_in_domain = cls.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=cls.own_domain)['user']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.users_v3_client.delete_user,
+            cls.user_in_domain)
+        cls.group_in_domain = cls.admin_client.groups_client.create_group(
+            name=data_utils.rand_name('group'),
+            domain_id=cls.own_domain)['group']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.groups_client.delete_group,
+            cls.group_in_domain)
+        cls.project_in_domain = (
+            cls.admin_client.projects_client.create_project(
+                name=data_utils.rand_name('project'),
+                domain_id=cls.own_domain)['project']['id'])
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project,
+            cls.project_in_domain)
+        cls.other_domain = cls.admin_client.domains_client.create_domain(
+            name=data_utils.rand_name('domain'))['domain']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.delete_domain,
+            cls.other_domain)
+        cls.addClassResourceCleanup(
+            cls.admin_client.domains_client.update_domain,
+            cls.other_domain,
+            enabled=False)
+        cls.user_other_domain = cls.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name('user'),
+            domain_id=cls.other_domain)['user']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.users_v3_client.delete_user,
+            cls.user_other_domain)
+        cls.group_other_domain = cls.admin_client.groups_client.create_group(
+            name=data_utils.rand_name('group'),
+            domain_id=cls.other_domain)['group']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.groups_client.delete_group,
+            cls.group_other_domain)
+        cls.project_other_domain = (
+            cls.admin_client.projects_client.create_project(
+                name=data_utils.rand_name('project'),
+                domain_id=cls.other_domain)['project']['id'])
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project,
+            cls.project_other_domain)
+
+        roles_client = cls.admin_client.roles_v3_client
+        roles_client.create_user_role_on_project(
+            cls.project_in_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_project(
+            cls.project_in_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_project(
+            cls.project_other_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_project(
+            cls.project_other_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.own_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.own_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.other_domain,
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_domain(
+            cls.other_domain,
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_system(
+            cls.user_in_domain,
+            cls.role_id)
+        roles_client.create_user_role_on_system(
+            cls.user_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_in_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_in_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_other_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_project(
+            cls.project_other_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.own_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.own_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.other_domain,
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_domain(
+            cls.other_domain,
+            cls.group_other_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_system(
+            cls.group_in_domain,
+            cls.role_id)
+        roles_client.create_group_role_on_system(
+            cls.group_other_domain,
+            cls.role_id)
+
+        cls.assignments = [
+            {
+                'user_id': cls.user_in_domain,
+                'project_id': cls.project_in_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_other_domain,
+                'project_id': cls.project_in_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_in_domain,
+                'project_id': cls.project_other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_other_domain,
+                'project_id': cls.project_other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_in_domain,
+                'domain_id': cls.own_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_other_domain,
+                'domain_id': cls.own_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_in_domain,
+                'domain_id': cls.other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_other_domain,
+                'domain_id': cls.other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_in_domain,
+                'system': 'all',
+                'role_id': cls.role_id
+            },
+            {
+                'user_id': cls.user_other_domain,
+                'system': 'all',
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_in_domain,
+                'project_id': cls.project_in_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_other_domain,
+                'project_id': cls.project_in_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_in_domain,
+                'project_id': cls.project_other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_other_domain,
+                'project_id': cls.project_other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_in_domain,
+                'domain_id': cls.own_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_other_domain,
+                'domain_id': cls.own_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_in_domain,
+                'domain_id': cls.other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_other_domain,
+                'domain_id': cls.other_domain,
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_in_domain,
+                'system': 'all',
+                'role_id': cls.role_id
+            },
+            {
+                'group_id': cls.group_other_domain,
+                'system': 'all',
+                'role_id': cls.role_id
+            },
+        ]
+
+    def _extract_role_assignments_from_response_body(self, r):
+        # Condense the role assignment details into a set of key things we can
+        # use in assertions.
+        assignments = []
+        for assignment in r['role_assignments']:
+            a = {}
+            if 'project' in assignment['scope']:
+                a['project_id'] = assignment['scope']['project']['id']
+            elif 'domain' in assignment['scope']:
+                a['domain_id'] = assignment['scope']['domain']['id']
+            elif 'system' in assignment['scope']:
+                a['system'] = 'all'
+
+            if 'user' in assignment:
+                a['user_id'] = assignment['user']['id']
+            elif 'group' in assignment:
+                a['group_id'] = assignment['group']['id']
+
+            a['role_id'] = assignment['role']['id']
+
+            assignments.append(a)
+        return assignments
+
+    @abc.abstractmethod
+    def test_identity_list_role_assignments(self):
+        """Test identity:list_role_assignments policy.
+
+        This test must check:
+          * whether the persona can list all user and group assignments across
+            the deployment
+          * whether the persona can list all user and group assignments in
+            a domain
+          * whether the persona can list user and group assignments with names
+          * whether the persona can filter user and group assignments by domain
+          * whether the persona can filter user and group assignments by
+            project in their own domain
+          * whether the persona can filter user and group assignments by
+            project in another domain
+          * whether the persona can filter user and group assignments by system
+          * whether the persona can filter user assignments by user in their
+            own domain
+          * whether the persona can filter user assignments by user in another
+            domain
+          * whether the persona can filter group assignments by group in their
+            own domain
+          * whether the persona can filter group assignments by group in
+            another domain
+          * whether the persona can filter role assignments by global role
+          * whether the persona can filter assignments by project and role
+          * whether the persona can filter assignments by domain and role
+          * whether the persona can filter assignments by system and role
+          * whether the persona can filter assignments by user and role
+          * whether the persona can filter assignments by group and role
+          * whether the persona can filter assignments by project and user
+          * whether the persona can filter assignments by project and group
+          * whether the persona can filter assignments by domain and user
+          * whether the persona can filter assignments by domain and group
+
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_role_assignments_for_tree(self):
+        """Test identity:list_role_assignments_for_tree policy.
+
+        This test must check:
+          * whether the persona can list role assignments for a subtree of a
+            project in their own domain
+          * whether the persona can list role assignments for a subtree of a
+            project in another domain
+          * whether the persona can list role assignments for a subtree of a
+            project on which they have a role assignment (if applicable)
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacAssignmentTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_list_role_assignments(self):
+        # Listing all assignments with no filters should return all assignments
+        resp = self.do_request('list_role_assignments')
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in self.assignments:
+            self.assertIn(assignment, actual)
+
+        # Listing all assignments with names
+        query = {'include_names': True}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in self.assignments:
+            self.assertIn(assignment, actual)
+
+        # Filter assignments by own domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.own_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.domain.id': self.own_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by other domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.other_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.domain.id': self.other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by project in own domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.project.id': self.project_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by project in other domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_other_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.project.id': self.project_other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by system should succeed
+        expected = [a for a in self.assignments if a.get('system') == 'all']
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.system': 'all'}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by user in own domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('user_id') == self.user_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by user in other domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('user_id') == self.user_other_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by group in own domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('group_id') == self.group_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by group in other domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('group_id') == self.group_other_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by global role should succeed
+        expected = self.assignments
+        query = {'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by project and role should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.project.id': self.project_in_domain,
+                 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by domain and role should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.other_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.domain.id': self.other_domain, 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by system and role should succeed
+        expected = [a for a in self.assignments if a.get('system') == 'all']
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.system': 'all', 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by user and role should succeed
+        expected = [a for a in self.assignments
+                    if a.get('user_id') == self.user_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain, 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by group and role should succeed
+        expected = [a for a in self.assignments
+                    if a.get('group_id') == self.group_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain, 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by project and user should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain
+                    and a.get('user_id') == self.user_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain,
+                 'scope.project.id': self.project_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by project and group should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain
+                    and a.get('group_id') == self.group_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain,
+                 'scope.project.id': self.project_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by domain and user should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.own_domain
+                    and a.get('user_id') == self.user_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain,
+                 'scope.domain.id': self.own_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by domain and group should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.own_domain
+                    and a.get('group_id') == self.group_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain,
+                 'scope.domain.id': self.own_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+    def test_identity_list_role_assignments_for_tree(self):
+        # Should see subtree assignments for project in own domain
+        subproject_id = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'),
+            domain_id=self.own_domain,
+            parent_id=self.project_in_domain)['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        subproject_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            subproject_id, self.user_in_domain, self.role_id)
+        query = {'scope.project.id': self.project_in_domain,
+                 'include_subtree': True}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        expected_assignment = {'user_id': self.user_in_domain,
+                               'project_id': subproject_id,
+                               'role_id': self.role_id}
+        self.assertIn(expected_assignment, actual)
+
+        # Should see subtree assignments for project in other domain
+        subproject_id = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'),
+            domain_id=self.other_domain,
+            parent_id=self.project_other_domain)['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        subproject_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            subproject_id, self.user_in_domain, self.role_id)
+        query = {'scope.project.id': self.project_other_domain,
+                 'include_subtree': True}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        expected_assignment = {'user_id': self.user_in_domain,
+                               'project_id': subproject_id,
+                               'role_id': self.role_id}
+        self.assertIn(expected_assignment, actual)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacAssignmentTest, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_list_role_assignments(self):
+        # Listing all assignments with no filters should only return
+        # assignments in own domain
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain
+                    or a.get('domain_id') == self.own_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        resp = self.do_request('list_role_assignments')
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Listing all assignments with names and no filters should only return
+        # assignments in own domain
+        query = {'include_names': True}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by own domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.own_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.domain.id': self.own_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by other domain should be empty
+        query = {'scope.domain.id': self.other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        self.assertEmpty(actual)
+
+        # Filter assignments by project in own domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.project.id': self.project_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by project in other domain should be empty
+        query = {'scope.project.id': self.project_other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        self.assertEmpty(actual)
+
+        # Filter assignments by system should be empty
+        query = {'scope.system': 'all'}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        self.assertEmpty(actual)
+
+        # Filter assignments by user in own domain should get assignments for
+        # that user only for projects in own domain or for own domain itself
+        expected = [a for a in self.assignments
+                    if a.get('user_id') == self.user_in_domain
+                    and (a.get('project_id') == self.project_in_domain
+                         or a.get('domain_id') == self.own_domain)]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by user in other domain should still work but only
+        # return assignments for projects in own domain or for own domain
+        # itself
+        expected = [a for a in self.assignments
+                    if a.get('user_id') == self.user_other_domain
+                    and (a.get('project_id') == self.project_in_domain
+                         or a.get('domain_id') == self.own_domain)]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by group in own domain should succeed
+        expected = [a for a in self.assignments
+                    if a.get('group_id') == self.group_in_domain
+                    and (a.get('project_id') == self.project_in_domain
+                         or a.get('domain_id') == self.own_domain)]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by group in other domain should still work but
+        # only return assignments for projects in own domain or for own domain
+        # itself
+        expected = [a for a in self.assignments
+                    if a.get('group_id') == self.group_other_domain
+                    and (a.get('project_id') == self.project_in_domain
+                         or a.get('domain_id') == self.own_domain)]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_other_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by global role should only return role
+        # assignments for own domain
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain
+                    or a.get('domain_id') == self.own_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by project and role should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.project.id': self.project_in_domain,
+                 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by domain and role should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.other_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'scope.domain.id': self.other_domain, 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by system and role should be empty
+        query = {'scope.system': 'all', 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        self.assertEmpty(actual)
+
+        # Filter assignments by user and role should should get assignments for
+        # that user only for projects in own domain or for own domain itself
+        expected = [a for a in self.assignments
+                    if a.get('user_id') == self.user_in_domain
+                    and (a.get('project_id') == self.project_in_domain
+                         or a.get('domain_id') == self.own_domain)]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain, 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by group and role should get assignments for
+        # that group only for projects in own domain or for own domain itself
+        expected = [a for a in self.assignments
+                    if a.get('group_id') == self.group_in_domain
+                    and (a.get('project_id') == self.project_in_domain
+                         or a.get('domain_id') == self.own_domain)]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain, 'role.id': self.role_id}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+        # Reverse the check: only ephemeral tempest roles should be in the list
+        for assignment in actual:
+            self.assertIn(assignment, expected)
+
+        # Filter assignments by project and user should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain
+                    and a.get('user_id') == self.user_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain,
+                 'scope.project.id': self.project_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by project and group should succeed
+        expected = [a for a in self.assignments
+                    if a.get('project_id') == self.project_in_domain
+                    and a.get('group_id') == self.group_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain,
+                 'scope.project.id': self.project_in_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by domain and user should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.own_domain
+                    and a.get('user_id') == self.user_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'user.id': self.user_in_domain,
+                 'scope.domain.id': self.own_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+        # Filter assignments by domain and group should succeed
+        expected = [a for a in self.assignments
+                    if a.get('domain_id') == self.own_domain
+                    and a.get('group_id') == self.group_in_domain]
+        not_expected = [a for a in self.assignments if a not in expected]
+        query = {'group.id': self.group_in_domain,
+                 'scope.domain.id': self.own_domain}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        for assignment in expected:
+            self.assertIn(assignment, actual)
+        for assignment in not_expected:
+            self.assertNotIn(assignment, actual)
+
+    def test_identity_list_role_assignments_for_tree(self):
+        # Should see subtree assignments for project in own domain
+        subproject_id = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'),
+            domain_id=self.own_domain,
+            parent_id=self.project_in_domain)['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        subproject_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            subproject_id, self.user_in_domain, self.role_id)
+        query = {'scope.project.id': self.project_in_domain,
+                 'include_subtree': True}
+        resp = self.do_request('list_role_assignments', **query)
+        actual = self._extract_role_assignments_from_response_body(resp)
+        expected_assignment = {'user_id': self.user_in_domain,
+                               'project_id': subproject_id,
+                               'role_id': self.role_id}
+        self.assertIn(expected_assignment, actual)
+
+        # Should not see subtree assignments for project in other domain
+        query = {'scope.project.id': self.project_other_domain,
+                 'include_subtree': True}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacAssignmentTest, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_identity_list_role_assignments(self):
+        # Listing all assignments with no filters should fail
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden)
+
+        # Listing all assignments with names and no filters should fail
+        query = {'include_names': True}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by own domain should fail
+        query = {'scope.domain.id': self.own_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by other domain should fail
+        query = {'scope.domain.id': self.other_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by project in own domain should fail
+        query = {'scope.project.id': self.project_in_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by project in other domain should fail
+        query = {'scope.project.id': self.project_other_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by system should fail
+        query = {'scope.system': 'all'}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by user in own domain should fail
+        query = {'user.id': self.user_in_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by user in other domain should fail
+        query = {'user.id': self.user_other_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by group in own domain should fail
+        query = {'group.id': self.group_in_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by group in other domain should fail
+        query = {'group.id': self.group_other_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by global role should fail
+        query = {'role.id': self.role_id}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by project and role should fail
+        query = {'scope.project.id': self.project_in_domain,
+                 'role.id': self.role_id}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by domain and role should fail
+        query = {'scope.domain.id': self.other_domain, 'role.id': self.role_id}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by system and role should fail
+        query = {'scope.system': 'all', 'role.id': self.role_id}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by user and role should should fail
+        query = {'user.id': self.user_in_domain, 'role.id': self.role_id}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by group and role should fail
+        query = {'group.id': self.group_in_domain, 'role.id': self.role_id}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by project and user should fail
+        query = {'user.id': self.user_in_domain,
+                 'scope.project.id': self.project_in_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by project and group should fail
+        query = {'group.id': self.group_in_domain,
+                 'scope.project.id': self.project_in_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by domain and user should fail
+        query = {'user.id': self.user_in_domain,
+                 'scope.domain.id': self.own_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Filter assignments by domain and group should fail
+        query = {'group.id': self.group_in_domain,
+                 'scope.domain.id': self.own_domain}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+    def test_identity_list_role_assignments_for_tree(self):
+        # Should not see subtree assignments for project in own domain
+        query = {'scope.project.id': self.project_in_domain,
+                 'include_subtree': True}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Should not see subtree assignments for project in other domain
+        query = {'scope.project.id': self.project_other_domain,
+                 'include_subtree': True}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Should see subtree for own project
+        own_project = self.persona.credentials.project_id
+        subproject_id = self.admin_client.projects_client.create_project(
+            name=data_utils.rand_name('project'),
+            domain_id=self.own_domain,
+            parent_id=own_project)['project']['id']
+        self.addCleanup(self.admin_client.projects_client.delete_project,
+                        subproject_id)
+        self.admin_client.roles_v3_client.create_user_role_on_project(
+            subproject_id, self.user_other_domain, self.role_id)
+        query = {'scope.project.id': own_project,
+                 'include_subtree': True}
+        resp = self.do_request('list_role_assignments', **query)
+        expected_assignment = {'user_id': self.user_other_domain,
+                               'project_id': subproject_id,
+                               'role_id': self.role_id}
+        actual = self._extract_role_assignments_from_response_body(resp)
+        self.assertIn(expected_assignment, actual)
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+    def test_identity_list_role_assignments_for_tree(self):
+        # Should not see subtree assignments for project in own domain
+        query = {'scope.project.id': self.project_in_domain,
+                 'include_subtree': True}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Should not see subtree assignments for project in other domain
+        query = {'scope.project.id': self.project_other_domain,
+                 'include_subtree': True}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+        # Should not see subtree for own project
+        own_project = self.persona.credentials.project_id
+        query = {'scope.project.id': own_project,
+                 'include_subtree': True}
+        self.do_request('list_role_assignments',
+                        expected_status=exceptions.Forbidden, **query)
+
+
+class ProjectReaderTests(ProjectMemberTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_service.py b/keystone_tempest_plugin/tests/rbac/v3/test_service.py
new file mode 100644
index 0000000..a6a9083
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_service.py
@@ -0,0 +1,233 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacServiceTests(rbac_base.IdentityV3RbacBaseTests,
+                                 metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacServiceTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.identity_services_v3_client
+        admin_client = cls.os_system_admin
+        cls.admin_services_client = admin_client.identity_services_v3_client
+
+    def service(self):
+        return {
+            'name': data_utils.rand_name('service_name'),
+            'type': data_utils.rand_name('service_type'),
+        }
+
+    @abc.abstractmethod
+    def test_identity_create_service(self):
+        """Test identity:create_service policy.
+
+        This test must check:
+          * whether the persona can create a service
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_service(self):
+        """Test identity:get_service policy.
+
+        This test must check:
+          * whether the persona can get a service
+          * whether the persona can get a service that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_services(self):
+        """Test identity:list_services policy.
+
+        This test must check:
+          * whether the persona can list all services
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_service(self):
+        """Test identity:update_service policy.
+
+        This test must check:
+          * whether the persona can update a service
+          * whether the persona can update a service that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_service(self):
+        """Test identity:delete_service policy.
+
+        This test must check
+          * whether the persona can delete a service
+          * whether the persona can delete a service that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacServiceTests, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_service(self):
+        service_id = self.do_request(
+            'create_service', expected_status=201,
+            **self.service())['service']['id']
+        self.addCleanup(
+            self.admin_services_client.delete_service,
+            service_id=service_id)
+
+    def test_identity_get_service(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.addCleanup(
+            self.admin_services_client.delete_service,
+            service_id=service_id)
+        self.do_request('show_service', service_id=service_id)
+        # user gets a 404 for nonexistent service
+        self.do_request('show_service', expected_status=exceptions.NotFound,
+                        service_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_services(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.addCleanup(
+            self.admin_services_client.delete_service,
+            service_id=service_id)
+        resp = self.do_request('list_services')
+        self.assertIn(service_id, [e['id'] for e in resp['services']])
+
+    def test_identity_update_service(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.addCleanup(
+            self.admin_services_client.delete_service,
+            service_id=service_id)
+        self.do_request('update_service',
+                        service_id=service_id,
+                        type=data_utils.rand_name('service_type'))
+        # user gets a 404 for nonexistent service
+        self.do_request('update_service', expected_status=exceptions.NotFound,
+                        service_id=data_utils.rand_uuid_hex(),
+                        type=data_utils.rand_name('service_type'))
+
+    def test_identity_delete_service(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.do_request('delete_service', expected_status=204,
+                        service_id=service_id)
+        # user gets a 404 for nonexistent service
+        self.do_request('delete_service', expected_status=exceptions.NotFound,
+                        service_id=data_utils.rand_uuid_hex())
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_service(self):
+        self.do_request(
+            'create_service', expected_status=exceptions.Forbidden,
+            **self.service())
+
+    def test_identity_update_service(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.addCleanup(
+            self.admin_services_client.delete_service,
+            service_id=service_id)
+        self.do_request('update_service',
+                        expected_status=exceptions.Forbidden,
+                        service_id=service_id,
+                        type=data_utils.rand_name('service_type'))
+        # user gets a 403 for nonexistent service
+        self.do_request('update_service', expected_status=exceptions.Forbidden,
+                        service_id=data_utils.rand_uuid_hex(),
+                        type=data_utils.rand_name('service_type'))
+
+    def test_identity_delete_service(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.do_request('delete_service',
+                        expected_status=exceptions.Forbidden,
+                        service_id=service_id)
+        # user gets a 403 for nonexistent service
+        self.do_request('delete_service', expected_status=exceptions.Forbidden,
+                        service_id=data_utils.rand_uuid_hex())
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_service(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.addCleanup(
+            self.admin_services_client.delete_service,
+            service_id=service_id)
+        self.do_request('show_service', expected_status=exceptions.Forbidden,
+                        service_id=service_id)
+        # user gets a 403 for nonexistent service
+        self.do_request('show_service', expected_status=exceptions.Forbidden,
+                        service_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_services(self):
+        service_id = self.admin_services_client.create_service(
+            **self.service())['service']['id']
+        self.addCleanup(
+            self.admin_services_client.delete_service,
+            service_id=service_id)
+        self.do_request('list_services', expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_service_provider.py b/keystone_tempest_plugin/tests/rbac/v3/test_service_provider.py
new file mode 100644
index 0000000..6cf54b9
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_service_provider.py
@@ -0,0 +1,250 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin import clients
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacServiceProviderTests(rbac_base.IdentityV3RbacBaseTests,
+                                         metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacServiceProviderTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        persona_mgr = clients.Manager(cls.persona.credentials)
+        cls.client = persona_mgr.service_providers_client
+        admin_client = cls.os_system_admin
+        admin_mgr = clients.Manager(admin_client.credentials)
+        cls.admin_sp_client = admin_mgr.service_providers_client
+
+    @abc.abstractmethod
+    def test_identity_create_service_provider(self):
+        """Test identity:create_service_provider policy.
+
+        This test must check:
+          * whether the persona can create a service provider
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_service_provider(self):
+        """Test identity:get_service_provider policy.
+
+        This test must check:
+          * whether the persona can get a service provider
+          * whether the persona can get a service provider that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_service_providers(self):
+        """Test identity:list_service_providers policy.
+
+        This test must check:
+          * whether the persona can list all identity providers
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_service_provider(self):
+        """Test identity:update_service_provider policy.
+
+        This test must check:
+          * whether the persona can update a service provider
+          * whether the persona can update a service provider that does not
+            exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_service_provider(self):
+        """Test identity:delete_service_provider policy.
+
+        This test must check
+          * whether the persona can delete a service provider
+          * whether the persona can delete a service provider that does not
+            exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacServiceProviderTests,
+                       base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_service_provider(self):
+        sp_id = self.do_request(
+            'create_service_provider', expected_status=201,
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url()
+        )['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+
+    def test_identity_get_service_provider(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+        self.do_request('show_service_provider', sp_id=sp_id)
+        # user gets a 404 for nonexistent sp
+        self.do_request('show_service_provider',
+                        expected_status=exceptions.NotFound,
+                        sp_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_service_providers(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+        resp = self.do_request('list_service_providers')
+        self.assertIn(sp_id, [i['id'] for i in resp['service_providers']])
+
+    def test_identity_update_service_provider(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+        self.do_request('update_service_provider',
+                        sp_id=sp_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 404 for nonexistent sp
+        self.do_request('update_service_provider',
+                        expected_status=exceptions.NotFound,
+                        sp_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_service_provider(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.do_request('delete_service_provider', expected_status=204,
+                        sp_id=sp_id)
+        # user gets a 404 for nonexistent sp
+        self.do_request('delete_service_provider',
+                        expected_status=exceptions.NotFound,
+                        sp_id=sp_id)
+
+
+class SystemMemberTests(SystemAdminTests, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_service_provider(self):
+        self.do_request('create_service_provider',
+                        expected_status=exceptions.Forbidden,
+                        sp_id=data_utils.rand_name(),
+                        auth_url=data_utils.rand_url(),
+                        sp_url=data_utils.rand_url())
+
+    def test_identity_update_service_provider(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+        self.do_request('update_service_provider',
+                        expected_status=exceptions.Forbidden,
+                        sp_id=sp_id,
+                        description=data_utils.arbitrary_string())
+        # user gets a 403 for nonexistent sp
+        self.do_request('update_service_provider',
+                        expected_status=exceptions.Forbidden,
+                        sp_id=data_utils.rand_uuid_hex(),
+                        description=data_utils.arbitrary_string())
+
+    def test_identity_delete_service_provider(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+        self.do_request('delete_service_provider',
+                        expected_status=exceptions.Forbidden,
+                        sp_id=sp_id)
+        # user gets a 403 for nonexistent sp
+        self.do_request('delete_service_provider',
+                        expected_status=exceptions.Forbidden,
+                        sp_id=sp_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_service_provider(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+        self.do_request('show_service_provider',
+                        expected_status=exceptions.Forbidden,
+                        sp_id=sp_id)
+        # user gets a 403 for nonexistent sp
+        self.do_request('show_service_provider',
+                        expected_status=exceptions.Forbidden,
+                        sp_id=data_utils.rand_uuid_hex())
+
+    def test_identity_list_service_providers(self):
+        sp_id = self.admin_sp_client.create_service_provider(
+            sp_id=data_utils.rand_name(),
+            auth_url=data_utils.rand_url(),
+            sp_url=data_utils.rand_url())['service_provider']['id']
+        self.addCleanup(self.admin_sp_client.delete_service_provider, sp_id)
+        self.do_request('list_service_providers',
+                        expected_status=exceptions.Forbidden)
+
+
+class DomainMemberTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainReaderTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_token.py b/keystone_tempest_plugin/tests/rbac/v3/test_token.py
new file mode 100644
index 0000000..e5d10ed
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_token.py
@@ -0,0 +1,309 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest import clients
+from tempest.lib import auth
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacTokenTest(rbac_base.IdentityV3RbacBaseTests,
+                              metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacTokenTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.identity_v3_client
+
+    @classmethod
+    def resource_setup(cls):
+        admin_client = cls.os_system_admin
+        cls.user = {
+            'name': data_utils.rand_name('user'),
+            'password': data_utils.rand_password(),
+        }
+        cls.user_id = admin_client.users_v3_client.create_user(
+            **cls.user)['user']['id']
+        cls.addClassResourceCleanup(
+            admin_client.users_v3_client.delete_user, user_id=cls.user_id)
+        cls.project_id = admin_client.projects_client.create_project(
+            name=data_utils.rand_name()
+        )['project']['id']
+        cls.addClassResourceCleanup(
+            admin_client.projects_client.delete_project,
+            project_id=cls.project_id)
+        cls.domain_id = admin_client.domains_client.create_domain(
+            name=data_utils.rand_name()
+        )['domain']['id']
+        cls.addClassResourceCleanup(
+            admin_client.domains_client.delete_domain,
+            domain_id=cls.domain_id)
+        cls.addClassResourceCleanup(
+            admin_client.domains_client.update_domain,
+            domain_id=cls.domain_id, enabled=False)
+        role_id = admin_client.roles_v3_client.create_role(
+            name=data_utils.rand_name())['role']['id']
+        cls.addClassResourceCleanup(
+            admin_client.roles_v3_client.delete_role,
+            role_id=role_id)
+        admin_client.roles_v3_client.create_user_role_on_project(
+            project_id=cls.project_id,
+            user_id=cls.user_id,
+            role_id=role_id
+        )
+        admin_client.roles_v3_client.create_user_role_on_domain(
+            domain_id=cls.domain_id,
+            user_id=cls.user_id,
+            role_id=role_id
+        )
+        admin_client.roles_v3_client.create_user_role_on_system(
+            user_id=cls.user_id,
+            role_id=role_id
+        )
+
+    def setUp(self):
+        super(IdentityV3RbacTokenTest, self).setUp()
+        own_creds = auth.KeystoneV3Credentials(**self.own_keystone_creds)
+        own_creds = clients.get_auth_provider(own_creds).fill_credentials()
+        self.own_token = clients.Manager(
+            credentials=own_creds).identity_v3_client.token
+        project_creds = auth.KeystoneV3Credentials(
+            user_id=self.user_id,
+            password=self.user['password'],
+            project_id=self.project_id)
+        project_creds = clients.get_auth_provider(
+            project_creds).fill_credentials()
+        self.project_token = clients.Manager(
+            credentials=project_creds).identity_v3_client.token
+        domain_creds = auth.KeystoneV3Credentials(
+            user_id=self.user_id,
+            password=self.user['password'],
+            domain_id=self.domain_id)
+        domain_creds = clients.get_auth_provider(
+            domain_creds).fill_credentials()
+        self.domain_token = clients.Manager(
+            credentials=domain_creds).identity_v3_client.token
+        system_creds = auth.KeystoneV3Credentials(
+            user_id=self.user_id,
+            password=self.user['password'],
+            system='all')
+        system_creds = clients.get_auth_provider(
+            system_creds).fill_credentials()
+        self.system_token = clients.Manager(
+            credentials=system_creds).identity_v3_client.token
+
+    @abc.abstractmethod
+    def test_identity_check_token(self):
+        """Test identity:check_token policy.
+
+        This test must check:
+          * whether the persona can check their own token in their current
+            scope
+          * whether the persona can check a system-scoped token for a different
+            user
+          * whether the persona can check a domain-scoped token for a different
+            user
+          * whether the persona can check a project-scoped token for a
+            different user
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_validate_token(self):
+        """Test identity:validate_token policy.
+
+        This test must validate:
+          * whether the persona can validate their own token in their current
+            scope
+          * whether the persona can validate a system-scoped token for a
+            different user
+          * whether the persona can validate a domain-scoped token for a
+            different user
+          * whether the persona can validate a project-scoped token for a
+            different user
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_revoke_token(self):
+        """Test identity:revoke_token policy.
+
+        This test must revoke:
+          * whether the persona can revoke their own token in their current
+            scope
+          * whether the persona can revoke a system-scoped token for a
+            different user
+          * whether the persona can revoke a domain-scoped token for a
+            different user
+          * whether the persona can revoke a project-scoped token for a
+            different user
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacTokenTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def setUp(self):
+        self.own_keystone_creds = {
+            'user_id': self.persona.credentials.user_id,
+            'password': self.persona.credentials.password,
+            'system': 'all'
+        }
+        super(SystemAdminTests, self).setUp()
+
+    def test_identity_check_token(self):
+        # user can check own token
+        self.do_request('check_token_existence', resp_token=self.own_token)
+        # user can check other system user's token
+        self.do_request('check_token_existence', resp_token=self.system_token)
+        # user can check domain user's token
+        self.do_request('check_token_existence', resp_token=self.domain_token)
+        # user can check project user's token
+        self.do_request('check_token_existence', resp_token=self.project_token)
+
+    def test_identity_validate_token(self):
+        # user can validate own token
+        self.do_request('show_token', resp_token=self.own_token)
+        # user can validate other system user's token
+        self.do_request('show_token', resp_token=self.system_token)
+        # user can validate domain user's token
+        self.do_request('show_token', resp_token=self.domain_token)
+        # user can validate project user's token
+        self.do_request('show_token', resp_token=self.project_token)
+
+    def test_identity_revoke_token(self):
+        # user can revoke own token
+        self.do_request('delete_token', expected_status=204,
+                        resp_token=self.own_token)
+        # user can revoke other system user's token
+        self.do_request('delete_token', expected_status=204,
+                        resp_token=self.system_token)
+        # user can revoke domain user's token
+        self.do_request('delete_token', expected_status=204,
+                        resp_token=self.domain_token)
+        # user can revoke project user's token
+        self.do_request('delete_token', expected_status=204,
+                        resp_token=self.project_token)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_revoke_token(self):
+        # user can revoke own token
+        self.do_request('delete_token', expected_status=204,
+                        resp_token=self.own_token)
+        # user cannot revoke other system user's token
+        self.do_request('delete_token', expected_status=exceptions.Forbidden,
+                        resp_token=self.system_token)
+        # user cannot revoke domain user's token
+        self.do_request('delete_token', expected_status=exceptions.Forbidden,
+                        resp_token=self.domain_token)
+        # user cannot revoke project user's token
+        self.do_request('delete_token', expected_status=exceptions.Forbidden,
+                        resp_token=self.project_token)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def setUp(self):
+        self.own_keystone_creds = {
+            'user_id': self.persona.credentials.user_id,
+            'password': self.persona.credentials.password,
+            'domain_id': self.persona.credentials.domain_id
+        }
+        # call base setUp directly to ensure we don't use system creds
+        super(SystemAdminTests, self).setUp()
+
+    def test_identity_check_token(self):
+        # user can check own token
+        self.do_request('check_token_existence', resp_token=self.own_token)
+        # user cannot check other system user's token
+        self.do_request('check_token_existence',
+                        expected_status=exceptions.Forbidden,
+                        resp_token=self.system_token)
+        # user cannot check domain user's token
+        self.do_request('check_token_existence',
+                        expected_status=exceptions.Forbidden,
+                        resp_token=self.domain_token)
+        # user cannot check project user's token
+        self.do_request('check_token_existence',
+                        expected_status=exceptions.Forbidden,
+                        resp_token=self.project_token)
+
+    def test_identity_validate_token(self):
+        # user can validate own token
+        self.do_request('show_token', resp_token=self.own_token)
+        # user cannot validate other system user's token
+        self.do_request('show_token',
+                        expected_status=exceptions.Forbidden,
+                        resp_token=self.system_token)
+        # user cannot validate domain user's token
+        self.do_request('show_token',
+                        expected_status=exceptions.Forbidden,
+                        resp_token=self.domain_token)
+        # user cannot validate project user's token
+        self.do_request('show_token',
+                        expected_status=exceptions.Forbidden,
+                        resp_token=self.project_token)
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainAdminTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(DomainAdminTests, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def setUp(self):
+        self.own_keystone_creds = {
+            'user_id': self.persona.credentials.user_id,
+            'password': self.persona.credentials.password,
+            'project_id': self.persona.credentials.project_id
+        }
+        # call base setUp directly to ensure we don't use system creds
+        super(SystemAdminTests, self).setUp()
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_trust.py b/keystone_tempest_plugin/tests/rbac/v3/test_trust.py
new file mode 100644
index 0000000..c30f9eb
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_trust.py
@@ -0,0 +1,559 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest import clients
+from tempest.lib import auth
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacTrustTest(rbac_base.IdentityV3RbacBaseTests,
+                              metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacTrustTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.trusts_client
+        cls.admin_client = cls.os_system_admin
+        cls.admin_trusts_client = cls.admin_client.trusts_client
+
+    @classmethod
+    def resource_setup(cls):
+        trustor_user = {
+            'name': data_utils.rand_name('user'),
+            'password': data_utils.rand_password(),
+        }
+        cls.trustor = cls.admin_client.users_v3_client.create_user(
+            **trustor_user)['user']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.users_v3_client.delete_user,
+            user_id=cls.trustor)
+        cls.trustee = cls.admin_client.users_v3_client.create_user(
+            name=data_utils.rand_name())['user']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.users_v3_client.delete_user,
+            user_id=cls.trustee)
+        cls.project = cls.admin_client.projects_client.create_project(
+            name=data_utils.rand_name()
+        )['project']['id']
+        cls.addClassResourceCleanup(
+            cls.admin_client.projects_client.delete_project,
+            project_id=cls.project)
+        cls.roles = [
+            {'id': cls.admin_client.roles_v3_client.create_role(
+                name=data_utils.rand_name())['role']['id']}
+        ]
+        cls.addClassResourceCleanup(
+            cls.admin_client.roles_v3_client.delete_role,
+            role_id=cls.roles[0]['id'])
+        cls.admin_client.roles_v3_client.create_user_role_on_project(
+            project_id=cls.project,
+            user_id=cls.trustor,
+            role_id=cls.roles[0]['id']
+        )
+        creds = auth.KeystoneV3Credentials(
+            user_id=cls.trustor,
+            password=trustor_user['password'],
+            project_id=cls.project)
+        auth_provider = clients.get_auth_provider(creds)
+        creds = auth_provider.fill_credentials()
+        user_client = clients.Manager(credentials=creds)
+        cls.user_trust_client = user_client.trusts_client
+
+        cls.admin_role_id = cls.admin_client.roles_v3_client.list_roles(
+            name='admin')['roles'][0]['id']
+        cls.member_role_id = cls.admin_client.roles_v3_client.list_roles(
+            name='member')['roles'][0]['id']
+        cls.reader_role_id = cls.admin_client.roles_v3_client.list_roles(
+            name='reader')['roles'][0]['id']
+
+    def trust(self, trustor=None, trustee=None, project_id=None, roles=None):
+        trust = {}
+        trust['trustor_user_id'] = trustor or self.trustor
+        trust['trustee_user_id'] = trustee or self.trustee
+        trust['project_id'] = project_id or self.project
+        trust['roles'] = roles or self.roles
+        trust['impersonation'] = False
+        return trust
+
+    @abc.abstractmethod
+    def test_identity_create_trust(self):
+        """Test identity:create_trust policy.
+
+        This test must check:
+          * whether the persona can create a trust for themself
+          * whether the persona can create a trust for another user
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_trust(self):
+        """Test identity:get_trust policy.
+
+        This test must check:
+          * whether the persona can get a trust for which they are the trustor
+          * whether the persona can get a trust for which they are the trustee
+          * whether the persona can get a trust with which they are
+            unaffiliated
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_trusts(self):
+        """Test identity:list_trusts policy.
+
+        This test must check:
+          * whether the persona can list all trusts
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_trusts_for_trustor(self):
+        """Test identity:list_trusts_for_trustor policy.
+
+        This test must check:
+          * whether the persona can list trusts by trustor for which they are
+            the trustor
+          * whether the persona can list trusts by trustor for which another
+            user is trustor
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_trusts_for_trustee(self):
+        """Test identity:list_trusts_for_trustee policy.
+
+        This test must check:
+          * whether the persona can list trusts by trustee for which they are
+            the trustee
+          * whether the persona can list trusts by trustee for which another
+            user is trustee
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_roles_for_trust(self):
+        """Test identity:list_roles_for_trust policy.
+
+        This test must check:
+          * whether the persona can list the roles of a trust for which they
+            are the trustee
+          * whether the persona can list the roles of a trust for which they
+            are the trustor
+          * whether the persona can list the roles of a trust with which they
+            are unaffiliated
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_role_for_trust(self):
+        """Test identity:get_role_for_trust policy.
+
+        This test must check:
+          * whether the persona can get a role of a trust for which they are
+            the trustee
+          * whether the persona can get a role of a trust for which they are
+            the trustor
+          * whether the persona can get a role of a trust with which they are
+            unaffiliated
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_trust(self):
+        """Test identity:delete_trust policy.
+
+        This test must check
+          * whether the persona can delete a trust for which they are the
+            trustor
+          * whether the persona can delete a trust for which they are the
+            trustee
+          * whether the persona can delete a trust which which they are
+            unaffiliated
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacTrustTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_trust(self):
+        # user cannot create trust for themself
+        self.do_request('create_trust',
+                        expected_status=exceptions.Forbidden,
+                        **self.trust(trustor=self.persona.credentials.user_id))
+        # user cannot create trust for another user
+        self.do_request('create_trust',
+                        expected_status=exceptions.Forbidden,
+                        **self.trust())
+
+    def test_identity_get_trust(self):
+        # user cannot have their own trust
+        # user can get trust for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('show_trust', trust_id=trust_id)
+
+    def test_identity_list_trusts(self):
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        resp = self.do_request('list_trusts')
+        self.assertIn(trust_id, [t['id'] for t in resp['trusts']])
+
+    def test_identity_list_trusts_for_trustor(self):
+        # user cannot have their own trust
+        # user can list trusts for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('list_trusts', trustor_user_id=self.trustor)
+
+    def test_identity_list_trusts_for_trustee(self):
+        # user cannot have their own trust
+        # user can list trusts for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('list_trusts', trustee_user_id=self.trustee)
+
+    def test_identity_list_roles_for_trust(self):
+        # user cannot have their own trust
+        # user can list roles of trust for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        resp = self.do_request('list_trust_roles', trust_id=trust_id)
+        self.assertIn(self.roles[0]['id'], [r['id'] for r in resp['roles']])
+
+    def test_identity_get_role_for_trust(self):
+        # user cannot have their own trust
+        # user can get role of trust for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('show_trust_role',
+                        trust_id=trust_id, role_id=self.roles[0]['id'])
+
+    def test_identity_delete_trust(self):
+        # user cannot have their own trust
+        # user can delete a user's trust
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.do_request('delete_trust', expected_status=204, trust_id=trust_id)
+
+
+class SystemMemberTests(SystemAdminTests):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_delete_trust(self):
+        # system user cannot have their own trust
+        # user cannot delete another user's trust
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('delete_trust', expected_status=exceptions.Forbidden,
+                        trust_id=trust_id)
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(SystemReaderTests, base.BaseIdentityTest):
+
+    # Domain admins cannot create their own trusts (trusts can only be
+    # scoped to projects) and domain admins have no special privileges over the
+    # trusts own by users in their domains.
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def test_identity_get_trust(self):
+        # user cannot have their own trust
+        # user can get trust for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('show_trust', expected_status=exceptions.Forbidden,
+                        trust_id=trust_id)
+
+    def test_identity_list_trusts(self):
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('list_trusts',
+                        expected_status=exceptions.Forbidden)
+
+    def test_identity_list_trusts_for_trustor(self):
+        # user cannot have their own trust
+        # user can list trusts for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('list_trusts', expected_status=exceptions.Forbidden,
+                        trustor_user_id=self.trustor)
+
+    def test_identity_list_trusts_for_trustee(self):
+        # user cannot have their own trust
+        # user can list trusts for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('list_trusts', expected_status=exceptions.Forbidden,
+                        trustee_user_id=self.trustee)
+
+    def test_identity_list_roles_for_trust(self):
+        # user cannot have their own trust
+        # user can list roles of trust for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('list_trust_roles',
+                        expected_status=exceptions.Forbidden,
+                        trust_id=trust_id)
+
+    def test_identity_get_role_for_trust(self):
+        # user cannot have their own trust
+        # user can get role of trust for other user
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('show_trust_role',
+                        expected_status=exceptions.Forbidden,
+                        trust_id=trust_id, role_id=self.roles[0]['id'])
+
+
+class DomainMemberTests(DomainAdminTests):
+
+    credentials = ['domain_member', 'system_admin']
+
+
+class DomainReaderTests(DomainAdminTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacTrustTest, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def setUp(self):
+        super(ProjectAdminTests, self).setUp()
+        self.role_id = self.admin_role_id
+
+    def test_identity_create_trust(self):
+        # user can create a trust for their own project
+        trustor_user_id = self.persona.credentials.user_id
+        project_id = self.persona.credentials.project_id
+        resp = self.do_request(
+            'create_trust',
+            expected_status=201,
+            **self.trust(
+                trustor=trustor_user_id,
+                project_id=project_id,
+                roles=[{'id': self.role_id}])
+        )['trust']
+        self.addCleanup(self.client.delete_trust, resp['id'])
+
+        # user cannot create trust with another user as trustor
+        self.do_request(
+            'create_trust',
+            expected_status=exceptions.Forbidden,
+            **self.trust())
+
+    def test_identity_get_trust(self):
+        # user can get a trust for which they are trustor
+        trustor_user_id = self.persona.credentials.user_id
+        project_id = self.persona.credentials.project_id
+        trust_id = self.client.create_trust(
+            **self.trust(trustor=trustor_user_id,
+                         project_id=project_id,
+                         roles=[{'id': self.role_id}]))['trust']['id']
+        self.addCleanup(self.client.delete_trust, trust_id=trust_id)
+        self.do_request('show_trust', trust_id=trust_id)
+
+        # user can get a trust for which they are trustee
+        trustee_user_id = self.persona.credentials.user_id
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust(trustee=trustee_user_id))['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('show_trust', trust_id=trust_id)
+
+        # user cannot get a trust with which they are unaffiliated
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('show_trust', expected_status=exceptions.Forbidden,
+                        trust_id=trust_id)
+
+    def test_identity_list_trusts(self):
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.admin_trusts_client.delete_trust,
+                        trust_id=trust_id)
+        self.do_request('list_trusts',
+                        expected_status=exceptions.Forbidden)
+
+    def test_identity_list_trusts_for_trustor(self):
+        # user can list their own trusts
+        trustor_user_id = self.persona.credentials.user_id
+        project_id = self.persona.credentials.project_id
+        trust_id = self.client.create_trust(
+            **self.trust(trustor=trustor_user_id,
+                         project_id=project_id,
+                         roles=[{'id': self.role_id}]))['trust']['id']
+        self.addCleanup(self.client.delete_trust, trust_id=trust_id)
+        self.do_request('list_trusts', trustor_user_id=trustor_user_id)
+
+        # user cannot list another user's trusts
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('list_trusts', expected_status=exceptions.Forbidden,
+                        trustor_user_id=self.trustor)
+
+    def test_identity_list_trusts_for_trustee(self):
+        # user can list their own trusts
+        trustee_user_id = self.persona.credentials.user_id
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust(trustee=trustee_user_id))['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('list_trusts', trustee_user_id=trustee_user_id)
+
+        # user cannot list another user's trusts
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('list_trusts', expected_status=exceptions.Forbidden,
+                        trustee_user_id=self.trustee)
+
+    def test_identity_list_roles_for_trust(self):
+        # user can list roles for trust for which they are trustor
+        trustor_user_id = self.persona.credentials.user_id
+        project_id = self.persona.credentials.project_id
+        trust_id = self.client.create_trust(
+            **self.trust(trustor=trustor_user_id,
+                         project_id=project_id,
+                         roles=[{'id': self.role_id}]))['trust']['id']
+        self.addCleanup(self.client.delete_trust, trust_id=trust_id)
+        self.do_request('list_trust_roles', trust_id=trust_id)
+
+        # user can list roles for trust for which they are trustee
+        trustee_user_id = self.persona.credentials.user_id
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust(trustee=trustee_user_id))['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('list_trust_roles', trust_id=trust_id)
+
+        # user cannot list roles for trust with which they are unaffiliated
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('list_trust_roles',
+                        expected_status=exceptions.Forbidden,
+                        trust_id=trust_id)
+
+    def test_identity_get_role_for_trust(self):
+        # user can get roles for trust for which they are trustor
+        trustor_user_id = self.persona.credentials.user_id
+        project_id = self.persona.credentials.project_id
+        trust_id = self.client.create_trust(
+            **self.trust(trustor=trustor_user_id,
+                         project_id=project_id,
+                         roles=[{'id': self.role_id}]))['trust']['id']
+        self.addCleanup(self.client.delete_trust, trust_id=trust_id)
+        self.do_request('show_trust_role',
+                        trust_id=trust_id, role_id=self.role_id)
+
+        # user can list roles for trust for which they are trustee
+        trustee_user_id = self.persona.credentials.user_id
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust(trustee=trustee_user_id))['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('show_trust_role',
+                        trust_id=trust_id, role_id=self.roles[0]['id'])
+
+        # user cannot list roles for trust with which they are unaffiliated
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('show_trust_role',
+                        expected_status=exceptions.Forbidden,
+                        trust_id=trust_id, role_id=self.role_id)
+
+    def test_identity_delete_trust(self):
+        # user can delete trust for which they are the trustor
+        trustor_user_id = self.persona.credentials.user_id
+        project_id = self.persona.credentials.project_id
+        trust_id = self.client.create_trust(
+            **self.trust(trustor=trustor_user_id,
+                         project_id=project_id,
+                         roles=[{'id': self.role_id}]))['trust']['id']
+        self.do_request('delete_trust', expected_status=204, trust_id=trust_id)
+
+        # user cannot delete trust for which they are the trustee
+        trustee_user_id = self.persona.credentials.user_id
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust(trustee=trustee_user_id))['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('delete_trust', expected_status=exceptions.Forbidden,
+                        trust_id=trust_id)
+
+        # user cannot delete trust with which they are unaffiliated
+        trust_id = self.user_trust_client.create_trust(
+            **self.trust())['trust']['id']
+        self.addCleanup(self.user_trust_client.delete_trust, trust_id=trust_id)
+        self.do_request('delete_trust', expected_status=exceptions.Forbidden,
+                        trust_id=trust_id)
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+    def setUp(self):
+        super(ProjectMemberTests, self).setUp()
+        self.role_id = self.member_role_id
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
+
+    def setUp(self):
+        super(ProjectReaderTests, self).setUp()
+        self.role_id = self.reader_role_id
diff --git a/keystone_tempest_plugin/tests/rbac/v3/test_user.py b/keystone_tempest_plugin/tests/rbac/v3/test_user.py
new file mode 100644
index 0000000..7dcfdc7
--- /dev/null
+++ b/keystone_tempest_plugin/tests/rbac/v3/test_user.py
@@ -0,0 +1,540 @@
+# Copyright 2020 SUSE LLC
+#
+# 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.
+
+import abc
+
+from tempest.api.identity import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions
+
+from keystone_tempest_plugin.tests.rbac.v3 import base as rbac_base
+
+
+class IdentityV3RbacUserTest(rbac_base.IdentityV3RbacBaseTests,
+                             metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityV3RbacUserTest, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.users_v3_client
+        admin_client = cls.os_system_admin
+        cls.admin_users_client = admin_client.users_v3_client
+        cls.admin_domains_client = admin_client.domains_client
+
+    def user(self):
+        user = {}
+        name = data_utils.rand_name('user')
+        user['name'] = name
+        user['description'] = name + 'description'
+        user['email'] = name + '@testmail.tm'
+        user['password'] = data_utils.rand_password()
+        user['enabled'] = False
+        return user
+
+    @abc.abstractmethod
+    def test_identity_create_user(self):
+        """Test identity:create_user policy.
+
+        This test must check:
+          * whether the persona can create an arbitrary user
+          * whether the persona can create a user in another domain
+            (if applicable)
+          * whether the persona can create a user in their own domain
+            (if applicable)
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_get_user(self):
+        """Test identity:get_user policy.
+
+        This test must check:
+          * whether the persona can get an arbitary user
+          * whether the persona can get their own user
+          * whether the persona can get a user in another domain
+            (if applicable)
+          * whether the persona can get a user in their own domain
+            (if applicable)
+          * whether the persona can get a user that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_list_users(self):
+        """Test identity:list_users policy.
+
+        This test must check:
+          * whether the persona can list all users
+          * whether the result list is appropriately filtered to domain
+            (if applicable)
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_update_user(self):
+        """Test identity:update_users policy.
+
+        This test must check:
+          * whether the persona can update an arbitrary user
+          * whether the persona can update a user in another domain
+            (if applicable)
+          * whether the persona can update a user in their own domain
+            (if applicable)
+          * whether the persona can update a user that does not exist
+        """
+        pass
+
+    @abc.abstractmethod
+    def test_identity_delete_user(self):
+        """Test identity:delete_user policy.
+
+        This test must check
+          * whether the persona can delete an arbitrary user
+          * whether the persona can delete a user in another domain
+            (if applicable)
+          * whether the persona can delete a user in their own domain
+            (if applicable)
+          * whether the persona can delete a user that does not exist
+        """
+        pass
+
+
+class SystemAdminTests(IdentityV3RbacUserTest, base.BaseIdentityTest):
+
+    credentials = ['system_admin']
+
+    def test_identity_create_user(self):
+        resp = self.do_request('create_user',
+                               expected_status=201,
+                               **self.user())
+        self.addCleanup(self.admin_users_client.delete_user,
+                        resp['user']['id'])
+
+    def test_identity_get_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        # user can get arbitrary user
+        resp = self.do_request('show_user', user_id=user['id'])
+        self.assertEqual(resp['user']['id'], user['id'])
+        # user can get own user
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request('show_user', user_id=user_id)
+        self.assertEqual(resp['user']['id'], user_id)
+        # user gets a 404 for nonexistent user
+        self.do_request('show_user', expected_status=exceptions.NotFound,
+                        user_id='fakeuser')
+
+    def test_identity_list_users(self):
+        domain = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']
+        user_create = self.user()
+        # create user in default domain
+        user1 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user1['id'])
+        # create user in arbitrary domain
+        user_create['domain_id'] = domain['id']
+        user2 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user2['id'])
+        resp = self.do_request('list_users')
+        user_ids = set(u['id'] for u in resp['users'])
+        # both users should be in the list
+        self.assertIn(user1['id'], user_ids)
+        self.assertIn(user2['id'], user_ids)
+
+    def test_identity_update_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        user_update = {
+            'user_id': user['id'],
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', **user_update)
+        # user gets a 404 for nonexistent user
+        user_update = {
+            'user_id': 'fakeuser',
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.NotFound,
+                        **user_update)
+
+    def test_identity_delete_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.do_request('delete_user', expected_status=204, user_id=user['id'])
+        # user gets a 404 for nonexistent user
+        self.do_request('delete_user', expected_status=exceptions.NotFound,
+                        user_id='fakeuser')
+
+
+class SystemMemberTests(IdentityV3RbacUserTest, base.BaseIdentityTest):
+
+    credentials = ['system_member', 'system_admin']
+
+    def test_identity_create_user(self):
+        self.do_request('create_user', expected_status=exceptions.Forbidden,
+                        **self.user())
+
+    def test_identity_get_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        # user can get arbitrary user
+        resp = self.do_request('show_user', user_id=user['id'])
+        self.assertEqual(resp['user']['id'], user['id'])
+        # user can get own user
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request('show_user', user_id=user_id)
+        self.assertEqual(resp['user']['id'], user_id)
+        # user gets a 404 for nonexistent user
+        self.do_request('show_user', expected_status=exceptions.NotFound,
+                        user_id='fakeuser')
+
+    def test_identity_list_users(self):
+        domain = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']
+        user_create = self.user()
+        # create user in default domain
+        user1 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user1['id'])
+        # create user in arbitrary domain
+        user_create['domain_id'] = domain['id']
+        user2 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user2['id'])
+        resp = self.do_request('list_users')
+        user_ids = set(u['id'] for u in resp['users'])
+        # both users should be in the list
+        self.assertIn(user1['id'], user_ids)
+        self.assertIn(user2['id'], user_ids)
+
+    def test_identity_update_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        user_update = {
+            'user_id': user['id'],
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+        # user gets a 403 for nonexistent user
+        user_update = {
+            'user_id': 'fakeuser',
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+
+    def test_identity_delete_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+
+class SystemReaderTests(SystemMemberTests):
+
+    credentials = ['system_reader', 'system_admin']
+
+
+class DomainAdminTests(IdentityV3RbacUserTest, base.BaseIdentityTest):
+
+    credentials = ['domain_admin', 'system_admin']
+
+    def setUp(self):
+        super(DomainAdminTests, self).setUp()
+        self.other_domain = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']
+        self.addCleanup(self.admin_domains_client.delete_domain,
+                        self.other_domain['id'])
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=self.other_domain['id'], enabled=False)
+
+    def test_identity_create_user(self):
+        user_create = self.user()
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        self.do_request('create_user', expected_status=exceptions.Forbidden,
+                        **user_create)
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        resp = self.do_request('create_user',
+                               expected_status=201,
+                               **user_create)
+        self.addCleanup(self.admin_users_client.delete_user,
+                        resp['user']['id'])
+
+    def test_identity_get_user(self):
+        user_create = self.user()
+        user_create['domain_id'] = self.other_domain['id']
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        # user cannot get user in other domain
+        self.do_request('show_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        # user can get user in own domain
+        resp = self.do_request('show_user', user_id=user['id'])
+        self.assertEqual(resp['user']['id'], user['id'])
+        # user can get own user
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request('show_user', user_id=user_id)
+        self.assertEqual(resp['user']['id'], user_id)
+        # user gets a 403 for nonexistent user
+        self.do_request('show_user', expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+    def test_identity_list_users(self):
+        user_create = self.user()
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        user1 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user1['id'])
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user2 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user2['id'])
+        resp = self.do_request('list_users')
+        user_ids = set(u['id'] for u in resp['users'])
+        self.assertNotIn(user1['id'], user_ids)
+        self.assertIn(user2['id'], user_ids)
+
+    def test_identity_update_user(self):
+        user_create = self.user()
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        user_update = {
+            'user_id': user['id'],
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        user_update = {
+            'user_id': user['id'],
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', **user_update)
+        # user gets a 403 for nonexistent user
+        user_update = {
+            'user_id': 'fakeuser',
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+
+    def test_identity_delete_user(self):
+        user_create = self.user()
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.do_request('delete_user', expected_status=204, user_id=user['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+
+class DomainMemberTests(IdentityV3RbacUserTest, base.BaseIdentityTest):
+
+    credentials = ['domain_member', 'system_admin']
+
+    def setUp(self):
+        super(DomainMemberTests, self).setUp()
+        self.other_domain = self.admin_domains_client.create_domain(
+            name=data_utils.rand_name())['domain']
+        self.addCleanup(self.admin_domains_client.delete_domain,
+                        self.other_domain['id'])
+        self.addCleanup(self.admin_domains_client.update_domain,
+                        domain_id=self.other_domain['id'], enabled=False)
+
+    def test_identity_create_user(self):
+        user_create = self.user()
+        # create user without domain specified
+        self.do_request('create_user', expected_status=exceptions.Forbidden,
+                        **user_create)
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        self.do_request('create_user', expected_status=exceptions.Forbidden,
+                        **user_create)
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        self.do_request('create_user', expected_status=exceptions.Forbidden,
+                        **user_create)
+
+    def test_identity_get_user(self):
+        user_create = self.user()
+        user_create['domain_id'] = self.other_domain['id']
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        # user cannot get user in other domain
+        self.do_request('show_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        # user can get user in own domain
+        resp = self.do_request('show_user', user_id=user['id'])
+        self.assertEqual(resp['user']['id'], user['id'])
+        # user can get own user
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request('show_user', user_id=user_id)
+        self.assertEqual(resp['user']['id'], user_id)
+        # user gets a 403 for nonexistent user
+        self.do_request('show_user', expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+    def test_identity_list_users(self):
+        user_create = self.user()
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        user1 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user1['id'])
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user2 = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user2['id'])
+        resp = self.do_request('list_users')
+        user_ids = set(u['id'] for u in resp['users'])
+        self.assertNotIn(user1['id'], user_ids)
+        self.assertIn(user2['id'], user_ids)
+
+    def test_identity_update_user(self):
+        user_create = self.user()
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        user_update = {
+            'user_id': user['id'],
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        user_update = {
+            'user_id': user['id'],
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+        # user gets a 403 for nonexistent user
+        user_update = {
+            'user_id': 'fakeuser',
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+
+    def test_identity_delete_user(self):
+        user_create = self.user()
+        # create user in other domain
+        user_create['domain_id'] = self.other_domain['id']
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        # create user in own domain
+        user_create['domain_id'] = self.persona.credentials.domain_id
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+
+class DomainReaderTests(DomainMemberTests):
+
+    credentials = ['domain_reader', 'system_admin']
+
+
+class ProjectAdminTests(IdentityV3RbacUserTest, base.BaseIdentityTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_identity_create_user(self):
+        self.do_request('create_user', expected_status=exceptions.Forbidden,
+                        **self.user())
+
+    def test_identity_get_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        # user cannot get arbitrary user
+        self.do_request('show_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        # user can get own user
+        user_id = self.persona.credentials.user_id
+        resp = self.do_request('show_user', user_id=user_id)
+        self.assertEqual(resp['user']['id'], user_id)
+        # user gets a 403 for nonexistent user
+        self.do_request('show_user', expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+    def test_identity_list_users(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        self.do_request('list_users', expected_status=exceptions.Forbidden)
+
+    def test_identity_update_user(self):
+        user_create = self.user()
+        user = self.admin_users_client.create_user(**user_create)['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        user_update = {
+            'user_id': user['id'],
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+        # user gets a 403 for nonexistent user
+        user_update = {
+            'user_id': 'fakeuser',
+            'description': data_utils.arbitrary_string()
+        }
+        self.do_request('update_user', expected_status=exceptions.Forbidden,
+                        **user_update)
+
+    def test_identity_delete_user(self):
+        user = self.admin_users_client.create_user(**self.user())['user']
+        self.addCleanup(self.admin_users_client.delete_user, user['id'])
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id=user['id'])
+        # user gets a 403 for nonexistent user
+        self.do_request('delete_user', expected_status=exceptions.Forbidden,
+                        user_id='fakeuser')
+
+
+class ProjectMemberTests(ProjectAdminTests):
+
+    credentials = ['project_member', 'system_admin']
+
+
+class ProjectReaderTests(ProjectAdminTests):
+
+    credentials = ['project_reader', 'system_admin']
diff --git a/tox.ini b/tox.ini
index 8b4a058..570c029 100644
--- a/tox.ini
+++ b/tox.ini
@@ -42,6 +42,6 @@
 # E123, E125 skipped as they are invalid PEP-8.
 
 show-source = True
-ignore = E123,E125
+ignore = E123,E125,W503
 builtins = _
 exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build