diff options
author | Liem Nguyen <liem.m.nguyen@gmail.com> | 2012-05-23 18:16:50 +0000 |
---|---|---|
committer | Adam Young <ayoung@redhat.com> | 2012-07-03 17:26:34 -0400 |
commit | abc7c47c18f54c33668e9862fac614b7ce1d6d0a (patch) | |
tree | c3391d09de94271bd69a7dc59fa84234cb603aa7 | |
parent | 29be6d081df065e3075f963199641c59b23007cc (diff) | |
download | python-keystoneclient-abc7c47c18f54c33668e9862fac614b7ce1d6d0a.tar.gz |
Support 2-way SSL with Keystone server if it is configured to enforce
2-way SSL. See also https://review.openstack.org/#/c/7706/ for the
corresponding review for the 2-way SSL addition to Keystone.
Change-Id: If0cb46a43d663687396d93604a7139d85a4e7114
-rw-r--r-- | doc/source/shell.rst | 21 | ||||
-rw-r--r-- | keystoneclient/client.py | 10 | ||||
-rw-r--r-- | keystoneclient/service_catalog.py | 2 | ||||
-rw-r--r-- | keystoneclient/shell.py | 45 | ||||
-rw-r--r-- | tests/test_https.py | 60 |
5 files changed, 132 insertions, 6 deletions
diff --git a/doc/source/shell.rst b/doc/source/shell.rst index f86af72..209d681 100644 --- a/doc/source/shell.rst +++ b/doc/source/shell.rst @@ -42,13 +42,32 @@ options, it is easier to just set them as environment variables: The OpenStack Identity API version. +.. envvar:: OS_CA_CERT + + The location for the CA truststore (PEM formatted) for this client. + +.. envvar:: OS_CERT + + The location for the keystore (PEM formatted) containing the public + key of this client. This keystore can also optionally contain the + private key of this client. + +.. envvar:: OS_KEY + + The location for the keystore (PEM formatted) containing the private + key of this client. This value can be empty if the private key is + included in the OS_CERT file. + For example, in Bash you'd use:: export OS_USERNAME=yourname export OS_PASSWORD=yadayadayada export OS_TENANT_NAME=myproject - export OS_AUTH_URL=http://example.com:5000/v2.0/ + export OS_AUTH_URL=http(s)://example.com:5000/v2.0/ export OS_IDENTITY_API_VERSION=2.0 + export OS_CA_CERT=/etc/keystone/yourca.pem + export OS_CERT=/etc/keystone/yourpublickey.pem + export OS_KEY=/etc/keystone/yourprivatekey.pem From there, all shell commands take the form:: diff --git a/keystoneclient/client.py b/keystoneclient/client.py index 337f048..b53d7cf 100644 --- a/keystoneclient/client.py +++ b/keystoneclient/client.py @@ -38,8 +38,14 @@ class HTTPClient(httplib2.Http): def __init__(self, username=None, tenant_id=None, tenant_name=None, password=None, auth_url=None, region_name=None, timeout=None, - endpoint=None, token=None): - super(HTTPClient, self).__init__(timeout=timeout) + endpoint=None, token=None, cacert=None, key=None, + cert=None): + super(HTTPClient, self).__init__(timeout=timeout, ca_certs=cacert) + if cert: + if key: + self.add_certificate(key=key, cert=cert, domain='') + else: + self.add_certificate(key=cert, cert=cert, domain='') self.username = username self.tenant_id = tenant_id self.tenant_name = tenant_name diff --git a/keystoneclient/service_catalog.py b/keystoneclient/service_catalog.py index b78d019..faff74b 100644 --- a/keystoneclient/service_catalog.py +++ b/keystoneclient/service_catalog.py @@ -39,7 +39,7 @@ class ServiceCatalog(object): return token def url_for(self, attr=None, filter_value=None, - service_type='identity', endpoint_type='publicURL'): + service_type='identity', endpoint_type='publicURL'): """Fetch an endpoint from the service catalog. Fetch the specified endpoint from the service catalog for diff --git a/keystoneclient/shell.py b/keystoneclient/shell.py index 4c4c5eb..b1752e2 100644 --- a/keystoneclient/shell.py +++ b/keystoneclient/shell.py @@ -127,6 +127,41 @@ class OpenStackIdentityShell(object): default=env('SERVICE_ENDPOINT'), help='Defaults to env[SERVICE_ENDPOINT]') + parser.add_argument('--os_cacert', metavar='<ca-certificate>', + default=env('OS_CA_CERT'), + help='Defaults to env[OS_CA_CERT]') + + parser.add_argument('--os_cert', metavar='<certificate>', + default=env('OS_CERT'), + help='Defaults to env[OS_CERT]') + + parser.add_argument('--os_key', metavar='<key>', + default=env('OS_KEY'), + help='Defaults to env[OS_KEY]') + + # FIXME(dtroyer): The args below are here for diablo compatibility, + # remove them in folsum cycle + + parser.add_argument('--username', + metavar='<auth-user-name>', + help='Deprecated') + + parser.add_argument('--password', + metavar='<auth-password>', + help='Deprecated') + + parser.add_argument('--tenant_name', + metavar='<tenant-name>', + help='Deprecated') + + parser.add_argument('--auth_url', + metavar='<auth-url>', + help='Deprecated') + + parser.add_argument('--region_name', + metavar='<region-name>', + help='Deprecated') + return parser def get_subcommand_parser(self, version): @@ -246,7 +281,10 @@ class OpenStackIdentityShell(object): 'env[OS_AUTH_URL]') if utils.isunauthenticated(args.func): - self.cs = shell_generic.CLIENT_CLASS(endpoint=args.os_auth_url) + self.cs = shell_generic.CLIENT_CLASS(endpoint=args.os_auth_url, + cacert=args.os_cacert, + key=args.os_key, + cert=args.os_cert) else: token = None endpoint = None @@ -262,7 +300,10 @@ class OpenStackIdentityShell(object): endpoint=endpoint, password=args.os_password, auth_url=args.os_auth_url, - region_name=args.os_region_name) + region_name=args.os_region_name, + cacert=args.os_cacert, + key=args.os_key, + cert=args.os_cert) try: args.func(self.cs, args) diff --git a/tests/test_https.py b/tests/test_https.py new file mode 100644 index 0000000..4d433c6 --- /dev/null +++ b/tests/test_https.py @@ -0,0 +1,60 @@ +import httplib2 +import mock + +from keystoneclient import client +from tests import utils + + +FAKE_RESPONSE = httplib2.Response({"status": 200}) +FAKE_BODY = '{"hi": "there"}' +MOCK_REQUEST = mock.Mock(return_value=(FAKE_RESPONSE, FAKE_BODY)) + + +def get_client(): + cl = client.HTTPClient(username="username", password="password", + tenant_id="tenant", auth_url="auth_test", + cacert="ca.pem", key="key.pem", cert="cert.pem") + return cl + + +def get_authed_client(): + cl = get_client() + cl.management_url = "https://127.0.0.1:5000" + cl.auth_token = "token" + return cl + + +class ClientTest(utils.TestCase): + + def test_get(self): + cl = get_authed_client() + + @mock.patch.object(httplib2.Http, "request", MOCK_REQUEST) + @mock.patch('time.time', mock.Mock(return_value=1234)) + def test_get_call(): + resp, body = cl.get("/hi") + headers = {"X-Auth-Token": "token", + "User-Agent": cl.USER_AGENT} + MOCK_REQUEST.assert_called_with("https://127.0.0.1:5000/hi", + "GET", headers=headers) + # Automatic JSON parsing + self.assertEqual(body, {"hi": "there"}) + + test_get_call() + + def test_post(self): + cl = get_authed_client() + + @mock.patch.object(httplib2.Http, "request", MOCK_REQUEST) + def test_post_call(): + cl.post("/hi", body=[1, 2, 3]) + headers = { + "X-Auth-Token": "token", + "Content-Type": "application/json", + "User-Agent": cl.USER_AGENT + } + MOCK_REQUEST.assert_called_with("https://127.0.0.1:5000/hi", + "POST", headers=headers, + body='[1, 2, 3]') + + test_post_call() |