Merge lp:~zulcss/ubuntu/precise/python-keystoneclient/new into lp:~ubuntu-cloud-archive/ubuntu/precise/python-keystoneclient/trunk

Proposed by Chuck Short
Status: Merged
Merged at revision: 26
Proposed branch: lp:~zulcss/ubuntu/precise/python-keystoneclient/new
Merge into: lp:~ubuntu-cloud-archive/ubuntu/precise/python-keystoneclient/trunk
Diff against target: 11180 lines (+8589/-754)
109 files modified
.gitignore (+2/-0)
AUTHORS (+11/-0)
PKG-INFO (+93/-64)
README.rst (+90/-62)
babel.cfg (+1/-0)
debian/changelog (+23/-0)
debian/control (+3/-3)
doc/source/_theme/layout.html (+83/-0)
doc/source/_theme/theme.conf (+4/-0)
doc/source/conf.py (+22/-11)
doc/source/index.rst (+11/-10)
doc/source/ref/client.rst (+0/-8)
doc/source/ref/endpoints.rst (+0/-11)
doc/source/ref/exceptions.rst (+0/-8)
doc/source/ref/generic-client.rst (+0/-12)
doc/source/ref/index.rst (+0/-16)
doc/source/ref/roles.rst (+0/-9)
doc/source/ref/services.rst (+0/-11)
doc/source/ref/tenants.rst (+0/-11)
doc/source/ref/users.rst (+0/-9)
doc/source/releases.rst (+28/-101)
doc/source/shell.rst (+34/-13)
doc/source/static/basic.css (+416/-0)
doc/source/static/default.css (+230/-0)
doc/source/static/jquery.tweet.js (+154/-0)
doc/source/static/nature.css (+245/-0)
doc/source/static/tweaks.css (+94/-0)
doc/source/using-api.rst (+19/-15)
examples/pki/certs/cacert.pem (+18/-0)
examples/pki/certs/middleware.pem (+33/-0)
examples/pki/certs/signing_cert.pem (+17/-0)
examples/pki/certs/ssl_cert.pem (+17/-0)
examples/pki/cms/auth_token_revoked.json (+1/-0)
examples/pki/cms/auth_token_revoked.pem (+42/-0)
examples/pki/cms/auth_token_scoped.json (+1/-0)
examples/pki/cms/auth_token_scoped.pem (+41/-0)
examples/pki/cms/auth_token_unscoped.json (+1/-0)
examples/pki/cms/auth_token_unscoped.pem (+17/-0)
examples/pki/cms/revocation_list.json (+1/-0)
examples/pki/cms/revocation_list.pem (+12/-0)
examples/pki/gen_pki.sh (+222/-0)
examples/pki/private/cakey.pem (+16/-0)
examples/pki/private/signing_key.pem (+16/-0)
examples/pki/private/ssl_key.pem (+16/-0)
keystoneclient/access.py (+144/-0)
keystoneclient/base.py (+126/-4)
keystoneclient/client.py (+72/-41)
keystoneclient/common/cms.py (+169/-0)
keystoneclient/contrib/bootstrap/shell.py (+28/-0)
keystoneclient/exceptions.py (+19/-1)
keystoneclient/generic/shell.py (+9/-7)
keystoneclient/locale/keystoneclient.pot (+20/-0)
keystoneclient/middleware/auth_token.py (+864/-0)
keystoneclient/middleware/test.py (+67/-0)
keystoneclient/openstack/common/cfg.py (+1653/-0)
keystoneclient/openstack/common/iniparser.py (+130/-0)
keystoneclient/openstack/common/jsonutils.py (+148/-0)
keystoneclient/openstack/common/setup.py (+63/-39)
keystoneclient/openstack/common/timeutils.py (+137/-0)
keystoneclient/service_catalog.py (+17/-3)
keystoneclient/shell.py (+97/-75)
keystoneclient/utils.py (+25/-4)
keystoneclient/v2_0/client.py (+135/-37)
keystoneclient/v2_0/shell.py (+28/-7)
keystoneclient/v2_0/tenants.py (+15/-3)
keystoneclient/v2_0/tokens.py (+9/-1)
keystoneclient/v3/__init__.py (+1/-0)
keystoneclient/v3/client.py (+85/-0)
keystoneclient/v3/credentials.py (+57/-0)
keystoneclient/v3/domains.py (+55/-0)
keystoneclient/v3/endpoints.py (+86/-0)
keystoneclient/v3/policies.py (+77/-0)
keystoneclient/v3/projects.py (+82/-0)
keystoneclient/v3/roles.py (+110/-0)
keystoneclient/v3/services.py (+60/-0)
keystoneclient/v3/users.py (+70/-0)
keystoneclient/versioninfo (+1/-1)
openstack-common.conf (+1/-1)
python_keystoneclient.egg-info/PKG-INFO (+93/-64)
python_keystoneclient.egg-info/SOURCES.txt (+66/-9)
python_keystoneclient.egg-info/requires.txt (+1/-1)
run_tests.sh (+6/-1)
setup.cfg (+14/-0)
setup.py (+2/-1)
tests/client_fixtures.py (+72/-0)
tests/test_access.py (+56/-0)
tests/test_auth_token_middleware.py (+670/-0)
tests/test_client.py (+37/-0)
tests/test_http.py (+31/-19)
tests/test_https.py (+10/-16)
tests/v2_0/test_auth.py (+0/-6)
tests/v2_0/test_ec2.py (+1/-1)
tests/v2_0/test_endpoints.py (+1/-1)
tests/v2_0/test_roles.py (+15/-17)
tests/v2_0/test_tenants.py (+55/-17)
tests/v2_0/test_tokens.py (+1/-1)
tests/v2_0/test_users.py (+1/-1)
tests/v3/test_credentials.py (+22/-0)
tests/v3/test_domains.py (+20/-0)
tests/v3/test_endpoints.py (+78/-0)
tests/v3/test_policies.py (+21/-0)
tests/v3/test_projects.py (+69/-0)
tests/v3/test_roles.py (+252/-0)
tests/v3/test_services.py (+21/-0)
tests/v3/test_users.py (+23/-0)
tests/v3/utils.py (+227/-0)
tools/install_venv.py (+25/-0)
tools/pip-requires (+1/-1)
tools/test-requires (+4/-0)
To merge this branch: bzr merge lp:~zulcss/ubuntu/precise/python-keystoneclient/new
Reviewer Review Type Date Requested Status
James Page Approve
Review via email: mp+137202@code.launchpad.net
To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

LGTM; please upload with -v to ensure change history maintained.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.gitignore'
2--- .gitignore 2012-08-16 12:34:22 +0000
3+++ .gitignore 2012-11-30 14:11:19 +0000
4@@ -6,8 +6,10 @@
5 .idea
6 *.swp
7 *~
8+.tox
9 AUTHORS
10 build
11 dist
12 python_keystoneclient.egg-info
13 keystoneclient/versioninfo
14+doc/source/api
15
16=== modified file 'AUTHORS'
17--- AUTHORS 2012-09-07 13:13:18 +0000
18+++ AUTHORS 2012-11-30 14:11:19 +0000
19@@ -1,5 +1,7 @@
20 Adam Gandelman <adamg@canonical.com>
21+Adam Young <ayoung@redhat.com>
22 Alan Pevec <apevec@redhat.com>
23+Alex Meade <alex.meade@rackspace.com>
24 Anthony Young <sleepsonthefloor@gmail.com>
25 Bhuvan Arumugam <bhuvan@apache.org>
26 Brian Waldon <bcwaldon@gmail.com>
27@@ -8,20 +10,29 @@
28 Dean Troyer <dtroyer@gmail.com>
29 Dolph Mathews <dolph.mathews@gmail.com>
30 Dominik Heidler <dheidler@suse.de>
31+Doug Hellmann <doug.hellmann@dreamhost.com>
32 Everett Toews <everett.toews@gmail.com>
33 Gabriel Hurley <gabriel@strikeawe.com>
34 Ghe Rivero <ghe@debian.org>
35 Hengqing Hu <hudayou@hotmail.com>
36+Henry Nash <henryn@linux.vnet.ibm.com>
37+Ionuț Arțăriși <iartarisi@suse.cz>
38 jakedahn <jake@ansolabs.com>
39+Jay Pipes <jaypipes@gmail.com>
40 Jesse Andrews <anotherjesse@gmail.com>
41+Joe Gordon <jogo@cloudscaling.com>
42 Joe Heck <heckj@mac.com>
43+Joseph W. Breu <breu@breu.org>
44 Josh Kearney <josh@jk0.org>
45 Ken Thomas <krt@yahoo-inc.com>
46+Kevin L. Mitchell <kevin.mitchell@rackspace.com>
47+Laurence Miao <laurence.miao@gmail.com>
48 Liem Nguyen <liem.m.nguyen@gmail.com>
49 Lorin Hochstein <lorin@nimbisservices.com>
50 lrqrun <lrqrun@gmail.com>
51 Monty Taylor <mordred@inaugust.com>
52 Peng Yong <ppyy@pubyun.com>
53+Sam Morrison <sorrison@gmail.com>
54 Sascha Peilicke <saschpe@suse.de>
55 termie <github@anarkystic.com>
56 Thierry Carrez <thierry@openstack.org>
57
58=== modified file 'PKG-INFO'
59--- PKG-INFO 2012-09-07 13:13:18 +0000
60+++ PKG-INFO 2012-11-30 14:11:19 +0000
61@@ -1,28 +1,27 @@
62 Metadata-Version: 1.1
63 Name: python-keystoneclient
64-Version: 0.1.3
65-Summary: Client library for OpenStack Keystone API
66+Version: 0.2.0
67+Summary: Client library for OpenStack Identity API (Keystone)
68 Home-page: https://github.com/openstack/python-keystoneclient
69 Author: Nebula Inc, based on work by Rackspace and Jacob Kaplan-Moss
70 Author-email: gabriel.hurley@nebula.com
71 License: Apache
72-Description: Python bindings to the OpenStack Keystone API
73- =============================================
74-
75- This is a client for the OpenStack Keystone API. There's a Python API (the
76- ``keystoneclient`` module), and a command-line script (``keystone``). The
77- Keystone 2.0 API is still a moving target, so this module will remain in
78- "Beta" status until the API is finalized and fully implemented.
79-
80- Development takes place via the usual OpenStack processes as outlined in
81- the `OpenStack wiki`_. The master repository is on GitHub__.
82+Description: Python bindings to the OpenStack Identity API (Keystone)
83+ ========================================================
84+
85+ This is a client for the OpenStack Identity API, implemented by Keystone.
86+ There's a Python API (the ``keystoneclient`` module), and a command-line script
87+ (``keystone``).
88+
89+ Development takes place via the usual OpenStack processes as outlined in the
90+ `OpenStack wiki`_. The master repository is on GitHub__.
91
92 __ http://wiki.openstack.org/HowToContribute
93 __ http://github.com/openstack/python-keystoneclient
94
95- This code a fork of `Rackspace's python-novaclient`__ which is in turn a fork of
96- `Jacobian's python-cloudservers`__. The python-keystoneclient is licensed under
97- the Apache License like the rest of OpenStack.
98+ This code a fork of `Rackspace's python-novaclient`__ which is in turn a fork
99+ of `Jacobian's python-cloudservers`__. The python-keystoneclient is licensed
100+ under the Apache License like the rest of OpenStack.
101
102 __ http://github.com/rackspace/python-novaclient
103 __ http://github.com/jacobian/python-cloudservers
104@@ -35,7 +34,7 @@
105
106 By way of a quick-start::
107
108- # use v2.0 auth with http://example.com:5000/v2.0")
109+ # use v2.0 auth with http://example.com:5000/v2.0
110 >>> from keystoneclient.v2_0 import client
111 >>> keystone = client.Client(username=USERNAME, password=PASSWORD, tenant_name=TENANT, auth_url=AUTH_URL)
112 >>> keystone.tenants.list()
113@@ -46,40 +45,45 @@
114 Command-line API
115 ----------------
116
117- Installing this package gets you a shell command, ``keystone``, that you
118- can use to interact with Keystone's Identity API.
119+ Installing this package gets you a shell command, ``keystone``, that you can
120+ use to interact with OpenStack's Identity API.
121
122- You'll need to provide your OpenStack tenant, username and password. You can
123- do this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password``
124+ You'll need to provide your OpenStack tenant, username and password. You can do
125+ this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password``
126 params, but it's easier to just set them as environment variables::
127
128 export OS_TENANT_NAME=project
129 export OS_USERNAME=user
130 export OS_PASSWORD=pass
131
132- You will also need to define the authentication url with ``--os-auth-url`` and the
133- version of the API with ``--os-identity-api-version``. Or set them as an environment
134- variables as well::
135+ You will also need to define the authentication url with ``--os-auth-url`` and
136+ the version of the API with ``--os-identity-api-version``. Or set them as an
137+ environment variables as well::
138
139 export OS_AUTH_URL=http://example.com:5000/v2.0
140 export OS_IDENTITY_API_VERSION=2.0
141
142- Alternatively, to authenticate to Keystone without a username/password,
143- such as when there are no users in the database yet, use the service
144- token and endpoint arguemnts. The service token is set in keystone.conf as
145- ``admin_token``; set it with ``service_token``. Note: keep the service token
146- secret as it allows total access to Keystone's database. The admin endpoint is set
147- with ``--endpoint`` or ``SERVICE_ENDPOINT``::
148-
149- export SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog
150- export SERVICE_ENDPOINT=http://example.com:35357/v2.0
151-
152- Since Keystone can return multiple regions in the Service Catalog, you
153- can specify the one you want with ``--region_name`` (or
154- ``export OS_REGION_NAME``). It defaults to the first in the list returned.
155-
156- You'll find complete documentation on the shell by running
157- ``keystone help``::
158+ Alternatively, to bypass username/password authentication, you can provide a
159+ pre-established token. In Keystone, this approach is necessary to bootstrap the
160+ service with an administrative user, tenant & role (to do so, provide the
161+ client with the value of your ``admin_token`` defined in ``keystone.conf`` in
162+ addition to the URL of your admin API deployment, typically on port 35357)::
163+
164+ export OS_SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog
165+ export OS_SERVICE_ENDPOINT=http://example.com:35357/v2.0
166+
167+ Since the Identity service can return multiple regions in the service catalog,
168+ you can specify the one you want with ``--os-region-name`` (or ``export
169+ OS_REGION_NAME``)::
170+
171+ export OS_REGION_NAME=north
172+
173+ .. WARNING::
174+
175+ If a region is not specified and multiple regions are returned by the
176+ Identity service, the client may not access the same region consistently.
177+
178+ You'll find complete documentation on the shell by running ``keystone help``::
179
180 usage: keystone [--os-username <auth-user-name>]
181 [--os-password <auth-password>]
182@@ -87,14 +91,17 @@
183 [--os-tenant-id <tenant-id>] [--os-auth-url <auth-url>]
184 [--os-region-name <region-name>]
185 [--os-identity-api-version <identity-api-version>]
186- [--token <service-token>] [--endpoint <service-endpoint>]
187+ [--os-token <service-token>]
188+ [--os-endpoint <service-endpoint>]
189+ [--os-cacert <ca-certificate>] [--os-cert <certificate>]
190+ [--os-key <key>] [--insecure]
191 <subcommand> ...
192
193 Command-line interface to the OpenStack Identity API.
194
195 Positional arguments:
196- <subcommand>
197- catalog List service catalog, possibly filtered by service.
198+ <subcommand>
199+ catalog
200 ec2-credentials-create
201 Create EC2-compatibile credentials for user per tenant
202 ec2-credentials-delete
203@@ -105,13 +112,12 @@
204 List EC2-compatibile credentials for a user
205 endpoint-create Create a new endpoint associated with a service
206 endpoint-delete Delete a service endpoint
207- endpoint-get Find endpoint filtered by a specific attribute or
208- service type
209+ endpoint-get
210 endpoint-list List configured service endpoints
211 role-create Create new role
212 role-delete Delete role
213 role-get Display role details
214- role-list List all available roles
215+ role-list List all roles
216 service-create Add service to Service Catalog
217 service-delete Delete service from Service Catalog
218 service-get Display service from Service Catalog
219@@ -121,46 +127,69 @@
220 tenant-get Display tenant details
221 tenant-list List all tenants
222 tenant-update Update tenant name, description, enabled status
223- token-get Display the current user token
224+ token-get
225 user-create Create new user
226 user-delete Delete user
227+ user-get Display user details.
228 user-list List users
229 user-password-update
230 Update user password
231 user-role-add Add role to user
232+ user-role-list List roles granted to a user
233 user-role-remove Remove role from user
234- user-role-list List roles for user
235 user-update Update user's name, email, and enabled status
236 discover Discover Keystone servers and show authentication
237 protocols and
238+ bootstrap Grants a new role to a new user on a new tenant, after
239+ creating each.
240+ bash-completion Prints all of the commands and options to stdout.
241 help Display help about this program or one of its
242 subcommands.
243
244 Optional arguments:
245- --os-username <auth-user-name>
246- Defaults to env[OS_USERNAME]
247- --os-password <auth-password>
248- Defaults to env[OS_PASSWORD]
249- --os-tenant-name <auth-tenant-name>
250- Defaults to env[OS_TENANT_NAME]
251- --os-tenant-id <tenant-id>
252- Defaults to env[OS_TENANT_ID]
253- --os-auth-url <auth-url>
254- Defaults to env[OS_AUTH_URL]
255- --os-region-name <region-name>
256+ --os-username <auth-user-name>
257+ Name used for authentication with the OpenStack
258+ Identity service. Defaults to env[OS_USERNAME]
259+ --os-password <auth-password>
260+ Password used for authentication with the OpenStack
261+ Identity service. Defaults to env[OS_PASSWORD]
262+ --os-tenant-name <auth-tenant-name>
263+ Tenant to request authorization on. Defaults to
264+ env[OS_TENANT_NAME]
265+ --os-tenant-id <tenant-id>
266+ Tenant to request authorization on. Defaults to
267+ env[OS_TENANT_ID]
268+ --os-auth-url <auth-url>
269+ Specify the Identity endpoint to use for
270+ authentication. Defaults to env[OS_AUTH_URL]
271+ --os-region-name <region-name>
272 Defaults to env[OS_REGION_NAME]
273- --os-identity-api-version <identity-api-version>
274+ --os-identity-api-version <identity-api-version>
275 Defaults to env[OS_IDENTITY_API_VERSION] or 2.0
276- --token <service-token>
277- Defaults to env[SERVICE_TOKEN]
278- --endpoint <service-endpoint>
279- Defaults to env[SERVICE_ENDPOINT]
280+ --os-token <service-token>
281+ Specify an existing token to use instead of retrieving
282+ one via authentication (e.g. with username &
283+ password). Defaults to env[OS_SERVICE_TOKEN]
284+ --os-endpoint <service-endpoint>
285+ Specify an endpoint to use instead of retrieving one
286+ from the service catalog (via authentication).
287+ Defaults to env[OS_SERVICE_ENDPOINT]
288+ --os-cacert <ca-certificate>
289+ Defaults to env[OS_CACERT]
290+ --os-cert <certificate>
291+ Defaults to env[OS_CERT]
292+ --os-key <key> Defaults to env[OS_KEY]
293+ --insecure Explicitly allow keystoneclient to perform "insecure"
294+ SSL (https) requests. The server's certificate will
295+ not be verified against any certificate authorities.
296+ This option should be used with caution.
297
298- See "keystone help COMMAND" for help on a specific command.
299+ See "keystone help COMMAND" for help on a specific command.
300
301 Platform: UNKNOWN
302 Classifier: Development Status :: 4 - Beta
303 Classifier: Environment :: Console
304+Classifier: Environment :: OpenStack
305 Classifier: Intended Audience :: Developers
306 Classifier: Intended Audience :: Information Technology
307 Classifier: License :: OSI Approved :: Apache Software License
308
309=== modified file 'README.rst'
310--- README.rst 2012-08-16 12:34:22 +0000
311+++ README.rst 2012-11-30 14:11:19 +0000
312@@ -1,20 +1,19 @@
313-Python bindings to the OpenStack Keystone API
314-=============================================
315-
316-This is a client for the OpenStack Keystone API. There's a Python API (the
317-``keystoneclient`` module), and a command-line script (``keystone``). The
318-Keystone 2.0 API is still a moving target, so this module will remain in
319-"Beta" status until the API is finalized and fully implemented.
320-
321-Development takes place via the usual OpenStack processes as outlined in
322-the `OpenStack wiki`_. The master repository is on GitHub__.
323+Python bindings to the OpenStack Identity API (Keystone)
324+========================================================
325+
326+This is a client for the OpenStack Identity API, implemented by Keystone.
327+There's a Python API (the ``keystoneclient`` module), and a command-line script
328+(``keystone``).
329+
330+Development takes place via the usual OpenStack processes as outlined in the
331+`OpenStack wiki`_. The master repository is on GitHub__.
332
333 __ http://wiki.openstack.org/HowToContribute
334 __ http://github.com/openstack/python-keystoneclient
335
336-This code a fork of `Rackspace's python-novaclient`__ which is in turn a fork of
337-`Jacobian's python-cloudservers`__. The python-keystoneclient is licensed under
338-the Apache License like the rest of OpenStack.
339+This code a fork of `Rackspace's python-novaclient`__ which is in turn a fork
340+of `Jacobian's python-cloudservers`__. The python-keystoneclient is licensed
341+under the Apache License like the rest of OpenStack.
342
343 __ http://github.com/rackspace/python-novaclient
344 __ http://github.com/jacobian/python-cloudservers
345@@ -27,7 +26,7 @@
346
347 By way of a quick-start::
348
349- # use v2.0 auth with http://example.com:5000/v2.0")
350+ # use v2.0 auth with http://example.com:5000/v2.0
351 >>> from keystoneclient.v2_0 import client
352 >>> keystone = client.Client(username=USERNAME, password=PASSWORD, tenant_name=TENANT, auth_url=AUTH_URL)
353 >>> keystone.tenants.list()
354@@ -38,40 +37,45 @@
355 Command-line API
356 ----------------
357
358-Installing this package gets you a shell command, ``keystone``, that you
359-can use to interact with Keystone's Identity API.
360+Installing this package gets you a shell command, ``keystone``, that you can
361+use to interact with OpenStack's Identity API.
362
363-You'll need to provide your OpenStack tenant, username and password. You can
364-do this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password``
365+You'll need to provide your OpenStack tenant, username and password. You can do
366+this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password``
367 params, but it's easier to just set them as environment variables::
368
369 export OS_TENANT_NAME=project
370 export OS_USERNAME=user
371 export OS_PASSWORD=pass
372
373-You will also need to define the authentication url with ``--os-auth-url`` and the
374-version of the API with ``--os-identity-api-version``. Or set them as an environment
375-variables as well::
376+You will also need to define the authentication url with ``--os-auth-url`` and
377+the version of the API with ``--os-identity-api-version``. Or set them as an
378+environment variables as well::
379
380 export OS_AUTH_URL=http://example.com:5000/v2.0
381 export OS_IDENTITY_API_VERSION=2.0
382
383-Alternatively, to authenticate to Keystone without a username/password,
384-such as when there are no users in the database yet, use the service
385-token and endpoint arguemnts. The service token is set in keystone.conf as
386-``admin_token``; set it with ``service_token``. Note: keep the service token
387-secret as it allows total access to Keystone's database. The admin endpoint is set
388-with ``--endpoint`` or ``SERVICE_ENDPOINT``::
389-
390- export SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog
391- export SERVICE_ENDPOINT=http://example.com:35357/v2.0
392-
393-Since Keystone can return multiple regions in the Service Catalog, you
394-can specify the one you want with ``--region_name`` (or
395-``export OS_REGION_NAME``). It defaults to the first in the list returned.
396-
397-You'll find complete documentation on the shell by running
398-``keystone help``::
399+Alternatively, to bypass username/password authentication, you can provide a
400+pre-established token. In Keystone, this approach is necessary to bootstrap the
401+service with an administrative user, tenant & role (to do so, provide the
402+client with the value of your ``admin_token`` defined in ``keystone.conf`` in
403+addition to the URL of your admin API deployment, typically on port 35357)::
404+
405+ export OS_SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog
406+ export OS_SERVICE_ENDPOINT=http://example.com:35357/v2.0
407+
408+Since the Identity service can return multiple regions in the service catalog,
409+you can specify the one you want with ``--os-region-name`` (or ``export
410+OS_REGION_NAME``)::
411+
412+ export OS_REGION_NAME=north
413+
414+.. WARNING::
415+
416+ If a region is not specified and multiple regions are returned by the
417+ Identity service, the client may not access the same region consistently.
418+
419+You'll find complete documentation on the shell by running ``keystone help``::
420
421 usage: keystone [--os-username <auth-user-name>]
422 [--os-password <auth-password>]
423@@ -79,14 +83,17 @@
424 [--os-tenant-id <tenant-id>] [--os-auth-url <auth-url>]
425 [--os-region-name <region-name>]
426 [--os-identity-api-version <identity-api-version>]
427- [--token <service-token>] [--endpoint <service-endpoint>]
428+ [--os-token <service-token>]
429+ [--os-endpoint <service-endpoint>]
430+ [--os-cacert <ca-certificate>] [--os-cert <certificate>]
431+ [--os-key <key>] [--insecure]
432 <subcommand> ...
433
434 Command-line interface to the OpenStack Identity API.
435
436 Positional arguments:
437- <subcommand>
438- catalog List service catalog, possibly filtered by service.
439+ <subcommand>
440+ catalog
441 ec2-credentials-create
442 Create EC2-compatibile credentials for user per tenant
443 ec2-credentials-delete
444@@ -97,13 +104,12 @@
445 List EC2-compatibile credentials for a user
446 endpoint-create Create a new endpoint associated with a service
447 endpoint-delete Delete a service endpoint
448- endpoint-get Find endpoint filtered by a specific attribute or
449- service type
450+ endpoint-get
451 endpoint-list List configured service endpoints
452 role-create Create new role
453 role-delete Delete role
454 role-get Display role details
455- role-list List all available roles
456+ role-list List all roles
457 service-create Add service to Service Catalog
458 service-delete Delete service from Service Catalog
459 service-get Display service from Service Catalog
460@@ -113,39 +119,61 @@
461 tenant-get Display tenant details
462 tenant-list List all tenants
463 tenant-update Update tenant name, description, enabled status
464- token-get Display the current user token
465+ token-get
466 user-create Create new user
467 user-delete Delete user
468+ user-get Display user details.
469 user-list List users
470 user-password-update
471 Update user password
472 user-role-add Add role to user
473+ user-role-list List roles granted to a user
474 user-role-remove Remove role from user
475- user-role-list List roles for user
476 user-update Update user's name, email, and enabled status
477 discover Discover Keystone servers and show authentication
478 protocols and
479+ bootstrap Grants a new role to a new user on a new tenant, after
480+ creating each.
481+ bash-completion Prints all of the commands and options to stdout.
482 help Display help about this program or one of its
483 subcommands.
484
485 Optional arguments:
486- --os-username <auth-user-name>
487- Defaults to env[OS_USERNAME]
488- --os-password <auth-password>
489- Defaults to env[OS_PASSWORD]
490- --os-tenant-name <auth-tenant-name>
491- Defaults to env[OS_TENANT_NAME]
492- --os-tenant-id <tenant-id>
493- Defaults to env[OS_TENANT_ID]
494- --os-auth-url <auth-url>
495- Defaults to env[OS_AUTH_URL]
496- --os-region-name <region-name>
497+ --os-username <auth-user-name>
498+ Name used for authentication with the OpenStack
499+ Identity service. Defaults to env[OS_USERNAME]
500+ --os-password <auth-password>
501+ Password used for authentication with the OpenStack
502+ Identity service. Defaults to env[OS_PASSWORD]
503+ --os-tenant-name <auth-tenant-name>
504+ Tenant to request authorization on. Defaults to
505+ env[OS_TENANT_NAME]
506+ --os-tenant-id <tenant-id>
507+ Tenant to request authorization on. Defaults to
508+ env[OS_TENANT_ID]
509+ --os-auth-url <auth-url>
510+ Specify the Identity endpoint to use for
511+ authentication. Defaults to env[OS_AUTH_URL]
512+ --os-region-name <region-name>
513 Defaults to env[OS_REGION_NAME]
514- --os-identity-api-version <identity-api-version>
515+ --os-identity-api-version <identity-api-version>
516 Defaults to env[OS_IDENTITY_API_VERSION] or 2.0
517- --token <service-token>
518- Defaults to env[SERVICE_TOKEN]
519- --endpoint <service-endpoint>
520- Defaults to env[SERVICE_ENDPOINT]
521+ --os-token <service-token>
522+ Specify an existing token to use instead of retrieving
523+ one via authentication (e.g. with username &
524+ password). Defaults to env[OS_SERVICE_TOKEN]
525+ --os-endpoint <service-endpoint>
526+ Specify an endpoint to use instead of retrieving one
527+ from the service catalog (via authentication).
528+ Defaults to env[OS_SERVICE_ENDPOINT]
529+ --os-cacert <ca-certificate>
530+ Defaults to env[OS_CACERT]
531+ --os-cert <certificate>
532+ Defaults to env[OS_CERT]
533+ --os-key <key> Defaults to env[OS_KEY]
534+ --insecure Explicitly allow keystoneclient to perform "insecure"
535+ SSL (https) requests. The server's certificate will
536+ not be verified against any certificate authorities.
537+ This option should be used with caution.
538
539-See "keystone help COMMAND" for help on a specific command.
540+ See "keystone help COMMAND" for help on a specific command.
541
542=== added file 'babel.cfg'
543--- babel.cfg 1970-01-01 00:00:00 +0000
544+++ babel.cfg 2012-11-30 14:11:19 +0000
545@@ -0,0 +1,1 @@
546+[python: **.py]
547
548=== modified file 'debian/changelog'
549--- debian/changelog 2012-10-10 18:40:31 +0000
550+++ debian/changelog 2012-11-30 14:11:19 +0000
551@@ -1,3 +1,26 @@
552+python-keystoneclient (1:0.2.0-0ubuntu1~cloud0) precise-grizzly; urgency=low
553+
554+ * New upstream release for the Ubuntu Cloud Archive.
555+
556+ -- Chuck Short <zulcss@ubuntu.com> Fri, 30 Nov 2012 08:07:06 -0600
557+
558+python-keystoneclient (1:0.2.0-0ubuntu1) raring; urgency=low
559+
560+ * New upstream release.
561+ * debian/control: Add python-pkg-resources. (LP: #1071032)
562+
563+ -- Chuck Short <zulcss@ubuntu.com> Thu, 29 Nov 2012 12:46:21 -0600
564+
565+python-keystoneclient (1:0.1.3.37-0ubuntu1) raring-proposed; urgency=low
566+
567+ [ Adam Gandelman ]
568+ * New upstream release.
569+
570+ [ Adrien Cunin ]
571+ * Typo and cosmetic fixes to package description (LP: #960350)
572+
573+ -- Adam Gandelman <adamg@canonical.com> Wed, 31 Oct 2012 09:17:26 +0100
574+
575 python-keystoneclient (1:0.1.3-0ubuntu1~cloud0) precise-folsom; urgency=low
576
577 * New upstream snapshot for the Ubuntu Cloud Archive.
578
579=== modified file 'debian/control'
580--- debian/control 2012-03-02 10:31:44 +0000
581+++ debian/control 2012-11-30 14:11:19 +0000
582@@ -8,7 +8,7 @@
583
584 Package: python-keystoneclient
585 Architecture: all
586-Depends: ${python:Depends}, ${misc:Depends}, python-httplib2
587-Description: Client libary for Openstack Keystone API
588- This is a client for the OpenStack Keystone API. There's a Python API
589+Depends: ${python:Depends}, ${misc:Depends}, python-pkg-resources
590+Description: Client library for OpenStack Identity API
591+ This is a client for the OpenStack Identity API. There's a Python API
592 (the ``keystoneclient`` module), and a command-line script (``keystone``).
593
594=== added directory 'doc/source/_templates'
595=== added file 'doc/source/_templates/.placeholder'
596=== added directory 'doc/source/_theme'
597=== added file 'doc/source/_theme/layout.html'
598--- doc/source/_theme/layout.html 1970-01-01 00:00:00 +0000
599+++ doc/source/_theme/layout.html 2012-11-30 14:11:19 +0000
600@@ -0,0 +1,83 @@
601+{% extends "basic/layout.html" %}
602+{% set css_files = css_files + ['_static/tweaks.css'] %}
603+{% set script_files = script_files + ['_static/jquery.tweet.js'] %}
604+
605+{%- macro sidebar() %}
606+ {%- if not embedded %}{% if not theme_nosidebar|tobool %}
607+ <div class="sphinxsidebar">
608+ <div class="sphinxsidebarwrapper">
609+ {%- block sidebarlogo %}
610+ {%- if logo %}
611+ <p class="logo"><a href="{{ pathto(master_doc) }}">
612+ <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
613+ </a></p>
614+ {%- endif %}
615+ {%- endblock %}
616+ {%- block sidebartoc %}
617+ {%- if display_toc %}
618+ <h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
619+ {{ toc }}
620+ {%- endif %}
621+ {%- endblock %}
622+ {%- block sidebarrel %}
623+ {%- if prev %}
624+ <h4>{{ _('Previous topic') }}</h4>
625+ <p class="topless"><a href="{{ prev.link|e }}"
626+ title="{{ _('previous chapter') }}">{{ prev.title }}</a></p>
627+ {%- endif %}
628+ {%- if next %}
629+ <h4>{{ _('Next topic') }}</h4>
630+ <p class="topless"><a href="{{ next.link|e }}"
631+ title="{{ _('next chapter') }}">{{ next.title }}</a></p>
632+ {%- endif %}
633+ {%- endblock %}
634+ {%- block sidebarsourcelink %}
635+ {%- if show_source and has_source and sourcename %}
636+ <h3>{{ _('This Page') }}</h3>
637+ <ul class="this-page-menu">
638+ <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}"
639+ rel="nofollow">{{ _('Show Source') }}</a></li>
640+ </ul>
641+ {%- endif %}
642+ {%- endblock %}
643+ {%- if customsidebar %}
644+ {% include customsidebar %}
645+ {%- endif %}
646+ {%- block sidebarsearch %}
647+ {%- if pagename != "search" %}
648+ <div id="searchbox" style="display: none">
649+ <h3>{{ _('Quick search') }}</h3>
650+ <form class="search" action="{{ pathto('search') }}" method="get">
651+ <input type="text" name="q" size="18" />
652+ <input type="submit" value="{{ _('Go') }}" />
653+ <input type="hidden" name="check_keywords" value="yes" />
654+ <input type="hidden" name="area" value="default" />
655+ </form>
656+ <p class="searchtip" style="font-size: 90%">
657+ {{ _('Enter search terms or a module, class or function name.') }}
658+ </p>
659+ </div>
660+ <script type="text/javascript">$('#searchbox').show(0);</script>
661+ {%- endif %}
662+ {%- endblock %}
663+ </div>
664+ </div>
665+ {%- endif %}{% endif %}
666+{%- endmacro %}
667+
668+{% block relbar1 %}{% endblock relbar1 %}
669+
670+{% block header %}
671+ <div id="header">
672+ <h1 id="logo"><a href="http://www.openstack.org/">OpenStack</a></h1>
673+ <ul id="navigation">
674+ <li><a href="http://www.openstack.org/" title="Go to the Home page" class="link">Home</a></li>
675+ <li><a href="http://www.openstack.org/projects/" title="Go to the OpenStack Projects page">Projects</a></li>
676+ <li><a href="http://www.openstack.org/user-stories/" title="Go to the User Stories page" class="link">User Stories</a></li>
677+ <li><a href="http://www.openstack.org/community/" title="Go to the Community page" class="link">Community</a></li>
678+ <li><a href="http://www.openstack.org/blog/" title="Go to the OpenStack Blog">Blog</a></li>
679+ <li><a href="http://wiki.openstack.org/" title="Go to the OpenStack Wiki">Wiki</a></li>
680+ <li><a href="http://docs.openstack.org/" title="Go to OpenStack Documentation" class="current">Documentation</a></li>
681+ </ul>
682+ </div>
683+{% endblock %}
684\ No newline at end of file
685
686=== added file 'doc/source/_theme/theme.conf'
687--- doc/source/_theme/theme.conf 1970-01-01 00:00:00 +0000
688+++ doc/source/_theme/theme.conf 2012-11-30 14:11:19 +0000
689@@ -0,0 +1,4 @@
690+[theme]
691+inherit = basic
692+stylesheet = nature.css
693+pygments_style = tango
694
695=== modified file 'doc/source/conf.py'
696--- doc/source/conf.py 2012-09-07 13:13:18 +0000
697+++ doc/source/conf.py 2012-11-30 14:11:19 +0000
698@@ -16,7 +16,7 @@
699 import sys
700
701 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__),
702- "..", "..")))
703+ '..', '..')))
704
705 # If extensions (or modules to document with autodoc) are in another directory,
706 # add these directories to sys.path here. If the directory is relative to the
707@@ -28,7 +28,12 @@
708 # Add any Sphinx extension module names here, as strings. They can be
709 # extensions
710 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
711-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
712+extensions = ['sphinx.ext.autodoc',
713+ 'sphinx.ext.todo',
714+ 'sphinx.ext.coverage',
715+ 'sphinx.ext.intersphinx']
716+
717+todo_include_todos = True
718
719 # Add any paths that contain templates here, relative to this directory.
720 templates_path = ['_templates']
721@@ -50,10 +55,10 @@
722 # |version| and |release|, also used in various other places throughout the
723 # built documents.
724 #
725-# The short X.Y version.
726-version = '2.7'
727+# The short XXXX.Y version.
728+version = '2012.3'
729 # The full version, including alpha/beta/rc tags.
730-release = '2.7.0'
731+release = '2012.3-dev'
732
733 # The language for content autogenerated by Sphinx. Refer to documentation
734 # for a list of supported languages.
735@@ -98,7 +103,8 @@
736
737 # The theme to use for HTML and HTML Help pages. Major themes that come with
738 # Sphinx are currently 'default' and 'sphinxdoc'.
739-html_theme = 'nature'
740+html_theme_path = ["."]
741+html_theme = '_theme'
742
743 # Theme options are theme-specific and customize the look and feel of a theme
744 # further. For a list of options available for each theme, see the
745@@ -127,11 +133,12 @@
746 # Add any paths that contain custom static files (such as style sheets) here,
747 # relative to this directory. They are copied after the builtin static files,
748 # so a file named "default.css" will overwrite the builtin "default.css".
749-html_static_path = ['_static']
750+html_static_path = ['static']
751
752 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
753 # using the given strftime format.
754-#html_last_updated_fmt = '%b %d, %Y'
755+git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1"
756+html_last_updated_fmt = os.popen(git_cmd).read()
757
758 # If true, SmartyPants will be used to convert quotes and dashes to
759 # typographically correct entities.
760@@ -181,8 +188,9 @@
761 # .
762 latex_documents = [
763 ('index', 'python-keystoneclient.tex',
764- u'python-keystoneclient Documentation',
765- u'Nebula Inc, based on work by Rackspace and Jacob Kaplan-Moss', 'manual'),
766+ u'python-keystoneclient Documentation',
767+ u'Nebula Inc, based on work by Rackspace and Jacob Kaplan-Moss',
768+ 'manual'),
769 ]
770
771 # The name of an image file (relative to this directory) to place at the top of
772@@ -204,4 +212,7 @@
773
774
775 # Example configuration for intersphinx: refer to the Python standard library.
776-intersphinx_mapping = {'http://docs.python.org/': None}
777+intersphinx_mapping = {'python': ('http://docs.python.org/', None),
778+ 'nova': ('http://nova.openstack.org', None),
779+ 'swift': ('http://swift.openstack.org', None),
780+ 'glance': ('http://glance.openstack.org', None)}
781
782=== modified file 'doc/source/index.rst'
783--- doc/source/index.rst 2012-06-22 12:58:18 +0000
784+++ doc/source/index.rst 2012-11-30 14:11:19 +0000
785@@ -1,7 +1,7 @@
786-Python bindings to the OpenStack Keystone API
787-==================================================
788+Python bindings to the OpenStack Identity API (Keystone)
789+========================================================
790
791-This is a client for OpenStack Keystone API. There's :doc:`a Python API
792+This is a client for OpenStack Identity API. There's :doc:`a Python API
793 <using-api>` (the :mod:`keystoneclient` module), and a :doc:`command-line script
794 <shell>` (installed as :program:`keystone`).
795
796@@ -10,20 +10,21 @@
797 .. toctree::
798 :maxdepth: 1
799
800+ releases
801+ shell
802 using-api
803- shell
804- ref/index
805- releases
806+
807+ api/autoindex
808
809 Contributing
810 ============
811
812-Code is hosted `on GitHub`_. Submit bugs to the Keystone project on
813-`Launchpad`_. Submit code to the openstack/python-keystoneclient project using
814-`Gerrit`_.
815+Code is hosted `on GitHub`_. Submit bugs to the Keystone project on
816+`Launchpad`_. Submit code to the ``openstack/python-keystoneclient`` project
817+using `Gerrit`_.
818
819 .. _on GitHub: https://github.com/openstack/python-keystoneclient
820-.. _Launchpad: https://launchpad.net/keystone
821+.. _Launchpad: https://launchpad.net/python-keystoneclient
822 .. _Gerrit: http://wiki.openstack.org/GerritWorkflow
823
824 Run tests with ``python setup.py test``.
825
826=== removed directory 'doc/source/ref'
827=== removed file 'doc/source/ref/client.rst'
828--- doc/source/ref/client.rst 2012-06-22 12:58:18 +0000
829+++ doc/source/ref/client.rst 1970-01-01 00:00:00 +0000
830@@ -1,8 +0,0 @@
831-Client
832-======
833-
834-.. currentmodule:: keystoneclient.v2_0.client
835-
836-.. autoclass:: Client
837-
838- .. automethod:: authenticate
839
840=== removed file 'doc/source/ref/endpoints.rst'
841--- doc/source/ref/endpoints.rst 2012-06-22 12:58:18 +0000
842+++ doc/source/ref/endpoints.rst 1970-01-01 00:00:00 +0000
843@@ -1,11 +0,0 @@
844-==============================
845-Endpoint Manager and Endpoints
846-==============================
847-
848-
849-
850-.. currentmodule:: keystoneclient.v2_0.endpoints
851-
852-.. automodule:: keystoneclient.v2_0.endpoints
853- :members:
854-
855
856=== removed file 'doc/source/ref/exceptions.rst'
857--- doc/source/ref/exceptions.rst 2012-06-22 12:58:18 +0000
858+++ doc/source/ref/exceptions.rst 1970-01-01 00:00:00 +0000
859@@ -1,8 +0,0 @@
860-Exceptions
861-==========
862-
863-.. currentmodule:: keystoneclient.exceptions
864-
865-.. automodule:: keystoneclient.exceptions
866- :members:
867-
868
869=== removed file 'doc/source/ref/generic-client.rst'
870--- doc/source/ref/generic-client.rst 2012-06-22 12:58:18 +0000
871+++ doc/source/ref/generic-client.rst 1970-01-01 00:00:00 +0000
872@@ -1,12 +0,0 @@
873-Generic client
874-==============
875-
876-Use the generic client to obtain access to a specific endpoint version.
877-
878-
879-.. currentmodule:: keystoneclient.generic.client
880-
881-.. autoclass:: Client
882-
883- .. automethod:: discover
884-
885
886=== removed file 'doc/source/ref/index.rst'
887--- doc/source/ref/index.rst 2012-06-22 12:58:18 +0000
888+++ doc/source/ref/index.rst 1970-01-01 00:00:00 +0000
889@@ -1,16 +0,0 @@
890-API Reference
891-=============
892-
893-The following API reference documents are available:
894-
895-.. toctree::
896- :maxdepth: 1
897-
898- client
899- generic-client
900- tenants
901- users
902- roles
903- services
904- endpoints
905- exceptions
906
907=== removed file 'doc/source/ref/roles.rst'
908--- doc/source/ref/roles.rst 2012-06-22 12:58:18 +0000
909+++ doc/source/ref/roles.rst 1970-01-01 00:00:00 +0000
910@@ -1,9 +0,0 @@
911-======================
912-Role Manager and Roles
913-======================
914-
915-.. currentmodule:: keystoneclient.v2_0.roles
916-
917-.. automodule:: keystoneclient.v2_0.roles
918- :members:
919-
920
921=== removed file 'doc/source/ref/services.rst'
922--- doc/source/ref/services.rst 2012-06-22 12:58:18 +0000
923+++ doc/source/ref/services.rst 1970-01-01 00:00:00 +0000
924@@ -1,11 +0,0 @@
925-============================
926-Service Manager and Services
927-============================
928-
929-
930-
931-.. currentmodule:: keystoneclient.v2_0.services
932-
933-.. automodule:: keystoneclient.v2_0.services
934- :members:
935-
936
937=== removed file 'doc/source/ref/tenants.rst'
938--- doc/source/ref/tenants.rst 2012-06-22 12:58:18 +0000
939+++ doc/source/ref/tenants.rst 1970-01-01 00:00:00 +0000
940@@ -1,11 +0,0 @@
941-==========================
942-Tenant Manager and Tenants
943-==========================
944-
945-
946-
947-.. currentmodule:: keystoneclient.v2_0.tenants
948-
949-.. automodule:: keystoneclient.v2_0.tenants
950- :members:
951-
952
953=== removed file 'doc/source/ref/users.rst'
954--- doc/source/ref/users.rst 2012-06-22 12:58:18 +0000
955+++ doc/source/ref/users.rst 1970-01-01 00:00:00 +0000
956@@ -1,9 +0,0 @@
957-======================
958-User Manager and Users
959-======================
960-
961-.. currentmodule:: keystoneclient.v2_0.users
962-
963-.. automodule:: keystoneclient.v2_0.users
964- :members:
965-
966
967=== modified file 'doc/source/releases.rst'
968--- doc/source/releases.rst 2012-06-22 12:58:18 +0000
969+++ doc/source/releases.rst 2012-11-30 14:11:19 +0000
970@@ -2,105 +2,32 @@
971 Release notes
972 =============
973
974-2.7.0 (October 21, 2011)
975-========================
976-* Forked from http://github.com/rackspace/python-novaclient
977-* Rebranded to python-keystoneclient
978-* Refactored to support Keystone API (auth, tokens, services, roles, tenants,
979+0.1.3 (August 31, 2012)
980+=======================
981+* changed logging to report request and response independently in --debug mode
982+* changed options to use hyphens instead of underscores
983+* added support for PKI signed tokens with Keystone
984+
985+
986+0.1.2 (July 9, 2012)
987+====================
988+* added support for two-way SSL and --insecure option to allow for self-signed
989+ certificates
990+* added support for password prompting if not provided
991+* added support for bash completion for keystone
992+* updated CLI options to use dashes instead of underscores
993+
994+0.1.1 (June 25, 2012)
995+=====================
996+* corrected versioning
997+
998+0.1.0 (March 29, 2012)
999+======================
1000+* released with OpenStack Essex and Diablo compatibility
1001+* forked from http://github.com/rackspace/python-novaclient
1002+* refactored to support Identity API (auth, tokens, services, roles, tenants,
1003 users, etc.)
1004-
1005-2.5.8 (July 11, 2011)
1006-=====================
1007-* returns all public/private ips, not just first one
1008-* better 'nova list' search options
1009-
1010-2.5.7 - 2.5.6 = minor tweaks
1011-
1012-2.5.5 (June 21, 2011)
1013-=====================
1014-* zone-boot min/max instance count added thanks to comstud
1015-* create for user added thanks to cerberus
1016-* fixed tests
1017-
1018-2.5.3 (June 15, 2011)
1019-=====================
1020-* ProjectID can be None for backwards compatability.
1021-* README/docs updated for projectId thanks to usrleon
1022-
1023-2.5.1 (June 10, 2011)
1024-=====================
1025-* ProjectID now part of authentication
1026-
1027-2.5.0 (June 3, 2011)
1028-====================
1029-
1030-* better logging thanks to GridDynamics
1031-
1032-2.4.4 (June 1, 2011)
1033-====================
1034-
1035-* added support for GET /servers with reservation_id (and /servers/detail)
1036-
1037-2.4.3 (May 27, 2011)
1038-====================
1039-
1040-* added support for POST /zones/select (client only, not cmdline)
1041-
1042-2.4 (March 7, 2011)
1043-===================
1044-
1045-* added Jacob Kaplan-Moss copyright notices to older/untouched files.
1046-
1047-
1048-2.3 (March 2, 2011)
1049-===================
1050-
1051-* package renamed to python-novaclient. Module to novaclient
1052-
1053-
1054-2.2 (March 1, 2011)
1055-===================
1056-
1057-* removed some license/copywrite notices from source that wasn't
1058- significantly changed.
1059-
1060-
1061-2.1 (Feb 28, 2011)
1062-==================
1063-
1064-* shell renamed to nova from novatools
1065-
1066-* license changed from BSD to Apache
1067-
1068-2.0 (Feb 7, 2011)
1069-=================
1070-
1071-* Forked from https://github.com/jacobian/python-cloudservers
1072-
1073-* Rebranded to python-novatools
1074-
1075-* Auth URL support
1076-
1077-* New OpenStack specific commands added (pause, suspend, etc)
1078-
1079-1.2 (August 15, 2010)
1080-=====================
1081-
1082-* Support for Python 2.4 - 2.7.
1083-
1084-* Improved output of :program:`cloudservers ipgroup-list`.
1085-
1086-* Made ``cloudservers boot --ipgroup <name>`` work (as well as ``--ipgroup
1087- <id>``).
1088-
1089-1.1 (May 6, 2010)
1090-=================
1091-
1092-* Added a ``--files`` option to :program:`cloudservers boot` supporting
1093- the upload of (up to five) files at boot time.
1094-
1095-* Added a ``--key`` option to :program:`cloudservers boot` to key the server
1096- with an SSH public key at boot time. This is just a shortcut for ``--files``,
1097- but it's a useful shortcut.
1098-
1099-* Changed the default server image to Ubuntu 10.04 LTS.
1100+* removed legacy arguments of --username, --password, etc in migration to
1101+ support a cross-openstack unified CLI convention defined at
1102+ http://wiki.openstack.org/UnifiedCLI
1103+* required service ID for listing endpoints
1104
1105=== modified file 'doc/source/shell.rst'
1106--- doc/source/shell.rst 2012-09-07 13:13:18 +0000
1107+++ doc/source/shell.rst 2012-11-30 14:11:19 +0000
1108@@ -1,22 +1,43 @@
1109 The :program:`keystone` shell utility
1110-=========================================
1111+=====================================
1112
1113 .. program:: keystone
1114 .. highlight:: bash
1115
1116
1117-The :program:`keystone` shell utility interacts with OpenStack Keystone API
1118-from the command line. It supports the entirety of the OpenStack Keystone API.
1119-
1120-First, you'll need an OpenStack Keystone account. You get this by using the
1121-`keystone-manage` command in OpenStack Keystone.
1122-
1123-You'll need to provide :program:`keystone` with your OpenStack username and
1124-password. You can do this with the :option:`--os-username`, :option:`--os-password`.
1125-You can optionally specify a :option:`--os-tenant-id` or :option:`--os-tenant-name`,
1126-to scope your token to a specific tenant. If you don't specify a tenant, you
1127-will be scoped to your default tenant if you have one. Instead of using
1128-options, it is easier to just set them as environment variables:
1129+The :program:`keystone` shell utility interacts with OpenStack Identity API
1130+from the command line. It supports the entirety of the OpenStack Identity API.
1131+
1132+To communicate with the API, you will need to be authenticated - and the
1133+:program:`keystone` provides multiple options for this.
1134+
1135+While bootstrapping keystone the authentication is accomplished with a
1136+shared secret token and the location of the Identity API endpoint. The
1137+shared secret token is configured in keystone.conf as "admin_token".
1138+
1139+You can specify those values on the command line with :option:`--os-token`
1140+and :option:`--os-endpoint`, or set them in environment variables:
1141+
1142+.. envvar:: OS_SERVICE_TOKEN
1143+
1144+ Your keystone administrative token
1145+
1146+.. envvar:: OS_SERVICE_ENDPOINT
1147+
1148+ Your Identity API endpoint
1149+
1150+The command line options will override any environment variables set.
1151+
1152+If you already have accounts, you can use your OpenStack username and
1153+password. You can do this with the :option:`--os-username`,
1154+:option:`--os-password`.
1155+
1156+Keystone allows a user to be associated with one or more tenants. To specify
1157+the tenant for which you want to authorize against, you may optionally
1158+specify a :option:`--os-tenant-id` or :option:`--os-tenant-name`.
1159+
1160+Instead of using options, it is easier to just set them as environment
1161+variables:
1162
1163 .. envvar:: OS_USERNAME
1164
1165
1166=== added directory 'doc/source/static'
1167=== added file 'doc/source/static/basic.css'
1168--- doc/source/static/basic.css 1970-01-01 00:00:00 +0000
1169+++ doc/source/static/basic.css 2012-11-30 14:11:19 +0000
1170@@ -0,0 +1,416 @@
1171+/**
1172+ * Sphinx stylesheet -- basic theme
1173+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1174+ */
1175+
1176+/* -- main layout ----------------------------------------------------------- */
1177+
1178+div.clearer {
1179+ clear: both;
1180+}
1181+
1182+/* -- relbar ---------------------------------------------------------------- */
1183+
1184+div.related {
1185+ width: 100%;
1186+ font-size: 90%;
1187+}
1188+
1189+div.related h3 {
1190+ display: none;
1191+}
1192+
1193+div.related ul {
1194+ margin: 0;
1195+ padding: 0 0 0 10px;
1196+ list-style: none;
1197+}
1198+
1199+div.related li {
1200+ display: inline;
1201+}
1202+
1203+div.related li.right {
1204+ float: right;
1205+ margin-right: 5px;
1206+}
1207+
1208+/* -- sidebar --------------------------------------------------------------- */
1209+
1210+div.sphinxsidebarwrapper {
1211+ padding: 10px 5px 0 10px;
1212+}
1213+
1214+div.sphinxsidebar {
1215+ float: left;
1216+ width: 230px;
1217+ margin-left: -100%;
1218+ font-size: 90%;
1219+}
1220+
1221+div.sphinxsidebar ul {
1222+ list-style: none;
1223+}
1224+
1225+div.sphinxsidebar ul ul,
1226+div.sphinxsidebar ul.want-points {
1227+ margin-left: 20px;
1228+ list-style: square;
1229+}
1230+
1231+div.sphinxsidebar ul ul {
1232+ margin-top: 0;
1233+ margin-bottom: 0;
1234+}
1235+
1236+div.sphinxsidebar form {
1237+ margin-top: 10px;
1238+}
1239+
1240+div.sphinxsidebar input {
1241+ border: 1px solid #98dbcc;
1242+ font-family: sans-serif;
1243+ font-size: 1em;
1244+}
1245+
1246+img {
1247+ border: 0;
1248+}
1249+
1250+/* -- search page ----------------------------------------------------------- */
1251+
1252+ul.search {
1253+ margin: 10px 0 0 20px;
1254+ padding: 0;
1255+}
1256+
1257+ul.search li {
1258+ padding: 5px 0 5px 20px;
1259+ background-image: url(file.png);
1260+ background-repeat: no-repeat;
1261+ background-position: 0 7px;
1262+}
1263+
1264+ul.search li a {
1265+ font-weight: bold;
1266+}
1267+
1268+ul.search li div.context {
1269+ color: #888;
1270+ margin: 2px 0 0 30px;
1271+ text-align: left;
1272+}
1273+
1274+ul.keywordmatches li.goodmatch a {
1275+ font-weight: bold;
1276+}
1277+
1278+/* -- index page ------------------------------------------------------------ */
1279+
1280+table.contentstable {
1281+ width: 90%;
1282+}
1283+
1284+table.contentstable p.biglink {
1285+ line-height: 150%;
1286+}
1287+
1288+a.biglink {
1289+ font-size: 1.3em;
1290+}
1291+
1292+span.linkdescr {
1293+ font-style: italic;
1294+ padding-top: 5px;
1295+ font-size: 90%;
1296+}
1297+
1298+/* -- general index --------------------------------------------------------- */
1299+
1300+table.indextable td {
1301+ text-align: left;
1302+ vertical-align: top;
1303+}
1304+
1305+table.indextable dl, table.indextable dd {
1306+ margin-top: 0;
1307+ margin-bottom: 0;
1308+}
1309+
1310+table.indextable tr.pcap {
1311+ height: 10px;
1312+}
1313+
1314+table.indextable tr.cap {
1315+ margin-top: 10px;
1316+ background-color: #f2f2f2;
1317+}
1318+
1319+img.toggler {
1320+ margin-right: 3px;
1321+ margin-top: 3px;
1322+ cursor: pointer;
1323+}
1324+
1325+/* -- general body styles --------------------------------------------------- */
1326+
1327+a.headerlink {
1328+ visibility: hidden;
1329+}
1330+
1331+h1:hover > a.headerlink,
1332+h2:hover > a.headerlink,
1333+h3:hover > a.headerlink,
1334+h4:hover > a.headerlink,
1335+h5:hover > a.headerlink,
1336+h6:hover > a.headerlink,
1337+dt:hover > a.headerlink {
1338+ visibility: visible;
1339+}
1340+
1341+div.body p.caption {
1342+ text-align: inherit;
1343+}
1344+
1345+div.body td {
1346+ text-align: left;
1347+}
1348+
1349+.field-list ul {
1350+ padding-left: 1em;
1351+}
1352+
1353+.first {
1354+}
1355+
1356+p.rubric {
1357+ margin-top: 30px;
1358+ font-weight: bold;
1359+}
1360+
1361+/* -- sidebars -------------------------------------------------------------- */
1362+
1363+div.sidebar {
1364+ margin: 0 0 0.5em 1em;
1365+ border: 1px solid #ddb;
1366+ padding: 7px 7px 0 7px;
1367+ background-color: #ffe;
1368+ width: 40%;
1369+ float: right;
1370+}
1371+
1372+p.sidebar-title {
1373+ font-weight: bold;
1374+}
1375+
1376+/* -- topics ---------------------------------------------------------------- */
1377+
1378+div.topic {
1379+ border: 1px solid #ccc;
1380+ padding: 7px 7px 0 7px;
1381+ margin: 10px 0 10px 0;
1382+}
1383+
1384+p.topic-title {
1385+ font-size: 1.1em;
1386+ font-weight: bold;
1387+ margin-top: 10px;
1388+}
1389+
1390+/* -- admonitions ----------------------------------------------------------- */
1391+
1392+div.admonition {
1393+ margin-top: 10px;
1394+ margin-bottom: 10px;
1395+ padding: 7px;
1396+}
1397+
1398+div.admonition dt {
1399+ font-weight: bold;
1400+}
1401+
1402+div.admonition dl {
1403+ margin-bottom: 0;
1404+}
1405+
1406+p.admonition-title {
1407+ margin: 0px 10px 5px 0px;
1408+ font-weight: bold;
1409+}
1410+
1411+div.body p.centered {
1412+ text-align: center;
1413+ margin-top: 25px;
1414+}
1415+
1416+/* -- tables ---------------------------------------------------------------- */
1417+
1418+table.docutils {
1419+ border: 0;
1420+ border-collapse: collapse;
1421+}
1422+
1423+table.docutils td, table.docutils th {
1424+ padding: 1px 8px 1px 0;
1425+ border-top: 0;
1426+ border-left: 0;
1427+ border-right: 0;
1428+ border-bottom: 1px solid #aaa;
1429+}
1430+
1431+table.field-list td, table.field-list th {
1432+ border: 0 !important;
1433+}
1434+
1435+table.footnote td, table.footnote th {
1436+ border: 0 !important;
1437+}
1438+
1439+th {
1440+ text-align: left;
1441+ padding-right: 5px;
1442+}
1443+
1444+/* -- other body styles ----------------------------------------------------- */
1445+
1446+dl {
1447+ margin-bottom: 15px;
1448+}
1449+
1450+dd p {
1451+ margin-top: 0px;
1452+}
1453+
1454+dd ul, dd table {
1455+ margin-bottom: 10px;
1456+}
1457+
1458+dd {
1459+ margin-top: 3px;
1460+ margin-bottom: 10px;
1461+ margin-left: 30px;
1462+}
1463+
1464+dt:target, .highlight {
1465+ background-color: #fbe54e;
1466+}
1467+
1468+dl.glossary dt {
1469+ font-weight: bold;
1470+ font-size: 1.1em;
1471+}
1472+
1473+.field-list ul {
1474+ margin: 0;
1475+ padding-left: 1em;
1476+}
1477+
1478+.field-list p {
1479+ margin: 0;
1480+}
1481+
1482+.refcount {
1483+ color: #060;
1484+}
1485+
1486+.optional {
1487+ font-size: 1.3em;
1488+}
1489+
1490+.versionmodified {
1491+ font-style: italic;
1492+}
1493+
1494+.system-message {
1495+ background-color: #fda;
1496+ padding: 5px;
1497+ border: 3px solid red;
1498+}
1499+
1500+.footnote:target {
1501+ background-color: #ffa
1502+}
1503+
1504+.line-block {
1505+ display: block;
1506+ margin-top: 1em;
1507+ margin-bottom: 1em;
1508+}
1509+
1510+.line-block .line-block {
1511+ margin-top: 0;
1512+ margin-bottom: 0;
1513+ margin-left: 1.5em;
1514+}
1515+
1516+/* -- code displays --------------------------------------------------------- */
1517+
1518+pre {
1519+ overflow: auto;
1520+}
1521+
1522+td.linenos pre {
1523+ padding: 5px 0px;
1524+ border: 0;
1525+ background-color: transparent;
1526+ color: #aaa;
1527+}
1528+
1529+table.highlighttable {
1530+ margin-left: 0.5em;
1531+}
1532+
1533+table.highlighttable td {
1534+ padding: 0 0.5em 0 0.5em;
1535+}
1536+
1537+tt.descname {
1538+ background-color: transparent;
1539+ font-weight: bold;
1540+ font-size: 1.2em;
1541+}
1542+
1543+tt.descclassname {
1544+ background-color: transparent;
1545+}
1546+
1547+tt.xref, a tt {
1548+ background-color: transparent;
1549+ font-weight: bold;
1550+}
1551+
1552+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
1553+ background-color: transparent;
1554+}
1555+
1556+/* -- math display ---------------------------------------------------------- */
1557+
1558+img.math {
1559+ vertical-align: middle;
1560+}
1561+
1562+div.body div.math p {
1563+ text-align: center;
1564+}
1565+
1566+span.eqno {
1567+ float: right;
1568+}
1569+
1570+/* -- printout stylesheet --------------------------------------------------- */
1571+
1572+@media print {
1573+ div.document,
1574+ div.documentwrapper,
1575+ div.bodywrapper {
1576+ margin: 0 !important;
1577+ width: 100%;
1578+ }
1579+
1580+ div.sphinxsidebar,
1581+ div.related,
1582+ div.footer,
1583+ #top-link {
1584+ display: none;
1585+ }
1586+}
1587
1588=== added file 'doc/source/static/default.css'
1589--- doc/source/static/default.css 1970-01-01 00:00:00 +0000
1590+++ doc/source/static/default.css 2012-11-30 14:11:19 +0000
1591@@ -0,0 +1,230 @@
1592+/**
1593+ * Sphinx stylesheet -- default theme
1594+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1595+ */
1596+
1597+@import url("basic.css");
1598+
1599+/* -- page layout ----------------------------------------------------------- */
1600+
1601+body {
1602+ font-family: sans-serif;
1603+ font-size: 100%;
1604+ background-color: #11303d;
1605+ color: #000;
1606+ margin: 0;
1607+ padding: 0;
1608+}
1609+
1610+div.document {
1611+ background-color: #1c4e63;
1612+}
1613+
1614+div.documentwrapper {
1615+ float: left;
1616+ width: 100%;
1617+}
1618+
1619+div.bodywrapper {
1620+ margin: 0 0 0 230px;
1621+}
1622+
1623+div.body {
1624+ background-color: #ffffff;
1625+ color: #000000;
1626+ padding: 0 20px 30px 20px;
1627+}
1628+
1629+div.footer {
1630+ color: #ffffff;
1631+ width: 100%;
1632+ padding: 9px 0 9px 0;
1633+ text-align: center;
1634+ font-size: 75%;
1635+}
1636+
1637+div.footer a {
1638+ color: #ffffff;
1639+ text-decoration: underline;
1640+}
1641+
1642+div.related {
1643+ background-color: #133f52;
1644+ line-height: 30px;
1645+ color: #ffffff;
1646+}
1647+
1648+div.related a {
1649+ color: #ffffff;
1650+}
1651+
1652+div.sphinxsidebar {
1653+}
1654+
1655+div.sphinxsidebar h3 {
1656+ font-family: 'Trebuchet MS', sans-serif;
1657+ color: #ffffff;
1658+ font-size: 1.4em;
1659+ font-weight: normal;
1660+ margin: 0;
1661+ padding: 0;
1662+}
1663+
1664+div.sphinxsidebar h3 a {
1665+ color: #ffffff;
1666+}
1667+
1668+div.sphinxsidebar h4 {
1669+ font-family: 'Trebuchet MS', sans-serif;
1670+ color: #ffffff;
1671+ font-size: 1.3em;
1672+ font-weight: normal;
1673+ margin: 5px 0 0 0;
1674+ padding: 0;
1675+}
1676+
1677+div.sphinxsidebar p {
1678+ color: #ffffff;
1679+}
1680+
1681+div.sphinxsidebar p.topless {
1682+ margin: 5px 10px 10px 10px;
1683+}
1684+
1685+div.sphinxsidebar ul {
1686+ margin: 10px;
1687+ padding: 0;
1688+ color: #ffffff;
1689+}
1690+
1691+div.sphinxsidebar a {
1692+ color: #98dbcc;
1693+}
1694+
1695+div.sphinxsidebar input {
1696+ border: 1px solid #98dbcc;
1697+ font-family: sans-serif;
1698+ font-size: 1em;
1699+}
1700+
1701+/* -- body styles ----------------------------------------------------------- */
1702+
1703+a {
1704+ color: #355f7c;
1705+ text-decoration: none;
1706+}
1707+
1708+a:hover {
1709+ text-decoration: underline;
1710+}
1711+
1712+div.body p, div.body dd, div.body li {
1713+ text-align: left;
1714+ line-height: 130%;
1715+}
1716+
1717+div.body h1,
1718+div.body h2,
1719+div.body h3,
1720+div.body h4,
1721+div.body h5,
1722+div.body h6 {
1723+ font-family: 'Trebuchet MS', sans-serif;
1724+ background-color: #f2f2f2;
1725+ font-weight: normal;
1726+ color: #20435c;
1727+ border-bottom: 1px solid #ccc;
1728+ margin: 20px -20px 10px -20px;
1729+ padding: 3px 0 3px 10px;
1730+}
1731+
1732+div.body h1 { margin-top: 0; font-size: 200%; }
1733+div.body h2 { font-size: 160%; }
1734+div.body h3 { font-size: 140%; }
1735+div.body h4 { font-size: 120%; }
1736+div.body h5 { font-size: 110%; }
1737+div.body h6 { font-size: 100%; }
1738+
1739+a.headerlink {
1740+ color: #c60f0f;
1741+ font-size: 0.8em;
1742+ padding: 0 4px 0 4px;
1743+ text-decoration: none;
1744+}
1745+
1746+a.headerlink:hover {
1747+ background-color: #c60f0f;
1748+ color: white;
1749+}
1750+
1751+div.body p, div.body dd, div.body li {
1752+ text-align: left;
1753+ line-height: 130%;
1754+}
1755+
1756+div.admonition p.admonition-title + p {
1757+ display: inline;
1758+}
1759+
1760+div.admonition p {
1761+ margin-bottom: 5px;
1762+}
1763+
1764+div.admonition pre {
1765+ margin-bottom: 5px;
1766+}
1767+
1768+div.admonition ul, div.admonition ol {
1769+ margin-bottom: 5px;
1770+}
1771+
1772+div.note {
1773+ background-color: #eee;
1774+ border: 1px solid #ccc;
1775+}
1776+
1777+div.seealso {
1778+ background-color: #ffc;
1779+ border: 1px solid #ff6;
1780+}
1781+
1782+div.topic {
1783+ background-color: #eee;
1784+}
1785+
1786+div.warning {
1787+ background-color: #ffe4e4;
1788+ border: 1px solid #f66;
1789+}
1790+
1791+p.admonition-title {
1792+ display: inline;
1793+}
1794+
1795+p.admonition-title:after {
1796+ content: ":";
1797+}
1798+
1799+pre {
1800+ padding: 5px;
1801+ background-color: #eeffcc;
1802+ color: #333333;
1803+ line-height: 120%;
1804+ border: 1px solid #ac9;
1805+ border-left: none;
1806+ border-right: none;
1807+}
1808+
1809+tt {
1810+ background-color: #ecf0f3;
1811+ padding: 0 1px 0 1px;
1812+ font-size: 0.95em;
1813+}
1814+
1815+.warning tt {
1816+ background: #efc2c2;
1817+}
1818+
1819+.note tt {
1820+ background: #d6d6d6;
1821+}
1822
1823=== added file 'doc/source/static/header-line.gif'
1824Binary files doc/source/static/header-line.gif 1970-01-01 00:00:00 +0000 and doc/source/static/header-line.gif 2012-11-30 14:11:19 +0000 differ
1825=== added file 'doc/source/static/header_bg.jpg'
1826Binary files doc/source/static/header_bg.jpg 1970-01-01 00:00:00 +0000 and doc/source/static/header_bg.jpg 2012-11-30 14:11:19 +0000 differ
1827=== added file 'doc/source/static/jquery.tweet.js'
1828--- doc/source/static/jquery.tweet.js 1970-01-01 00:00:00 +0000
1829+++ doc/source/static/jquery.tweet.js 2012-11-30 14:11:19 +0000
1830@@ -0,0 +1,154 @@
1831+(function($) {
1832+
1833+ $.fn.tweet = function(o){
1834+ var s = {
1835+ username: ["seaofclouds"], // [string] required, unless you want to display our tweets. :) it can be an array, just do ["username1","username2","etc"]
1836+ list: null, //[string] optional name of list belonging to username
1837+ avatar_size: null, // [integer] height and width of avatar if displayed (48px max)
1838+ count: 3, // [integer] how many tweets to display?
1839+ intro_text: null, // [string] do you want text BEFORE your your tweets?
1840+ outro_text: null, // [string] do you want text AFTER your tweets?
1841+ join_text: null, // [string] optional text in between date and tweet, try setting to "auto"
1842+ auto_join_text_default: "i said,", // [string] auto text for non verb: "i said" bullocks
1843+ auto_join_text_ed: "i", // [string] auto text for past tense: "i" surfed
1844+ auto_join_text_ing: "i am", // [string] auto tense for present tense: "i was" surfing
1845+ auto_join_text_reply: "i replied to", // [string] auto tense for replies: "i replied to" @someone "with"
1846+ auto_join_text_url: "i was looking at", // [string] auto tense for urls: "i was looking at" http:...
1847+ loading_text: null, // [string] optional loading text, displayed while tweets load
1848+ query: null // [string] optional search query
1849+ };
1850+
1851+ if(o) $.extend(s, o);
1852+
1853+ $.fn.extend({
1854+ linkUrl: function() {
1855+ var returning = [];
1856+ var regexp = /((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
1857+ this.each(function() {
1858+ returning.push(this.replace(regexp,"<a href=\"$1\">$1</a>"));
1859+ });
1860+ return $(returning);
1861+ },
1862+ linkUser: function() {
1863+ var returning = [];
1864+ var regexp = /[\@]+([A-Za-z0-9-_]+)/gi;
1865+ this.each(function() {
1866+ returning.push(this.replace(regexp,"<a href=\"http://twitter.com/$1\">@$1</a>"));
1867+ });
1868+ return $(returning);
1869+ },
1870+ linkHash: function() {
1871+ var returning = [];
1872+ var regexp = / [\#]+([A-Za-z0-9-_]+)/gi;
1873+ this.each(function() {
1874+ returning.push(this.replace(regexp, ' <a href="http://search.twitter.com/search?q=&tag=$1&lang=all&from='+s.username.join("%2BOR%2B")+'">#$1</a>'));
1875+ });
1876+ return $(returning);
1877+ },
1878+ capAwesome: function() {
1879+ var returning = [];
1880+ this.each(function() {
1881+ returning.push(this.replace(/\b(awesome)\b/gi, '<span class="awesome">$1</span>'));
1882+ });
1883+ return $(returning);
1884+ },
1885+ capEpic: function() {
1886+ var returning = [];
1887+ this.each(function() {
1888+ returning.push(this.replace(/\b(epic)\b/gi, '<span class="epic">$1</span>'));
1889+ });
1890+ return $(returning);
1891+ },
1892+ makeHeart: function() {
1893+ var returning = [];
1894+ this.each(function() {
1895+ returning.push(this.replace(/(&lt;)+[3]/gi, "<tt class='heart'>&#x2665;</tt>"));
1896+ });
1897+ return $(returning);
1898+ }
1899+ });
1900+
1901+ function relative_time(time_value) {
1902+ var parsed_date = Date.parse(time_value);
1903+ var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
1904+ var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
1905+ var pluralize = function (singular, n) {
1906+ return '' + n + ' ' + singular + (n == 1 ? '' : 's');
1907+ };
1908+ if(delta < 60) {
1909+ return 'less than a minute ago';
1910+ } else if(delta < (45*60)) {
1911+ return 'about ' + pluralize("minute", parseInt(delta / 60)) + ' ago';
1912+ } else if(delta < (24*60*60)) {
1913+ return 'about ' + pluralize("hour", parseInt(delta / 3600)) + ' ago';
1914+ } else {
1915+ return 'about ' + pluralize("day", parseInt(delta / 86400)) + ' ago';
1916+ }
1917+ }
1918+
1919+ function build_url() {
1920+ var proto = ('https:' == document.location.protocol ? 'https:' : 'http:');
1921+ if (s.list) {
1922+ return proto+"//api.twitter.com/1/"+s.username[0]+"/lists/"+s.list+"/statuses.json?per_page="+s.count+"&callback=?";
1923+ } else if (s.query == null && s.username.length == 1) {
1924+ return proto+'//twitter.com/status/user_timeline/'+s.username[0]+'.json?count='+s.count+'&callback=?';
1925+ } else {
1926+ var query = (s.query || 'from:'+s.username.join('%20OR%20from:'));
1927+ return proto+'//search.twitter.com/search.json?&q='+query+'&rpp='+s.count+'&callback=?';
1928+ }
1929+ }
1930+
1931+ return this.each(function(){
1932+ var list = $('<ul class="tweet_list">').appendTo(this);
1933+ var intro = '<p class="tweet_intro">'+s.intro_text+'</p>';
1934+ var outro = '<p class="tweet_outro">'+s.outro_text+'</p>';
1935+ var loading = $('<p class="loading">'+s.loading_text+'</p>');
1936+
1937+ if(typeof(s.username) == "string"){
1938+ s.username = [s.username];
1939+ }
1940+
1941+ if (s.loading_text) $(this).append(loading);
1942+ $.getJSON(build_url(), function(data){
1943+ if (s.loading_text) loading.remove();
1944+ if (s.intro_text) list.before(intro);
1945+ $.each((data.results || data), function(i,item){
1946+ // auto join text based on verb tense and content
1947+ if (s.join_text == "auto") {
1948+ if (item.text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) {
1949+ var join_text = s.auto_join_text_reply;
1950+ } else if (item.text.match(/(^\w+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+) .*/i)) {
1951+ var join_text = s.auto_join_text_url;
1952+ } else if (item.text.match(/^((\w+ed)|just) .*/im)) {
1953+ var join_text = s.auto_join_text_ed;
1954+ } else if (item.text.match(/^(\w*ing) .*/i)) {
1955+ var join_text = s.auto_join_text_ing;
1956+ } else {
1957+ var join_text = s.auto_join_text_default;
1958+ }
1959+ } else {
1960+ var join_text = s.join_text;
1961+ };
1962+
1963+ var from_user = item.from_user || item.user.screen_name;
1964+ var profile_image_url = item.profile_image_url || item.user.profile_image_url;
1965+ var join_template = '<span class="tweet_join"> '+join_text+' </span>';
1966+ var join = ((s.join_text) ? join_template : ' ');
1967+ var avatar_template = '<a class="tweet_avatar" href="http://twitter.com/'+from_user+'"><img src="'+profile_image_url+'" height="'+s.avatar_size+'" width="'+s.avatar_size+'" alt="'+from_user+'\'s avatar" title="'+from_user+'\'s avatar" border="0"/></a>';
1968+ var avatar = (s.avatar_size ? avatar_template : '');
1969+ var date = '<a href="http://twitter.com/'+from_user+'/statuses/'+item.id+'" title="view tweet on twitter">'+relative_time(item.created_at)+'</a>';
1970+ var text = '<span class="tweet_text">' +$([item.text]).linkUrl().linkUser().linkHash().makeHeart().capAwesome().capEpic()[0]+ '</span>';
1971+
1972+ // until we create a template option, arrange the items below to alter a tweet's display.
1973+ list.append('<li>' + avatar + date + join + text + '</li>');
1974+
1975+ list.children('li:first').addClass('tweet_first');
1976+ list.children('li:odd').addClass('tweet_even');
1977+ list.children('li:even').addClass('tweet_odd');
1978+ });
1979+ if (s.outro_text) list.after(outro);
1980+ });
1981+
1982+ });
1983+ };
1984+})(jQuery);
1985\ No newline at end of file
1986
1987=== added file 'doc/source/static/nature.css'
1988--- doc/source/static/nature.css 1970-01-01 00:00:00 +0000
1989+++ doc/source/static/nature.css 2012-11-30 14:11:19 +0000
1990@@ -0,0 +1,245 @@
1991+/*
1992+ * nature.css_t
1993+ * ~~~~~~~~~~~~
1994+ *
1995+ * Sphinx stylesheet -- nature theme.
1996+ *
1997+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
1998+ * :license: BSD, see LICENSE for details.
1999+ *
2000+ */
2001+
2002+@import url("basic.css");
2003+
2004+/* -- page layout ----------------------------------------------------------- */
2005+
2006+body {
2007+ font-family: Arial, sans-serif;
2008+ font-size: 100%;
2009+ background-color: #111;
2010+ color: #555;
2011+ margin: 0;
2012+ padding: 0;
2013+}
2014+
2015+div.documentwrapper {
2016+ float: left;
2017+ width: 100%;
2018+}
2019+
2020+div.bodywrapper {
2021+ margin: 0 0 0 {{ theme_sidebarwidth|toint }}px;
2022+}
2023+
2024+hr {
2025+ border: 1px solid #B1B4B6;
2026+}
2027+
2028+div.document {
2029+ background-color: #eee;
2030+}
2031+
2032+div.body {
2033+ background-color: #ffffff;
2034+ color: #3E4349;
2035+ padding: 0 30px 30px 30px;
2036+ font-size: 0.9em;
2037+}
2038+
2039+div.footer {
2040+ color: #555;
2041+ width: 100%;
2042+ padding: 13px 0;
2043+ text-align: center;
2044+ font-size: 75%;
2045+}
2046+
2047+div.footer a {
2048+ color: #444;
2049+ text-decoration: underline;
2050+}
2051+
2052+div.related {
2053+ background-color: #6BA81E;
2054+ line-height: 32px;
2055+ color: #fff;
2056+ text-shadow: 0px 1px 0 #444;
2057+ font-size: 0.9em;
2058+}
2059+
2060+div.related a {
2061+ color: #E2F3CC;
2062+}
2063+
2064+div.sphinxsidebar {
2065+ font-size: 0.75em;
2066+ line-height: 1.5em;
2067+}
2068+
2069+div.sphinxsidebarwrapper{
2070+ padding: 20px 0;
2071+}
2072+
2073+div.sphinxsidebar h3,
2074+div.sphinxsidebar h4 {
2075+ font-family: Arial, sans-serif;
2076+ color: #222;
2077+ font-size: 1.2em;
2078+ font-weight: normal;
2079+ margin: 0;
2080+ padding: 5px 10px;
2081+ background-color: #ddd;
2082+ text-shadow: 1px 1px 0 white
2083+}
2084+
2085+div.sphinxsidebar h4{
2086+ font-size: 1.1em;
2087+}
2088+
2089+div.sphinxsidebar h3 a {
2090+ color: #444;
2091+}
2092+
2093+
2094+div.sphinxsidebar p {
2095+ color: #888;
2096+ padding: 5px 20px;
2097+}
2098+
2099+div.sphinxsidebar p.topless {
2100+}
2101+
2102+div.sphinxsidebar ul {
2103+ margin: 10px 20px;
2104+ padding: 0;
2105+ color: #000;
2106+}
2107+
2108+div.sphinxsidebar a {
2109+ color: #444;
2110+}
2111+
2112+div.sphinxsidebar input {
2113+ border: 1px solid #ccc;
2114+ font-family: sans-serif;
2115+ font-size: 1em;
2116+}
2117+
2118+div.sphinxsidebar input[type=text]{
2119+ margin-left: 20px;
2120+}
2121+
2122+/* -- body styles ----------------------------------------------------------- */
2123+
2124+a {
2125+ color: #005B81;
2126+ text-decoration: none;
2127+}
2128+
2129+a:hover {
2130+ color: #E32E00;
2131+ text-decoration: underline;
2132+}
2133+
2134+div.body h1,
2135+div.body h2,
2136+div.body h3,
2137+div.body h4,
2138+div.body h5,
2139+div.body h6 {
2140+ font-family: Arial, sans-serif;
2141+ background-color: #BED4EB;
2142+ font-weight: normal;
2143+ color: #212224;
2144+ margin: 30px 0px 10px 0px;
2145+ padding: 5px 0 5px 10px;
2146+ text-shadow: 0px 1px 0 white
2147+}
2148+
2149+div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
2150+div.body h2 { font-size: 150%; background-color: #C8D5E3; }
2151+div.body h3 { font-size: 120%; background-color: #D8DEE3; }
2152+div.body h4 { font-size: 110%; background-color: #D8DEE3; }
2153+div.body h5 { font-size: 100%; background-color: #D8DEE3; }
2154+div.body h6 { font-size: 100%; background-color: #D8DEE3; }
2155+
2156+a.headerlink {
2157+ color: #c60f0f;
2158+ font-size: 0.8em;
2159+ padding: 0 4px 0 4px;
2160+ text-decoration: none;
2161+}
2162+
2163+a.headerlink:hover {
2164+ background-color: #c60f0f;
2165+ color: white;
2166+}
2167+
2168+div.body p, div.body dd, div.body li {
2169+ line-height: 1.5em;
2170+}
2171+
2172+div.admonition p.admonition-title + p {
2173+ display: inline;
2174+}
2175+
2176+div.highlight{
2177+ background-color: white;
2178+}
2179+
2180+div.note {
2181+ background-color: #eee;
2182+ border: 1px solid #ccc;
2183+}
2184+
2185+div.seealso {
2186+ background-color: #ffc;
2187+ border: 1px solid #ff6;
2188+}
2189+
2190+div.topic {
2191+ background-color: #eee;
2192+}
2193+
2194+div.warning {
2195+ background-color: #ffe4e4;
2196+ border: 1px solid #f66;
2197+}
2198+
2199+p.admonition-title {
2200+ display: inline;
2201+}
2202+
2203+p.admonition-title:after {
2204+ content: ":";
2205+}
2206+
2207+pre {
2208+ padding: 10px;
2209+ background-color: White;
2210+ color: #222;
2211+ line-height: 1.2em;
2212+ border: 1px solid #C6C9CB;
2213+ font-size: 1.1em;
2214+ margin: 1.5em 0 1.5em 0;
2215+ -webkit-box-shadow: 1px 1px 1px #d8d8d8;
2216+ -moz-box-shadow: 1px 1px 1px #d8d8d8;
2217+}
2218+
2219+tt {
2220+ background-color: #ecf0f3;
2221+ color: #222;
2222+ /* padding: 1px 2px; */
2223+ font-size: 1.1em;
2224+ font-family: monospace;
2225+}
2226+
2227+.viewcode-back {
2228+ font-family: Arial, sans-serif;
2229+}
2230+
2231+div.viewcode-block:target {
2232+ background-color: #f4debf;
2233+ border-top: 1px solid #ac9;
2234+ border-bottom: 1px solid #ac9;
2235+}
2236
2237=== added file 'doc/source/static/openstack_logo.png'
2238Binary files doc/source/static/openstack_logo.png 1970-01-01 00:00:00 +0000 and doc/source/static/openstack_logo.png 2012-11-30 14:11:19 +0000 differ
2239=== added file 'doc/source/static/tweaks.css'
2240--- doc/source/static/tweaks.css 1970-01-01 00:00:00 +0000
2241+++ doc/source/static/tweaks.css 2012-11-30 14:11:19 +0000
2242@@ -0,0 +1,94 @@
2243+body {
2244+ background: #fff url(../_static/header_bg.jpg) top left no-repeat;
2245+}
2246+
2247+#header {
2248+ width: 950px;
2249+ margin: 0 auto;
2250+ height: 102px;
2251+}
2252+
2253+#header h1#logo {
2254+ background: url(../_static/openstack_logo.png) top left no-repeat;
2255+ display: block;
2256+ float: left;
2257+ text-indent: -9999px;
2258+ width: 175px;
2259+ height: 55px;
2260+}
2261+
2262+#navigation {
2263+ background: url(../_static/header-line.gif) repeat-x 0 bottom;
2264+ display: block;
2265+ float: left;
2266+ margin: 27px 0 0 25px;
2267+ padding: 0;
2268+}
2269+
2270+#navigation li{
2271+ float: left;
2272+ display: block;
2273+ margin-right: 25px;
2274+}
2275+
2276+#navigation li a {
2277+ display: block;
2278+ font-weight: normal;
2279+ text-decoration: none;
2280+ background-position: 50% 0;
2281+ padding: 20px 0 5px;
2282+ color: #353535;
2283+ font-size: 14px;
2284+}
2285+
2286+#navigation li a.current, #navigation li a.section {
2287+ border-bottom: 3px solid #cf2f19;
2288+ color: #cf2f19;
2289+}
2290+
2291+div.related {
2292+ background-color: #cde2f8;
2293+ border: 1px solid #b0d3f8;
2294+}
2295+
2296+div.related a {
2297+ color: #4078ba;
2298+ text-shadow: none;
2299+}
2300+
2301+div.sphinxsidebarwrapper {
2302+ padding-top: 0;
2303+}
2304+
2305+pre {
2306+ color: #555;
2307+}
2308+
2309+div.documentwrapper h1, div.documentwrapper h2, div.documentwrapper h3, div.documentwrapper h4, div.documentwrapper h5, div.documentwrapper h6 {
2310+ font-family: 'PT Sans', sans-serif !important;
2311+ color: #264D69;
2312+ border-bottom: 1px dotted #C5E2EA;
2313+ padding: 0;
2314+ background: none;
2315+ padding-bottom: 5px;
2316+}
2317+
2318+div.documentwrapper h3 {
2319+ color: #CF2F19;
2320+}
2321+
2322+a.headerlink {
2323+ color: #fff !important;
2324+ margin-left: 5px;
2325+ background: #CF2F19 !important;
2326+}
2327+
2328+div.body {
2329+ margin-top: -25px;
2330+ margin-left: 230px;
2331+}
2332+
2333+div.document {
2334+ width: 960px;
2335+ margin: 0 auto;
2336+}
2337\ No newline at end of file
2338
2339=== modified file 'doc/source/using-api.rst'
2340--- doc/source/using-api.rst 2012-06-22 12:58:18 +0000
2341+++ doc/source/using-api.rst 2012-11-30 14:11:19 +0000
2342@@ -4,7 +4,8 @@
2343
2344 Introduction
2345 ============
2346-The main concepts in the Keystone API are:
2347+
2348+The main concepts in the Identity API are:
2349
2350 * tenants
2351 * users
2352@@ -12,12 +13,13 @@
2353 * services
2354 * endpoints
2355
2356-The Keystone API lets you query and make changes through managers. For example,
2357-to maipulate tenants, you interact with a
2358-``keystoneclient.v2_0.tenants.TenantManger`` object.
2359+The Identity API lets you query and make changes through managers. For example,
2360+to manipulate tenants, you interact with a
2361+``keystoneclient.v2_0.tenants.TenantManager`` object.
2362
2363-You obtain access to managers through via atributes of the ``keystoneclient.v2_0.client.Client`` object. For example, the ``tenants`` attribute of the ``Client``
2364-class is a tenant manager::
2365+You obtain access to managers through via attributes of the
2366+``keystoneclient.v2_0.client.Client`` object. For example, the ``tenants``
2367+attribute of the ``Client`` class is a tenant manager::
2368
2369 >>> from keystoneclient.v2_0 import client
2370 >>> keystone = client.Client(...)
2371@@ -36,8 +38,8 @@
2372
2373 If you are an administrator, you can authenticate by connecting to the admin
2374 endpoint and using the admin token (sometimes referred to as the service
2375-token). The token is specified as the ``admin_token`` configuration option in your
2376-keystone.conf config file, which is typically in /etc/keystone::
2377+token). The token is specified as the ``admin_token`` configuration option in
2378+your keystone.conf config file, which is typically in /etc/keystone::
2379
2380 >>> from keystoneclient.v2_0 import client
2381 >>> token = '012345SECRET99TOKEN012345'
2382@@ -54,7 +56,7 @@
2383 >>> tenant_name='openstackDemo'
2384 >>> auth_url='http://192.168.206.130:5000/v2.0'
2385 >>> keystone = client.Client(username=username, password=password,
2386- ... tenant_name, auth_url=auth_url)
2387+ ... tenant_name=tenant_name, auth_url=auth_url)
2388
2389 Creating tenants
2390 ================
2391@@ -77,8 +79,9 @@
2392 >>> keystone = client.Client(...)
2393 >>> tenants = keystone.tenants.list()
2394 >>> my_tenant = [x for x in tenants if x.name=='openstackDemo'][0]
2395- >>> my_user = keystone.users.create(name="adminUser", password="secretword",
2396- ... tenant_id=my_tenant.id)
2397+ >>> my_user = keystone.users.create(name="adminUser",
2398+ ... password="secretword",
2399+ ... tenant_id=my_tenant.id)
2400
2401 Creating roles and adding users
2402 ===============================
2403@@ -103,7 +106,8 @@
2404 >>> keystone = client.Client(...)
2405 >>> service = keystone.services.create(name="nova", service_type="compute",
2406 ... description="Nova Compute Service")
2407- >>> keystone.endpoints.create(region="RegionOne", service_id=service.id,
2408- ... publicurl="http://192.168.206.130:8774/v2/%(tenant_id)s",
2409- ... adminurl="http://192.168.206.130:8774/v2/%(tenant_id)s",
2410- ... internalurl="http://192.168.206.130:8774/v2/%(tenant_id)s")
2411+ >>> keystone.endpoints.create(
2412+ ... region="RegionOne", service_id=service.id,
2413+ ... publicurl="http://192.168.206.130:8774/v2/%(tenant_id)s",
2414+ ... adminurl="http://192.168.206.130:8774/v2/%(tenant_id)s",
2415+ ... internalurl="http://192.168.206.130:8774/v2/%(tenant_id)s")
2416
2417=== added directory 'examples'
2418=== added directory 'examples/pki'
2419=== added directory 'examples/pki/certs'
2420=== added file 'examples/pki/certs/cacert.pem'
2421--- examples/pki/certs/cacert.pem 1970-01-01 00:00:00 +0000
2422+++ examples/pki/certs/cacert.pem 2012-11-30 14:11:19 +0000
2423@@ -0,0 +1,18 @@
2424+-----BEGIN CERTIFICATE-----
2425+MIIC0TCCAjqgAwIBAgIJAP2TNFqmE1KUMA0GCSqGSIb3DQEBBQUAMIGeMQowCAYD
2426+VQQFEwE1MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1bm55
2427+dmFsZTESMBAGA1UEChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTElMCMG
2428+CSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxMLU2Vs
2429+ZiBTaWduZWQwIBcNMTIxMTExMTA1NDA2WhgPMjA3MTA1MDYxMDU0MDZaMIGeMQow
2430+CAYDVQQFEwE1MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1
2431+bm55dmFsZTESMBAGA1UEChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTEl
2432+MCMGCSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxML
2433+U2VsZiBTaWduZWQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMXgnd5wlHAp
2434+GxZ58LrpEkHU995lT9PxtMgkp0tpFhg7R5HQw9K7TfQk5NHB28hNzf8UE/c0z2pJ
2435+XggPnAzvdx27NQeJGX5CWsi6fITZ8vH/+SxgfxxC+CE/6BkDpzw21MgBtq11vWL7
2436+XVaxNeU12Ax889U66i3CrObuCYt2mbpzAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMB
2437+Af8wDQYJKoZIhvcNAQEFBQADgYEAkFIbnr2/0/XWp+f80Gl6GAC7tdmZFlT9udVF
2438+q794rXyMlYY64pq34SzfQAn+4DztT4B9yzrTx03tLNr6Uf+5TS+ubcwG41UBBMs/
2439+Icf9zBMRqr+IXhijS49gQ7dPjqNTCqX+6ILbRWjdXP15ZWymI3ayQL/CMwFt/E+0
2440+kT6MLes=
2441+-----END CERTIFICATE-----
2442
2443=== added file 'examples/pki/certs/middleware.pem'
2444--- examples/pki/certs/middleware.pem 1970-01-01 00:00:00 +0000
2445+++ examples/pki/certs/middleware.pem 2012-11-30 14:11:19 +0000
2446@@ -0,0 +1,33 @@
2447+-----BEGIN CERTIFICATE-----
2448+MIICoTCCAgoCARAwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV
2449+BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK
2450+EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr
2451+ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x
2452+MjExMTExMDU0MDZaGA8yMDcxMDUwNjEwNTQwNlowgZAxCzAJBgNVBAYTAlVTMQsw
2453+CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh
2454+Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv
2455+cGVuc3RhY2sub3JnMRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEB
2456+BQADgY0AMIGJAoGBALVu4bjaOH33yAx0WdpEqj4UDVsLxVjWxEpIbOlDlc6IfJd+
2457+cUriQtxf6ahjxtzLPERS81SnwZmrICWZngbOn733pULMTZktTJH+o7C74NdKwUSN
2458+xjlCeWUy+FqIQoje4ygoJRPpMdkp1wHNO0ZERwRN9e8M5TIlx/LRtk+q8bT5AgMB
2459+AAEwDQYJKoZIhvcNAQEFBQADgYEAcp9ancue9Oq+MkaPucCrIqFhiUsdUThulJlB
2460+etPpUDGgStBSHgze/oxG2+flIjRoI6gG9Chfw//vWHOwDT7N32AHSgaI4b8/k/+s
2461+hAV2khYkV4PW2oS1TfeU/vxQzXbgApqhLBNqfFmJVW48aGAr/aqsJi3MYWN3269+
2462+6vChaVw=
2463+-----END CERTIFICATE-----
2464+-----BEGIN PRIVATE KEY-----
2465+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALVu4bjaOH33yAx0
2466+WdpEqj4UDVsLxVjWxEpIbOlDlc6IfJd+cUriQtxf6ahjxtzLPERS81SnwZmrICWZ
2467+ngbOn733pULMTZktTJH+o7C74NdKwUSNxjlCeWUy+FqIQoje4ygoJRPpMdkp1wHN
2468+O0ZERwRN9e8M5TIlx/LRtk+q8bT5AgMBAAECgYAmwq6EYFJrTvE0//JmN/8qzfvg
2469+dI5PoWpD+F8UInUxr2T2tHOdrOLd07vGVrKYXu7cJeCIOGKa4r02azAggioL/nE9
2470+FgPpqEC+QROvLuhFsk1gLZ2pGQ06sveKZVMH22h59BKZkYlhjh5qd4vlmhPqkmPp
2471+gdXj7ZjDCJhhQdFVkQJBANp18k2mVksn8q29LMieVTSIZNN3ucDA1QHbim+3fp/O
2472+GxCzU7Mv1Xfnu1zoRFu5/sF3YG0Zy3TGPDrEljBC3rUCQQDUnBjVFXL35OkBZqXW
2473+taJPzGbsPoqAO+Ls2juS97zNzeGxUNhvcKuEvHO63PXqDxp1535DpvJEBN1rT2FF
2474+iaO1AkEAt/QTWWFUTqrPxY6DNFdm5fpn9E1fg7icZJkKBDJeFJCH59MpCryfovzl
2475+n0ERtq9ynlQ4RQYwdR8rvkylLvRP9QJAOiXHFOAc5XeR0nREfwiGL9TzgUFJl/DJ
2476+C4ZULMnctVzNkTVPPItQHal87WppR26CCiUZ/161e6zo8eRv8hjG0QJABWqfYQuK
2477+dWH8nxlXS+NFUDbsCdL+XpOVE7iEH7hvSw/A/kz40mLx8sDp/Fz1ysrogR/L+NGC
2478+Vrlwm4q/WYJO0Q==
2479+-----END PRIVATE KEY-----
2480
2481=== added file 'examples/pki/certs/signing_cert.pem'
2482--- examples/pki/certs/signing_cert.pem 1970-01-01 00:00:00 +0000
2483+++ examples/pki/certs/signing_cert.pem 2012-11-30 14:11:19 +0000
2484@@ -0,0 +1,17 @@
2485+-----BEGIN CERTIFICATE-----
2486+MIICoDCCAgkCAREwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV
2487+BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK
2488+EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr
2489+ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x
2490+MjExMTExMDU0MDZaGA8yMDcxMDUwNjEwNTQwNlowgY8xCzAJBgNVBAYTAlVTMQsw
2491+CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh
2492+Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv
2493+cGVuc3RhY2sub3JnMREwDwYDVQQDEwhLZXlzdG9uZTCBnzANBgkqhkiG9w0BAQEF
2494+AAOBjQAwgYkCgYEAuoQC6IBqMxC5845c/ZkLsdcQbTHqIpYJHEkwEoxyeEjwiGFf
2495+iZmiZ91pSFNc9MfjdJnN+be/ndVS19w1nrrJvV/udVsf6JZWkTPX5HyxnllwznCH
2496+pP7gfvMZzGsqzWlSdiD6mcRbCYRX9hCCauG3jhCtISINCVYMYQGH6QSib9sCAwEA
2497+ATANBgkqhkiG9w0BAQUFAAOBgQBCssELi+1RSjEmzeqSnpgUqmtpvB9oxbcwl+xH
2498+rIrYvqMU6pV2aSxgLDqpGjjusLHUau9Bmu3Myc/fm9/mlPUQHNj0AWl8vvfSlq1b
2499+vsWMUa1h4UFlPWoF2DIUFd+noBxe5CbcLUV6K0oyJAcPO433OyuGl5oQkhxmoy1J
2500+w59KRg==
2501+-----END CERTIFICATE-----
2502
2503=== added file 'examples/pki/certs/ssl_cert.pem'
2504--- examples/pki/certs/ssl_cert.pem 1970-01-01 00:00:00 +0000
2505+++ examples/pki/certs/ssl_cert.pem 2012-11-30 14:11:19 +0000
2506@@ -0,0 +1,17 @@
2507+-----BEGIN CERTIFICATE-----
2508+MIICoTCCAgoCARAwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV
2509+BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK
2510+EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr
2511+ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x
2512+MjExMTExMDU0MDZaGA8yMDcxMDUwNjEwNTQwNlowgZAxCzAJBgNVBAYTAlVTMQsw
2513+CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh
2514+Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv
2515+cGVuc3RhY2sub3JnMRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEB
2516+BQADgY0AMIGJAoGBALVu4bjaOH33yAx0WdpEqj4UDVsLxVjWxEpIbOlDlc6IfJd+
2517+cUriQtxf6ahjxtzLPERS81SnwZmrICWZngbOn733pULMTZktTJH+o7C74NdKwUSN
2518+xjlCeWUy+FqIQoje4ygoJRPpMdkp1wHNO0ZERwRN9e8M5TIlx/LRtk+q8bT5AgMB
2519+AAEwDQYJKoZIhvcNAQEFBQADgYEAcp9ancue9Oq+MkaPucCrIqFhiUsdUThulJlB
2520+etPpUDGgStBSHgze/oxG2+flIjRoI6gG9Chfw//vWHOwDT7N32AHSgaI4b8/k/+s
2521+hAV2khYkV4PW2oS1TfeU/vxQzXbgApqhLBNqfFmJVW48aGAr/aqsJi3MYWN3269+
2522+6vChaVw=
2523+-----END CERTIFICATE-----
2524
2525=== added directory 'examples/pki/cms'
2526=== added file 'examples/pki/cms/auth_token_revoked.json'
2527--- examples/pki/cms/auth_token_revoked.json 1970-01-01 00:00:00 +0000
2528+++ examples/pki/cms/auth_token_revoked.json 2012-11-30 14:11:19 +0000
2529@@ -0,0 +1,1 @@
2530+{"access": {"serviceCatalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v2.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}],"token": {"expires": "2012-06-02T14:47:34Z", "id": "placeholder", "tenant": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1"}}, "user": {"username": "revoked_username1", "roles_links": ["role1","role2"], "id": "revoked_user_id1", "roles": [{"name": "role1"}, {"name": "role2"}], "name": "revoked_username1"}}}
2531
2532=== added file 'examples/pki/cms/auth_token_revoked.pem'
2533--- examples/pki/cms/auth_token_revoked.pem 1970-01-01 00:00:00 +0000
2534+++ examples/pki/cms/auth_token_revoked.pem 2012-11-30 14:11:19 +0000
2535@@ -0,0 +1,42 @@
2536+-----BEGIN CMS-----
2537+MIIHVgYJKoZIhvcNAQcCoIIHRzCCB0MCAQExCTAHBgUrDgMCGjCCBeQGCSqGSIb3
2538+DQEHAaCCBdUEggXReyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k
2539+cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx
2540+LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy
2541+ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2
2542+L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS
2543+TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh
2544+NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi
2545+OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si
2546+YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6
2547+ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
2548+MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi
2549+fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt
2550+ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw
2551+Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5
2552+YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog
2553+Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw
2554+ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3
2555+NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu
2556+ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi
2557+bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu
2558+MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy
2559+bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV
2560+UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf
2561+bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u
2562+ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMi0wNi0wMlQxNDo0NzozNFoi
2563+LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1
2564+ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg
2565+ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJyZXZv
2566+a2VkX3VzZXJuYW1lMSIsICJyb2xlc19saW5rcyI6IFsicm9sZTEiLCJyb2xlMiJd
2567+LCAiaWQiOiAicmV2b2tlZF91c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAi
2568+cm9sZTEifSwgeyJuYW1lIjogInJvbGUyIn1dLCAibmFtZSI6ICJyZXZva2VkX3Vz
2569+ZXJuYW1lMSJ9fX0NCjGCAUkwggFFAgEBMIGkMIGeMQowCAYDVQQFEwE1MQswCQYD
2570+VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTESMBAGA1UE
2571+ChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTElMCMGCSqGSIb3DQEJARYW
2572+a2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxMLU2VsZiBTaWduZWQCAREw
2573+BwYFKw4DAhowDQYJKoZIhvcNAQEBBQAEgYBhV5KrVjcdACPUNafkPY+lgCSlh6uc
2574+N55SATBcQmg1/argEUFg/cx2GcF7ftQV384iGepLEgsq+6om2wPw6DWA0RknpVLJ
2575+vMsHbWdGoXIZ5jRuAQTPtkXcJQOR677baDHvGJ+5zwBBDT2CmN2Tcv348+Xpjp7D
2576+hF/cmAXnYYo00g==
2577+-----END CMS-----
2578
2579=== added file 'examples/pki/cms/auth_token_scoped.json'
2580--- examples/pki/cms/auth_token_scoped.json 1970-01-01 00:00:00 +0000
2581+++ examples/pki/cms/auth_token_scoped.json 2012-11-30 14:11:19 +0000
2582@@ -0,0 +1,1 @@
2583+{"access": {"serviceCatalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v2.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}],"token": {"expires": "2012-06-02T14:47:34Z", "id": "placeholder", "tenant": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1"}}, "user": {"username": "user_name1", "roles_links": ["role1","role2"], "id": "user_id1", "roles": [{"name": "role1"}, {"name": "role2"}], "name": "user_name1"}}}
2584
2585=== added file 'examples/pki/cms/auth_token_scoped.pem'
2586--- examples/pki/cms/auth_token_scoped.pem 1970-01-01 00:00:00 +0000
2587+++ examples/pki/cms/auth_token_scoped.pem 2012-11-30 14:11:19 +0000
2588@@ -0,0 +1,41 @@
2589+-----BEGIN CMS-----
2590+MIIHQAYJKoZIhvcNAQcCoIIHMTCCBy0CAQExCTAHBgUrDgMCGjCCBc4GCSqGSIb3
2591+DQEHAaCCBb8EggW7eyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k
2592+cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx
2593+LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy
2594+ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2
2595+L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS
2596+TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh
2597+NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi
2598+OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si
2599+YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6
2600+ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
2601+MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi
2602+fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt
2603+ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw
2604+Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5
2605+YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog
2606+Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw
2607+ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3
2608+NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu
2609+ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi
2610+bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu
2611+MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy
2612+bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV
2613+UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf
2614+bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u
2615+ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMi0wNi0wMlQxNDo0NzozNFoi
2616+LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1
2617+ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg
2618+ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJ1c2Vy
2619+X25hbWUxIiwgInJvbGVzX2xpbmtzIjogWyJyb2xlMSIsInJvbGUyIl0sICJpZCI6
2620+ICJ1c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAicm9sZTEifSwgeyJuYW1l
2621+IjogInJvbGUyIn1dLCAibmFtZSI6ICJ1c2VyX25hbWUxIn19fQ0KMYIBSTCCAUUC
2622+AQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTES
2623+MBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sxETAPBgNVBAsT
2624+CEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3Jn
2625+MRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkqhkiG9w0BAQEF
2626+AASBgFizBVs3dCvlHx04nUHgXHpaA9RL+e3uaaNszK9UwCBpBlv8c6+74sz6i3+G
2627+eYDIpL9bc6QgNJ6cKhmW5yLmS8/+mmAMAcm06bdWc7p/mqC3Ild+xmQ+OHDYyyJg
2628+DvtRUgtidFUCvxne/nwKK0WHJlpY+iwWqel5F+Xqmb8vheb1
2629+-----END CMS-----
2630
2631=== added file 'examples/pki/cms/auth_token_unscoped.json'
2632--- examples/pki/cms/auth_token_unscoped.json 1970-01-01 00:00:00 +0000
2633+++ examples/pki/cms/auth_token_unscoped.json 2012-11-30 14:11:19 +0000
2634@@ -0,0 +1,1 @@
2635+{"access": {"token": {"expires": "2012-08-17T15:35:34Z", "id": "01e032c996ef4406b144335915a41e79"}, "serviceCatalog": {}, "user": {"username": "user_name1", "roles_links": [], "id": "c9c89e3be3ee453fbf00c7966f6d3fbd", "roles": [{'name': 'role1'},{'name': 'role2'},], "name": "user_name1"}}}
2636\ No newline at end of file
2637
2638=== added file 'examples/pki/cms/auth_token_unscoped.pem'
2639--- examples/pki/cms/auth_token_unscoped.pem 1970-01-01 00:00:00 +0000
2640+++ examples/pki/cms/auth_token_unscoped.pem 2012-11-30 14:11:19 +0000
2641@@ -0,0 +1,17 @@
2642+-----BEGIN CMS-----
2643+MIICpwYJKoZIhvcNAQcCoIICmDCCApQCAQExCTAHBgUrDgMCGjCCATUGCSqGSIb3
2644+DQEHAaCCASYEggEieyJhY2Nlc3MiOiB7InRva2VuIjogeyJleHBpcmVzIjogIjIw
2645+MTItMDgtMTdUMTU6MzU6MzRaIiwgImlkIjogIjAxZTAzMmM5OTZlZjQ0MDZiMTQ0
2646+MzM1OTE1YTQxZTc5In0sICJzZXJ2aWNlQ2F0YWxvZyI6IHt9LCAidXNlciI6IHsi
2647+dXNlcm5hbWUiOiAidXNlcl9uYW1lMSIsICJyb2xlc19saW5rcyI6IFtdLCAiaWQi
2648+OiAiYzljODllM2JlM2VlNDUzZmJmMDBjNzk2NmY2ZDNmYmQiLCAicm9sZXMiOiBb
2649+eyduYW1lJzogJ3JvbGUxJ30seyduYW1lJzogJ3JvbGUyJ30sXSwgIm5hbWUiOiAi
2650+dXNlcl9uYW1lMSJ9fX0xggFJMIIBRQIBATCBpDCBnjEKMAgGA1UEBRMBNTELMAkG
2651+A1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxEjAQBgNV
2652+BAoTCU9wZW5TdGFjazERMA8GA1UECxMIS2V5c3RvbmUxJTAjBgkqhkiG9w0BCQEW
2653+FmtleXN0b25lQG9wZW5zdGFjay5vcmcxFDASBgNVBAMTC1NlbGYgU2lnbmVkAgER
2654+MAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIGAITCwkW7cAbcWbCBD5GfGMGHB9hP/
2655+UagaCZ8HFhlzjdQoJjvC+Mtu+3lWlwqPGR8ztY9kBc1401S2qJxD4FGo+M3CkNpF
2656+s0mtaT2PUJfFkDCzHqeBQNFHyZeqLjkPYnokPcw4s3i60DBGTFfAiUT3xumn8a4h
2657+C+zEAee35C/A+Iw=
2658+-----END CMS-----
2659
2660=== added file 'examples/pki/cms/revocation_list.json'
2661--- examples/pki/cms/revocation_list.json 1970-01-01 00:00:00 +0000
2662+++ examples/pki/cms/revocation_list.json 2012-11-30 14:11:19 +0000
2663@@ -0,0 +1,1 @@
2664+{"revoked":[{"id":"7acfcfdaf6a14aebe97c61c5947bc4d3","expires":"2012-08-14T17:58:48Z"}]}
2665
2666=== added file 'examples/pki/cms/revocation_list.pem'
2667--- examples/pki/cms/revocation_list.pem 1970-01-01 00:00:00 +0000
2668+++ examples/pki/cms/revocation_list.pem 2012-11-30 14:11:19 +0000
2669@@ -0,0 +1,12 @@
2670+-----BEGIN CMS-----
2671+MIIB2QYJKoZIhvcNAQcCoIIByjCCAcYCAQExCTAHBgUrDgMCGjBpBgkqhkiG9w0B
2672+BwGgXARaeyJyZXZva2VkIjpbeyJpZCI6IjdhY2ZjZmRhZjZhMTRhZWJlOTdjNjFj
2673+NTk0N2JjNGQzIiwiZXhwaXJlcyI6IjIwMTItMDgtMTRUMTc6NTg6NDhaIn1dfQ0K
2674+MYIBSTCCAUUCAQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYD
2675+VQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sx
2676+ETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVu
2677+c3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkq
2678+hkiG9w0BAQEFAASBgDNDhvViAo8EqTVVvZ00pWUWjajTwoV1w1os1XDJ1XacBUo+
2679+rsh7gljIIVuvHL2F9C660I5jxhb7QVsTge3CwSiDmexxBAPOs4lNR5hFH7FdT47b
2680+OK2qd0XnRjo5F7odUxIkozuQ/UISaNTPeWxGEMNVhpTXo2Dwn8wN1wrs/Z2E
2681+-----END CMS-----
2682
2683=== added file 'examples/pki/gen_pki.sh'
2684--- examples/pki/gen_pki.sh 1970-01-01 00:00:00 +0000
2685+++ examples/pki/gen_pki.sh 2012-11-30 14:11:19 +0000
2686@@ -0,0 +1,222 @@
2687+#!/bin/bash
2688+
2689+# Copyright 2012 OpenStack LLC
2690+#
2691+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2692+# not use this file except in compliance with the License. You may obtain
2693+# a copy of the License at
2694+#
2695+# http://www.apache.org/licenses/LICENSE-2.0
2696+#
2697+# Unless required by applicable law or agreed to in writing, software
2698+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2699+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2700+# License for the specific language governing permissions and limitations
2701+# under the License.
2702+
2703+# This script generates the crypto necessary for the SSL tests.
2704+
2705+DIR=`dirname "$0"`
2706+CURRENT_DIR=`cd "$DIR" && pwd`
2707+CERTS_DIR=$CURRENT_DIR/certs
2708+PRIVATE_DIR=$CURRENT_DIR/private
2709+CMS_DIR=$CURRENT_DIR/cms
2710+
2711+
2712+function rm_old {
2713+ rm -rf $CERTS_DIR/*.pem
2714+ rm -rf $PRIVATE_DIR/*.pem
2715+}
2716+
2717+function cleanup {
2718+ rm -rf *.conf > /dev/null 2>&1
2719+ rm -rf index* > /dev/null 2>&1
2720+ rm -rf *.crt > /dev/null 2>&1
2721+ rm -rf newcerts > /dev/null 2>&1
2722+ rm -rf *.pem > /dev/null 2>&1
2723+ rm -rf serial* > /dev/null 2>&1
2724+}
2725+
2726+function generate_ca_conf {
2727+ echo '
2728+[ req ]
2729+default_bits = 1024
2730+default_keyfile = cakey.pem
2731+default_md = sha1
2732+
2733+prompt = no
2734+distinguished_name = ca_distinguished_name
2735+
2736+x509_extensions = ca_extensions
2737+
2738+[ ca_distinguished_name ]
2739+serialNumber = 5
2740+countryName = US
2741+stateOrProvinceName = CA
2742+localityName = Sunnyvale
2743+organizationName = OpenStack
2744+organizationalUnitName = Keystone
2745+emailAddress = keystone@openstack.org
2746+commonName = Self Signed
2747+
2748+[ ca_extensions ]
2749+basicConstraints = critical,CA:true
2750+' > ca.conf
2751+}
2752+
2753+function generate_ssl_req_conf {
2754+ echo '
2755+[ req ]
2756+default_bits = 1024
2757+default_keyfile = keystonekey.pem
2758+default_md = sha1
2759+
2760+prompt = no
2761+distinguished_name = distinguished_name
2762+
2763+[ distinguished_name ]
2764+countryName = US
2765+stateOrProvinceName = CA
2766+localityName = Sunnyvale
2767+organizationName = OpenStack
2768+organizationalUnitName = Keystone
2769+commonName = localhost
2770+emailAddress = keystone@openstack.org
2771+' > ssl_req.conf
2772+}
2773+
2774+function generate_cms_signing_req_conf {
2775+ echo '
2776+[ req ]
2777+default_bits = 1024
2778+default_keyfile = keystonekey.pem
2779+default_md = sha1
2780+
2781+prompt = no
2782+distinguished_name = distinguished_name
2783+
2784+[ distinguished_name ]
2785+countryName = US
2786+stateOrProvinceName = CA
2787+localityName = Sunnyvale
2788+organizationName = OpenStack
2789+organizationalUnitName = Keystone
2790+commonName = Keystone
2791+emailAddress = keystone@openstack.org
2792+' > cms_signing_req.conf
2793+}
2794+
2795+function generate_signing_conf {
2796+ echo '
2797+[ ca ]
2798+default_ca = signing_ca
2799+
2800+[ signing_ca ]
2801+dir = .
2802+database = $dir/index.txt
2803+new_certs_dir = $dir/newcerts
2804+
2805+certificate = $dir/certs/cacert.pem
2806+serial = $dir/serial
2807+private_key = $dir/private/cakey.pem
2808+
2809+default_days = 21360
2810+default_crl_days = 30
2811+default_md = sha1
2812+
2813+policy = policy_any
2814+
2815+[ policy_any ]
2816+countryName = supplied
2817+stateOrProvinceName = supplied
2818+localityName = optional
2819+organizationName = supplied
2820+organizationalUnitName = supplied
2821+emailAddress = supplied
2822+commonName = supplied
2823+' > signing.conf
2824+}
2825+
2826+function setup {
2827+ touch index.txt
2828+ echo '10' > serial
2829+ generate_ca_conf
2830+ mkdir newcerts
2831+}
2832+
2833+function check_error {
2834+ if [ $1 != 0 ] ; then
2835+ echo "Failed! rc=${1}"
2836+ echo 'Bailing ...'
2837+ cleanup
2838+ exit $1
2839+ else
2840+ echo 'Done'
2841+ fi
2842+}
2843+
2844+function generate_ca {
2845+ echo 'Generating New CA Certificate ...'
2846+ openssl req -x509 -newkey rsa:1024 -days 21360 -out $CERTS_DIR/cacert.pem -keyout $PRIVATE_DIR/cakey.pem -outform PEM -config ca.conf -nodes
2847+ check_error $?
2848+}
2849+
2850+function ssl_cert_req {
2851+ echo 'Generating SSL Certificate Request ...'
2852+ generate_ssl_req_conf
2853+ openssl req -newkey rsa:1024 -keyout $PRIVATE_DIR/ssl_key.pem -keyform PEM -out ssl_req.pem -outform PEM -config ssl_req.conf -nodes
2854+ check_error $?
2855+ #openssl req -in req.pem -text -noout
2856+}
2857+
2858+function cms_signing_cert_req {
2859+ echo 'Generating CMS Signing Certificate Request ...'
2860+ generate_cms_signing_req_conf
2861+ openssl req -newkey rsa:1024 -keyout $PRIVATE_DIR/signing_key.pem -keyform PEM -out cms_signing_req.pem -outform PEM -config cms_signing_req.conf -nodes
2862+ check_error $?
2863+ #openssl req -in req.pem -text -noout
2864+}
2865+
2866+function issue_certs {
2867+ generate_signing_conf
2868+ echo 'Issuing SSL Certificate ...'
2869+ openssl ca -in ssl_req.pem -config signing.conf -batch
2870+ check_error $?
2871+ openssl x509 -in $CURRENT_DIR/newcerts/10.pem -out $CERTS_DIR/ssl_cert.pem
2872+ check_error $?
2873+ echo 'Issuing CMS Signing Certificate ...'
2874+ openssl ca -in cms_signing_req.pem -config signing.conf -batch
2875+ check_error $?
2876+ openssl x509 -in $CURRENT_DIR/newcerts/11.pem -out $CERTS_DIR/signing_cert.pem
2877+ check_error $?
2878+}
2879+
2880+function create_middleware_cert {
2881+ cp $CERTS_DIR/ssl_cert.pem $CERTS_DIR/middleware.pem
2882+ cat $PRIVATE_DIR/ssl_key.pem >> $CERTS_DIR/middleware.pem
2883+}
2884+
2885+function check_openssl {
2886+ echo 'Checking openssl availability ...'
2887+ which openssl
2888+ check_error $?
2889+}
2890+
2891+function gen_sample_cms {
2892+ for json_file in "${CMS_DIR}/auth_token_revoked.json" "${CMS_DIR}/auth_token_unscoped.json" "${CMS_DIR}/auth_token_scoped.json" "${CMS_DIR}/revocation_list.json"
2893+ do
2894+ openssl cms -sign -in $json_file -nosmimecap -signer $CERTS_DIR/signing_cert.pem -inkey $PRIVATE_DIR/signing_key.pem -outform PEM -nodetach -nocerts -noattr -out ${json_file/.json/.pem}
2895+ done
2896+}
2897+
2898+check_openssl
2899+rm_old
2900+cleanup
2901+setup
2902+generate_ca
2903+ssl_cert_req
2904+cms_signing_cert_req
2905+issue_certs
2906+create_middleware_cert
2907+gen_sample_cms
2908+cleanup
2909
2910=== added directory 'examples/pki/private'
2911=== added file 'examples/pki/private/cakey.pem'
2912--- examples/pki/private/cakey.pem 1970-01-01 00:00:00 +0000
2913+++ examples/pki/private/cakey.pem 2012-11-30 14:11:19 +0000
2914@@ -0,0 +1,16 @@
2915+-----BEGIN PRIVATE KEY-----
2916+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMXgnd5wlHApGxZ5
2917+8LrpEkHU995lT9PxtMgkp0tpFhg7R5HQw9K7TfQk5NHB28hNzf8UE/c0z2pJXggP
2918+nAzvdx27NQeJGX5CWsi6fITZ8vH/+SxgfxxC+CE/6BkDpzw21MgBtq11vWL7XVax
2919+NeU12Ax889U66i3CrObuCYt2mbpzAgMBAAECgYEAligxJE9CFSrcR14Zc3zSQeqe
2920+fcFbpnXQveAyo2MHRTQWx2wobY19RjuI+DOn2IRSQbK2w+zrSLiMBonx3U8Kj8nx
2921+A4EQ75GLJEEr81TvBoIZSJAqrowNrkXNq8W++qwjlGXRjKiBAYlKMrFvR4lij4XN
2922+6cdB7kGdSIUmhvC20sECQQD4ebCGfsgFWnrqOrco6T9eQRTvP3+gJuqYXYLuVSTC
2923+R4gHxT5QVXSZt/Hv3UWJ0BLDbyLzLGHf30w1AqgwsUP5AkEAy96qXq6j2+IRa5w7
2924+2G+KZHF5N/MK/Hyy27Jw67GBVeGQj1Dwq2ZGAJBZrfXjTtQQAGdQ7EfOTCAOzHgX
2925+2Bx0ywJAYqfGbBBIkL+VEA0SDh9WNrE2g6u9m7P371kplEGgH7dRDmzFShYz/pin
2926+aep8IrTHzmsBAHY9wiqh0mZkqzim2QJADTYdxkr89WfeByI1wp3f0wiDeXu3j4sp
2927+MBGNPcjf/8fBTXhKUGEtUiYImbxggaA+dTg8x0MT/FzreJajvO6DJwJARMc6rhzv
2928+aTlm4IgApcDPBeuz6SKex9TfvDUJpqACoFM4lMgyHADi9NrJBslxFHPP5eTiM2Ag
2929+vI7EuW837e6raQ==
2930+-----END PRIVATE KEY-----
2931
2932=== added file 'examples/pki/private/signing_key.pem'
2933--- examples/pki/private/signing_key.pem 1970-01-01 00:00:00 +0000
2934+++ examples/pki/private/signing_key.pem 2012-11-30 14:11:19 +0000
2935@@ -0,0 +1,16 @@
2936+-----BEGIN PRIVATE KEY-----
2937+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALqEAuiAajMQufOO
2938+XP2ZC7HXEG0x6iKWCRxJMBKMcnhI8IhhX4mZomfdaUhTXPTH43SZzfm3v53VUtfc
2939+NZ66yb1f7nVbH+iWVpEz1+R8sZ5ZcM5wh6T+4H7zGcxrKs1pUnYg+pnEWwmEV/YQ
2940+gmrht44QrSEiDQlWDGEBh+kEom/bAgMBAAECgYBywfSUHya4gqsW2toGQps6cauu
2941+s85uN0glujY0w2tO7Pnpv5errvaI12cG1BvWlAIz5MohwlfIgc919wyavCyRJgQN
2942+xQo5v5MEMYKKc8ppmXpRr03HLwoPLOHVs6UHRJQT9dhOBfmLzMZIP7P/lJlt2/1X
2943+Okwxft/PWorczKX1aQJBAORlVqP+Cj4r5kz1A77agnCvINioV1VM5n9PvzPVzYLH
2944+5r1I53RWFooy1Hx2RUCmtSRQMZMeI9iGMg9c8d3LJ4UCQQDRDuIAd3AoNBcwXKC4
2945+BPNkbI9BSqnpIdZo87BzpY8rJ/ra3VHMHuq4w+gQsmmEy3pp01AZd1uBqv3s1wHy
2946+muffAkEAn2ZmiH+lUGy9B5q8qXfBL7naF7utb/gCqnnSvO+LxamUTSjTeKsYgg0l
2947+pVO503xF0fkyEDYp2FUYHQbGOwAtLQJAHkJ3N/YRx9/yU0+0+63LxQdpnNu/yDzb
2948+mglbywF1vZtl1fQe+NqowuGoX3JTj6McLuElQOpj1lr3siZU49bEJQJBANRazUzj
2949+Xfoja7wGuZ3PwHdxxoNDlJ2u0rYjcfK9VZuPGSz/25iCOkaar3OralJ3lfCWbFKA
2950+vvRp8Hl2Yk4hdKM=
2951+-----END PRIVATE KEY-----
2952
2953=== added file 'examples/pki/private/ssl_key.pem'
2954--- examples/pki/private/ssl_key.pem 1970-01-01 00:00:00 +0000
2955+++ examples/pki/private/ssl_key.pem 2012-11-30 14:11:19 +0000
2956@@ -0,0 +1,16 @@
2957+-----BEGIN PRIVATE KEY-----
2958+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALVu4bjaOH33yAx0
2959+WdpEqj4UDVsLxVjWxEpIbOlDlc6IfJd+cUriQtxf6ahjxtzLPERS81SnwZmrICWZ
2960+ngbOn733pULMTZktTJH+o7C74NdKwUSNxjlCeWUy+FqIQoje4ygoJRPpMdkp1wHN
2961+O0ZERwRN9e8M5TIlx/LRtk+q8bT5AgMBAAECgYAmwq6EYFJrTvE0//JmN/8qzfvg
2962+dI5PoWpD+F8UInUxr2T2tHOdrOLd07vGVrKYXu7cJeCIOGKa4r02azAggioL/nE9
2963+FgPpqEC+QROvLuhFsk1gLZ2pGQ06sveKZVMH22h59BKZkYlhjh5qd4vlmhPqkmPp
2964+gdXj7ZjDCJhhQdFVkQJBANp18k2mVksn8q29LMieVTSIZNN3ucDA1QHbim+3fp/O
2965+GxCzU7Mv1Xfnu1zoRFu5/sF3YG0Zy3TGPDrEljBC3rUCQQDUnBjVFXL35OkBZqXW
2966+taJPzGbsPoqAO+Ls2juS97zNzeGxUNhvcKuEvHO63PXqDxp1535DpvJEBN1rT2FF
2967+iaO1AkEAt/QTWWFUTqrPxY6DNFdm5fpn9E1fg7icZJkKBDJeFJCH59MpCryfovzl
2968+n0ERtq9ynlQ4RQYwdR8rvkylLvRP9QJAOiXHFOAc5XeR0nREfwiGL9TzgUFJl/DJ
2969+C4ZULMnctVzNkTVPPItQHal87WppR26CCiUZ/161e6zo8eRv8hjG0QJABWqfYQuK
2970+dWH8nxlXS+NFUDbsCdL+XpOVE7iEH7hvSw/A/kz40mLx8sDp/Fz1ysrogR/L+NGC
2971+Vrlwm4q/WYJO0Q==
2972+-----END PRIVATE KEY-----
2973
2974=== added file 'keystoneclient/access.py'
2975--- keystoneclient/access.py 1970-01-01 00:00:00 +0000
2976+++ keystoneclient/access.py 2012-11-30 14:11:19 +0000
2977@@ -0,0 +1,144 @@
2978+# Copyright 2012 Nebula, Inc.
2979+#
2980+# All Rights Reserved.
2981+#
2982+# Licensed under the Apache License, Version 2.0 (the "License");
2983+# you may not use this file except in compliance with the License.
2984+# You may obtain a copy of the License at
2985+#
2986+# http://www.apache.org/licenses/LICENSE-2.0
2987+#
2988+# Unless required by applicable law or agreed to in writing, software
2989+# distributed under the License is distributed on an "AS IS" BASIS,
2990+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2991+# See the License for the specific language governing permissions and
2992+# limitations under the License.
2993+
2994+
2995+class AccessInfo(dict):
2996+ """An object for encapsulating a raw authentication token from keystone
2997+ and helper methods for extracting useful values from that token."""
2998+
2999+ def __init__(self, *args, **kwargs):
3000+ dict.__init__(self, *args, **kwargs)
3001+
3002+ @property
3003+ def auth_token(self):
3004+ """ Returns the token_id associated with the auth request, to be used
3005+ in headers for authenticating OpenStack API requests.
3006+
3007+ :returns: str
3008+ """
3009+ return self['token'].get('id', None)
3010+
3011+ @property
3012+ def username(self):
3013+ """ Returns the username associated with the authentication request.
3014+ Follows the pattern defined in the V2 API of first looking for 'name',
3015+ returning that if available, and falling back to 'username' if name
3016+ is unavailable.
3017+
3018+ :returns: str
3019+ """
3020+ name = self['user'].get('name', None)
3021+ if name:
3022+ return name
3023+ else:
3024+ return self['user'].get('username', None)
3025+
3026+ @property
3027+ def user_id(self):
3028+ """ Returns the user id associated with the authentication request.
3029+
3030+ :returns: str
3031+ """
3032+ return self['user'].get('id', None)
3033+
3034+ @property
3035+ def tenant_name(self):
3036+ """ Returns the tenant (project) name associated with the
3037+ authentication request.
3038+
3039+ :returns: str
3040+ """
3041+ tenant_dict = self['token'].get('tenant', None)
3042+ if tenant_dict:
3043+ return tenant_dict.get('name', None)
3044+ return None
3045+
3046+ @property
3047+ def project_name(self):
3048+ """ Synonym for tenant_name """
3049+ return self.tenant_name
3050+
3051+ @property
3052+ def scoped(self):
3053+ """ Returns true if the authorization token was scoped to a tenant
3054+ (project), and contains a populated service catalog.
3055+
3056+ :returns: bool
3057+ """
3058+ if ('serviceCatalog' in self
3059+ and self['serviceCatalog']
3060+ and 'tenant' in self['token']):
3061+ return True
3062+ return False
3063+
3064+ @property
3065+ def tenant_id(self):
3066+ """ Returns the tenant (project) id associated with the authentication
3067+ request, or None if the authentication request wasn't scoped to a
3068+ tenant (project).
3069+
3070+ :returns: str
3071+ """
3072+ tenant_dict = self['token'].get('tenant', None)
3073+ if tenant_dict:
3074+ return tenant_dict.get('id', None)
3075+ return None
3076+
3077+ @property
3078+ def project_id(self):
3079+ """ Synonym for project_id """
3080+ return self.tenant_id
3081+
3082+ @property
3083+ def auth_url(self):
3084+ """ Returns a tuple of URLs from publicURL and adminURL for the service
3085+ 'identity' from the service catalog associated with the authorization
3086+ request. If the authentication request wasn't scoped to a tenant
3087+ (project), this property will return None.
3088+
3089+ :returns: tuple of urls
3090+ """
3091+ return_list = []
3092+ if 'serviceCatalog' in self and self['serviceCatalog']:
3093+ identity_services = [x for x in self['serviceCatalog']
3094+ if x['type'] == 'identity']
3095+ for svc in identity_services:
3096+ for endpoint in svc['endpoints']:
3097+ if 'publicURL' in endpoint:
3098+ return_list.append(endpoint['publicURL'])
3099+ if len(return_list) > 0:
3100+ return tuple(return_list)
3101+ return None
3102+
3103+ @property
3104+ def management_url(self):
3105+ """ Returns the first adminURL for 'identity' from the service catalog
3106+ associated with the authorization request, or None if the
3107+ authentication request wasn't scoped to a tenant (project).
3108+
3109+ :returns: tuple of urls
3110+ """
3111+ return_list = []
3112+ if 'serviceCatalog' in self and self['serviceCatalog']:
3113+ identity_services = [x for x in self['serviceCatalog']
3114+ if x['type'] == 'identity']
3115+ for svc in identity_services:
3116+ for endpoint in svc['endpoints']:
3117+ if 'adminURL' in endpoint:
3118+ return_list.append(endpoint['adminURL'])
3119+ if len(return_list) > 0:
3120+ return tuple(return_list)
3121+ return None
3122
3123=== modified file 'keystoneclient/base.py'
3124--- keystoneclient/base.py 2012-06-22 12:58:18 +0000
3125+++ keystoneclient/base.py 2012-11-30 14:11:19 +0000
3126@@ -18,6 +18,8 @@
3127 Base utilities to build API operation managers and objects on top of.
3128 """
3129
3130+import urllib
3131+
3132 from keystoneclient import exceptions
3133
3134
3135@@ -76,7 +78,11 @@
3136
3137 def _get(self, url, response_key):
3138 resp, body = self.api.get(url)
3139- return self.resource_class(self, body[response_key])
3140+ return self.resource_class(self, body[response_key], loaded=True)
3141+
3142+ def _head(self, url):
3143+ resp, body = self.api.head(url)
3144+ return resp.status == 204
3145
3146 def _create(self, url, body, response_key, return_raw=False):
3147 resp, body = self.api.post(url, body=body)
3148@@ -87,11 +93,15 @@
3149 def _delete(self, url):
3150 resp, body = self.api.delete(url)
3151
3152- def _update(self, url, body, response_key=None, method="PUT"):
3153+ def _update(self, url, body=None, response_key=None, method="PUT"):
3154 methods = {"PUT": self.api.put,
3155- "POST": self.api.post}
3156+ "POST": self.api.post,
3157+ "PATCH": self.api.patch}
3158 try:
3159- resp, body = methods[method](url, body=body)
3160+ if body is not None:
3161+ resp, body = methods[method](url, body=body)
3162+ else:
3163+ resp, body = methods[method](url)
3164 except KeyError:
3165 raise exceptions.ClientException("Invalid update method: %s"
3166 % method)
3167@@ -139,6 +149,115 @@
3168 return found
3169
3170
3171+class CrudManager(Manager):
3172+ """Base manager class for manipulating Keystone entities.
3173+
3174+ Children of this class are expected to define a `collection_key` and `key`.
3175+
3176+ - `collection_key`: Usually a plural noun by convention (e.g. `entities`);
3177+ used to refer collections in both URL's (e.g. `/v3/entities`) and JSON
3178+ objects containing a list of member resources (e.g. `{'entities': [{},
3179+ {}, {}]}`).
3180+ - `key`: Usually a singular noun by convention (e.g. `entity`); used to
3181+ refer to an individual member of the collection.
3182+
3183+ """
3184+ collection_key = None
3185+ key = None
3186+
3187+ def build_url(self, base_url=None, **kwargs):
3188+ """Builds a resource URL for the given kwargs.
3189+
3190+ Given an example collection where `collection_key = 'entities'` and
3191+ `key = 'entity'`, the following URL's could be generated.
3192+
3193+ By default, the URL will represent a collection of entities, e.g.::
3194+
3195+ /entities
3196+
3197+ If kwargs contains an `entity_id`, then the URL will represent a
3198+ specific member, e.g.::
3199+
3200+ /entities/{entity_id}
3201+
3202+ If a `base_url` is provided, the generated URL will be appended to it.
3203+
3204+ """
3205+ url = base_url if base_url is not None else ''
3206+
3207+ url += '/%s' % self.collection_key
3208+
3209+ # do we have a specific entity?
3210+ entity_id = kwargs.get('%s_id' % self.key)
3211+ if entity_id is not None:
3212+ url += '/%s' % entity_id
3213+
3214+ return url
3215+
3216+ def _filter_kwargs(self, kwargs):
3217+ # drop null values
3218+ for key, ref in kwargs.copy().iteritems():
3219+ if ref is None:
3220+ kwargs.pop(key)
3221+ else:
3222+ id_value = getid(ref)
3223+ if id_value != ref:
3224+ kwargs.pop(key)
3225+ kwargs['%s_id' % key] = id_value
3226+ return kwargs
3227+
3228+ def create(self, **kwargs):
3229+ kwargs = self._filter_kwargs(kwargs)
3230+ return self._create(
3231+ self.build_url(**kwargs),
3232+ {self.key: kwargs},
3233+ self.key)
3234+
3235+ def get(self, **kwargs):
3236+ kwargs = self._filter_kwargs(kwargs)
3237+ return self._get(
3238+ self.build_url(**kwargs),
3239+ self.key)
3240+
3241+ def head(self, **kwargs):
3242+ kwargs = self._filter_kwargs(kwargs)
3243+ return self._head(self.build_url(**kwargs))
3244+
3245+ def list(self, base_url=None, **kwargs):
3246+ kwargs = self._filter_kwargs(kwargs)
3247+
3248+ return self._list(
3249+ '%(base_url)s%(query)s' % {
3250+ 'base_url': self.build_url(base_url=base_url, **kwargs),
3251+ 'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '',
3252+ },
3253+ self.collection_key)
3254+
3255+ def put(self, base_url=None, **kwargs):
3256+ kwargs = self._filter_kwargs(kwargs)
3257+
3258+ return self._update(
3259+ self.build_url(base_url=base_url, **kwargs),
3260+ method='PUT')
3261+
3262+ def update(self, **kwargs):
3263+ kwargs = self._filter_kwargs(kwargs)
3264+ params = kwargs.copy()
3265+ params.pop('%s_id' % self.key)
3266+
3267+ return self._update(
3268+ self.build_url(**kwargs),
3269+ {self.key: params},
3270+ self.key,
3271+ method='PATCH')
3272+
3273+ def delete(self, **kwargs):
3274+ kwargs = self._filter_kwargs(kwargs)
3275+
3276+ return self._delete(
3277+ self.build_url(**kwargs))
3278+
3279+
3280 class Resource(object):
3281 """
3282 A resource represents a particular instance of an object (tenant, user,
3283@@ -185,6 +304,9 @@
3284 if new:
3285 self._add_details(new._info)
3286
3287+ def delete(self):
3288+ return self.manager.delete(self)
3289+
3290 def __eq__(self, other):
3291 if not isinstance(other, self.__class__):
3292 return False
3293
3294=== modified file 'keystoneclient/client.py'
3295--- keystoneclient/client.py 2012-09-07 13:13:18 +0000
3296+++ keystoneclient/client.py 2012-11-30 14:11:19 +0000
3297@@ -10,7 +10,6 @@
3298
3299 import copy
3300 import logging
3301-import os
3302 import urlparse
3303
3304 import httplib2
3305@@ -26,6 +25,7 @@
3306 urlparse.parse_qsl = cgi.parse_qsl
3307
3308
3309+from keystoneclient import access
3310 from keystoneclient import exceptions
3311
3312
3313@@ -39,40 +39,71 @@
3314 def __init__(self, username=None, tenant_id=None, tenant_name=None,
3315 password=None, auth_url=None, region_name=None, timeout=None,
3316 endpoint=None, token=None, cacert=None, key=None,
3317- cert=None, insecure=False):
3318+ cert=None, insecure=False, original_ip=None, debug=False,
3319+ auth_ref=None):
3320 super(HTTPClient, self).__init__(timeout=timeout, ca_certs=cacert)
3321 if cert:
3322 if key:
3323 self.add_certificate(key=key, cert=cert, domain='')
3324 else:
3325 self.add_certificate(key=cert, cert=cert, domain='')
3326- self.username = username
3327- self.tenant_id = tenant_id
3328- self.tenant_name = tenant_name
3329+ self.version = 'v2.0'
3330+ # set baseline defaults
3331+ self.username = None
3332+ self.tenant_id = None
3333+ self.tenant_name = None
3334+ self.auth_url = None
3335+ self.token = None
3336+ self.auth_token = None
3337+ self.management_url = None
3338+ # if loading from a dictionary passed in via auth_ref,
3339+ # load values from AccessInfo parsing that dictionary
3340+ self.auth_ref = access.AccessInfo(**auth_ref) if auth_ref else None
3341+ if self.auth_ref:
3342+ self.username = self.auth_ref.username
3343+ self.tenant_id = self.auth_ref.tenant_id
3344+ self.tenant_name = self.auth_ref.tenant_name
3345+ self.auth_url = self.auth_ref.auth_url[0]
3346+ self.management_url = self.auth_ref.management_url[0]
3347+ self.auth_token = self.auth_ref.auth_token
3348+ # allow override of the auth_ref defaults from explicit
3349+ # values provided to the client
3350+ if username:
3351+ self.username = username
3352+ if tenant_id:
3353+ self.tenant_id = tenant_id
3354+ if tenant_name:
3355+ self.tenant_name = tenant_name
3356+ if auth_url:
3357+ self.auth_url = auth_url.rstrip('/')
3358+ if token:
3359+ self.auth_token = token
3360+ if endpoint:
3361+ self.management_url = endpoint.rstrip('/')
3362 self.password = password
3363- self.auth_url = auth_url.rstrip('/') if auth_url else None
3364- self.version = 'v2.0'
3365+ self.original_ip = original_ip
3366 self.region_name = region_name
3367- self.auth_token = token
3368-
3369- self.management_url = endpoint
3370
3371 # httplib2 overrides
3372 self.force_exception_to_status_code = True
3373 self.disable_ssl_certificate_validation = insecure
3374
3375 # logging setup
3376- self.debug_log = os.environ.get('KEYSTONECLIENT_DEBUG', False)
3377+ self.debug_log = debug
3378 if self.debug_log:
3379 ch = logging.StreamHandler()
3380 _logger.setLevel(logging.DEBUG)
3381 _logger.addHandler(ch)
3382
3383 def authenticate(self):
3384- """ Authenticate against the keystone API.
3385+ """ Authenticate against the Identity API.
3386
3387 Not implemented here because auth protocols should be API
3388 version-specific.
3389+
3390+ Expected to authenticate or validate an existing authentication
3391+ reference already associated with the client. Invoking this call
3392+ *always* makes a call to the Keystone.
3393 """
3394 raise NotImplementedError
3395
3396@@ -107,6 +138,9 @@
3397 if self.debug_log:
3398 _logger.debug("RESP: %s\nRESP BODY: %s\n", resp, body)
3399
3400+ def serialize(self, entity):
3401+ return json.dumps(entity)
3402+
3403 def request(self, url, method, **kwargs):
3404 """ Send an http request with the specified characteristics.
3405
3406@@ -117,9 +151,12 @@
3407 request_kwargs = copy.copy(kwargs)
3408 request_kwargs.setdefault('headers', kwargs.get('headers', {}))
3409 request_kwargs['headers']['User-Agent'] = self.USER_AGENT
3410+ if self.original_ip:
3411+ request_kwargs['headers']['Forwarded'] = "for=%s;by=%s" % (
3412+ self.original_ip, self.USER_AGENT)
3413 if 'body' in kwargs:
3414 request_kwargs['headers']['Content-Type'] = 'application/json'
3415- request_kwargs['body'] = json.dumps(kwargs['body'])
3416+ request_kwargs['body'] = self.serialize(kwargs['body'])
3417
3418 self.http_log_req((url, method,), request_kwargs)
3419 resp, body = super(HTTPClient, self).request(url,
3420@@ -127,6 +164,13 @@
3421 **request_kwargs)
3422 self.http_log_resp(resp, body)
3423
3424+ if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
3425+ _logger.debug("Request returned failure status: %s", resp.status)
3426+ raise exceptions.from_response(resp, body)
3427+ elif resp.status in (301, 302, 305):
3428+ # Redirected. Reissue the request to the new location.
3429+ return self.request(resp['location'], method, **kwargs)
3430+
3431 if body:
3432 try:
3433 body = json.loads(body)
3434@@ -136,51 +180,38 @@
3435 _logger.debug("No body was returned.")
3436 body = None
3437
3438- if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
3439- _logger.exception("Request returned failure status.")
3440- raise exceptions.from_response(resp, body)
3441- elif resp.status in (301, 302, 305):
3442- # Redirected. Reissue the request to the new location.
3443- return self.request(resp['location'], method, **kwargs)
3444-
3445 return resp, body
3446
3447 def _cs_request(self, url, method, **kwargs):
3448- if not self.management_url:
3449- self.authenticate()
3450+ """ Makes an authenticated request to keystone endpoint by
3451+ concatenating self.management_url and url and passing in method and
3452+ any associated kwargs. """
3453
3454+ if self.management_url is None:
3455+ raise exceptions.AuthorizationFailure(
3456+ 'Current authorization does not have a known management url')
3457 kwargs.setdefault('headers', {})
3458 if self.auth_token:
3459 kwargs['headers']['X-Auth-Token'] = self.auth_token
3460
3461- # Perform the request once. If we get a 401 back then it
3462- # might be because the auth token expired, so try to
3463- # re-authenticate and try again. If it still fails, bail.
3464- try:
3465- resp, body = self.request(self.management_url + url, method,
3466- **kwargs)
3467- return resp, body
3468- except exceptions.Unauthorized:
3469- try:
3470- if getattr(self, '_failures', 0) < 1:
3471- self._failures = getattr(self, '_failures', 0) + 1
3472- self.authenticate()
3473- resp, body = self.request(self.management_url + url,
3474- method, **kwargs)
3475- return resp, body
3476- else:
3477- raise
3478- except exceptions.Unauthorized:
3479- raise
3480+ resp, body = self.request(self.management_url + url, method,
3481+ **kwargs)
3482+ return resp, body
3483
3484 def get(self, url, **kwargs):
3485 return self._cs_request(url, 'GET', **kwargs)
3486
3487+ def head(self, url, **kwargs):
3488+ return self._cs_request(url, 'HEAD', **kwargs)
3489+
3490 def post(self, url, **kwargs):
3491 return self._cs_request(url, 'POST', **kwargs)
3492
3493 def put(self, url, **kwargs):
3494 return self._cs_request(url, 'PUT', **kwargs)
3495
3496+ def patch(self, url, **kwargs):
3497+ return self._cs_request(url, 'PATCH', **kwargs)
3498+
3499 def delete(self, url, **kwargs):
3500 return self._cs_request(url, 'DELETE', **kwargs)
3501
3502=== added directory 'keystoneclient/common'
3503=== added file 'keystoneclient/common/__init__.py'
3504=== added file 'keystoneclient/common/cms.py'
3505--- keystoneclient/common/cms.py 1970-01-01 00:00:00 +0000
3506+++ keystoneclient/common/cms.py 2012-11-30 14:11:19 +0000
3507@@ -0,0 +1,169 @@
3508+import hashlib
3509+
3510+import logging
3511+
3512+
3513+subprocess = None
3514+LOG = logging.getLogger(__name__)
3515+PKI_ANS1_PREFIX = 'MII'
3516+
3517+
3518+def _ensure_subprocess():
3519+ # NOTE(vish): late loading subprocess so we can
3520+ # use the green version if we are in
3521+ # eventlet.
3522+ global subprocess
3523+ if not subprocess:
3524+ try:
3525+ from eventlet import patcher
3526+ if patcher.already_patched.get('os'):
3527+ from eventlet.green import subprocess
3528+ else:
3529+ import subprocess
3530+ except ImportError:
3531+ import subprocess
3532+
3533+
3534+def cms_verify(formatted, signing_cert_file_name, ca_file_name):
3535+ """
3536+ verifies the signature of the contents IAW CMS syntax
3537+ """
3538+ _ensure_subprocess()
3539+ process = subprocess.Popen(["openssl", "cms", "-verify",
3540+ "-certfile", signing_cert_file_name,
3541+ "-CAfile", ca_file_name,
3542+ "-inform", "PEM",
3543+ "-nosmimecap", "-nodetach",
3544+ "-nocerts", "-noattr"],
3545+ stdin=subprocess.PIPE,
3546+ stdout=subprocess.PIPE,
3547+ stderr=subprocess.PIPE)
3548+ output, err = process.communicate(formatted)
3549+ retcode = process.poll()
3550+ if retcode:
3551+ LOG.error('Verify error: %s' % err)
3552+ raise subprocess.CalledProcessError(retcode, "openssl", output=err)
3553+ return output
3554+
3555+
3556+def token_to_cms(signed_text):
3557+ copy_of_text = signed_text.replace('-', '/')
3558+
3559+ formatted = "-----BEGIN CMS-----\n"
3560+ line_length = 64
3561+ while len(copy_of_text) > 0:
3562+ if (len(copy_of_text) > line_length):
3563+ formatted += copy_of_text[:line_length]
3564+ copy_of_text = copy_of_text[line_length:]
3565+ else:
3566+ formatted += copy_of_text
3567+ copy_of_text = ""
3568+ formatted += "\n"
3569+
3570+ formatted += "-----END CMS-----\n"
3571+
3572+ return formatted
3573+
3574+
3575+def verify_token(token, signing_cert_file_name, ca_file_name):
3576+ return cms_verify(token_to_cms(token),
3577+ signing_cert_file_name,
3578+ ca_file_name)
3579+
3580+
3581+def is_ans1_token(token):
3582+ '''
3583+ thx to ayoung for sorting this out.
3584+
3585+ base64 decoded hex representation of MII is 3082
3586+ In [3]: binascii.hexlify(base64.b64decode('MII='))
3587+ Out[3]: '3082'
3588+
3589+ re: http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
3590+
3591+ pg4: For tags from 0 to 30 the first octet is the identfier
3592+ pg10: Hex 30 means sequence, followed by the length of that sequence.
3593+ pg5: Second octet is the length octet
3594+ first bit indicates short or long form, next 7 bits encode the number
3595+ of subsequent octets that make up the content length octets as an
3596+ unsigned binary int
3597+
3598+ 82 = 10000010 (first bit indicates long form)
3599+ 0000010 = 2 octets of content length
3600+ so read the next 2 octets to get the length of the content.
3601+
3602+ In the case of a very large content length there could be a requirement to
3603+ have more than 2 octets to designate the content length, therefore
3604+ requiring us to check for MIM, MIQ, etc.
3605+ In [4]: base64.b64encode(binascii.a2b_hex('3083'))
3606+ Out[4]: 'MIM='
3607+ In [5]: base64.b64encode(binascii.a2b_hex('3084'))
3608+ Out[5]: 'MIQ='
3609+ Checking for MI would become invalid at 16 octets of content length
3610+ 10010000 = 90
3611+ In [6]: base64.b64encode(binascii.a2b_hex('3090'))
3612+ Out[6]: 'MJA='
3613+ Checking for just M is insufficient
3614+
3615+ But we will only check for MII:
3616+ Max length of the content using 2 octets is 7FFF or 32767
3617+ It's not practical to support a token of this length or greater in http
3618+ therefore, we will check for MII only and ignore the case of larger tokens
3619+ '''
3620+ return token[:3] == PKI_ANS1_PREFIX
3621+
3622+
3623+def cms_sign_text(text, signing_cert_file_name, signing_key_file_name):
3624+ """ Uses OpenSSL to sign a document
3625+ Produces a Base64 encoding of a DER formatted CMS Document
3626+ http://en.wikipedia.org/wiki/Cryptographic_Message_Syntax
3627+ """
3628+ _ensure_subprocess()
3629+ process = subprocess.Popen(["openssl", "cms", "-sign",
3630+ "-signer", signing_cert_file_name,
3631+ "-inkey", signing_key_file_name,
3632+ "-outform", "PEM",
3633+ "-nosmimecap", "-nodetach",
3634+ "-nocerts", "-noattr"],
3635+ stdin=subprocess.PIPE,
3636+ stdout=subprocess.PIPE,
3637+ stderr=subprocess.PIPE)
3638+ output, err = process.communicate(text)
3639+ retcode = process.poll()
3640+ if retcode or "Error" in err:
3641+ LOG.error('Signing error: %s' % err)
3642+ raise subprocess.CalledProcessError(retcode, "openssl")
3643+ return output
3644+
3645+
3646+def cms_sign_token(text, signing_cert_file_name, signing_key_file_name):
3647+ output = cms_sign_text(text, signing_cert_file_name, signing_key_file_name)
3648+ return cms_to_token(output)
3649+
3650+
3651+def cms_to_token(cms_text):
3652+
3653+ start_delim = "-----BEGIN CMS-----"
3654+ end_delim = "-----END CMS-----"
3655+ signed_text = cms_text
3656+ signed_text = signed_text.replace('/', '-')
3657+ signed_text = signed_text.replace(start_delim, '')
3658+ signed_text = signed_text.replace(end_delim, '')
3659+ signed_text = signed_text.replace('\n', '')
3660+
3661+ return signed_text
3662+
3663+
3664+def cms_hash_token(token_id):
3665+ """
3666+ return: for ans1_token, returns the hash of the passed in token
3667+ otherwise, returns what it was passed in.
3668+ """
3669+ if token_id is None:
3670+ return None
3671+ if is_ans1_token(token_id):
3672+ hasher = hashlib.md5()
3673+ hasher.update(token_id)
3674+ return hasher.hexdigest()
3675+ else:
3676+ return token_id
3677
3678=== added directory 'keystoneclient/contrib'
3679=== added file 'keystoneclient/contrib/__init__.py'
3680=== added directory 'keystoneclient/contrib/bootstrap'
3681=== added file 'keystoneclient/contrib/bootstrap/__init__.py'
3682=== added file 'keystoneclient/contrib/bootstrap/shell.py'
3683--- keystoneclient/contrib/bootstrap/shell.py 1970-01-01 00:00:00 +0000
3684+++ keystoneclient/contrib/bootstrap/shell.py 2012-11-30 14:11:19 +0000
3685@@ -0,0 +1,28 @@
3686+from keystoneclient import utils
3687+from keystoneclient.v2_0 import client
3688+
3689+
3690+@utils.arg('--user-name', metavar='<user-name>', default='admin', dest='user',
3691+ help='The name of the user to be created (default="admin").')
3692+@utils.arg('--pass', metavar='<password>', required=True, dest='passwd',
3693+ help='The password for the new user.')
3694+@utils.arg('--role-name', metavar='<role-name>', default='admin', dest='role',
3695+ help='The name of the role to be created and granted to the user '
3696+ '(default="admin").')
3697+@utils.arg('--tenant-name', metavar='<tenant-name>', default='admin',
3698+ dest='tenant',
3699+ help='The name of the tenant to be created (default="admin").')
3700+def do_bootstrap(kc, args):
3701+ """Grants a new role to a new user on a new tenant, after creating each."""
3702+ tenant = kc.tenants.create(tenant_name=args.tenant)
3703+ role = kc.roles.create(name=args.role)
3704+ user = kc.users.create(name=args.user, password=args.passwd, email=None)
3705+ kc.roles.add_user_role(user=user, role=role, tenant=tenant)
3706+
3707+ # verify the result
3708+ user_client = client.Client(
3709+ username=args.user,
3710+ password=args.passwd,
3711+ tenant_name=args.tenant,
3712+ auth_url=kc.management_url)
3713+ user_client.authenticate()
3714
3715=== modified file 'keystoneclient/exceptions.py'
3716--- keystoneclient/exceptions.py 2012-06-22 12:58:18 +0000
3717+++ keystoneclient/exceptions.py 2012-11-30 14:11:19 +0000
3718@@ -9,6 +9,10 @@
3719 pass
3720
3721
3722+class ValidationError(Exception):
3723+ pass
3724+
3725+
3726 class AuthorizationFailure(Exception):
3727 pass
3728
3729@@ -24,6 +28,11 @@
3730 pass
3731
3732
3733+class EmptyCatalog(Exception):
3734+ """ The service catalog is empty. """
3735+ pass
3736+
3737+
3738 class ClientException(Exception):
3739 """
3740 The base exception class for all exceptions this library raises.
3741@@ -95,6 +104,14 @@
3742 message = "Not Implemented"
3743
3744
3745+class ServiceUnavailable(ClientException):
3746+ """
3747+ HTTP 503 - Service Unavailable: The server is currently unavailable.
3748+ """
3749+ http_status = 503
3750+ message = "Service Unavailable"
3751+
3752+
3753 # In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__()
3754 # so we can do this:
3755 # _code_map = dict((c.http_status, c)
3756@@ -106,7 +123,8 @@
3757 Forbidden,
3758 NotFound,
3759 OverLimit,
3760- HTTPNotImplemented])
3761+ HTTPNotImplemented,
3762+ ServiceUnavailable])
3763
3764
3765 def from_response(response, body):
3766
3767=== modified file 'keystoneclient/generic/shell.py'
3768--- keystoneclient/generic/shell.py 2012-03-26 14:01:05 +0000
3769+++ keystoneclient/generic/shell.py 2012-11-30 14:11:19 +0000
3770@@ -28,13 +28,15 @@
3771 extensions supported.
3772
3773 Usage::
3774- $ keystone discover
3775- Keystone found at http://localhost:35357
3776- - supports version v1.0 (DEPRECATED) here http://localhost:35357/v1.0
3777- - supports version v1.1 (CURRENT) here http://localhost:35357/v1.1
3778- - supports version v2.0 (BETA) here http://localhost:35357/v2.0
3779- - and RAX-KSKEY: Rackspace API Key Authentication Admin Extension
3780- - and RAX-KSGRP: Rackspace Keystone Group Extensions
3781+
3782+ $ keystone discover
3783+ Keystone found at http://localhost:35357
3784+
3785+ - supports version v1.0 (DEPRECATED) here http://localhost:35357/v1.0
3786+ - supports version v1.1 (CURRENT) here http://localhost:35357/v1.1
3787+ - supports version v2.0 (CURRENT) here http://localhost:35357/v2.0
3788+ - and RAX-KSKEY: Rackspace API Key Authentication Admin Extension
3789+ - and RAX-KSGRP: Rackspace Keystone Group Extensions
3790 """
3791 if cs.endpoint:
3792 versions = cs.discover(cs.endpoint)
3793
3794=== added directory 'keystoneclient/locale'
3795=== added file 'keystoneclient/locale/keystoneclient.pot'
3796--- keystoneclient/locale/keystoneclient.pot 1970-01-01 00:00:00 +0000
3797+++ keystoneclient/locale/keystoneclient.pot 2012-11-30 14:11:19 +0000
3798@@ -0,0 +1,20 @@
3799+# Translations template for python-keystoneclient.
3800+# Copyright (C) 2012 ORGANIZATION
3801+# This file is distributed under the same license as the
3802+# python-keystoneclient project.
3803+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
3804+#
3805+#, fuzzy
3806+msgid ""
3807+msgstr ""
3808+"Project-Id-Version: python-keystoneclient 0.1.3.12\n"
3809+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
3810+"POT-Creation-Date: 2012-09-29 16:02-0700\n"
3811+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
3812+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
3813+"Language-Team: LANGUAGE <LL@li.org>\n"
3814+"MIME-Version: 1.0\n"
3815+"Content-Type: text/plain; charset=utf-8\n"
3816+"Content-Transfer-Encoding: 8bit\n"
3817+"Generated-By: Babel 0.9.6\n"
3818+
3819
3820=== added directory 'keystoneclient/middleware'
3821=== added file 'keystoneclient/middleware/__init__.py'
3822=== added file 'keystoneclient/middleware/auth_token.py'
3823--- keystoneclient/middleware/auth_token.py 1970-01-01 00:00:00 +0000
3824+++ keystoneclient/middleware/auth_token.py 2012-11-30 14:11:19 +0000
3825@@ -0,0 +1,864 @@
3826+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3827+
3828+# Copyright 2010-2012 OpenStack LLC
3829+#
3830+# Licensed under the Apache License, Version 2.0 (the "License");
3831+# you may not use this file except in compliance with the License.
3832+# You may obtain a copy of the License at
3833+#
3834+# http://www.apache.org/licenses/LICENSE-2.0
3835+#
3836+# Unless required by applicable law or agreed to in writing, software
3837+# distributed under the License is distributed on an "AS IS" BASIS,
3838+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
3839+# implied.
3840+# See the License for the specific language governing permissions and
3841+# limitations under the License.
3842+
3843+"""
3844+TOKEN-BASED AUTH MIDDLEWARE
3845+
3846+This WSGI component:
3847+
3848+* Verifies that incoming client requests have valid tokens by validating
3849+ tokens with the auth service.
3850+* Rejects unauthenticated requests UNLESS it is in 'delay_auth_decision'
3851+ mode, which means the final decision is delegated to the downstream WSGI
3852+ component (usually the OpenStack service)
3853+* Collects and forwards identity information based on a valid token
3854+ such as user name, tenant, etc
3855+
3856+Refer to: http://keystone.openstack.org/middlewarearchitecture.html
3857+
3858+HEADERS
3859+-------
3860+
3861+* Headers starting with HTTP\_ is a standard http header
3862+* Headers starting with HTTP_X is an extended http header
3863+
3864+Coming in from initial call from client or customer
3865+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3866+
3867+HTTP_X_AUTH_TOKEN
3868+ The client token being passed in.
3869+
3870+HTTP_X_STORAGE_TOKEN
3871+ The client token being passed in (legacy Rackspace use) to support
3872+ swift/cloud files
3873+
3874+Used for communication between components
3875+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3876+
3877+WWW-Authenticate
3878+ HTTP header returned to a user indicating which endpoint to use
3879+ to retrieve a new token
3880+
3881+What we add to the request for use by the OpenStack service
3882+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3883+
3884+HTTP_X_IDENTITY_STATUS
3885+ 'Confirmed' or 'Invalid'
3886+ The underlying service will only see a value of 'Invalid' if the Middleware
3887+ is configured to run in 'delay_auth_decision' mode
3888+
3889+HTTP_X_TENANT_ID
3890+ Identity service managed unique identifier, string
3891+
3892+HTTP_X_TENANT_NAME
3893+ Unique tenant identifier, string
3894+
3895+HTTP_X_USER_ID
3896+ Identity-service managed unique identifier, string
3897+
3898+HTTP_X_USER_NAME
3899+ Unique user identifier, string
3900+
3901+HTTP_X_ROLES
3902+ Comma delimited list of case-sensitive Roles
3903+
3904+HTTP_X_SERVICE_CATALOG
3905+ json encoded keystone service catalog (optional).
3906+
3907+HTTP_X_TENANT
3908+ *Deprecated* in favor of HTTP_X_TENANT_ID and HTTP_X_TENANT_NAME
3909+ Keystone-assigned unique identifier, deprecated
3910+
3911+HTTP_X_USER
3912+ *Deprecated* in favor of HTTP_X_USER_ID and HTTP_X_USER_NAME
3913+ Unique user name, string
3914+
3915+HTTP_X_ROLE
3916+ *Deprecated* in favor of HTTP_X_ROLES
3917+ This is being renamed, and the new header contains the same data.
3918+
3919+OTHER ENVIRONMENT VARIABLES
3920+---------------------------
3921+
3922+keystone.token_info
3923+ Information about the token discovered in the process of
3924+ validation. This may include extended information returned by the
3925+ Keystone token validation call, as well as basic information about
3926+ the tenant and user.
3927+
3928+"""
3929+
3930+import datetime
3931+import httplib
3932+import json
3933+import logging
3934+import os
3935+import stat
3936+import time
3937+import webob
3938+import webob.exc
3939+
3940+from keystoneclient.openstack.common import jsonutils
3941+from keystoneclient.common import cms
3942+from keystoneclient import utils
3943+from keystoneclient.openstack.common import timeutils
3944+
3945+CONF = None
3946+try:
3947+ from openstack.common import cfg
3948+ CONF = cfg.CONF
3949+except ImportError:
3950+ # cfg is not a library yet, try application copies
3951+ for app in 'nova', 'glance', 'quantum', 'cinder':
3952+ try:
3953+ cfg = __import__('%s.openstack.common.cfg' % app,
3954+ fromlist=['%s.openstack.common' % app])
3955+ # test which application middleware is running in
3956+ if hasattr(cfg, 'CONF') and 'config_file' in cfg.CONF:
3957+ CONF = cfg.CONF
3958+ break
3959+ except ImportError:
3960+ pass
3961+if not CONF:
3962+ from keystoneclient.openstack.common import cfg
3963+ CONF = cfg.CONF
3964+LOG = logging.getLogger(__name__)
3965+
3966+# alternative middleware configuration in the main application's
3967+# configuration file e.g. in nova.conf
3968+# [keystone_authtoken]
3969+# auth_host = 127.0.0.1
3970+# auth_port = 35357
3971+# auth_protocol = http
3972+# admin_tenant_name = admin
3973+# admin_user = admin
3974+# admin_password = badpassword
3975+opts = [
3976+ cfg.StrOpt('auth_admin_prefix', default=''),
3977+ cfg.StrOpt('auth_host', default='127.0.0.1'),
3978+ cfg.IntOpt('auth_port', default=35357),
3979+ cfg.StrOpt('auth_protocol', default='https'),
3980+ cfg.StrOpt('auth_uri', default=None),
3981+ cfg.BoolOpt('delay_auth_decision', default=False),
3982+ cfg.StrOpt('admin_token'),
3983+ cfg.StrOpt('admin_user'),
3984+ cfg.StrOpt('admin_password'),
3985+ cfg.StrOpt('admin_tenant_name', default='admin'),
3986+ cfg.StrOpt('certfile'),
3987+ cfg.StrOpt('keyfile'),
3988+ cfg.StrOpt('signing_dir'),
3989+ cfg.ListOpt('memcache_servers'),
3990+ cfg.IntOpt('token_cache_time', default=300),
3991+]
3992+CONF.register_opts(opts, group='keystone_authtoken')
3993+
3994+
3995+def will_expire_soon(expiry):
3996+ """ Determines if expiration is about to occur.
3997+
3998+ :param expiry: a datetime of the expected expiration
3999+ :returns: boolean : true if expiration is within 30 seconds
4000+ """
4001+ soon = (timeutils.utcnow() + datetime.timedelta(seconds=30))
4002+ return expiry < soon
4003+
4004+
4005+class InvalidUserToken(Exception):
4006+ pass
4007+
4008+
4009+class ServiceError(Exception):
4010+ pass
4011+
4012+
4013+class ConfigurationError(Exception):
4014+ pass
4015+
4016+
4017+class AuthProtocol(object):
4018+ """Auth Middleware that handles authenticating client calls."""
4019+
4020+ def __init__(self, app, conf):
4021+ LOG.info('Starting keystone auth_token middleware')
4022+ self.conf = conf
4023+ self.app = app
4024+
4025+ # delay_auth_decision means we still allow unauthenticated requests
4026+ # through and we let the downstream service make the final decision
4027+ self.delay_auth_decision = (self._conf_get('delay_auth_decision') in
4028+ (True, 'true', 't', '1', 'on', 'yes', 'y'))
4029+
4030+ # where to find the auth service (we use this to validate tokens)
4031+ self.auth_host = self._conf_get('auth_host')
4032+ self.auth_port = int(self._conf_get('auth_port'))
4033+ self.auth_protocol = self._conf_get('auth_protocol')
4034+ if self.auth_protocol == 'http':
4035+ self.http_client_class = httplib.HTTPConnection
4036+ else:
4037+ self.http_client_class = httplib.HTTPSConnection
4038+
4039+ self.auth_admin_prefix = self._conf_get('auth_admin_prefix')
4040+ self.auth_uri = self._conf_get('auth_uri')
4041+ if self.auth_uri is None:
4042+ self.auth_uri = '%s://%s:%s' % (self.auth_protocol,
4043+ self.auth_host,
4044+ self.auth_port)
4045+
4046+ # SSL
4047+ self.cert_file = self._conf_get('certfile')
4048+ self.key_file = self._conf_get('keyfile')
4049+
4050+ #signing
4051+ self.signing_dirname = self._conf_get('signing_dir')
4052+ if self.signing_dirname is None:
4053+ self.signing_dirname = '%s/keystone-signing' % os.environ['HOME']
4054+ LOG.info('Using %s as cache directory for signing certificate' %
4055+ self.signing_dirname)
4056+ if (os.path.exists(self.signing_dirname) and
4057+ not os.access(self.signing_dirname, os.W_OK)):
4058+ raise ConfigurationError("unable to access signing dir %s" %
4059+ self.signing_dirname)
4060+
4061+ if not os.path.exists(self.signing_dirname):
4062+ os.makedirs(self.signing_dirname)
4063+ #will throw IOError if it cannot change permissions
4064+ os.chmod(self.signing_dirname, stat.S_IRWXU)
4065+
4066+ val = '%s/signing_cert.pem' % self.signing_dirname
4067+ self.signing_cert_file_name = val
4068+ val = '%s/cacert.pem' % self.signing_dirname
4069+ self.ca_file_name = val
4070+ val = '%s/revoked.pem' % self.signing_dirname
4071+ self.revoked_file_name = val
4072+
4073+ # Credentials used to verify this component with the Auth service since
4074+ # validating tokens is a privileged call
4075+ self.admin_token = self._conf_get('admin_token')
4076+ self.admin_token_expiry = None
4077+ self.admin_user = self._conf_get('admin_user')
4078+ self.admin_password = self._conf_get('admin_password')
4079+ self.admin_tenant_name = self._conf_get('admin_tenant_name')
4080+
4081+ # Token caching via memcache
4082+ self._cache = None
4083+ self._iso8601 = None
4084+ memcache_servers = self._conf_get('memcache_servers')
4085+ # By default the token will be cached for 5 minutes
4086+ self.token_cache_time = int(self._conf_get('token_cache_time'))
4087+ self._token_revocation_list = None
4088+ self._token_revocation_list_fetched_time = None
4089+ cache_timeout = datetime.timedelta(seconds=0)
4090+ self.token_revocation_list_cache_timeout = cache_timeout
4091+ if memcache_servers:
4092+ try:
4093+ import memcache
4094+ import iso8601
4095+ LOG.info('Using memcache for caching token')
4096+ self._cache = memcache.Client(memcache_servers.split(','))
4097+ self._iso8601 = iso8601
4098+ except ImportError as e:
4099+ LOG.warn('disabled caching due to missing libraries %s', e)
4100+
4101+ def _conf_get(self, name):
4102+ # try config from paste-deploy first
4103+ if name in self.conf:
4104+ return self.conf[name]
4105+ else:
4106+ return CONF.keystone_authtoken[name]
4107+
4108+ def __call__(self, env, start_response):
4109+ """Handle incoming request.
4110+
4111+ Authenticate send downstream on success. Reject request if
4112+ we can't authenticate.
4113+
4114+ """
4115+ LOG.debug('Authenticating user token')
4116+ try:
4117+ self._remove_auth_headers(env)
4118+ user_token = self._get_user_token_from_header(env)
4119+ token_info = self._validate_user_token(user_token)
4120+ env['keystone.token_info'] = token_info
4121+ user_headers = self._build_user_headers(token_info)
4122+ self._add_headers(env, user_headers)
4123+ return self.app(env, start_response)
4124+
4125+ except InvalidUserToken:
4126+ if self.delay_auth_decision:
4127+ LOG.info('Invalid user token - deferring reject downstream')
4128+ self._add_headers(env, {'X-Identity-Status': 'Invalid'})
4129+ return self.app(env, start_response)
4130+ else:
4131+ LOG.info('Invalid user token - rejecting request')
4132+ return self._reject_request(env, start_response)
4133+
4134+ except ServiceError as e:
4135+ LOG.critical('Unable to obtain admin token: %s' % e)
4136+ resp = webob.exc.HTTPServiceUnavailable()
4137+ return resp(env, start_response)
4138+
4139+ def _remove_auth_headers(self, env):
4140+ """Remove headers so a user can't fake authentication.
4141+
4142+ :param env: wsgi request environment
4143+
4144+ """
4145+ auth_headers = (
4146+ 'X-Identity-Status',
4147+ 'X-Tenant-Id',
4148+ 'X-Tenant-Name',
4149+ 'X-User-Id',
4150+ 'X-User-Name',
4151+ 'X-Roles',
4152+ 'X-Service-Catalog',
4153+ # Deprecated
4154+ 'X-User',
4155+ 'X-Tenant',
4156+ 'X-Role',
4157+ )
4158+ LOG.debug('Removing headers from request environment: %s' %
4159+ ','.join(auth_headers))
4160+ self._remove_headers(env, auth_headers)
4161+
4162+ def _get_user_token_from_header(self, env):
4163+ """Get token id from request.
4164+
4165+ :param env: wsgi request environment
4166+ :return token id
4167+ :raises InvalidUserToken if no token is provided in request
4168+
4169+ """
4170+ token = self._get_header(env, 'X-Auth-Token',
4171+ self._get_header(env, 'X-Storage-Token'))
4172+ if token:
4173+ return token
4174+ else:
4175+ LOG.warn("Unable to find authentication token in headers: %s", env)
4176+ raise InvalidUserToken('Unable to find token in headers')
4177+
4178+ def _reject_request(self, env, start_response):
4179+ """Redirect client to auth server.
4180+
4181+ :param env: wsgi request environment
4182+ :param start_response: wsgi response callback
4183+ :returns HTTPUnauthorized http response
4184+
4185+ """
4186+ headers = [('WWW-Authenticate', 'Keystone uri=\'%s\'' % self.auth_uri)]
4187+ resp = webob.exc.HTTPUnauthorized('Authentication required', headers)
4188+ return resp(env, start_response)
4189+
4190+ def get_admin_token(self):
4191+ """Return admin token, possibly fetching a new one.
4192+
4193+ if self.admin_token_expiry is set from fetching an admin token, check
4194+ it for expiration, and request a new token is the existing token
4195+ is about to expire.
4196+
4197+ :return admin token id
4198+ :raise ServiceError when unable to retrieve token from keystone
4199+
4200+ """
4201+ if self.admin_token_expiry:
4202+ if will_expire_soon(self.admin_token_expiry):
4203+ self.admin_token = None
4204+
4205+ if not self.admin_token:
4206+ (self.admin_token,
4207+ self.admin_token_expiry) = self._request_admin_token()
4208+
4209+ return self.admin_token
4210+
4211+ def _get_http_connection(self):
4212+ if self.auth_protocol == 'http':
4213+ return self.http_client_class(self.auth_host, self.auth_port)
4214+ else:
4215+ return self.http_client_class(self.auth_host,
4216+ self.auth_port,
4217+ self.key_file,
4218+ self.cert_file)
4219+
4220+ def _http_request(self, method, path):
4221+ """HTTP request helper used to make unspecified content type requests.
4222+
4223+ :param method: http method
4224+ :param path: relative request url
4225+ :return (http response object)
4226+ :raise ServerError when unable to communicate with keystone
4227+
4228+ """
4229+ conn = self._get_http_connection()
4230+
4231+ try:
4232+ conn.request(method, path)
4233+ response = conn.getresponse()
4234+ body = response.read()
4235+ except Exception as e:
4236+ LOG.error('HTTP connection exception: %s' % e)
4237+ raise ServiceError('Unable to communicate with keystone')
4238+ finally:
4239+ conn.close()
4240+
4241+ return response, body
4242+
4243+ def _json_request(self, method, path, body=None, additional_headers=None):
4244+ """HTTP request helper used to make json requests.
4245+
4246+ :param method: http method
4247+ :param path: relative request url
4248+ :param body: dict to encode to json as request body. Optional.
4249+ :param additional_headers: dict of additional headers to send with
4250+ http request. Optional.
4251+ :return (http response object, response body parsed as json)
4252+ :raise ServerError when unable to communicate with keystone
4253+
4254+ """
4255+ conn = self._get_http_connection()
4256+
4257+ kwargs = {
4258+ 'headers': {
4259+ 'Content-type': 'application/json',
4260+ 'Accept': 'application/json',
4261+ },
4262+ }
4263+
4264+ if additional_headers:
4265+ kwargs['headers'].update(additional_headers)
4266+
4267+ if body:
4268+ kwargs['body'] = jsonutils.dumps(body)
4269+
4270+ full_path = self.auth_admin_prefix + path
4271+ try:
4272+ conn.request(method, full_path, **kwargs)
4273+ response = conn.getresponse()
4274+ body = response.read()
4275+ except Exception as e:
4276+ LOG.error('HTTP connection exception: %s' % e)
4277+ raise ServiceError('Unable to communicate with keystone')
4278+ finally:
4279+ conn.close()
4280+
4281+ try:
4282+ data = jsonutils.loads(body)
4283+ except ValueError:
4284+ LOG.debug('Keystone did not return json-encoded body')
4285+ data = {}
4286+
4287+ return response, data
4288+
4289+ def _request_admin_token(self):
4290+ """Retrieve new token as admin user from keystone.
4291+
4292+ :return token id upon success
4293+ :raises ServerError when unable to communicate with keystone
4294+
4295+ """
4296+ params = {
4297+ 'auth': {
4298+ 'passwordCredentials': {
4299+ 'username': self.admin_user,
4300+ 'password': self.admin_password,
4301+ },
4302+ 'tenantName': self.admin_tenant_name,
4303+ }
4304+ }
4305+
4306+ response, data = self._json_request('POST',
4307+ '/v2.0/tokens',
4308+ body=params)
4309+
4310+ try:
4311+ token = data['access']['token']['id']
4312+ expiry = data['access']['token']['expires']
4313+ assert token
4314+ assert expiry
4315+ datetime_expiry = timeutils.parse_isotime(expiry)
4316+ return (token, timeutils.normalize_time(datetime_expiry))
4317+ except (AssertionError, KeyError):
4318+ LOG.warn("Unexpected response from keystone service: %s", data)
4319+ raise ServiceError('invalid json response')
4320+ except (ValueError):
4321+ LOG.warn("Unable to parse expiration time from token: %s", data)
4322+ raise ServiceError('invalid json response')
4323+
4324+ def _validate_user_token(self, user_token, retry=True):
4325+ """Authenticate user using PKI
4326+
4327+ :param user_token: user's token id
4328+ :param retry: Ignored, as it is not longer relevant
4329+ :return uncrypted body of the token if the token is valid
4330+ :raise InvalidUserToken if token is rejected
4331+ :no longer raises ServiceError since it no longer makes RPC
4332+
4333+ """
4334+ try:
4335+ token_id = cms.cms_hash_token(user_token)
4336+ cached = self._cache_get(token_id)
4337+ if cached:
4338+ return cached
4339+ if cms.is_ans1_token(user_token):
4340+ verified = self.verify_signed_token(user_token)
4341+ data = json.loads(verified)
4342+ else:
4343+ data = self.verify_uuid_token(user_token, retry)
4344+ self._cache_put(token_id, data)
4345+ return data
4346+ except Exception as e:
4347+ LOG.debug('Token validation failure.', exc_info=True)
4348+ self._cache_store_invalid(user_token)
4349+ LOG.warn("Authorization failed for token %s", user_token)
4350+ raise InvalidUserToken('Token authorization failed')
4351+
4352+ def _build_user_headers(self, token_info):
4353+ """Convert token object into headers.
4354+
4355+ Build headers that represent authenticated user:
4356+ * X_IDENTITY_STATUS: Confirmed or Invalid
4357+ * X_TENANT_ID: id of tenant if tenant is present
4358+ * X_TENANT_NAME: name of tenant if tenant is present
4359+ * X_USER_ID: id of user
4360+ * X_USER_NAME: name of user
4361+ * X_ROLES: list of roles
4362+ * X_SERVICE_CATALOG: service catalog
4363+
4364+ Additional (deprecated) headers include:
4365+ * X_USER: name of user
4366+ * X_TENANT: For legacy compatibility before we had ID and Name
4367+ * X_ROLE: list of roles
4368+
4369+ :param token_info: token object returned by keystone on authentication
4370+ :raise InvalidUserToken when unable to parse token object
4371+
4372+ """
4373+ user = token_info['access']['user']
4374+ token = token_info['access']['token']
4375+ roles = ','.join([role['name'] for role in user.get('roles', [])])
4376+
4377+ def get_tenant_info():
4378+ """Returns a (tenant_id, tenant_name) tuple from context."""
4379+ def essex():
4380+ """Essex puts the tenant ID and name on the token."""
4381+ return (token['tenant']['id'], token['tenant']['name'])
4382+
4383+ def pre_diablo():
4384+ """Pre-diablo, Keystone only provided tenantId."""
4385+ return (token['tenantId'], token['tenantId'])
4386+
4387+ def default_tenant():
4388+ """Assume the user's default tenant."""
4389+ return (user['tenantId'], user['tenantName'])
4390+
4391+ for method in [essex, pre_diablo, default_tenant]:
4392+ try:
4393+ return method()
4394+ except KeyError:
4395+ pass
4396+
4397+ raise InvalidUserToken('Unable to determine tenancy.')
4398+
4399+ tenant_id, tenant_name = get_tenant_info()
4400+
4401+ user_id = user['id']
4402+ user_name = user['name']
4403+
4404+ rval = {
4405+ 'X-Identity-Status': 'Confirmed',
4406+ 'X-Tenant-Id': tenant_id,
4407+ 'X-Tenant-Name': tenant_name,
4408+ 'X-User-Id': user_id,
4409+ 'X-User-Name': user_name,
4410+ 'X-Roles': roles,
4411+ # Deprecated
4412+ 'X-User': user_name,
4413+ 'X-Tenant': tenant_name,
4414+ 'X-Role': roles,
4415+ }
4416+
4417+ try:
4418+ catalog = token_info['access']['serviceCatalog']
4419+ rval['X-Service-Catalog'] = jsonutils.dumps(catalog)
4420+ except KeyError:
4421+ pass
4422+
4423+ return rval
4424+
4425+ def _header_to_env_var(self, key):
4426+ """Convert header to wsgi env variable.
4427+
4428+ :param key: http header name (ex. 'X-Auth-Token')
4429+ :return wsgi env variable name (ex. 'HTTP_X_AUTH_TOKEN')
4430+
4431+ """
4432+ return 'HTTP_%s' % key.replace('-', '_').upper()
4433+
4434+ def _add_headers(self, env, headers):
4435+ """Add http headers to environment."""
4436+ for (k, v) in headers.iteritems():
4437+ env_key = self._header_to_env_var(k)
4438+ env[env_key] = v
4439+
4440+ def _remove_headers(self, env, keys):
4441+ """Remove http headers from environment."""
4442+ for k in keys:
4443+ env_key = self._header_to_env_var(k)
4444+ try:
4445+ del env[env_key]
4446+ except KeyError:
4447+ pass
4448+
4449+ def _get_header(self, env, key, default=None):
4450+ """Get http header from environment."""
4451+ env_key = self._header_to_env_var(key)
4452+ return env.get(env_key, default)
4453+
4454+ def _cache_get(self, token):
4455+ """Return token information from cache.
4456+
4457+ If token is invalid raise InvalidUserToken
4458+ return token only if fresh (not expired).
4459+ """
4460+ if self._cache and token:
4461+ key = 'tokens/%s' % token
4462+ cached = self._cache.get(key)
4463+ if cached == 'invalid':
4464+ LOG.debug('Cached Token %s is marked unauthorized', token)
4465+ raise InvalidUserToken('Token authorization failed')
4466+ if cached:
4467+ data, expires = cached
4468+ if time.time() < float(expires):
4469+ LOG.debug('Returning cached token %s', token)
4470+ return data
4471+ else:
4472+ LOG.debug('Cached Token %s seems expired', token)
4473+
4474+ def _cache_put(self, token, data):
4475+ """Put token data into the cache.
4476+
4477+ Stores the parsed expire date in cache allowing
4478+ quick check of token freshness on retrieval.
4479+ """
4480+ if self._cache and data:
4481+ key = 'tokens/%s' % token
4482+ if 'token' in data.get('access', {}):
4483+ timestamp = data['access']['token']['expires']
4484+ expires = self._iso8601.parse_date(timestamp).strftime('%s')
4485+ else:
4486+ LOG.error('invalid token format')
4487+ return
4488+ LOG.debug('Storing %s token in memcache', token)
4489+ self._cache.set(key,
4490+ (data, expires),
4491+ time=self.token_cache_time)
4492+
4493+ def _cache_store_invalid(self, token):
4494+ """Store invalid token in cache."""
4495+ if self._cache:
4496+ key = 'tokens/%s' % token
4497+ LOG.debug('Marking token %s as unauthorized in memcache', token)
4498+ self._cache.set(key,
4499+ 'invalid',
4500+ time=self.token_cache_time)
4501+
4502+ def cert_file_missing(self, called_proc_err, file_name):
4503+ return (called_proc_err.output.find(file_name)
4504+ and not os.path.exists(file_name))
4505+
4506+ def verify_uuid_token(self, user_token, retry=True):
4507+ """Authenticate user token with keystone.
4508+
4509+ :param user_token: user's token id
4510+ :param retry: flag that forces the middleware to retry
4511+ user authentication when an indeterminate
4512+ response is received. Optional.
4513+ :return token object received from keystone on success
4514+ :raise InvalidUserToken if token is rejected
4515+ :raise ServiceError if unable to authenticate token
4516+
4517+ """
4518+
4519+ headers = {'X-Auth-Token': self.get_admin_token()}
4520+ response, data = self._json_request('GET',
4521+ '/v2.0/tokens/%s' % user_token,
4522+ additional_headers=headers)
4523+
4524+ if response.status == 200:
4525+ self._cache_put(user_token, data)
4526+ return data
4527+ if response.status == 404:
4528+ # FIXME(ja): I'm assuming the 404 status means that user_token is
4529+ # invalid - not that the admin_token is invalid
4530+ self._cache_store_invalid(user_token)
4531+ LOG.warn("Authorization failed for token %s", user_token)
4532+ raise InvalidUserToken('Token authorization failed')
4533+ if response.status == 401:
4534+ LOG.info('Keystone rejected admin token %s, resetting', headers)
4535+ self.admin_token = None
4536+ else:
4537+ LOG.error('Bad response code while validating token: %s' %
4538+ response.status)
4539+ if retry:
4540+ LOG.info('Retrying validation')
4541+ return self._validate_user_token(user_token, False)
4542+ else:
4543+ LOG.warn("Invalid user token: %s. Keystone response: %s.",
4544+ user_token, data)
4545+
4546+ raise InvalidUserToken()
4547+
4548+ def is_signed_token_revoked(self, signed_text):
4549+ """Indicate whether the token appears in the revocation list."""
4550+ revocation_list = self.token_revocation_list
4551+ revoked_tokens = revocation_list.get('revoked', [])
4552+ if not revoked_tokens:
4553+ return
4554+ revoked_ids = (x['id'] for x in revoked_tokens)
4555+ token_id = utils.hash_signed_token(signed_text)
4556+ for revoked_id in revoked_ids:
4557+ if token_id == revoked_id:
4558+ LOG.debug('Token %s is marked as having been revoked',
4559+ token_id)
4560+ return True
4561+ return False
4562+
4563+ def cms_verify(self, data):
4564+ """Verifies the signature of the provided data's IAW CMS syntax.
4565+
4566+ If either of the certificate files are missing, fetch them and
4567+ retry.
4568+ """
4569+ while True:
4570+ try:
4571+ output = cms.cms_verify(data, self.signing_cert_file_name,
4572+ self.ca_file_name)
4573+ except cms.subprocess.CalledProcessError as err:
4574+ if self.cert_file_missing(err, self.signing_cert_file_name):
4575+ self.fetch_signing_cert()
4576+ continue
4577+ if self.cert_file_missing(err, self.ca_file_name):
4578+ self.fetch_ca_cert()
4579+ continue
4580+ raise err
4581+ return output
4582+
4583+ def verify_signed_token(self, signed_text):
4584+ """Check that the token is unrevoked and has a valid signature."""
4585+ if self.is_signed_token_revoked(signed_text):
4586+ raise InvalidUserToken('Token has been revoked')
4587+
4588+ formatted = cms.token_to_cms(signed_text)
4589+ return self.cms_verify(formatted)
4590+
4591+ @property
4592+ def token_revocation_list_fetched_time(self):
4593+ if not self._token_revocation_list_fetched_time:
4594+ # If the fetched list has been written to disk, use its
4595+ # modification time.
4596+ if os.path.exists(self.revoked_file_name):
4597+ mtime = os.path.getmtime(self.revoked_file_name)
4598+ fetched_time = datetime.datetime.fromtimestamp(mtime)
4599+ # Otherwise the list will need to be fetched.
4600+ else:
4601+ fetched_time = datetime.datetime.min
4602+ self._token_revocation_list_fetched_time = fetched_time
4603+ return self._token_revocation_list_fetched_time
4604+
4605+ @token_revocation_list_fetched_time.setter
4606+ def token_revocation_list_fetched_time(self, value):
4607+ self._token_revocation_list_fetched_time = value
4608+
4609+ @property
4610+ def token_revocation_list(self):
4611+ timeout = (self.token_revocation_list_fetched_time +
4612+ self.token_revocation_list_cache_timeout)
4613+ list_is_current = timeutils.utcnow() < timeout
4614+ if list_is_current:
4615+ # Load the list from disk if required
4616+ if not self._token_revocation_list:
4617+ with open(self.revoked_file_name, 'r') as f:
4618+ self._token_revocation_list = jsonutils.loads(f.read())
4619+ else:
4620+ self.token_revocation_list = self.fetch_revocation_list()
4621+ return self._token_revocation_list
4622+
4623+ @token_revocation_list.setter
4624+ def token_revocation_list(self, value):
4625+ """Save a revocation list to memory and to disk.
4626+
4627+ :param value: A json-encoded revocation list
4628+
4629+ """
4630+ self._token_revocation_list = jsonutils.loads(value)
4631+ self.token_revocation_list_fetched_time = timeutils.utcnow()
4632+ with open(self.revoked_file_name, 'w') as f:
4633+ f.write(value)
4634+
4635+ def fetch_revocation_list(self, retry=True):
4636+ headers = {'X-Auth-Token': self.get_admin_token()}
4637+ response, data = self._json_request('GET', '/v2.0/tokens/revoked',
4638+ additional_headers=headers)
4639+ if response.status == 401:
4640+ if retry:
4641+ LOG.info('Keystone rejected admin token %s, resetting admin '
4642+ 'token', headers)
4643+ self.admin_token = None
4644+ return self.fetch_revocation_list(retry=False)
4645+ if response.status != 200:
4646+ raise ServiceError('Unable to fetch token revocation list.')
4647+ if (not 'signed' in data):
4648+ raise ServiceError('Revocation list inmproperly formatted.')
4649+ return self.cms_verify(data['signed'])
4650+
4651+ def fetch_signing_cert(self):
4652+ response, data = self._http_request('GET',
4653+ '/v2.0/certificates/signing')
4654+ try:
4655+ #todo check response
4656+ certfile = open(self.signing_cert_file_name, 'w')
4657+ certfile.write(data)
4658+ certfile.close()
4659+ except (AssertionError, KeyError):
4660+ LOG.warn("Unexpected response from keystone service: %s", data)
4661+ raise ServiceError('invalid json response')
4662+
4663+ def fetch_ca_cert(self):
4664+ response, data = self._http_request('GET',
4665+ '/v2.0/certificates/ca')
4666+ try:
4667+ #todo check response
4668+ certfile = open(self.ca_file_name, 'w')
4669+ certfile.write(data)
4670+ certfile.close()
4671+ except (AssertionError, KeyError):
4672+ LOG.warn("Unexpected response from keystone service: %s", data)
4673+ raise ServiceError('invalid json response')
4674+
4675+
4676+def filter_factory(global_conf, **local_conf):
4677+ """Returns a WSGI filter app for use with paste.deploy."""
4678+ conf = global_conf.copy()
4679+ conf.update(local_conf)
4680+
4681+ def auth_filter(app):
4682+ return AuthProtocol(app, conf)
4683+ return auth_filter
4684+
4685+
4686+def app_factory(global_conf, **local_conf):
4687+ conf = global_conf.copy()
4688+ conf.update(local_conf)
4689+ return AuthProtocol(None, conf)
4690
4691=== added file 'keystoneclient/middleware/test.py'
4692--- keystoneclient/middleware/test.py 1970-01-01 00:00:00 +0000
4693+++ keystoneclient/middleware/test.py 2012-11-30 14:11:19 +0000
4694@@ -0,0 +1,67 @@
4695+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4696+
4697+# Copyright 2012 OpenStack LLC
4698+#
4699+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4700+# not use this file except in compliance with the License. You may obtain
4701+# a copy of the License at
4702+#
4703+# http://www.apache.org/licenses/LICENSE-2.0
4704+#
4705+# Unless required by applicable law or agreed to in writing, software
4706+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4707+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4708+# License for the specific language governing permissions and limitations
4709+# under the License.
4710+
4711+#
4712+# Test support for middleware authentication
4713+#
4714+
4715+import os
4716+import sys
4717+
4718+
4719+ROOTDIR = os.path.dirname(os.path.abspath(os.curdir))
4720+
4721+
4722+def rootdir(*p):
4723+ return os.path.join(ROOTDIR, *p)
4724+
4725+
4726+class NoModule(object):
4727+ """A mixin class to provide support for unloading/disabling modules."""
4728+
4729+ def __init__(self, *args, **kw):
4730+ super(NoModule, self).__init__(*args, **kw)
4731+ self._finders = []
4732+ self._cleared_modules = {}
4733+
4734+ def tearDown(self):
4735+ super(NoModule, self).tearDown()
4736+ for finder in self._finders:
4737+ sys.meta_path.remove(finder)
4738+ sys.modules.update(self._cleared_modules)
4739+
4740+ def clear_module(self, module):
4741+ cleared_modules = {}
4742+ for fullname in sys.modules.keys():
4743+ if fullname == module or fullname.startswith(module + '.'):
4744+ cleared_modules[fullname] = sys.modules.pop(fullname)
4745+ return cleared_modules
4746+
4747+ def disable_module(self, module):
4748+ """Ensure ImportError for the specified module."""
4749+
4750+ # Clear 'module' references in sys.modules
4751+ self._cleared_modules.update(self.clear_module(module))
4752+
4753+ # Disallow further imports of 'module'
4754+ class NoModule(object):
4755+ def find_module(self, fullname, path):
4756+ if fullname == module or fullname.startswith(module + '.'):
4757+ raise ImportError
4758+
4759+ finder = NoModule()
4760+ self._finders.append(finder)
4761+ sys.meta_path.insert(0, finder)
4762
4763=== added file 'keystoneclient/openstack/common/cfg.py'
4764--- keystoneclient/openstack/common/cfg.py 1970-01-01 00:00:00 +0000
4765+++ keystoneclient/openstack/common/cfg.py 2012-11-30 14:11:19 +0000
4766@@ -0,0 +1,1653 @@
4767+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4768+
4769+# Copyright 2012 Red Hat, Inc.
4770+#
4771+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4772+# not use this file except in compliance with the License. You may obtain
4773+# a copy of the License at
4774+#
4775+# http://www.apache.org/licenses/LICENSE-2.0
4776+#
4777+# Unless required by applicable law or agreed to in writing, software
4778+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4779+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4780+# License for the specific language governing permissions and limitations
4781+# under the License.
4782+
4783+r"""
4784+Configuration options which may be set on the command line or in config files.
4785+
4786+The schema for each option is defined using the Opt sub-classes, e.g.:
4787+
4788+::
4789+
4790+ common_opts = [
4791+ cfg.StrOpt('bind_host',
4792+ default='0.0.0.0',
4793+ help='IP address to listen on'),
4794+ cfg.IntOpt('bind_port',
4795+ default=9292,
4796+ help='Port number to listen on')
4797+ ]
4798+
4799+Options can be strings, integers, floats, booleans, lists or 'multi strings'::
4800+
4801+ enabled_apis_opt = cfg.ListOpt('enabled_apis',
4802+ default=['ec2', 'osapi_compute'],
4803+ help='List of APIs to enable by default')
4804+
4805+ DEFAULT_EXTENSIONS = [
4806+ 'nova.api.openstack.compute.contrib.standard_extensions'
4807+ ]
4808+ osapi_compute_extension_opt = cfg.MultiStrOpt('osapi_compute_extension',
4809+ default=DEFAULT_EXTENSIONS)
4810+
4811+Option schemas are registered with the config manager at runtime, but before
4812+the option is referenced::
4813+
4814+ class ExtensionManager(object):
4815+
4816+ enabled_apis_opt = cfg.ListOpt(...)
4817+
4818+ def __init__(self, conf):
4819+ self.conf = conf
4820+ self.conf.register_opt(enabled_apis_opt)
4821+ ...
4822+
4823+ def _load_extensions(self):
4824+ for ext_factory in self.conf.osapi_compute_extension:
4825+ ....
4826+
4827+A common usage pattern is for each option schema to be defined in the module or
4828+class which uses the option::
4829+
4830+ opts = ...
4831+
4832+ def add_common_opts(conf):
4833+ conf.register_opts(opts)
4834+
4835+ def get_bind_host(conf):
4836+ return conf.bind_host
4837+
4838+ def get_bind_port(conf):
4839+ return conf.bind_port
4840+
4841+An option may optionally be made available via the command line. Such options
4842+must registered with the config manager before the command line is parsed (for
4843+the purposes of --help and CLI arg validation)::
4844+
4845+ cli_opts = [
4846+ cfg.BoolOpt('verbose',
4847+ short='v',
4848+ default=False,
4849+ help='Print more verbose output'),
4850+ cfg.BoolOpt('debug',
4851+ short='d',
4852+ default=False,
4853+ help='Print debugging output'),
4854+ ]
4855+
4856+ def add_common_opts(conf):
4857+ conf.register_cli_opts(cli_opts)
4858+
4859+The config manager has two CLI options defined by default, --config-file
4860+and --config-dir::
4861+
4862+ class ConfigOpts(object):
4863+
4864+ def __call__(self, ...):
4865+
4866+ opts = [
4867+ MultiStrOpt('config-file',
4868+ ...),
4869+ StrOpt('config-dir',
4870+ ...),
4871+ ]
4872+
4873+ self.register_cli_opts(opts)
4874+
4875+Option values are parsed from any supplied config files using
4876+openstack.common.iniparser. If none are specified, a default set is used
4877+e.g. glance-api.conf and glance-common.conf::
4878+
4879+ glance-api.conf:
4880+ [DEFAULT]
4881+ bind_port = 9292
4882+
4883+ glance-common.conf:
4884+ [DEFAULT]
4885+ bind_host = 0.0.0.0
4886+
4887+Option values in config files override those on the command line. Config files
4888+are parsed in order, with values in later files overriding those in earlier
4889+files.
4890+
4891+The parsing of CLI args and config files is initiated by invoking the config
4892+manager e.g.::
4893+
4894+ conf = ConfigOpts()
4895+ conf.register_opt(BoolOpt('verbose', ...))
4896+ conf(sys.argv[1:])
4897+ if conf.verbose:
4898+ ...
4899+
4900+Options can be registered as belonging to a group::
4901+
4902+ rabbit_group = cfg.OptGroup(name='rabbit',
4903+ title='RabbitMQ options')
4904+
4905+ rabbit_host_opt = cfg.StrOpt('host',
4906+ default='localhost',
4907+ help='IP/hostname to listen on'),
4908+ rabbit_port_opt = cfg.IntOpt('port',
4909+ default=5672,
4910+ help='Port number to listen on')
4911+
4912+ def register_rabbit_opts(conf):
4913+ conf.register_group(rabbit_group)
4914+ # options can be registered under a group in either of these ways:
4915+ conf.register_opt(rabbit_host_opt, group=rabbit_group)
4916+ conf.register_opt(rabbit_port_opt, group='rabbit')
4917+
4918+If it no group attributes are required other than the group name, the group
4919+need not be explicitly registered e.g.
4920+
4921+ def register_rabbit_opts(conf):
4922+ # The group will automatically be created, equivalent calling::
4923+ # conf.register_group(OptGroup(name='rabbit'))
4924+ conf.register_opt(rabbit_port_opt, group='rabbit')
4925+
4926+If no group is specified, options belong to the 'DEFAULT' section of config
4927+files::
4928+
4929+ glance-api.conf:
4930+ [DEFAULT]
4931+ bind_port = 9292
4932+ ...
4933+
4934+ [rabbit]
4935+ host = localhost
4936+ port = 5672
4937+ use_ssl = False
4938+ userid = guest
4939+ password = guest
4940+ virtual_host = /
4941+
4942+Command-line options in a group are automatically prefixed with the
4943+group name::
4944+
4945+ --rabbit-host localhost --rabbit-port 9999
4946+
4947+Option values in the default group are referenced as attributes/properties on
4948+the config manager; groups are also attributes on the config manager, with
4949+attributes for each of the options associated with the group::
4950+
4951+ server.start(app, conf.bind_port, conf.bind_host, conf)
4952+
4953+ self.connection = kombu.connection.BrokerConnection(
4954+ hostname=conf.rabbit.host,
4955+ port=conf.rabbit.port,
4956+ ...)
4957+
4958+Option values may reference other values using PEP 292 string substitution::
4959+
4960+ opts = [
4961+ cfg.StrOpt('state_path',
4962+ default=os.path.join(os.path.dirname(__file__), '../'),
4963+ help='Top-level directory for maintaining nova state'),
4964+ cfg.StrOpt('sqlite_db',
4965+ default='nova.sqlite',
4966+ help='file name for sqlite'),
4967+ cfg.StrOpt('sql_connection',
4968+ default='sqlite:///$state_path/$sqlite_db',
4969+ help='connection string for sql database'),
4970+ ]
4971+
4972+Note that interpolation can be avoided by using '$$'.
4973+
4974+For command line utilities that dispatch to other command line utilities, the
4975+disable_interspersed_args() method is available. If this this method is called,
4976+then parsing e.g.::
4977+
4978+ script --verbose cmd --debug /tmp/mything
4979+
4980+will no longer return::
4981+
4982+ ['cmd', '/tmp/mything']
4983+
4984+as the leftover arguments, but will instead return::
4985+
4986+ ['cmd', '--debug', '/tmp/mything']
4987+
4988+i.e. argument parsing is stopped at the first non-option argument.
4989+
4990+Options may be declared as required so that an error is raised if the user
4991+does not supply a value for the option.
4992+
4993+Options may be declared as secret so that their values are not leaked into
4994+log files:
4995+
4996+ opts = [
4997+ cfg.StrOpt('s3_store_access_key', secret=True),
4998+ cfg.StrOpt('s3_store_secret_key', secret=True),
4999+ ...
5000+ ]
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches