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)
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')
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)
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)