# 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 testtools
from testtools import matchers

from openstack.cloud import exc
from openstack.tests.unit import base


class TestRoleAssignment(base.TestCase):

    def _build_role_assignment_response(self, role_id, scope_type, scope_id,
                                        entity_type, entity_id):
        self.assertThat(['group', 'user'], matchers.Contains(entity_type))
        self.assertThat(['project', 'domain'], matchers.Contains(scope_type))
        # NOTE(notmorgan): Links are thrown out by shade, but we construct them
        # for corectness.
        link_str = ('https://identity.example.com/identity/v3/{scope_t}s'
                    '/{scopeid}/{entity_t}s/{entityid}/roles/{roleid}')
        return [{
            'links': {'assignment': link_str.format(
                scope_t=scope_type, scopeid=scope_id, entity_t=entity_type,
                entityid=entity_id, roleid=role_id)},
            'role': {'id': role_id},
            'scope': {scope_type: {'id': scope_id}},
            entity_type: {'id': entity_id}
        }]

    def setUp(self, cloud_config_fixture='clouds.yaml'):
        super(TestRoleAssignment, self).setUp(cloud_config_fixture)
        self.role_data = self._get_role_data()
        self.domain_data = self._get_domain_data()
        self.user_data = self._get_user_data(
            domain_id=self.domain_data.domain_id)
        self.project_data = self._get_project_data(
            domain_id=self.domain_data.domain_id)
        self.project_data_v2 = self._get_project_data(
            project_name=self.project_data.project_name,
            project_id=self.project_data.project_id,
            v3=False)
        self.group_data = self._get_group_data(
            domain_id=self.domain_data.domain_id)

        self.user_project_assignment = self._build_role_assignment_response(
            role_id=self.role_data.role_id, scope_type='project',
            scope_id=self.project_data.project_id, entity_type='user',
            entity_id=self.user_data.user_id)

        self.group_project_assignment = self._build_role_assignment_response(
            role_id=self.role_data.role_id, scope_type='project',
            scope_id=self.project_data.project_id, entity_type='group',
            entity_id=self.group_data.group_id)

        self.user_domain_assignment = self._build_role_assignment_response(
            role_id=self.role_data.role_id, scope_type='domain',
            scope_id=self.domain_data.domain_id, entity_type='user',
            entity_id=self.user_data.user_id)

        self.group_domain_assignment = self._build_role_assignment_response(
            role_id=self.role_data.role_id, scope_type='domain',
            scope_id=self.domain_data.domain_id, entity_type='group',
            entity_id=self.group_data.group_id)

        # Cleanup of instances to ensure garbage collection/no leaking memory
        # in tests.
        self.addCleanup(delattr, self, 'role_data')
        self.addCleanup(delattr, self, 'user_data')
        self.addCleanup(delattr, self, 'domain_data')
        self.addCleanup(delattr, self, 'group_data')
        self.addCleanup(delattr, self, 'project_data')
        self.addCleanup(delattr, self, 'project_data_v2')
        self.addCleanup(delattr, self, 'user_project_assignment')
        self.addCleanup(delattr, self, 'group_project_assignment')
        self.addCleanup(delattr, self, 'user_domain_assignment')
        self.addCleanup(delattr, self, 'group_domain_assignment')

    def get_mock_url(self, service_type='identity', interface='public',
                     resource='role_assignments', append=None,
                     base_url_append='v3', qs_elements=None):
        return super(TestRoleAssignment, self).get_mock_url(
            service_type, interface, resource, append, base_url_append,
            qs_elements)

    def __get(self, resource, data, attr, qs_elements, use_name=False,
              is_found=True):
        if not use_name:
            if is_found:
                return [dict(
                    method='GET',
                    uri=self.get_mock_url(
                        resource=resource + 's',  # do roles from role
                        append=[getattr(data, attr)],
                        qs_elements=qs_elements),
                    status_code=200,
                    json=data.json_response)
                ]
            else:
                return [
                    dict(
                        method='GET',
                        uri=self.get_mock_url(
                            resource=resource + 's',  # do roles from role
                            append=[getattr(data, attr)],
                            qs_elements=qs_elements),
                        status_code=404),
                    dict(
                        method='GET',
                        uri=self.get_mock_url(
                            resource=resource + 's',  # do roles from role
                            qs_elements=qs_elements),
                        status_code=200,
                        json={(resource + 's'): []})
                ]
        else:
            return [
                dict(
                    method='GET',
                    uri=self.get_mock_url(
                        resource=resource + 's',
                        append=[getattr(data, attr)],
                        qs_elements=qs_elements),
                    status_code=404),
                dict(
                    method='GET',
                    uri=self.get_mock_url(
                        resource=resource + 's',
                        qs_elements=[
                            'name=' + getattr(data, attr)] + qs_elements),
                    status_code=200,
                    json={
                        (resource + 's'): [data.json_response[resource]]})
            ]

    def __user_mocks(self, user_data, use_name, is_found=True):
        uri_mocks = []
        if not use_name:
            uri_mocks.append(dict(
                method='GET',
                uri=self.get_mock_url(resource='users'),
                status_code=200,
                json={'users': [user_data.json_response['user']] if is_found
                      else []}))
        else:
            uri_mocks.append(dict(
                method='GET',
                uri=self.get_mock_url(
                    resource='users',
                    qs_elements=[
                        'name=' + user_data.name
                    ]
                ),
                status_code=200,
                json={'users': [user_data.json_response['user']] if is_found
                      else []}))
        return uri_mocks

    def _get_mock_role_query_urls(
        self, role_data, domain_data=None, project_data=None,
        group_data=None, user_data=None,
        use_role_name=False, use_domain_name=False, use_project_name=False,
        use_group_name=False, use_user_name=False, use_domain_in_query=True
    ):
        """Build uri mocks for querying role assignments
        """
        uri_mocks = []

        if domain_data:
            uri_mocks.extend(
                self.__get(
                    'domain', domain_data,
                    'domain_id' if not use_domain_name else 'domain_name',
                    [], use_name=use_domain_name)
            )

        qs_elements = []
        if domain_data and use_domain_in_query:
            qs_elements = [
                'domain_id=' + domain_data.domain_id
            ]

        uri_mocks.extend(
            self.__get(
                'role', role_data,
                'role_id' if not use_role_name else 'role_name',
                [], use_name=use_role_name)
        )

        if user_data:
            uri_mocks.extend(self.__user_mocks(
                user_data, use_user_name, is_found=True))

        if group_data:
            uri_mocks.extend(
                self.__get(
                    'group', group_data,
                    'group_id' if not use_group_name else 'group_name',
                    qs_elements, use_name=use_group_name)
            )

        if project_data:
            uri_mocks.extend(
                self.__get(
                    'project', project_data,
                    'project_id' if not use_project_name else 'project_name',
                    qs_elements, use_name=use_project_name)
            )

        return uri_mocks

    def test_grant_role_user_id_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data, project_data=self.project_data,
            user_data=self.user_data, use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.user_id,
                project=self.project_data.project_id))
        self.assert_calls()

    def test_grant_role_user_name_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.name,
                project=self.project_data.project_id))

    def test_grant_role_user_id_project_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_id,
            user=self.user_data.user_id,
            project=self.project_data.project_id))
        self.assert_calls()

    def test_grant_role_user_name_project_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_name,
            user=self.user_data.name,
            project=self.project_data.project_id))
        self.assert_calls()

    def test_grant_role_group_id_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data,
            use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                group=self.group_data.group_id,
                project=self.project_data.project_id))
        self.assert_calls()

    def test_grant_role_group_name_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                group=self.group_data.group_name,
                project=self.project_data.project_id))
        self.assert_calls()

    def test_grant_role_group_id_project_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_id,
            group=self.group_data.group_id,
            project=self.project_data.project_id))
        self.assert_calls()

    def test_grant_role_group_name_project_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_name,
            group=self.group_data.group_name,
            project=self.project_data.project_id))
        self.assert_calls()

# ===== Domain
    def test_grant_role_user_id_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data, domain_data=self.domain_data,
            user_data=self.user_data, use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.user_id,
                domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_grant_role_user_name_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.name,
                domain=self.domain_data.domain_id))

    def test_grant_role_user_id_domain_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, user_data=self.user_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_id,
            user=self.user_data.user_id,
            domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_grant_role_user_name_domain_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_name,
            user=self.user_data.name,
            domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_grant_role_group_id_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data,
            use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                group=self.group_data.group_id,
                domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_grant_role_group_name_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                group=self.group_data.group_name,
                domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_grant_role_group_id_domain_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_id,
            group=self.group_data.group_id,
            domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_grant_role_group_name_domain_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.grant_role(
            self.role_data.role_name,
            group=self.group_data.group_name,
            domain=self.domain_data.domain_id))
        self.assert_calls()

# ==== Revoke
    def test_revoke_role_user_id_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data, project_data=self.project_data,
            user_data=self.user_data, use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.user_id,
                project=self.project_data.project_id))
        self.assert_calls()

    def test_revoke_role_user_name_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.name,
                project=self.project_data.project_id))

    def test_revoke_role_user_id_project_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_id,
            user=self.user_data.user_id,
            project=self.project_data.project_id))
        self.assert_calls()

    def test_revoke_role_user_name_project_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_name,
            user=self.user_data.name,
            project=self.project_data.project_id))
        self.assert_calls()

    def test_revoke_role_group_id_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data,
            use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                group=self.group_data.group_id,
                project=self.project_data.project_id))
        self.assert_calls()

    def test_revoke_role_group_name_project(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                group=self.group_data.group_name,
                project=self.project_data.project_id))
        self.assert_calls()

    def test_revoke_role_group_id_project_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_id,
            group=self.group_data.group_id,
            project=self.project_data.project_id))
        self.assert_calls()

    def test_revoke_role_group_name_project_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_name,
            group=self.group_data.group_name,
            project=self.project_data.project_id))
        self.assert_calls()

# ==== Domain
    def test_revoke_role_user_id_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data, domain_data=self.domain_data,
            user_data=self.user_data, use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.user_id,
                domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_revoke_role_user_name_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.name,
                domain=self.domain_data.domain_id))

    def test_revoke_role_user_id_domain_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, user_data=self.user_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_id,
            user=self.user_data.user_id,
            domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_revoke_role_user_name_domain_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, user_data=self.user_data,
            use_role_name=True, use_user_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_name,
            user=self.user_data.name,
            domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_revoke_role_group_id_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data,
            use_role_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                group=self.group_data.group_id,
                domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_revoke_role_group_name_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[self.domain_data.domain_id,
                             'groups', self.group_data.group_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                group=self.group_data.group_name,
                domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_revoke_role_group_id_domain_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_id,
            group=self.group_data.group_id,
            domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_revoke_role_group_name_domain_not_exists(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            domain_data=self.domain_data, group_data=self.group_data,
            use_role_name=True, use_group_name=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='domains',
                     append=[
                         self.domain_data.domain_id,
                         'groups', self.group_data.group_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
        ])
        self.register_uris(uris)

        self.assertFalse(self.cloud.revoke_role(
            self.role_data.role_name,
            group=self.group_data.group_name,
            domain=self.domain_data.domain_id))
        self.assert_calls()

    def test_grant_no_role(self):
        uris = self.__get(
            'domain', self.domain_data, 'domain_name', [], use_name=True)
        uris.extend([
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='roles',
                     append=[self.role_data.role_name],
                 ),
                 status_code=404),
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='roles',
                     qs_elements=[
                         'name=' + self.role_data.role_name,
                     ]),
                 status_code=200,
                 json={'roles': []})
        ])
        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Role {0} not found'.format(self.role_data.role_name)
        ):
            self.cloud.grant_role(
                self.role_data.role_name,
                group=self.group_data.group_name,
                domain=self.domain_data.domain_name)
        self.assert_calls()

    def test_revoke_no_role(self):
        uris = self.__get(
            'domain', self.domain_data, 'domain_name', [], use_name=True)
        uris.extend([
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='roles',
                     append=[self.role_data.role_name],
                 ),
                 status_code=404),
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='roles',
                     qs_elements=[
                         'name=' + self.role_data.role_name,
                     ]),
                 status_code=200,
                 json={'roles': []})
        ])
        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Role {0} not found'.format(self.role_data.role_name)
        ):
            self.cloud.revoke_role(
                self.role_data.role_name,
                group=self.group_data.group_name,
                domain=self.domain_data.domain_name)
        self.assert_calls()

    def test_grant_no_user_or_group_specified(self):
        uris = self.__get(
            'role', self.role_data, 'role_name', [], use_name=True)
        self.register_uris(uris)
        with testtools.ExpectedException(
                exc.OpenStackCloudException,
                'Must specify either a user or a group'
        ):
            self.cloud.grant_role(self.role_data.role_name)
        self.assert_calls()

    def test_revoke_no_user_or_group_specified(self):
        uris = self.__get(
            'role', self.role_data, 'role_name', [], use_name=True)
        self.register_uris(uris)
        with testtools.ExpectedException(
                exc.OpenStackCloudException,
                'Must specify either a user or a group'
        ):
            self.cloud.revoke_role(self.role_data.role_name)
        self.assert_calls()

    def test_grant_no_user_or_group(self):
        uris = self.__get(
            'role', self.role_data, 'role_name', [], use_name=True)
        uris.extend(self.__user_mocks(self.user_data, use_name=True,
                                      is_found=False))
        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Must specify either a user or a group'
        ):
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.name)
        self.assert_calls()

    def test_revoke_no_user_or_group(self):
        uris = self.__get(
            'role', self.role_data, 'role_name', [], use_name=True)
        uris.extend(self.__user_mocks(self.user_data, use_name=True,
                                      is_found=False))
        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Must specify either a user or a group'
        ):
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.name)
        self.assert_calls()

    def test_grant_both_user_and_group(self):
        uris = self.__get(
            'role', self.role_data, 'role_name', [], use_name=True)
        uris.extend(self.__user_mocks(self.user_data, use_name=True))
        uris.extend(self.__get(
            'group', self.group_data, 'group_name', [], use_name=True))
        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Specify either a group or a user, not both'
        ):
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.name,
                group=self.group_data.group_name)
        self.assert_calls()

    def test_revoke_both_user_and_group(self):
        uris = self.__get(
            'role', self.role_data, 'role_name', [], use_name=True)
        uris.extend(self.__user_mocks(self.user_data, use_name=True))
        uris.extend(self.__get(
            'group', self.group_data, 'group_name', [], use_name=True))
        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Specify either a group or a user, not both'
        ):
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.name,
                group=self.group_data.group_name)

    def test_grant_both_project_and_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data,
            domain_data=self.domain_data,
            use_role_name=True, use_user_name=True, use_project_name=True,
            use_domain_name=True, use_domain_in_query=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=404),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.name,
                project=self.project_data.project_name,
                domain=self.domain_data.domain_name))

    def test_revoke_both_project_and_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            project_data=self.project_data, user_data=self.user_data,
            domain_data=self.domain_data,
            use_role_name=True, use_user_name=True, use_project_name=True,
            use_domain_name=True, use_domain_in_query=True)
        uris.extend([
            dict(method='HEAD',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[
                         self.project_data.project_id,
                         'users', self.user_data.user_id,
                         'roles', self.role_data.role_id
                     ]),
                 complete_qs=True,
                 status_code=204),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     resource='projects',
                     append=[self.project_data.project_id,
                             'users', self.user_data.user_id,
                             'roles', self.role_data.role_id]),
                 status_code=200),
        ])
        self.register_uris(uris)

        self.assertTrue(
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.name,
                project=self.project_data.project_name,
                domain=self.domain_data.domain_name))

    def test_grant_no_project_or_domain(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            user_data=self.user_data,
            use_role_name=True, use_user_name=True)

        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Must specify either a domain, project or system'
        ):
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.name)
        self.assert_calls()

    def test_revoke_no_project_or_domain_or_system(self):
        uris = self._get_mock_role_query_urls(
            self.role_data,
            user_data=self.user_data,
            use_role_name=True, use_user_name=True)

        self.register_uris(uris)

        with testtools.ExpectedException(
            exc.OpenStackCloudException,
            'Must specify either a domain, project or system'
        ):
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.name)
        self.assert_calls()

    def test_grant_bad_domain_exception(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='domains', append=['baddomain']),
                 status_code=404),
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='domains',
                     qs_elements=['name=baddomain']),
                 status_code=404)
        ])
        with testtools.ExpectedException(exc.OpenStackCloudURINotFound):
            self.cloud.grant_role(
                self.role_data.role_name,
                user=self.user_data.name,
                domain='baddomain')
        self.assert_calls()

    def test_revoke_bad_domain_exception(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='domains', append=['baddomain']),
                 status_code=404),
            dict(method='GET',
                 uri=self.get_mock_url(
                     resource='domains',
                     qs_elements=['name=baddomain']),
                 status_code=404)
        ])
        with testtools.ExpectedException(exc.OpenStackCloudURINotFound):
            self.cloud.revoke_role(
                self.role_data.role_name,
                user=self.user_data.name,
                domain='baddomain')
        self.assert_calls()
