Esempio n. 1
0
def _load_auth_plugin(conf):
    auth_plugin = ks_loading.load_auth_from_conf_options(
        conf, nova.conf.cinder.cinder_group.name)

    if auth_plugin:
        return auth_plugin

    err_msg = _('Unknown auth type: %s') % conf.cinder.auth_type
    raise cinder_exception.Unauthorized(401, message=err_msg)
Esempio n. 2
0
    def authenticate(self):
        magic_tuple = urlparse.urlsplit(self.auth_url)
        scheme, netloc, path, query, frag = magic_tuple
        port = magic_tuple.port
        if port is None:
            port = 80
        path_parts = path.split('/')
        for part in path_parts:
            if len(part) > 0 and part[0] == 'v':
                self.version = part
                break

        # TODO(sandy): Assume admin endpoint is 35357 for now.
        # Ideally this is going to have to be provided by the service catalog.
        new_netloc = netloc.replace(':%d' % port, ':%d' % (35357, ))
        admin_url = urlparse.urlunsplit(
            (scheme, new_netloc, path, query, frag))

        auth_url = self.auth_url
        if self.version == "v2.0":
            while auth_url:
                if not self.auth_system or self.auth_system == 'keystone':
                    auth_url = self._v2_auth(auth_url)
                else:
                    auth_url = self._plugin_auth(auth_url)

            # Are we acting on behalf of another user via an
            # existing token? If so, our actual endpoints may
            # be different than that of the admin token.
            if self.proxy_token:
                if self.bypass_url:
                    self.set_management_url(self.bypass_url)
                else:
                    self._fetch_endpoints_from_auth(admin_url)
                # Since keystone no longer returns the user token
                # with the endpoints any more, we need to replace
                # our service account token with the user token.
                self.auth_token = self.proxy_token
        else:
            try:
                while auth_url:
                    auth_url = self._v1_auth(auth_url)
            # In some configurations cinder makes redirection to
            # v2.0 keystone endpoint. Also, new location does not contain
            # real endpoint, only hostname and port.
            except exceptions.AuthorizationFailure:
                if auth_url.find('v2.0') < 0:
                    auth_url = auth_url + '/v2.0'
                self._v2_auth(auth_url)

        if self.bypass_url:
            self.set_management_url(self.bypass_url)
        elif not self.management_url:
            raise exceptions.Unauthorized('Cinder Client')
Esempio n. 3
0
def _load_auth_plugin(conf):
    auth_plugin = ks_loading.load_auth_from_conf_options(
        conf, nova.conf.cinder.cinder_group.name)

    if auth_plugin:
        return auth_plugin

    if conf.cinder.auth_type is None:
        LOG.error('The [cinder] section of your nova configuration file '
                  'must be configured for authentication with the '
                  'block-storage service endpoint.')
    err_msg = _('Unknown auth type: %s') % conf.cinder.auth_type
    raise cinder_exception.Unauthorized(401, message=err_msg)
class ShellTest(utils.TestCase):

    FAKE_ENV = {
        'OS_USERNAME': '******',
        'OS_PASSWORD': '******',
        'OS_TENANT_NAME': 'tenant_name',
        'OS_AUTH_URL': 'http://no.where/v2.0',
    }

    # Patch os.environ to avoid required auth info.
    def make_env(self, exclude=None, include=None):
        env = dict((k, v) for k, v in self.FAKE_ENV.items() if k != exclude)
        env.update(include or {})
        self.useFixture(fixtures.MonkeyPatch('os.environ', env))

    def setUp(self):
        super(ShellTest, self).setUp()
        for var in self.FAKE_ENV:
            self.useFixture(
                fixtures.EnvironmentVariable(var, self.FAKE_ENV[var]))

    def shell(self, argstr):
        orig = sys.stdout
        try:
            sys.stdout = moves.StringIO()
            _shell = shell.OpenStackCinderShell()
            _shell.main(argstr.split())
        except SystemExit:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            self.assertEqual(0, exc_value.code)
        finally:
            out = sys.stdout.getvalue()
            sys.stdout.close()
            sys.stdout = orig

        return out

    def test_help_unknown_command(self):
        self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')

    def test_help(self):
        required = [
            '.*?^usage: ',
            '.*?(?m)^\s+create\s+Creates a volume.',
            '.*?(?m)^Run "cinder help SUBCOMMAND" for help on a subcommand.',
        ]
        help_text = self.shell('help')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_on_subcommand(self):
        required = [
            '.*?^usage: cinder list',
            '.*?(?m)^Lists all volumes.',
        ]
        help_text = self.shell('help list')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def register_keystone_auth_fixture(self, mocker, url):
        mocker.register_uri('GET',
                            url,
                            text=keystone_client.keystone_request_callback)

    @requests_mock.Mocker()
    def test_version_discovery(self, mocker):
        _shell = shell.OpenStackCinderShell()
        sess = session.Session()

        os_auth_url = "https://wrongdiscoveryresponse.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)

        self.assertRaises(DiscoveryFailure,
                          _shell._discover_auth_versions,
                          sess,
                          auth_url=os_auth_url)

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v2_url, "Expected v2 url")
        self.assertIsNone(v3_url, "Expected no v3 url")

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v3_url, "Expected v3 url")
        self.assertIsNone(v2_url, "Expected no v2 url")

    @requests_mock.Mocker()
    def list_volumes_on_service(self, count, mocker):
        os_auth_url = "http://multiple.service.names/v2.0"
        mocker.register_uri('POST',
                            os_auth_url + "/tokens",
                            text=keystone_client.keystone_request_callback)
        mocker.register_uri('GET',
                            "http://cinder%i.api.com/v2/volumes/detail" %
                            count,
                            text='{"volumes": []}')
        self.make_env(include={
            'OS_AUTH_URL': os_auth_url,
            'CINDER_SERVICE_NAME': 'cinder%i' % count
        })
        _shell = shell.OpenStackCinderShell()
        _shell.main(['list'])

    def test_cinder_service_name(self):
        # Failing with 'No mock address' means we are not
        # choosing the correct endpoint
        for count in range(1, 4):
            self.list_volumes_on_service(count)

    @mock.patch('keystoneauth1.identity.v2.Password')
    @mock.patch('keystoneauth1.adapter.Adapter.get_token',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('keystoneauth1.discover.Discover',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('sys.stdin', side_effect=mock.Mock)
    @mock.patch('getpass.getpass', return_value='password')
    def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover,
                               mock_token, mock_password):
        self.make_env(exclude='OS_PASSWORD')
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(ks_exc.ConnectFailure, _shell.main, ['list'])
        mock_getpass.assert_called_with('OS Password: '******'password'
        # equal to mock_getpass.return_value.
        mock_password.assert_called_with(
            self.FAKE_ENV['OS_AUTH_URL'],
            password=mock_getpass.return_value,
            tenant_id='',
            tenant_name=self.FAKE_ENV['OS_TENANT_NAME'],
            username=self.FAKE_ENV['OS_USERNAME'])

    @mock.patch.object(requests, "request")
    @mock.patch.object(pkg_resources, "iter_entry_points")
    def test_auth_system_not_keystone(self, mock_iter_entry_points,
                                      mock_request):
        """Test that we can authenticate using the auth plugin system."""
        non_keystone_auth_url = "http://non-keystone-url.com/v2.0"

        class MockEntrypoint(pkg_resources.EntryPoint):
            def load(self):
                return FakePlugin

        class FakePlugin(auth_plugin.BaseAuthPlugin):
            def authenticate(self, cls, auth_url):
                cls._authenticate(auth_url, {"fake": "me"})

            def get_auth_url(self):
                return non_keystone_auth_url

        mock_iter_entry_points.side_effect = lambda _t: [
            MockEntrypoint("fake", "fake", ["FakePlugin"])
        ]

        mock_request.side_effect = mock_http_request()

        # Tell the shell we wish to use our 'fake' auth instead of keystone
        # and the auth plugin will provide the auth url
        self.make_env(exclude="OS_AUTH_URL",
                      include={'OS_AUTH_SYSTEM': 'fake'})
        # This should fail as we have not setup a mock response for 'list',
        # however auth should have been called
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(KeyError, _shell.main, ['list'])

        headers = requested_headers(_shell.cs)
        token_url = _shell.cs.client.auth_url + "/tokens"
        self.assertEqual(non_keystone_auth_url + "/tokens", token_url)

        mock_request.assert_any_call("POST",
                                     token_url,
                                     headers=headers,
                                     data='{"fake": "me"}',
                                     allow_redirects=True,
                                     **self.TEST_REQUEST_BASE)

    @mock.patch.object(cinderclient.client.HTTPClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    # Easiest way to make cinderclient use httpclient is a None session
    @mock.patch.object(cinderclient.shell.OpenStackCinderShell,
                       '_get_keystone_session',
                       return_value=None)
    def test_http_client_insecure(self, mock_authenticate, mock_session):
        self.make_env(include={'CINDERCLIENT_INSECURE': True})

        _shell = shell.OpenStackCinderShell()

        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main, ['list'])

        self.assertEqual(False, _shell.cs.client.verify_cert)

    @mock.patch.object(cinderclient.client.SessionClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    def test_session_client_debug_logger(self, mock_session):
        _shell = shell.OpenStackCinderShell()
        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main,
                          ['--debug', 'list'])
        # In case of SessionClient when --debug switch is specified
        # 'keystoneauth' logger should be initialized.
        self.assertEqual('keystoneauth', _shell.cs.client.logger.name)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert_and_key(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', '--os-key', 'mickey', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert=('minnie', 'mickey'),
                                             verify=mock.ANY)
Esempio n. 5
0
class ShellTest(utils.TestCase):

    FAKE_ENV = {
        'OS_USERNAME': '******',
        'OS_PASSWORD': '******',
        'OS_PROJECT_NAME': 'tenant_name',
        'OS_AUTH_URL': 'http://no.where/v2.0',
    }

    # Patch os.environ to avoid required auth info.
    def make_env(self, exclude=None, include=None):
        env = dict((k, v) for k, v in self.FAKE_ENV.items() if k != exclude)
        env.update(include or {})
        self.useFixture(fixtures.MonkeyPatch('os.environ', env))

    def setUp(self):
        super(ShellTest, self).setUp()
        for var in self.FAKE_ENV:
            self.useFixture(
                fixtures.EnvironmentVariable(var, self.FAKE_ENV[var]))

        self.mock_completion()

    def shell(self, argstr):
        orig = sys.stdout
        try:
            sys.stdout = moves.StringIO()
            _shell = shell.OpenStackCinderShell()
            _shell.main(argstr.split())
        except SystemExit:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            self.assertEqual(0, exc_value.code)
        finally:
            out = sys.stdout.getvalue()
            sys.stdout.close()
            sys.stdout = orig

        return out

    def test_default_auth_env(self):
        _shell = shell.OpenStackCinderShell()
        args, __ = _shell.get_base_parser().parse_known_args([])
        self.assertEqual('', args.os_auth_type)

    def test_auth_type_env(self):
        self.make_env(exclude='OS_PASSWORD',
                      include={
                          'OS_AUTH_SYSTEM': 'non existent auth',
                          'OS_AUTH_TYPE': 'noauth'
                      })
        _shell = shell.OpenStackCinderShell()
        args, __ = _shell.get_base_parser().parse_known_args([])
        self.assertEqual('noauth', args.os_auth_type)

    def test_auth_system_env(self):
        self.make_env(exclude='OS_PASSWORD',
                      include={'OS_AUTH_SYSTEM': 'noauth'})
        _shell = shell.OpenStackCinderShell()
        args, __ = _shell.get_base_parser().parse_known_args([])
        self.assertEqual('noauth', args.os_auth_type)

    @mock.patch.object(cinderclient.shell.OpenStackCinderShell,
                       '_get_keystone_session')
    @mock.patch.object(cinderclient.client.SessionClient,
                       'authenticate',
                       side_effect=RuntimeError())
    def test_password_auth_type(self, mock_authenticate, mock_get_session):
        self.make_env(include={'OS_AUTH_TYPE': 'password'})
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Client instantiation because this test
        # focuses only keystoneauth1 indentity cli opts parsing.
        self.assertRaises(RuntimeError, _shell.main, ['list'])
        self.assertIsInstance(_shell.cs.client.session.auth, ks_password)

    def test_help_unknown_command(self):
        self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')

    def test_help(self):
        # Some expected help output, including microversioned commands
        required = [
            '.*?^usage: ',
            '.*?(?m)^\s+create\s+Creates a volume.',
            '.*?(?m)^\s+summary\s+Get volumes summary.',
            '.*?(?m)^Run "cinder help SUBCOMMAND" for help on a subcommand.',
        ]
        help_text = self.shell('help')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_on_subcommand(self):
        required = [
            '.*?^usage: cinder list',
            '.*?(?m)^Lists all volumes.',
        ]
        help_text = self.shell('help list')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_on_subcommand_mv(self):
        required = [
            '.*?^usage: cinder summary',
            '.*?(?m)^Get volumes summary.',
        ]
        help_text = self.shell('help summary')
        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    @ddt.data('backup-create --help', '--help backup-create')
    def test_dash_dash_help_on_subcommand(self, cmd):
        required = ['.*?^Creates a volume backup.']
        help_text = self.shell(cmd)

        for r in required:
            self.assertThat(help_text,
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def register_keystone_auth_fixture(self, mocker, url):
        mocker.register_uri('GET',
                            url,
                            text=keystone_client.keystone_request_callback)

    @requests_mock.Mocker()
    def test_version_discovery(self, mocker):
        _shell = shell.OpenStackCinderShell()
        sess = session.Session()

        os_auth_url = "https://wrongdiscoveryresponse.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)

        self.assertRaises(DiscoveryFailure,
                          _shell._discover_auth_versions,
                          sess,
                          auth_url=os_auth_url)

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v2_url, "Expected v2 url")
        self.assertIsNone(v3_url, "Expected no v3 url")

        os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
        self.register_keystone_auth_fixture(mocker, os_auth_url)
        v2_url, v3_url = _shell._discover_auth_versions(sess,
                                                        auth_url=os_auth_url)
        self.assertEqual(os_auth_url, v3_url, "Expected v3 url")
        self.assertIsNone(v2_url, "Expected no v2 url")

    @requests_mock.Mocker()
    def list_volumes_on_service(self, count, mocker):
        os_auth_url = "http://multiple.service.names/v2.0"
        mocker.register_uri('POST',
                            os_auth_url + "/tokens",
                            text=keystone_client.keystone_request_callback)
        mocker.register_uri('GET',
                            "http://cinder%i.api.com/v2/volumes/detail" %
                            count,
                            text='{"volumes": []}')
        self.make_env(include={
            'OS_AUTH_URL': os_auth_url,
            'CINDER_SERVICE_NAME': 'cinder%i' % count
        })
        _shell = shell.OpenStackCinderShell()
        _shell.main(['list'])

    def test_duplicate_filters(self):
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(exceptions.CommandError, _shell.main,
                          ['list', '--name', 'abc', '--filters', 'name=xyz'])

    @unittest.skip("Skip cuz I broke it")
    def test_cinder_service_name(self):
        # Failing with 'No mock address' means we are not
        # choosing the correct endpoint
        for count in range(1, 4):
            self.list_volumes_on_service(count)

    @mock.patch('keystoneauth1.identity.v2.Password')
    @mock.patch('keystoneauth1.adapter.Adapter.get_token',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('keystoneauth1.discover.Discover',
                side_effect=ks_exc.ConnectFailure())
    @mock.patch('sys.stdin', side_effect=mock.Mock)
    @mock.patch('getpass.getpass', return_value='password')
    def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover,
                               mock_token, mock_password):
        self.make_env(exclude='OS_PASSWORD')
        _shell = shell.OpenStackCinderShell()
        self.assertRaises(ks_exc.ConnectFailure, _shell.main, ['list'])
        mock_getpass.assert_called_with('OS Password: '******'password'
        # equal to mock_getpass.return_value.
        mock_password.assert_called_with(
            self.FAKE_ENV['OS_AUTH_URL'],
            password=mock_getpass.return_value,
            tenant_id='',
            tenant_name=self.FAKE_ENV['OS_PROJECT_NAME'],
            username=self.FAKE_ENV['OS_USERNAME'])

    @requests_mock.Mocker()
    def test_noauth_plugin(self, mocker):
        os_auth_url = "http://example.com/v2"
        mocker.register_uri('GET',
                            "%s/admin/volumes/detail" % os_auth_url,
                            text='{"volumes": []}')
        _shell = shell.OpenStackCinderShell()
        args = [
            '--os-endpoint', os_auth_url, '--os-auth-type', 'noauth',
            '--os-user-id', 'admin', '--os-project-id', 'admin', 'list'
        ]
        _shell.main(args)
        self.assertIsInstance(_shell.cs.client.session.auth,
                              noauth.CinderNoAuthPlugin)

    @mock.patch.object(cinderclient.client.HTTPClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    # Easiest way to make cinderclient use httpclient is a None session
    @mock.patch.object(cinderclient.shell.OpenStackCinderShell,
                       '_get_keystone_session',
                       return_value=None)
    def test_http_client_insecure(self, mock_authenticate, mock_session):
        self.make_env(include={'CINDERCLIENT_INSECURE': True})

        _shell = shell.OpenStackCinderShell()

        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main, ['list'])

        self.assertEqual(False, _shell.cs.client.verify_cert)

    @mock.patch.object(cinderclient.client.SessionClient,
                       'authenticate',
                       side_effect=exceptions.Unauthorized('No'))
    def test_session_client_debug_logger(self, mock_session):
        _shell = shell.OpenStackCinderShell()
        # This "fails" but instantiates the client.
        self.assertRaises(exceptions.CommandError, _shell.main,
                          ['--debug', 'list'])
        # In case of SessionClient when --debug switch is specified
        # 'keystoneauth' logger should be initialized.
        self.assertEqual('keystoneauth', _shell.cs.client.logger.name)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY)

    @mock.patch('keystoneauth1.session.Session.__init__',
                side_effect=RuntimeError())
    def test_http_client_with_cert_and_key(self, mock_session):
        _shell = shell.OpenStackCinderShell()

        # We crash the command after Session instantiation because this test
        # focuses only on arguments provided to Session.__init__
        args = '--os-cert', 'minnie', '--os-key', 'mickey', 'list'
        self.assertRaises(RuntimeError, _shell.main, args)
        mock_session.assert_called_once_with(cert=('minnie', 'mickey'),
                                             verify=mock.ANY)