# Copyright 2014 IBM Corp.
#
#    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 fixtures
import mock
from oslo_config import cfg

from tempest.common import credentials_factory as credentials
from tempest import config
from tempest.lib.common import dynamic_creds
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
from tempest.lib.services.identity.v2 import identity_client as v2_iden_client
from tempest.lib.services.identity.v2 import roles_client as v2_roles_client
from tempest.lib.services.identity.v2 import tenants_client as \
    v2_tenants_client
from tempest.lib.services.identity.v2 import token_client as v2_token_client
from tempest.lib.services.identity.v2 import users_client as v2_users_client
from tempest.lib.services.identity.v3 import domains_client
from tempest.lib.services.identity.v3 import identity_client as v3_iden_client
from tempest.lib.services.identity.v3 import projects_client as \
    v3_projects_client
from tempest.lib.services.identity.v3 import roles_client as v3_roles_client
from tempest.lib.services.identity.v3 import token_client as v3_token_client
from tempest.lib.services.identity.v3 import users_client as \
    v3_users_client
from tempest.lib.services.network import routers_client
from tempest.tests import base
from tempest.tests import fake_config
from tempest.tests.lib import fake_http
from tempest.tests.lib import fake_identity
from tempest.tests.lib.services import registry_fixture


class TestDynamicCredentialProvider(base.TestCase):

    fixed_params = {'name': 'test class',
                    'identity_version': 'v2',
                    'admin_role': 'admin',
                    'identity_uri': 'fake_uri'}

    token_client = v2_token_client
    iden_client = v2_iden_client
    roles_client = v2_roles_client
    tenants_client = v2_tenants_client
    users_client = v2_users_client
    token_client_class = token_client.TokenClient
    fake_response = fake_identity._fake_v2_response
    tenants_client_class = tenants_client.TenantsClient
    delete_tenant = 'delete_tenant'

    def setUp(self):
        super(TestDynamicCredentialProvider, self).setUp()
        self.useFixture(fake_config.ConfigFixture())
        self.useFixture(registry_fixture.RegistryFixture())
        self.patchobject(config, 'TempestConfigPrivate',
                         fake_config.FakePrivate)
        self.patchobject(self.token_client_class, 'raw_request',
                         self.fake_response)
        cfg.CONF.set_default('operator_role', 'FakeRole',
                             group='object-storage')
        self._mock_list_ec2_credentials('fake_user_id', 'fake_tenant_id')
        self.fixed_params.update(
            admin_creds=self._get_fake_admin_creds())

    def test_tempest_client(self):
        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
        self.assertIsInstance(creds.identity_admin_client,
                              self.iden_client.IdentityClient)

    def _get_fake_admin_creds(self):
        return credentials.get_credentials(
            fill_in=False,
            identity_version=self.fixed_params['identity_version'],
            username='fake_username', password='fake_password',
            tenant_name='fake_tenant')

    def _mock_user_create(self, id, name):
        user_fix = self.useFixture(fixtures.MockPatchObject(
            self.users_client.UsersClient,
            'create_user',
            return_value=(rest_client.ResponseBody
                          (200, {'user': {'id': id, 'name': name}}))))
        return user_fix

    def _mock_tenant_create(self, id, name):
        tenant_fix = self.useFixture(fixtures.MockPatchObject(
            self.tenants_client.TenantsClient,
            'create_tenant',
            return_value=(rest_client.ResponseBody
                          (200, {'tenant': {'id': id, 'name': name}}))))
        return tenant_fix

    def _mock_list_roles(self, id, name):
        roles_fix = self.useFixture(fixtures.MockPatchObject(
            self.roles_client.RolesClient,
            'list_roles',
            return_value=(rest_client.ResponseBody
                          (200,
                           {'roles': [{'id': id, 'name': name},
                                      {'id': '1', 'name': 'FakeRole'},
                                      {'id': '2', 'name': 'Member'}]}))))
        return roles_fix

    def _mock_list_2_roles(self):
        roles_fix = self.useFixture(fixtures.MockPatchObject(
            self.roles_client.RolesClient,
            'list_roles',
            return_value=(rest_client.ResponseBody
                          (200,
                           {'roles': [{'id': '1234', 'name': 'role1'},
                                      {'id': '1', 'name': 'FakeRole'},
                                      {'id': '12345', 'name': 'role2'}]}))))
        return roles_fix

    def _mock_assign_user_role(self):
        tenant_fix = self.useFixture(fixtures.MockPatchObject(
            self.roles_client.RolesClient,
            'create_user_role_on_project',
            return_value=(rest_client.ResponseBody
                          (200, {}))))
        return tenant_fix

    def _mock_list_role(self):
        roles_fix = self.useFixture(fixtures.MockPatchObject(
            self.roles_client.RolesClient,
            'list_roles',
            return_value=(rest_client.ResponseBody
                          (200, {'roles': [
                              {'id': '1', 'name': 'FakeRole'},
                              {'id': '2', 'name': 'Member'}]}))))
        return roles_fix

    def _mock_list_ec2_credentials(self, user_id, tenant_id):
        ec2_creds_fix = self.useFixture(fixtures.MockPatchObject(
            self.users_client.UsersClient,
            'list_user_ec2_credentials',
            return_value=(rest_client.ResponseBody
                          (200, {'credentials': [{
                                 'access': 'fake_access',
                                 'secret': 'fake_secret',
                                 'tenant_id': tenant_id,
                                 'user_id': user_id,
                                 'trust_id': None}]}))))
        return ec2_creds_fix

    def _mock_network_create(self, iso_creds, id, name):
        net_fix = self.useFixture(fixtures.MockPatchObject(
            iso_creds.networks_admin_client,
            'create_network',
            return_value={'network': {'id': id, 'name': name}}))
        return net_fix

    def _mock_subnet_create(self, iso_creds, id, name):
        subnet_fix = self.useFixture(fixtures.MockPatchObject(
            iso_creds.subnets_admin_client,
            'create_subnet',
            return_value={'subnet': {'id': id, 'name': name}}))
        return subnet_fix

    def _mock_router_create(self, id, name):
        router_fix = self.useFixture(fixtures.MockPatchObject(
            routers_client.RoutersClient,
            'create_router',
            return_value={'router': {'id': id, 'name': name}}))
        return router_fix

    @mock.patch('tempest.lib.common.rest_client.RestClient')
    def test_primary_creds(self, MockRestClient):
        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
        self._mock_assign_user_role()
        self._mock_list_role()
        self._mock_tenant_create('1234', 'fake_prim_tenant')
        self._mock_user_create('1234', 'fake_prim_user')
        primary_creds = creds.get_primary_creds()
        self.assertEqual(primary_creds.username, 'fake_prim_user')
        self.assertEqual(primary_creds.tenant_name, 'fake_prim_tenant')
        # Verify IDs
        self.assertEqual(primary_creds.tenant_id, '1234')
        self.assertEqual(primary_creds.user_id, '1234')

    @mock.patch('tempest.lib.common.rest_client.RestClient')
    def test_admin_creds(self, MockRestClient):
        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
        self._mock_list_roles('1234', 'admin')
        self._mock_user_create('1234', 'fake_admin_user')
        self._mock_tenant_create('1234', 'fake_admin_tenant')

        user_mock = mock.patch.object(self.roles_client.RolesClient,
                                      'create_user_role_on_project')
        user_mock.start()
        self.addCleanup(user_mock.stop)
        with mock.patch.object(self.roles_client.RolesClient,
                               'create_user_role_on_project') as user_mock:
            admin_creds = creds.get_admin_creds()
        user_mock.assert_has_calls([
            mock.call('1234', '1234', '1234')])
        self.assertEqual(admin_creds.username, 'fake_admin_user')
        self.assertEqual(admin_creds.tenant_name, 'fake_admin_tenant')
        # Verify IDs
        self.assertEqual(admin_creds.tenant_id, '1234')
        self.assertEqual(admin_creds.user_id, '1234')

    @mock.patch('tempest.lib.common.rest_client.RestClient')
    def test_role_creds(self, MockRestClient):
        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
        self._mock_list_2_roles()
        self._mock_user_create('1234', 'fake_role_user')
        self._mock_tenant_create('1234', 'fake_role_tenant')

        user_mock = mock.patch.object(self.roles_client.RolesClient,
                                      'create_user_role_on_project')
        user_mock.start()
        self.addCleanup(user_mock.stop)
        with mock.patch.object(self.roles_client.RolesClient,
                               'create_user_role_on_project') as user_mock:
            role_creds = creds.get_creds_by_roles(
                roles=['role1', 'role2'])
        calls = user_mock.mock_calls
        # Assert that the role creation is called with the 2 specified roles
        self.assertEqual(len(calls), 2)
        args = map(lambda x: x[1], calls)
        args = list(args)
        self.assertIn(('1234', '1234', '1234'), args)
        self.assertIn(('1234', '1234', '12345'), args)
        self.assertEqual(role_creds.username, 'fake_role_user')
        self.assertEqual(role_creds.tenant_name, 'fake_role_tenant')
        # Verify IDs
        self.assertEqual(role_creds.tenant_id, '1234')
        self.assertEqual(role_creds.user_id, '1234')

    @mock.patch('tempest.lib.common.rest_client.RestClient')
    def test_all_cred_cleanup(self, MockRestClient):
        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
        self._mock_assign_user_role()
        self._mock_list_role()
        self._mock_tenant_create('1234', 'fake_prim_tenant')
        self._mock_user_create('1234', 'fake_prim_user')
        creds.get_primary_creds()
        self._mock_tenant_create('12345', 'fake_alt_tenant')
        self._mock_user_create('12345', 'fake_alt_user')
        creds.get_alt_creds()
        self._mock_tenant_create('123456', 'fake_admin_tenant')
        self._mock_user_create('123456', 'fake_admin_user')
        self._mock_list_roles('123456', 'admin')
        creds.get_admin_creds()
        user_mock = self.patchobject(self.users_client.UsersClient,
                                     'delete_user')
        tenant_mock = self.patchobject(self.tenants_client_class,
                                       self.delete_tenant)
        creds.clear_creds()
        # Verify user delete calls
        calls = user_mock.mock_calls
        self.assertEqual(len(calls), 3)
        args = map(lambda x: x[1][0], calls)
        args = list(args)
        self.assertIn('1234', args)
        self.assertIn('12345', args)
        self.assertIn('123456', args)
        # Verify tenant delete calls
        calls = tenant_mock.mock_calls