summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSai Krishna <saikrishna1511@gmail.com>2011-04-28 17:13:27 +0530
committerSai Krishna <saikrishna1511@gmail.com>2011-04-28 17:13:27 +0530
commit112f06fd9377d4e6964f8e0ee447f60267920cc0 (patch)
treef58b93c34d26c369108eb6d9484aaa3bd47a0f64
parentefd7eb952a478ba968a60daed0dacd52ffe67b89 (diff)
Adding First kestone repo
-rw-r--r--.gitignore1
-rw-r--r--HACKING68
-rw-r--r--LICENSE202
-rw-r--r--README.md162
-rw-r--r--docs/guide/pom.xml99
-rw-r--r--docs/guide/src/docbkx/idm.wadl355
-rw-r--r--docs/guide/src/docbkx/idmdevguide.xml1018
-rw-r--r--docs/guide/src/docbkx/img/Check_mark_23x20_02.svg60
-rw-r--r--docs/guide/src/docbkx/samples/auth.json19
-rw-r--r--docs/guide/src/docbkx/samples/auth.xml10
-rw-r--r--docs/guide/src/docbkx/samples/auth_credentials.json7
-rw-r--r--docs/guide/src/docbkx/samples/auth_credentials.xml5
-rw-r--r--docs/guide/src/docbkx/samples/choices.json50
-rw-r--r--docs/guide/src/docbkx/samples/choices.xml29
-rw-r--r--docs/guide/src/docbkx/samples/ext-getuser.json21
-rw-r--r--docs/guide/src/docbkx/samples/ext-getuser.xml13
-rw-r--r--docs/guide/src/docbkx/samples/extension.json21
-rw-r--r--docs/guide/src/docbkx/samples/extension.xml23
-rw-r--r--docs/guide/src/docbkx/samples/extensions.json44
-rw-r--r--docs/guide/src/docbkx/samples/extensions.xml39
-rw-r--r--docs/guide/src/docbkx/samples/getuser-1.json29
-rw-r--r--docs/guide/src/docbkx/samples/getuser-1.xml14
-rw-r--r--docs/guide/src/docbkx/samples/idm_fault.json7
-rw-r--r--docs/guide/src/docbkx/samples/idm_fault.xml6
-rw-r--r--docs/guide/src/docbkx/samples/item_not_found.json7
-rw-r--r--docs/guide/src/docbkx/samples/item_not_found.xml6
-rw-r--r--docs/guide/src/docbkx/samples/samplerequestheader.json4
-rw-r--r--docs/guide/src/docbkx/samples/sampleresponseheader.json5
-rw-r--r--docs/guide/src/docbkx/samples/tenant.json7
-rw-r--r--docs/guide/src/docbkx/samples/tenant.xml5
-rw-r--r--docs/guide/src/docbkx/samples/tenantlock.json5
-rw-r--r--docs/guide/src/docbkx/samples/tenantlock.xml4
-rw-r--r--docs/guide/src/docbkx/samples/tenants-1.json16
-rw-r--r--docs/guide/src/docbkx/samples/tenants-1.xml10
-rw-r--r--docs/guide/src/docbkx/samples/tenants-2.json20
-rw-r--r--docs/guide/src/docbkx/samples/tenants-2.xml13
-rw-r--r--docs/guide/src/docbkx/samples/tenants-3.json16
-rw-r--r--docs/guide/src/docbkx/samples/tenants-3.xml10
-rw-r--r--docs/guide/src/docbkx/samples/tenants.json15
-rw-r--r--docs/guide/src/docbkx/samples/tenants.xml9
-rw-r--r--docs/guide/src/docbkx/samples/updatedtenant.json7
-rw-r--r--docs/guide/src/docbkx/samples/updatedtenant.xml5
-rw-r--r--docs/guide/src/docbkx/samples/validatetoken.json19
-rw-r--r--docs/guide/src/docbkx/samples/validatetoken.xml10
-rw-r--r--docs/guide/src/docbkx/samples/version-atom.xml19
-rw-r--r--docs/guide/src/docbkx/samples/version.json33
-rw-r--r--docs/guide/src/docbkx/samples/version.xml23
-rw-r--r--docs/guide/src/docbkx/samples/versions-atom.xml22
-rw-r--r--docs/guide/src/docbkx/samples/versions.json28
-rw-r--r--docs/guide/src/docbkx/samples/versions.xml18
-rw-r--r--docs/guide/src/docbkx/xsd/api-common.xsd56
-rw-r--r--docs/guide/src/docbkx/xsd/api.xsd14
-rw-r--r--docs/guide/src/docbkx/xsd/atom/atom.xsd115
-rw-r--r--docs/guide/src/docbkx/xsd/atom/xml.xsd287
-rw-r--r--docs/guide/src/docbkx/xsd/extensions.xsd56
-rw-r--r--docs/guide/src/docbkx/xsd/fault.xsd135
-rw-r--r--docs/guide/src/docbkx/xsd/tenant.xsd40
-rw-r--r--docs/guide/src/docbkx/xsd/token.xsd72
-rw-r--r--docs/guide/src/docbkx/xsd/version.xsd200
-rw-r--r--echo/echo/__init__.py1
-rw-r--r--echo/echo/echo.ini36
-rw-r--r--echo/echo/echo.py121
-rw-r--r--echo/echo/echo.wadl86
-rw-r--r--echo/echo/echo_remote.ini19
-rw-r--r--echo/echo/samples/echo.json11
-rw-r--r--echo/echo/samples/echo.xml8
-rw-r--r--echo/echo/xsd/echo.xsd48
-rw-r--r--echo/echo/xsl/echo.xsl41
-rw-r--r--echo/echo_client.py72
-rw-r--r--echo/setup.py39
-rw-r--r--keystone/__init__.py15
-rw-r--r--keystone/auth_protocols/__init__.py0
-rw-r--r--keystone/auth_protocols/auth_basic.py114
-rw-r--r--keystone/auth_protocols/auth_openid.py113
-rw-r--r--keystone/auth_protocols/auth_token.ini18
-rw-r--r--keystone/auth_protocols/auth_token.py265
-rw-r--r--keystone/common/__init__.py0
-rw-r--r--keystone/common/bufferedhttp.py165
-rw-r--r--keystone/content/extensions.json1
-rw-r--r--keystone/content/extensions.xml5
-rw-r--r--keystone/content/idmdevguide.pdfbin0 -> 208102 bytes
-rw-r--r--keystone/content/version.json.tpl33
-rw-r--r--keystone/content/version.xml.tpl23
-rw-r--r--keystone/db/__init__.py0
-rw-r--r--keystone/db/sqlalchemy/__init__.py24
-rw-r--r--keystone/db/sqlalchemy/api.py179
-rw-r--r--keystone/db/sqlalchemy/models.py135
-rw-r--r--keystone/db/sqlalchemy/session.py64
-rw-r--r--keystone/identity.py302
l---------keystone/identity.wadl1
-rw-r--r--keystone/keystone.ini16
-rw-r--r--keystone/logic/__init__.py0
-rw-r--r--keystone/logic/service.py216
-rw-r--r--keystone/logic/types/__init__.py0
-rw-r--r--keystone/logic/types/atom.py25
-rw-r--r--keystone/logic/types/auth.py157
-rw-r--r--keystone/logic/types/fault.py121
-rw-r--r--keystone/logic/types/tenant.py119
-rw-r--r--keystone/middleware/__init__.py0
-rw-r--r--keystone/middleware/remoteauth.py105
l---------keystone/samples1
l---------keystone/xsd1
-rw-r--r--management/delgroup.py42
-rw-r--r--management/getgroup.py40
-rw-r--r--management/getgroups.py37
-rw-r--r--management/getgroupusers.py39
-rw-r--r--management/getuser.py37
-rw-r--r--management/getusergroups.py38
-rw-r--r--management/getusers.py38
-rw-r--r--management/groupadd.py40
-rw-r--r--management/setuserlock.py49
-rw-r--r--management/setuserpswd.py41
-rw-r--r--management/updategroup.py42
-rw-r--r--management/useradd.py42
-rw-r--r--management/userdel.py39
-rw-r--r--management/userupdate.py41
-rw-r--r--pip-requires9
-rw-r--r--setup.py42
-rw-r--r--test1
-rw-r--r--test/EchoSOAPUI.xml2
-rw-r--r--test/IdentitySOAPUI.xml1355
-rw-r--r--test/kill.sql10
-rw-r--r--test/test_setup.sql71
-rw-r--r--test/unit/test_identity.py966
124 files changed, 9333 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..0d20b648
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/HACKING b/HACKING
new file mode 100644
index 00000000..e58d60e5
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,68 @@
+Nova Style Commandments
+=======================
+
+Step 1: Read http://www.python.org/dev/peps/pep-0008/
+Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
+Step 3: Read on
+
+Imports
+-------
+- thou shalt not import objects, only modules
+- thou shalt not import more than one module per line
+- thou shalt not make relative imports
+- thou shalt organize your imports according to the following template
+
+::
+ # vim: tabstop=4 shiftwidth=4 softtabstop=4
+ {{stdlib imports in human alphabetical order}}
+ \n
+ {{nova imports in human alphabetical order}}
+ \n
+ \n
+ {{begin your code}}
+
+
+General
+-------
+- thou shalt put two newlines twixt toplevel code (funcs, classes, etc)
+- thou shalt put one newline twixt methods in classes and anywhere else
+- thou shalt not write "except:", use "except Exception:" at the very least
+- thou shalt include your name with TODOs as in "TODO(termie)"
+- thou shalt not name anything the same name as a builtin or reserved word
+- thou shalt not violate causality in our time cone, or else
+
+
+Human Alphabetical Order Examples
+---------------------------------
+::
+ import httplib
+ import logging
+ import random
+ import StringIO
+ import time
+ import unittest
+
+ from nova import flags
+ from nova import test
+ from nova.auth import users
+ from nova.endpoint import api
+ from nova.endpoint import cloud
+
+Docstrings
+----------
+ """Summary of the function, class or method, less than 80 characters.
+
+ New paragraph after newline that explains in more detail any general
+ information about the function, class or method. After this, if defining
+ parameters and return types use the Sphinx format. After that an extra
+ newline then close the quotations.
+
+ When writing the docstring for a class, an extra line should be placed
+ after the closing quotations. For more in-depth explanations for these
+ decisions see http://www.python.org/dev/peps/pep-0257/
+
+ :param foo: the foo parameter
+ :param bar: the bar parameter
+ :returns: description of the return value
+
+ """
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..75b52484
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..3753b402
--- /dev/null
+++ b/README.md
@@ -0,0 +1,162 @@
+Keystone: Identity Service
+==========================
+
+Keystone is a proposed independent authentication service for [OpenStack](http://www.openstack.org).
+
+This initial proof of concept aims to address the current use cases in Swift and Nova which are:
+
+* REST-based, token auth for Swift
+* many-to-many relationship between identity and tenant for Nova.
+
+
+SERVICES:
+---------
+
+* Keystone - authentication service
+* Auth_Token - WSGI middleware that can be used to handle token auth protocol (WSGI or remote proxy)
+* Echo - A sample service that responds by returning call details
+
+Also included:
+* Auth_Basic - Stub for WSGI middleware that will be used to handle basic auth
+* Auth_OpenID - Stub for WSGI middleware that will be used to handle openid auth protocol
+* RemoteAuth - WSGI middleware that can be used in services (like Swift, Nova, and Glance) when Auth middleware is running remotely
+
+
+DEPENDENCIES:
+-------------
+
+* bottle
+* eventlet
+* lxml
+* Paste
+* PasteDeploy
+* PasteScript
+* SQLAlchemy
+* SQLite3
+* webob
+
+
+SETUP:
+------
+
+Install http://pypi.python.org/pypi/setuptools
+
+ sudo easy_install bottle
+ sudo easy_install eventlet
+ sudo easy_install lxml
+ sudo easy_install paste
+ sudo easy_install pastedeploy
+ sudo easy_install pastescript
+ sudo easy_install pysqlite
+ sudo easy_install sqlalchemy
+ sudo easy_install webob
+
+Or using pip:
+
+ sudo pip install -r pip-requires
+
+
+RUNNING KEYSTONE:
+-----------------
+
+ $ cd keystone
+ $ python identity.py
+
+
+RUNNING TEST SERVICE:
+---------------------
+
+ Standalone stack (with Auth_Token)
+ $ cd echo/echo
+ $ python echo.py
+
+ Distributed stack (with RemoteAuth local and Auth_Token remote)
+ $ cd echo/echo
+ $ python echo.py --remote
+
+ in separate session
+ $ cd keystone/auth_protocols
+ $ python auth_token.py --remote
+
+DEMO CLIENT:
+---------------------
+ $ cd echo/echo
+ $ python echo_client.py
+
+
+INSTALLING KEYSTONE:
+--------------------
+
+ $ python setup.py build
+ $ sudo python setup.py install
+
+
+INSTALLING TEST SERVICE:
+------------------------
+
+ $ cd echo
+ $ python setup.py build
+ $ sudo python setup.py install
+
+
+TESTING
+-------
+
+After starting identity.py a keystone.db sql-lite database should be created.
+
+To test setup the test database:
+
+ $ sqlite3 keystone/keystone.db < test/test_setup.sql
+
+To clean the test database
+
+ $ sqlite3 keystone/keystone.db < test/kill.sql
+
+To run unit tests:
+
+ $ python test/unit/test_identity.py
+
+To run client demo (with all auth middleware running locally on sample service):
+
+ $ python echo/echo/echo.py
+ $ python echo/echo/echo_client.py
+
+
+To perform contract validation and load testing, use SoapUI (for now).
+
+Using SOAPUI:
+
+Download [SOAPUI](http://sourceforge.net/projects/soapui/files/):
+
+To Test Identity Service:
+
+* File->Import Project
+* Select tests/IdentitySOAPUI.xml
+* Double click on "Keystone Tests" and press the green play (>) button
+
+
+Unit Test on Identity Services
+------------------------------
+In order to run the unit test on identity services, run from the keystone directory
+
+ python identity.py
+
+Once the Identity service is running, go to unit test/unit directory
+
+ python test_identity.py
+
+For more on unit testing please refer
+
+ python test_identity --help
+
+
+
+DATABASE SCHEMA
+---------------
+
+ CREATE TABLE groups(group_id varchar(255),group_desc varchar(255),tenant_id varchar(255),FOREIGN KEY(tenant_id) REFERENCES tenant(tenant_id));
+ CREATE TABLE tenants(tenant_id varchar(255), tenant_desc varchar(255), tenant_enabled INTEGER, PRIMARY KEY(tenant_id ASC));
+ CREATE TABLE token(token_id varchar(255),user_id varchar(255),expires datetime,tenant_id varchar(255));
+ CREATE TABLE user_group(user_id varchar(255),group_id varchar(255), FOREIGN KEY(user_id) REFERENCES user(id), FOREIGN KEY(group_id) REFERENCES groups(group_id));
+ CREATE TABLE user_tenant(tenant_id varchar(255),user_id varchar(255),FOREIGN KEY(tenant_id) REFERENCES tenant(tenant_id),FOREIGN KEY(user_id) REFERENCES user(id));
+ CREATE TABLE users(id varchar(255),password varchar(255),email varchar(255),enabled integer);
diff --git a/docs/guide/pom.xml b/docs/guide/pom.xml
new file mode 100644
index 00000000..ec944cf4
--- /dev/null
+++ b/docs/guide/pom.xml
@@ -0,0 +1,99 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.rackspace.idm</groupId>
+ <artifactId>docs</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>docs</name>
+ <url>http://maven.apache.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-impl</artifactId>
+ <version>2.1.12</version>
+ </dependency>
+ <dependency>
+ <groupId>com.thoughtworks.xstream</groupId>
+ <artifactId>xstream</artifactId>
+ <version>1.3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jettison</groupId>
+ <artifactId>jettison</artifactId>
+ <version>1.1</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <resources>
+ <resource>
+ <directory>target/docbkx/pdf</directory>
+ <excludes>
+ <exclude>**/*.fo</exclude>
+ </excludes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>com.rackspace.cloud.api</groupId>
+ <artifactId>clouddocs-maven-plugin</artifactId>
+ <version>1.0.4-SNAPSHOT</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-pdf</goal>
+ <goal>generate-webhelp</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.docbook</groupId>
+ <artifactId>docbook-xml</artifactId>
+ <version>4.4</version>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <xincludeSupported>true</xincludeSupported>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>Rackspace Research Repositories</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <repositories>
+ <repository>
+ <id>rackspace-research</id>
+ <name>Rackspace Research Repository</name>
+ <url>http://maven.research.rackspacecloud.com/content/groups/public/</url>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>rackspace-research</id>
+ <name>Rackspace Research Repository</name>
+ <url>http://maven.research.rackspacecloud.com/content/groups/public/</url>
+ </pluginRepository>
+ </pluginRepositories>
+ </profile>
+</profiles>
+</project>
diff --git a/docs/guide/src/docbkx/idm.wadl b/docs/guide/src/docbkx/idm.wadl
new file mode 100644
index 00000000..39135b71
--- /dev/null
+++ b/docs/guide/src/docbkx/idm.wadl
@@ -0,0 +1,355 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<application xmlns="http://wadl.dev.java.net/2009/02"
+ xmlns:idm="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:capi="http://docs.openstack.org/common/api/v1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xsi:schemaLocation="http://docs.openstack.org/idm/api/v1.0
+ xsd/api.xsd
+ http://docs.openstack.org/common/api/v1.0
+ xsd/api-common.xsd
+ ">
+
+ <grammars>
+ <include href="xsd/api.xsd"/>
+ <include href="xsd/api-common.xsd"/>
+ </grammars>
+
+ <!-- We should use SSL in production -->
+ <resources base="http://localhost:8080">
+ <resource id="version" path="v1.0">
+ <method href="#getVersionInfo"/>
+ <resource id="extensions" path="extensions">
+ <method href="#getExtensions"/>
+ <resource id="alias" path="{alias}">
+ <param name="alias" style="template" type="xsd:string"/>
+ <method href="#getExtension"/>
+ </resource>
+ </resource>
+ <resource id="token" path="token">
+ <method href="#authenticate" />
+ <resource id="tokenId" path="{tokenId}">
+ <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/>
+ <param name="tokenId" style="template" type="xsd:string"/>
+ <method href="#validateToken"/>
+ <method href="#revokeToken"/>
+ </resource>
+ </resource>
+ <resource id="tenants" path="tenants">
+ <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/>
+ <method href="#getTenants" />
+ <method href="#createTenant" />
+ <resource id="tenantId" path="{tenantId}">
+ <param name="tenantId" style="template" type="xsd:string"/>
+ <method href="#getTenant" />
+ <method href="#updateTenant" />
+ <method href="#deleteTenant" />
+ </resource>
+ </resource>
+ </resource>
+ </resources>
+
+ <!-- Extensions -->
+ <method name="GET" id="getExtensions">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="capi:extensions"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getExtension">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="capi:extension"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <!-- Version Info -->
+ <method name="GET" id="getVersionInfo">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="capi:version"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <!-- Token Operations -->
+ <method name="POST" id="authenticate">
+ <request>
+ <representation mediaType="application/xml" element="idm:passwordCredentials"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:auth"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:userDisabled"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="401 403 400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="validateToken">
+ <request>
+ <param name="belongsTo" style="query"
+ required="false" type="xsd:string"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:auth"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ <representation mediaType="application/xml" element="idm:userDisabled"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="DELETE" id="revokeToken">
+ <response status="204"/>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <!-- Tenant Operations -->
+ <method name="GET" id="getTenants">
+ <request>
+ <param name="marker" style="query"
+ required="false" type="xsd:string"/>
+ <param name="limit" style="query"
+ required="false" type="xsd:int"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:tenants"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="POST" id="createTenant">
+ <request>
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="201">
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="409">
+ <representation mediaType="application/xml" element="idm:tenantConflict"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="401 403 400 409 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getTenant">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="PUT" id="updateTenant">
+ <request>
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="401 403 404 400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="DELETE" id="deleteTenant">
+ <response status="204"/>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+</application>
diff --git a/docs/guide/src/docbkx/idmdevguide.xml b/docs/guide/src/docbkx/idmdevguide.xml
new file mode 100644
index 00000000..4b279cef
--- /dev/null
+++ b/docs/guide/src/docbkx/idmdevguide.xml
@@ -0,0 +1,1018 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE book [<!-- Some useful entities borrowed from HTML -->
+ <!ENTITY ndash "&#x2013;">
+ <!ENTITY mdash "&#x2014;">
+ <!ENTITY hellip "&#x2026;">
+
+ <!-- Useful for describing APIs -->
+ <!ENTITY GET '<command xmlns="http://docbook.org/ns/docbook">GET</command>'>
+ <!ENTITY PUT '<command xmlns="http://docbook.org/ns/docbook">PUT</command>'>
+ <!ENTITY POST '<command xmlns="http://docbook.org/ns/docbook">POST</command>'>
+ <!ENTITY DELETE '<command xmlns="http://docbook.org/ns/docbook">DELETE</command>'>
+
+
+ <!ENTITY CHECK '<inlinemediaobject xmlns="http://docbook.org/ns/docbook">
+ <imageobject>
+ <imagedata fileref="img/Check_mark_23x20_02.svg"
+ format="SVG" scale="60"/>
+ </imageobject>
+ </inlinemediaobject>'>
+ <!ENTITY CODES 'Normal Response Code(s):'>
+ <!ENTITY ERROR_CODES 'Error Response Code(s):'>
+ <!ENTITY NO_REQUEST '<para xmlns="http://docbook.org/ns/docbook">
+ This operation does not require a request body.</para>'>
+ <!ENTITY LONG_URI_REFHEAD '
+ <thead xmlns="http://docbook.org/ns/docbook">
+ <tr>
+ <td colspan="1">Verb</td>
+ <td colspan="4">URI</td>
+ <td colspan="3">Description</td>
+ </tr>
+ </thead>'>
+ <!ENTITY URI_REFHEAD '
+ <thead xmlns="http://docbook.org/ns/docbook">
+ <tr>
+ <td colspan="1">Verb</td>
+ <td colspan="1">URI</td>
+ <td colspan="4">Description</td>
+ </tr>
+ </thead>'>
+]>
+<book xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:db="http://docbook.org/ns/docbook" version="5.0" status="draft">
+ <title>Cloud Identity Developer Guide</title>
+ <info>
+ <author>
+ <personname>
+ <firstname/>
+ <surname/>
+ </personname>
+ <affiliation>
+ <orgname>Rackspace Cloud</orgname>
+ </affiliation>
+ </author>
+ <copyright>
+ <year>2010</year>
+ <year>2011</year>
+ <holder>Rackspace Hosting, Inc.</holder>
+ </copyright>
+ <releaseinfo>API v1.0</releaseinfo>
+ <productname>Cloud Identity</productname>
+ <pubdate>2011-04-23</pubdate>
+ <legalnotice role="apache2">
+ <annotation>
+ <remark>Copyright details are filled in by the template.</remark>
+ </annotation>
+ </legalnotice>
+ <abstract>
+ <para>
+ This document is intended for software developers interested
+ in developing applications which utilize the Cloud Identity
+ Service for authentication. This document also includes
+ details on how to integrate services with the Cloud Identity
+ Service.
+ </para>
+ </abstract>
+ </info>
+ <chapter>
+ <title>Overview</title>
+ <para>
+ The Cloud Identity Service allows applications to obtain
+ tokens that can be used to access OpenStack resources. This
+ document is intended for software developers interested in
+ developing applications which utilize the Cloud Identity
+ Service for authentication. This document also includes
+ details on how to integrate services with the Cloud Identity
+ Service.
+ </para>
+ <para>
+ This Guide assumes the reader is familiar with RESTful web
+ services, HTTP/1.1, and JSON and/or XML serialization formats.
+ </para>
+ </chapter>
+ <chapter>
+ <title>Concepts</title>
+ <para>
+ The Cloud Identity Service has several key concepts that are
+ important to understand:
+ </para>
+ <section>
+ <title>Token</title>
+ <para>
+ A token is an arbitrary bit of text that is used to access
+ resources. Each token has a scope which describes which
+ resources are accessible with it. A token may be
+ revoked at anytime and is valid for a finite duration.
+ </para>
+ </section>
+ <section>
+ <title>Tenant</title>
+ <para>
+ Depending on the operator, a tenant may map to a customer,
+ account, organization, or project.
+ </para>
+ </section>
+ <section>
+ <title>User</title>
+ <para>
+ Users have a login and may be assigned tokens to access
+ resources.
+ </para>
+ </section>
+ <section>
+ <title>Group</title>
+ <para>
+ A group of users. Global groups are managed by
+ operators. They are used to organize and assign privileges
+ to a group of related users. For example, an operator may
+ create a "delinquent" group, which will assign limited
+ privileges to users who have past due bills.
+ </para>
+ </section>
+ </chapter>
+ <chapter>
+ <title>General API Information</title>
+ <para>The IdM API is implemented using a RESTful web service interface. All requests to
+ authenticate and operate against the IdM API are performed using SSL over HTTP (HTTPS) on TCP
+ port 443.</para>
+ <section>
+ <title>Request/Response Types</title>
+ <para> The IdM API supports both the JSON and XML data serialization formats. The request
+ format is specified using the <code>Content-Type</code> header and is required for
+ operations that have a request body. The response format can be specified in requests using
+ either the <code>Accept</code> header or adding an <code>.xml</code> or <code>.json</code>
+ extension to the request URI. Note that it is possible for a response to be serialized using
+ a format different from the request (see example below). If no response format is specified,
+ JSON is the default. If conflicting formats are specified using both an <code>Accept</code>
+ header and a query extension, the query extension takes precedence.</para>
+ <table rules="all">
+ <caption>Response Types</caption>
+ <thead>
+ <tr>
+ <td>Format</td>
+ <td>Accept Header</td>
+ <td>Query Extension</td>
+ <td>Default</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>JSON</td>
+ <td>application/json</td>
+ <td>.json</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <td>XML</td>
+ <td>application/xml</td>
+ <td>.xml</td>
+ <td>No</td>
+ </tr>
+ </tbody>
+ </table>
+ <example>
+ <title>JSON Request with Headers</title>
+ <programlisting language="xml">
+<xi:include href="samples/samplerequestheader.json" parse="text"/>
+</programlisting>
+ <programlisting language="xml">
+<xi:include href="samples/auth_credentials.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>XML Response with Headers</title>
+ <programlisting language="xml">
+<xi:include href="samples/sampleresponseheader.json" parse="text"/>
+</programlisting>
+ <programlisting language="xml">
+<xi:include href="samples/auth.xml" parse="text"/>
+</programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Content Compression</title>
+ <para>Request and response body data my be encoded with gzip compression in order to
+ accelerate interactive performance of API calls and responses. This is controlled using the
+ <code>Accept-Encoding</code> header on the request from the client and indicated by the
+ <code>Content-Encoding</code> header in the server response. Unless the header is
+ explicitly set, encoding defaults to disabled.</para>
+ <table rules="all">
+ <caption>Compression Headers</caption>
+ <thead>
+ <tr>
+ <td>Header Type</td>
+ <td>Name</td>
+ <td>Value</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>HTTP/1.1 Request</td>
+ <td>Accept-Encoding</td>
+ <td>gzip</td>
+ </tr>
+ <tr>
+ <td>HTTP/1.1 Response</td>
+ <td>Content-Encoding</td>
+ <td>gzip</td>
+ </tr>
+ </tbody>
+ </table>
+ </section>
+ <section>
+ <title>Paginated Collections</title>
+ <para>
+ To reduce load on the service, list operations will
+ return a maximum number of items at a time. The
+ maximum number of items returned is determined by the
+ IDM provider. To navigate the collection, the
+ parameters <parameter>limit</parameter> and
+ <parameter>marker</parameter> can be set in the URI
+ (e.g.?<parameter>limit</parameter>=100&amp;<parameter>marker</parameter>=1234).
+ The <parameter>marker</parameter> parameter is the ID
+ of the last item in the previous list. Items are
+ sorted by update time. When an update time is not
+ available they are sorted by ID. The
+ <parameter>limit</parameter> parameter sets the page
+ size. Both parameters are optional. If the client
+ requests a <parameter>limit</parameter> beyond that
+ which is supported by the deployment an overLimit
+ (<errorcode>413</errorcode>) fault may be thrown. A
+ marker with an invalid ID will return an itemNotFound
+ (<errorcode>404</errorcode>) fault.
+ </para>
+ <note>
+ <para>
+ Paginated collections never return itemNotFound
+ (<errorcode>404</errorcode>) faults when the
+ collection is empty &mdash; clients should expect
+ an empty collection.
+ </para>
+ </note>
+ <para>
+ For convenience, collections contain atom "next" and
+ "previous" links. The first page in the list will not
+ contain a "previous" link, the last page in the list
+ will not contain a "next" link. The following examples
+ illustrate three pages in a collection of tenants. The
+ first page was retrieved via a &GET; to
+ http://idm.api.openstack.org/v1.0/1234/tenants?limit=1.
+ In these examples, the <parameter>limit</parameter>
+ parameter sets the page size to a single item.
+ Subsequent "next" and "previous" links will honor the
+ initial page size. Thus, a client may follow links to
+ traverse a paginated collection without having to
+ input the <parameter>marker</parameter> parameter.
+ </para>
+ <example>
+ <title>Tenant Collection, First Page: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenants-1.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Tenant Collection, First Page: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/tenants-1.json" parse="text"/></programlisting>
+ </example>
+ <example>
+ <title>Tenant Collection, Second Page: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenants-2.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Tenant Collection, Second Page: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/tenants-2.json" parse="text"/></programlisting>
+ </example>
+ <example>
+ <title>Tenant Collection, Last Page: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenants-3.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Tenant Collection, Last Page: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/tenants-3.json" parse="text"/></programlisting>
+ </example>
+ <para>
+ In the JSON representation, paginated collections contain
+ a <property>values</property> property that contains the
+ items in the collections. Links are accessed via the
+ <property>links</property> property. The approach allows
+ for extensibility of both the collection members and of
+ the paginated collection itself. It also allows
+ collections to be embedded in other objects as illustrated
+ below. Here, a subset of grups are presented within a
+ user. Clients must follow the "next" link to continue to
+ retrive additonal groups belonging to a user.
+ </para>
+ <example>
+ <title>Paginated Groups in a User: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/getuser-1.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Paginated Groups in an User: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/getuser-1.json" parse="text"/></programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Versions</title>
+ <para>
+ The OpenStack IDM API uses both a URI and a MIME
+ type versioning scheme. In the URI scheme, the first
+ element of the path contains the target version
+ identifier (e.g. https://idm.api.openstack.org/
+ v1.0/&hellip;). The MIME type versioning scheme uses
+ HTTP content negotiation where the <code>Accept</code>
+ or <code>Content-Type</code> headers contains a MIME
+ type that identifies the version
+ (application/vnd.openstack.idm-v1.1+xml). A
+ version MIME type is always linked to a base MIME type
+ (application/xml or application/json). If conflicting
+ versions are specified using both an HTTP header and a
+ URI, the URI takes precedence.
+ </para>
+ <example>
+ <title>Request with MIME type versioning</title>
+ <literallayout class="monospaced">
+GET /tenants HTTP/1.1
+Host: idm.api.openstack.org
+Accept: application/vnd.openstack.idm-v1.1+xml
+X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb
+ </literallayout>
+ </example>
+ <example>
+ <title>Request with URI versioning</title>
+ <literallayout class="monospaced">
+GET /v1.1/tenants HTTP/1.1
+Host: idm.api.openstack.org
+Accept: application/xml
+X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb
+ </literallayout>
+ </example>
+ <note>
+ <para>
+ The MIME type versioning approach allows for the
+ creating of permanent links, because the version
+ scheme is not specified in the URI path:
+ https://api.idm.openstack.org/tenants/12234.
+ </para>
+ </note>
+ <para>
+ If a request is made without a version specified in
+ the URI or via HTTP headers, then a multiple-choices
+ response (<returnvalue>300</returnvalue>) will follow
+ providing links and MIME types to available versions.
+ </para>
+ <example>
+ <title>Multiple Choices Response: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/choices.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Multiple Choices Response: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/choices.json" parse="text"/></programlisting>
+ </example>
+ <para>
+ New features and functionality that do not break
+ API-compatibility will be introduced in the current
+ version of the API as extensions (see below) and the
+ URI and MIME types will remain unchanged. Features or
+ functionality changes that would necessitate a break
+ in API-compatibility will require a new version, which
+ will result in URI and MIME type version being updated
+ accordingly. When new API versions are released, older
+ versions will be marked as
+ <code>DEPRECATED</code>. Providers should work with
+ developers and partners to ensure there is adequate
+ time to migrate to the new version before deprecated
+ versions are discontinued.
+ </para>
+ <para>
+ Your application can programmatically determine
+ available API versions by performing a &GET; on the
+ root URL (i.e. with the version and everything to the
+ right of it truncated) returned from the
+ authentication system. Note that an Atom
+ representation of the versions resources is supported
+ when issuing a request with the <code>Accept</code>
+ header containing application/atom+xml or by adding a
+ .atom to the request URI. This allows standard Atom
+ clients to track version changes.
+ </para>
+ <example>
+ <title>Versions List Request</title>
+ <literallayout class="monospaced">
+GET HTTP/1.1
+Host: idm.api.openstack.org
+ </literallayout>
+ </example>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; badRequest
+ (<errorcode>400</errorcode>), idmFault
+ (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ &NO_REQUEST;
+ <example>
+ <title>Versions List Response: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/versions.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Versions List Response: Atom</title>
+ <programlisting language="xml">
+<xi:include href="samples/versions-atom.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Versions List Response: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/versions.json" parse="text"/></programlisting>
+ </example>
+ <para>
+ You can also obtain additional information about a
+ specific version by performing a &GET; on the base
+ version URL
+ (e.g. https://idm.api.openstack.org/v1.1/).
+ Version request URLs should always end with a trailing
+ slash (/). If the slash is omitted, the server may
+ respond with a <returnvalue>302</returnvalue>
+ redirection request. Format extensions may be placed
+ after the slash
+ (e.g. https://idm.api.openstack.org/v1.1/.xml). Note
+ that this is a special case that does not hold true
+ for other API requests. In general, requests such as
+ /tenants.xml and /tenants/.xml are handled
+ equivalently.
+ </para>
+ <example>
+ <title>Version Details Request</title>
+ <literallayout class="monospaced">
+GET HTTP/1.1
+Host: idm.api.openstack.org/v1.1/
+ </literallayout>
+ </example>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; badRequest
+ (<errorcode>400</errorcode>), idmFault
+ (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ &NO_REQUEST;
+ <example>
+ <title>Version Details Response: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/version.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Version Details Response: Atom</title>
+ <programlisting language="xml">
+<xi:include href="samples/version-atom.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Version Details Response: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/version.json" parse="text"/></programlisting>
+ </example>
+ <para>
+ The detailed version response contains pointers to
+ both a human-readable and a machine-processable
+ description of the API service. The machine-processable description is written in the Web
+ Application Description Language (WADL).
+ </para>
+ <note>
+ <para>If there is a discrepancy between the two specifications, the WADL is
+ authoritative as it contains the most accurate and up-to-date description of the
+ API service. </para>
+ </note>
+ </section>
+ <section>
+ <title>Extensions</title>
+ <para>
+ The OpenStack IDM API is extensible. Extensions
+ serve two purposes: They allow the introduction of new
+ features in the API without requiring a version change
+ and they allow the introduction of vendor specific
+ niche functionality. Applications can programmatically
+ determine what extensions are available by performing
+ a &GET; on the /extensions URI. Note that this is a
+ versioned request &mdash; that is, an extension
+ available in one API version may not be available in
+ another.
+ </para>
+ <informaltable rules="all">
+ <thead>
+ <tr>
+ <td colspan="1">Verb</td>
+ <td colspan="2">URI</td>
+ <td colspan="3">Description</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td colspan="1">&GET;</td>
+ <td colspan="2">/extensions</td>
+ <td colspan="3">Returns a list of available extensions</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; badRequest
+ (<errorcode>400</errorcode>), idmFault (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ &NO_REQUEST;
+ <para>
+ Each extension is identified by two unique identifiers, a
+ <property>namespace</property> and an
+ <property>alias</property>. Additionally an extension
+ contains documentation links in various formats.
+ </para>
+ <example>
+ <title>Extensions Response: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/extensions.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Extensions Response: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/extensions.json" parse="text"/></programlisting>
+ </example>
+ <para>
+ Extensions may also be queried individually by their
+ unique alias. This provides the simplest method of
+ checking if an extension is available as an unavailable
+ extension will issue an itemNotFound
+ (<errorcode>404</errorcode>) response.
+ </para>
+ <informaltable rules="all">
+ <thead>
+ <tr>
+ <td colspan="1">Verb</td>
+ <td colspan="2">URI</td>
+ <td colspan="3">Description</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td colspan="1">&GET;</td>
+ <td colspan="2">/extensions/<parameter>alias</parameter></td>
+ <td colspan="3">Return details of a single extension</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; itemNotFound
+ (<errorcode>404</errorcode>), badRequest
+ (<errorcode>400</errorcode>), idmFault
+ (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ &NO_REQUEST;
+ <example>
+ <title>Extension Response: xml</title>
+ <programlisting language="xml">
+<xi:include href="samples/extension.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example>
+ <title>Extensions Response: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/extension.json" parse="text"/></programlisting>
+ </example>
+ <para>
+ Extensions may define new data types, parameters, actions,
+ headers, states, and resources. In XML, additional
+ elements and attributes may be defined. These elements
+ must be defined in the extension's namespace. In JSON, the
+ alias must be used. The volumes element in the <xref
+ linkend="UserEXT" xrefstyle="template: Examples %n"/> and
+ <xref linkend="UserEXTJ" xrefstyle="select: labelnumber"/>
+ is defined in the <code>RS-META</code> namespace. Extended
+ headers are always prefixed with <code>X-</code> followed
+ by the alias and a dash: (<code>X-RS-META-HEADER1</code>).
+ Parameters must be prefixed with the extension alias
+ followed by a colon.
+ </para>
+ <important>
+ <para>
+ Applications should be prepared to ignore response
+ data that contains extension elements. Also,
+ applications should also verify that an extension is
+ available before submitting an extended request.
+ </para>
+ </important>
+ <example xml:id="UserEXT">
+ <title>Extended User Response: XML</title>
+ <programlisting language="xml">
+<xi:include href="samples/ext-getuser.xml" parse="text"/>
+ </programlisting>
+ </example>
+ <example xml:id="UserEXTJ">
+ <title>Extended User Response: JSON</title>
+ <programlisting language="javascript"><xi:include
+ href="samples/ext-getuser.json" parse="text"/></programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Faults</title>
+ <para>When an error occurs the system will return an HTTP error response code denoting the
+ type of error. The system will also return additional information about the fault in the
+ body of the response. </para>
+ <example>
+ <title>XML Fault Response</title>
+ <programlisting language="xml">
+<xi:include href="samples/idm_fault.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Fault Response</title>
+ <programlisting language="javascript">
+<xi:include href="samples/idm_fault.json" parse="text"/>
+</programlisting>
+ </example>
+ <para>The error code is returned in the body of the response for convenience. The message
+ section returns a human readable message. The details section is optional and may contain
+ useful information for tracking down an error (e.g a stack trace). </para>
+ <para>The root element of the fault (e.g. idmFault) may change depending on the type of error.
+ The following is an example of an itemNotFound error. </para>
+ <example>
+ <title>XML Not Found Fault</title>
+ <programlisting language="xml">
+<xi:include href="samples/item_not_found.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Not Found Fault</title>
+ <programlisting language="javascript">
+<xi:include href="samples/item_not_found.json" parse="text"/>
+</programlisting>
+ </example>
+ <para> The following is a list of possible fault types along with their associated error
+ codes. </para>
+ <table rules="all">
+ <caption>Fault Types</caption>
+ <thead>
+ <tr>
+ <td>Fault Element</td>
+ <td>Associated Error Code</td>
+ <td>Expected in All Requests</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr align="center">
+ <td>idmFault</td>
+ <td>500, 400</td>
+ <td> &CHECK; </td>
+ </tr>
+ <tr align="center">
+ <td>serviceUnavailable</td>
+ <td>503</td>
+ <td> &CHECK; </td>
+ </tr>
+ <tr align="center">
+ <td>badRequest</td>
+ <td>400</td>
+ <td> &CHECK; </td>
+ </tr>
+ <tr align="center">
+ <td>unauthorized</td>
+ <td>401</td>
+ <td> &CHECK; </td>
+ </tr>
+ <tr align="center">
+ <td>overLimit</td>
+ <td>413</td>
+ <td/>
+ </tr>
+ <tr align="center">
+ <td>userDisabled</td>
+ <td>403</td>
+ <td/>
+ </tr>
+ <tr align="center">
+ <td>forbidden</td>
+ <td>403</td>
+ <td/>
+ </tr>
+ <tr align="center">
+ <td>itemNotFound</td>
+ <td>404</td>
+ <td/>
+ </tr>
+ <tr align="center">
+ <td>tenantConflict</td>
+ <td>409</td>
+ <td/>
+ </tr>
+ </tbody>
+ </table>
+ <para>From an XML schema perspective, all API faults are extensions of the base fault type
+ <type>idmFault</type>. When working with a system that binds XML to actual classes (such
+ as JAXB), one should be capable of using <type>idmFault</type> as a “catch-all” if
+ there&apos;s no interest in distinguishing between individual fault types. </para>
+ </section>
+ </chapter>
+ <chapter>
+ <title>Service Developer Operations</title>
+ <section>
+ <title>Overview</title>
+ <para>The operations described in this chapter allow service developers to get and validate
+ access tokens, manage users, and manage tenants. </para>
+ </section>
+ <section>
+ <title>Token Operations</title>
+ <section>
+ <title>Authenticate</title>
+ <informaltable rules="all">
+ &URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &POST; </td>
+ <td colspan="1">/token</td>
+ <td colspan="4">Authenticate to generate a token.</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>), userDisabled
+ (<errorcode>403</errorcode>), badRequest (<errorcode>400</errorcode>), idmFault
+ (<errorcode>500</errorcode>), serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ <para>
+ TenantID is optional and may be used to specify that a
+ token should be returned that has access for resources
+ that particular tenant.
+ </para>
+ <example>
+ <title>XML Auth Request</title>
+ <programlisting language="xml">
+ <xi:include href="samples/auth_credentials.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Auth Request</title>
+ <programlisting language="javascript">
+<xi:include href="samples/auth_credentials.json" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>XML Auth Response</title>
+ <programlisting language="xml">
+<xi:include href="samples/auth.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Auth Response</title>
+ <programlisting language="javascript">
+<xi:include href="samples/auth.json" parse="text"/>
+</programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Validate Token</title>
+ <informaltable rules="all">
+ &LONG_URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &GET; </td>
+ <td colspan="4"
+ >/token/<parameter>tokenId</parameter>?belongsTo=<parameter>tenantId</parameter></td>
+ <td colspan="3">Check that a token is valid and that it belongs to a particular user
+ and return the permissions relevant to a particular client.</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>), forbidden
+ (<returnvalue>403</returnvalue>), userDisabled(<returnvalue>403</returnvalue>),
+ badRequest (<errorcode>400</errorcode>), itemNotFound (<errorcode>404</errorcode>),
+ idmFault(<returnvalue>500</returnvalue>),
+ serviceUnavailable(<returnvalue>503</returnvalue>)</simpara>
+ &NO_REQUEST;
+ <para>
+ Valid tokens will exist in the
+ /token/<parameter>tokenId</parameter> path and invalid
+ tokens will not. In other words, a user should expect an
+ itemNotFound (<errorcode>404</errorcode>) fault for an
+ invalid token.
+ </para>
+ <example>
+ <title>XML Validate Token Response</title>
+ <programlisting language="xml">
+<xi:include href="samples/validatetoken.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Validate Token Response</title>
+ <programlisting language="javascript">
+<xi:include href="samples/validatetoken.json" parse="text"/>
+</programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Revoke Token</title>
+ <informaltable rules="all">
+ &LONG_URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &DELETE; </td>
+ <td colspan="4">/token/<parameter>tokenId</parameter></td>
+ <td colspan="3"> Revoke an existing token.</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>204</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>), forbidden
+ (<returnvalue>403</returnvalue>), userDisabled(<returnvalue>403</returnvalue>),
+ badRequest (<errorcode>400</errorcode>), itemNotFound (<errorcode>404</errorcode>),
+ idmFault(<returnvalue>500</returnvalue>),
+ serviceUnavailable(<returnvalue>503</returnvalue>)</simpara>
+ &NO_REQUEST;
+ </section>
+ </section>
+ <section>
+ <title>Tenant Operations </title>
+ <section>
+ <title>Create a Tenant</title>
+ <informaltable rules="all">
+ &LONG_URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &POST; </td>
+ <td colspan="4">/tenants</td>
+ <td colspan="3">Create a tenant</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>201</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>),
+ forbidden(<errorcode>403</errorcode>),
+ badRequest (<errorcode>400</errorcode>), idmFault (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ <example>
+ <title>XML Tenant Create Request</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenant.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Tenant Create Request</title>
+ <programlisting language="javascript">
+<xi:include href="samples/tenant.json" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>XML Tenant Create Response</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenant.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Tenant Create Response</title>
+ <programlisting language="javascript">
+<xi:include href="samples/tenant.json" parse="text"/>
+</programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Get Tenants</title>
+ <informaltable rules="all">
+ &LONG_URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &GET; </td>
+ <td colspan="4">/tenants</td>
+ <td colspan="3">Get a list of tenants.</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>),
+ forbidden(<errorcode>403</errorcode>), overLimit(<errorcode>413</errorcode>),
+ badRequest (<errorcode>400</errorcode>), idmFault (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ <para>
+ The operation returns a list of tenants. The list may be
+ filtered to return only those tenants which the caller has
+ access to.
+ </para>
+ &NO_REQUEST;
+ <example>
+ <title>XML Tenants Response</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenants.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Tenants Response</title>
+ <programlisting language="javascript">
+<xi:include href="samples/tenants.json" parse="text"/>
+</programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Get a Tenant</title>
+ <informaltable rules="all">
+ &LONG_URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &GET; </td>
+ <td colspan="4">/tenants/<parameter>tenantId</parameter></td>
+ <td colspan="3">Get a tenant.</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>200</returnvalue>, <returnvalue>203</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>),
+ forbidden(<errorcode>403</errorcode>), itemNotFound(<errorcode>404</errorcode>),
+ badRequest (<errorcode>400</errorcode>), idmFault (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ &NO_REQUEST;
+ <example>
+ <title>XML Tenant Response</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenant.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Tenant Response</title>
+ <programlisting language="javascript">
+<xi:include href="samples/tenant.json" parse="text"/>
+</programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Update a Tenant</title>
+ <informaltable rules="all">
+ &LONG_URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &PUT; </td>
+ <td colspan="4">/tenants/<parameter>tenantId</parameter></td>
+ <td colspan="3">Update a tenant..</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>200</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>),
+ forbidden(<errorcode>403</errorcode>), itemNotFound(<errorcode>404</errorcode>),
+ badRequest (<errorcode>400</errorcode>), idmFault (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ <example>
+ <title>XML Tenant Update Request</title>
+ <programlisting language="xml">
+<xi:include href="samples/tenantlock.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Tenant Update Request</title>
+ <programlisting language="javascript">
+<xi:include href="samples/tenantlock.json" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>XML Tenant Update Response</title>
+ <programlisting language="xml">
+<xi:include href="samples/updatedtenant.xml" parse="text"/>
+</programlisting>
+ </example>
+ <example>
+ <title>JSON Tenant Update Response</title>
+ <programlisting language="javascript">
+<xi:include href="samples/updatedtenant.json" parse="text"/>
+</programlisting>
+ </example>
+ </section>
+ <section>
+ <title>Delete a Tenant</title>
+ <informaltable rules="all">
+ &LONG_URI_REFHEAD;
+ <tbody>
+ <tr>
+ <td colspan="1"> &DELETE; </td>
+ <td colspan="4">/tenants/<parameter>tenantId</parameter></td>
+ <td colspan="3">Delete a Tenant.</td>
+ </tr>
+ </tbody>
+ </informaltable>
+ <simpara>&CODES;<returnvalue>204</returnvalue></simpara>
+ <simpara>&ERROR_CODES; unauthorized (<errorcode>401</errorcode>), badRequest
+ (<errorcode>400</errorcode>), forbidden (<errorcode>403</errorcode>), itemNotFound
+ (<errorcode>404</errorcode>), idmFault (<errorcode>500</errorcode>),
+ serviceUnavailable(<errorcode>503</errorcode>)</simpara>
+ &NO_REQUEST;
+ </section>
+ </section>
+ </chapter>
+</book>
diff --git a/docs/guide/src/docbkx/img/Check_mark_23x20_02.svg b/docs/guide/src/docbkx/img/Check_mark_23x20_02.svg
new file mode 100644
index 00000000..3051a2f9
--- /dev/null
+++ b/docs/guide/src/docbkx/img/Check_mark_23x20_02.svg
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="19.21315"
+ height="18.294994"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45"
+ sodipodi:modified="true"
+ version="1.0">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7.9195959"
+ inkscape:cx="17.757032"
+ inkscape:cy="7.298821"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="984"
+ inkscape:window-height="852"
+ inkscape:window-x="148"
+ inkscape:window-y="66" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-192.905,-516.02064)">
+ <path
+ style="fill:#000000"
+ d="M 197.67968,534.31563 C 197.40468,534.31208 196.21788,532.53719 195.04234,530.37143 L 192.905,526.43368 L 193.45901,525.87968 C 193.76371,525.57497 194.58269,525.32567 195.27896,525.32567 L 196.5449,525.32567 L 197.18129,527.33076 L 197.81768,529.33584 L 202.88215,523.79451 C 205.66761,520.74678 208.88522,517.75085 210.03239,517.13691 L 212.11815,516.02064 L 207.90871,520.80282 C 205.59351,523.43302 202.45735,527.55085 200.93947,529.95355 C 199.42159,532.35625 197.95468,534.31919 197.67968,534.31563 z "
+ id="path2223" />
+ </g>
+</svg>
diff --git a/docs/guide/src/docbkx/samples/auth.json b/docs/guide/src/docbkx/samples/auth.json
new file mode 100644
index 00000000..fa56c131
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/auth.json
@@ -0,0 +1,19 @@
+{
+ "auth" : {
+ "token": {
+ "id": "ab48a9efdfedb23ty3494",
+ "expires": "2010-11-01T03:32:15-05:00"
+ },
+ "user" : {
+ "groups": {
+ "group": [
+ {
+ "tenantId" : "1234",
+ "id": "Admin"
+ }
+ ]},
+ "username": "jqsmith",
+ "tenantId": "1234"
+ }
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/auth.xml b/docs/guide/src/docbkx/samples/auth.xml
new file mode 100644
index 00000000..df8d69d0
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/auth.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<auth xmlns="http://docs.openstack.org/idm/api/v1.0">
+ <token expires="2010-11-01T03:32:15-05:00"
+ id="ab48a9efdfedb23ty3494"/>
+ <user tenantId="1245" username="jqsmith">
+ <groups>
+ <group tenantId="1245" id="Admin"/>
+ </groups>
+ </user>
+</auth>
diff --git a/docs/guide/src/docbkx/samples/auth_credentials.json b/docs/guide/src/docbkx/samples/auth_credentials.json
new file mode 100644
index 00000000..67b06304
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/auth_credentials.json
@@ -0,0 +1,7 @@
+{
+ "passwordCredentials" : {
+ "username" : "test_user",
+ "password" : "a86850deb2742ec3cb41518e26aa2d89",
+ "tenantId" : "77654"
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/auth_credentials.xml b/docs/guide/src/docbkx/samples/auth_credentials.xml
new file mode 100644
index 00000000..fbdf5633
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/auth_credentials.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<passwordCredentials
+ xmlns="http://docs.openstack.org/idm/api/v1.0"
+ password="P@ssword1" username="testuser"
+ tenantId="77654"/>
diff --git a/docs/guide/src/docbkx/samples/choices.json b/docs/guide/src/docbkx/samples/choices.json
new file mode 100644
index 00000000..6b0b2607
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/choices.json
@@ -0,0 +1,50 @@
+{
+ "choices" : {
+ "values" : [
+ {
+ "id" : "v1.0",
+ "status" : "DEPRECATED",
+ "links": [
+ {
+ "rel" : "self",
+ "href" : "http://idm.api.openstack.org/v1.0/tenants/12"
+ }
+ ],
+ "media-types": {
+ "values" : [
+ {
+ "base" : "application/xml",
+ "type" : "application/vnd.openstack.idm-v1.0+xml"
+ },
+ {
+ "base" : "application/json",
+ "type" : "application/vnd.openstack.idm-v1.0+json"
+ }
+ ]
+ }
+ },
+ {
+ "id" : "v1.1",
+ "status" : "CURRENT",
+ "links": [
+ {
+ "rel" : "self",
+ "href" : "http://idm.api.openstack.org/v1.1/tenants/12"
+ }
+ ],
+ "media-types": {
+ "values" : [
+ {
+ "base" : "application/xml",
+ "type" : "application/vnd.openstack.idm-v1.1+xml"
+ },
+ {
+ "base" : "application/json",
+ "type" : "application/vnd.openstack.idm-v1.1+json"
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/choices.xml b/docs/guide/src/docbkx/samples/choices.xml
new file mode 100644
index 00000000..a9a91ece
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/choices.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<choices xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+
+ <version id="v1.0" status="DEPRECATED">
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.idm-v1.0+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.idm-v1.0+json"/>
+ </media-types>
+
+ <atom:link rel="self"
+ href="http://idm.api.openstack.org/v1.0/tenants/12"/>
+ </version>
+
+ <version id="v1.1" status="CURRENT">
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.idm-v1.1+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.idm-v1.1+json"/>
+ </media-types>
+
+ <atom:link rel="self"
+ href="http://idm.api.openstack.org/v1.1/tenants/12"/>
+ </version>
+</choices>
diff --git a/docs/guide/src/docbkx/samples/ext-getuser.json b/docs/guide/src/docbkx/samples/ext-getuser.json
new file mode 100644
index 00000000..03f74dda
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/ext-getuser.json
@@ -0,0 +1,21 @@
+{"user":
+ {
+ "groups": {
+ "values": [
+ {
+ "tenantId" : "1234",
+ "id": "Admin"
+ }
+ ]},
+ "id": "jqsmith",
+ "tenantId": "1234",
+ "email": "john.smith@example.org",
+ "enabled": true,
+ "RS-META:metadata" : {
+ "values" : {
+ "MetaKey1" : "MetaValue1",
+ "MetaKey2" : "MetaValue2"
+ }
+ },
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/ext-getuser.xml b/docs/guide/src/docbkx/samples/ext-getuser.xml
new file mode 100644
index 00000000..07c185b1
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/ext-getuser.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<user xmlns="http://docs.openstack.org/idm/api/v1.0"
+ enabled="true" email="john.smith@example.org"
+ tenantId="1234" id="jqsmith">
+ <groups>
+ <group tenantId="1234" id="Admin"/>
+ </groups>
+ <metadata
+ xmlns="http://docs.rackspacecloud.com/idm/api/ext/meta/v1.0">
+ <meta key="MetaKey1">MetaValue1</meta>
+ <meta key="MetaKey2">MetaValue2</meta>
+ </metadata>
+</user>
diff --git a/docs/guide/src/docbkx/samples/extension.json b/docs/guide/src/docbkx/samples/extension.json
new file mode 100644
index 00000000..1d7e8bb3
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/extension.json
@@ -0,0 +1,21 @@
+{
+ "extension" : {
+ "name" : "User Metadata Extension",
+ "namespace" : "http://docs.rackspacecloud.com/idm/api/ext/meta/v1.0",
+ "alias" : "RS-META",
+ "updated" : "2011-01-12T11:22:33-06:00",
+ "description" : "Allows associating arbritrary metadata with a user.",
+ "links" : [
+ {
+ "rel" : "describedby",
+ "type" : "application/pdf",
+ "href" : "http://docs.rackspacecloud.com/idm/api/ext/idm-meta-20111201.pdf"
+ },
+ {
+ "rel" : "describedby",
+ "type" : "application/vnd.sun.wadl+xml",
+ "href" : "http://docs.rackspacecloud.com/idm/api/ext/idm-cbs.wadl"
+ }
+ ]
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/extension.xml b/docs/guide/src/docbkx/samples/extension.xml
new file mode 100644
index 00000000..8d932f49
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/extension.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<extension xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ name="User Metadata Extension"
+ namespace="http://docs.rackspacecloud.com/idm/api/ext/meta/v1.0"
+ alias="RS-META"
+ updated="2011-01-12T11:22:33-06:00">
+
+ <description>
+ Allows associating arbritrary metadata with a user.
+ </description>
+
+ <atom:link rel="describedby"
+ type="application/pdf"
+ href="http://docs.rackspacecloud.com/idm/api/ext/idm-meta-20111201.pdf"/>
+ <atom:link rel="describedby"
+ type="application/vnd.sun.wadl+xml"
+ href="http://docs.rackspacecloud.com/idm/api/ext/idm-meta.wadl"/>
+
+</extension>
+
+
diff --git a/docs/guide/src/docbkx/samples/extensions.json b/docs/guide/src/docbkx/samples/extensions.json
new file mode 100644
index 00000000..11064a90
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/extensions.json
@@ -0,0 +1,44 @@
+{
+ "extensions" : {
+ "values" : [
+ {
+ "name" : "Reset Password Extension",
+ "namespace" : "http://docs.rackspacecloud.com/idm/api/ext/rpe/v1.0",
+ "alias" : "RS-RPE",
+ "updated" : "2011-01-22T13:25:27-06:00",
+ "description" : "Adds the capability to reset a user's password. The user is emailed when the password has been reset.",
+ "links" : [
+ {
+ "rel" : "describedby",
+ "type" : "application/pdf",
+ "href" : "http://docs.rackspacecloud.com/idm/api/ext/idm-rpe-20111111.pdf"
+ },
+ {
+ "rel" : "describedby",
+ "type" : "application/vnd.sun.wadl+xml",
+ "href" : "http://docs.rackspacecloud.com/idm/api/ext/idm-rpe.wadl"
+ }
+ ]
+ },
+ {
+ "name" : "User Metadata Extension",
+ "namespace" : "http://docs.rackspacecloud.com/idm/api/ext/meta/v1.0",
+ "alias" : "RS-META",
+ "updated" : "2011-01-12T11:22:33-06:00",
+ "description" : "Allows associating arbritrary metadata with a user.",
+ "links" : [
+ {
+ "rel" : "describedby",
+ "type" : "application/pdf",
+ "href" : "http://docs.rackspacecloud.com/idm/api/ext/idm-meta-20111201.pdf"
+ },
+ {
+ "rel" : "describedby",
+ "type" : "application/vnd.sun.wadl+xml",
+ "href" : "http://docs.rackspacecloud.com/idm/api/ext/idm-meta.wadl"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/extensions.xml b/docs/guide/src/docbkx/samples/extensions.xml
new file mode 100644
index 00000000..ddd7e173
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/extensions.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<extensions xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <extension
+ name="Reset Password Extension"
+ namespace="http://docs.rackspacecloud.com/idm/api/ext/rpe/v1.0"
+ alias="RS-RPE"
+ updated="2011-01-22T13:25:27-06:00">
+
+ <description>
+ Adds the capability to reset a user's password. The user is
+ emailed when the password has been reset.
+ </description>
+
+ <atom:link rel="describedby"
+ type="application/pdf"
+ href="http://docs.rackspacecloud.com/idm/api/ext/idm-rpe-20111111.pdf"/>
+ <atom:link rel="describedby"
+ type="application/vnd.sun.wadl+xml"
+ href="http://docs.rackspacecloud.com/idm/api/ext/idm-rpe.wadl"/>
+ </extension>
+ <extension
+ name="User Metadata Extension"
+ namespace="http://docs.rackspacecloud.com/idm/api/ext/meta/v1.0"
+ alias="RS-META"
+ updated="2011-01-12T11:22:33-06:00">
+ <description>
+ Allows associating arbritrary metadata with a user.
+ </description>
+
+ <atom:link rel="describedby"
+ type="application/pdf"
+ href="http://docs.rackspacecloud.com/idm/api/ext/idm-meta-20111201.pdf"/>
+ <atom:link rel="describedby"
+ type="application/vnd.sun.wadl+xml"
+ href="http://docs.rackspacecloud.com/idm/api/ext/idm-meta.wadl"/>
+ </extension>
+</extensions>
diff --git a/docs/guide/src/docbkx/samples/getuser-1.json b/docs/guide/src/docbkx/samples/getuser-1.json
new file mode 100644
index 00000000..544d4600
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/getuser-1.json
@@ -0,0 +1,29 @@
+{"user":
+ {
+ "groups": {
+ "values": [
+ {
+ "tenantId" : "1234",
+ "id": "Admin"
+ },
+ {
+ "tenantId" : "1234",
+ "id" : "DBUser"
+ },
+ {
+ "id" : "Super"
+ }
+ ],
+ "links" : [
+ {
+ "rel" : "next",
+ "href" : "http://idm.api.openstack.org/v1.0/1234/tenants/1234/users/jqsmith/groups?marker=Super"
+ }
+ ]
+ },
+ "id": "jqsmith",
+ "tenantId": "1234",
+ "email": "john.smith@example.org",
+ "enabled": true
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/getuser-1.xml b/docs/guide/src/docbkx/samples/getuser-1.xml
new file mode 100644
index 00000000..2b513072
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/getuser-1.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<user xmlns="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ enabled="true" email="john.smith@example.org"
+ tenantId="1234" id="jqsmith">
+ <groups>
+ <group tenantId="1234" id="Admin"/>
+ <group tenantId="1234" id="DBUser"/>
+ <group id="Super"/>
+ <atom:link
+ rel="next"
+ href="http://idm.api.openstack.org/v1.0/1234/tenants/1234/users/jqsmith/groups?marker=Super"/>
+ </groups>
+</user>
diff --git a/docs/guide/src/docbkx/samples/idm_fault.json b/docs/guide/src/docbkx/samples/idm_fault.json
new file mode 100644
index 00000000..84e3908e
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/idm_fault.json
@@ -0,0 +1,7 @@
+{"idmFault":
+ {
+ "message": "Fault",
+ "details": "Error Details...",
+ "code": 500
+ }
+} \ No newline at end of file
diff --git a/docs/guide/src/docbkx/samples/idm_fault.xml b/docs/guide/src/docbkx/samples/idm_fault.xml
new file mode 100644
index 00000000..e0f695f2
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/idm_fault.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<idmFault xmlns="http://docs.openstack.org/idm/api/v1.0"
+ code="500">
+ <message>Fault</message>
+ <details>Error Details...</details>
+</idmFault>
diff --git a/docs/guide/src/docbkx/samples/item_not_found.json b/docs/guide/src/docbkx/samples/item_not_found.json
new file mode 100644
index 00000000..dda88a59
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/item_not_found.json
@@ -0,0 +1,7 @@
+{"itemNotFound":
+ {
+ "message": "Item not found.",
+ "details": "Error Details...",
+ "code": 404
+ }
+} \ No newline at end of file
diff --git a/docs/guide/src/docbkx/samples/item_not_found.xml b/docs/guide/src/docbkx/samples/item_not_found.xml
new file mode 100644
index 00000000..f967b76a
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/item_not_found.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<itemNotFound xmlns="http://docs.openstack.org/idm/api/v1.0"
+ code="404">
+ <message>Item not found.</message>
+ <details>Error Details...</details>
+</itemNotFound>
diff --git a/docs/guide/src/docbkx/samples/samplerequestheader.json b/docs/guide/src/docbkx/samples/samplerequestheader.json
new file mode 100644
index 00000000..a4647076
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/samplerequestheader.json
@@ -0,0 +1,4 @@
+POST /v1.0/token HTTP/1.1
+Host: idm.api.rackspace.com
+Content-Type: application/json
+Accept: application/xml \ No newline at end of file
diff --git a/docs/guide/src/docbkx/samples/sampleresponseheader.json b/docs/guide/src/docbkx/samples/sampleresponseheader.json
new file mode 100644
index 00000000..0b08f684
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/sampleresponseheader.json
@@ -0,0 +1,5 @@
+HTTP/1.1 200 OKAY
+Date: Mon, 12 Nov 2010 15:55:01 GMT
+Server: Apache
+Content-Length:
+Content-Type: application/xml; charset=UTF-8 \ No newline at end of file
diff --git a/docs/guide/src/docbkx/samples/tenant.json b/docs/guide/src/docbkx/samples/tenant.json
new file mode 100644
index 00000000..7ff7ce32
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenant.json
@@ -0,0 +1,7 @@
+{"tenant":
+ {
+ "id": "1234",
+ "description": "A description ...",
+ "enabled": true
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/tenant.xml b/docs/guide/src/docbkx/samples/tenant.xml
new file mode 100644
index 00000000..ea6a63dc
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenant.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tenant xmlns="http://docs.openstack.org/idm/api/v1.0"
+ enabled="true" id="1234">
+ <description>A description...</description>
+</tenant>
diff --git a/docs/guide/src/docbkx/samples/tenantlock.json b/docs/guide/src/docbkx/samples/tenantlock.json
new file mode 100644
index 00000000..584c21a4
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenantlock.json
@@ -0,0 +1,5 @@
+{"tenant":
+ {
+ "description": "A NEW description..."
+ }
+} \ No newline at end of file
diff --git a/docs/guide/src/docbkx/samples/tenantlock.xml b/docs/guide/src/docbkx/samples/tenantlock.xml
new file mode 100644
index 00000000..9d7081d4
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenantlock.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tenant xmlns="http://docs.openstack.org/idm/api/v1.0">
+ <description>A NEW description...</description>
+</tenant>
diff --git a/docs/guide/src/docbkx/samples/tenants-1.json b/docs/guide/src/docbkx/samples/tenants-1.json
new file mode 100644
index 00000000..6f45f1c8
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants-1.json
@@ -0,0 +1,16 @@
+{
+"tenants": {
+ "values" : [
+ {
+ "id": "1234",
+ "description": "A description ...",
+ "enabled": true
+ }
+ ],
+ "links" : [
+ {
+ "rel" : "next",
+ "href" : "http://idm.api.openstack.org/v1.0/1234/tenants?limit=1&marker=1234"
+ }
+ ]
+}
diff --git a/docs/guide/src/docbkx/samples/tenants-1.xml b/docs/guide/src/docbkx/samples/tenants-1.xml
new file mode 100644
index 00000000..67101816
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants-1.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tenants xmlns="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <tenant enabled="true" id="1234">
+ <description>A description...</description>
+ </tenant>
+ <atom:link
+ rel="next"
+ href="http://idm.api.openstack.org/v1.0/1234/tenants?limit=1&amp;marker=1234"/>
+</tenants>
diff --git a/docs/guide/src/docbkx/samples/tenants-2.json b/docs/guide/src/docbkx/samples/tenants-2.json
new file mode 100644
index 00000000..7cb3ca6b
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants-2.json
@@ -0,0 +1,20 @@
+{
+"tenants": {
+ "values" : [
+ {
+ "id": "3645",
+ "description": "A description ...",
+ "enabled": true
+ }
+ ],
+ "links" : [
+ {
+ "rel" : "next",
+ "href" : "http://idm.api.openstack.org/v1.0/1234/tenants?limit=1&marker=3645"
+ },
+ {
+ "rel" : "previous",
+ "href" : "http://idm.api.openstack.org/v1.0/1234/tenants?limit=1"
+ }
+ ]
+}
diff --git a/docs/guide/src/docbkx/samples/tenants-2.xml b/docs/guide/src/docbkx/samples/tenants-2.xml
new file mode 100644
index 00000000..7be4f537
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants-2.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tenants xmlns="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <tenant enabled="true" id="3645">
+ <description>A description...</description>
+ </tenant>
+ <atom:link
+ rel="previous"
+ href="http://idm.api.openstack.org/v1.0/1234/tenants?limit=1"/>
+ <atom:link
+ rel="next"
+ href="http://idm.api.openstack.org/v1.0/1234/tenants?limit=1&amp;marker=3645"/>
+</tenants>
diff --git a/docs/guide/src/docbkx/samples/tenants-3.json b/docs/guide/src/docbkx/samples/tenants-3.json
new file mode 100644
index 00000000..febcf225
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants-3.json
@@ -0,0 +1,16 @@
+{
+"tenants": {
+ "values" : [
+ {
+ "id": "9999",
+ "description": "A description ...",
+ "enabled": true
+ }
+ ],
+ "links" : [
+ {
+ "rel" : "previous",
+ "href" : "http://idm.api.openstack.org/v1.0/1234/tenants?limit=1&marker=1234"
+ }
+ ]
+}
diff --git a/docs/guide/src/docbkx/samples/tenants-3.xml b/docs/guide/src/docbkx/samples/tenants-3.xml
new file mode 100644
index 00000000..5e5b49fa
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants-3.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tenants xmlns="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <tenant enabled="true" id="9999">
+ <description>A description...</description>
+ </tenant>
+ <atom:link
+ rel="previous"
+ href="http://idm.api.openstack.org/v1.0/1234/tenants?limit=1&amp;marker=1234"/>
+</tenants>
diff --git a/docs/guide/src/docbkx/samples/tenants.json b/docs/guide/src/docbkx/samples/tenants.json
new file mode 100644
index 00000000..7fb848f7
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants.json
@@ -0,0 +1,15 @@
+{"tenants": {
+ "values" : [
+ {
+ "id": "1234",
+ "description": "A description ...",
+ "enabled": true
+ },
+ {
+ "id": "3456",
+ "description": "A description ...",
+ "enabled": true
+ }
+ ]
+}
+}
diff --git a/docs/guide/src/docbkx/samples/tenants.xml b/docs/guide/src/docbkx/samples/tenants.xml
new file mode 100644
index 00000000..9b503ce2
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/tenants.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tenants xmlns="http://docs.openstack.org/idm/api/v1.0">
+ <tenant enabled="true" id="1234">
+ <description>A description...</description>
+ </tenant>
+ <tenant enabled="true" id="3645">
+ <description>A description...</description>
+ </tenant>
+</tenants>
diff --git a/docs/guide/src/docbkx/samples/updatedtenant.json b/docs/guide/src/docbkx/samples/updatedtenant.json
new file mode 100644
index 00000000..05df6a3a
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/updatedtenant.json
@@ -0,0 +1,7 @@
+{"tenant":
+ {
+ "id": "1234",
+ "description": "A NEW description...",
+ "enabled": true
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/updatedtenant.xml b/docs/guide/src/docbkx/samples/updatedtenant.xml
new file mode 100644
index 00000000..8bcce9bf
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/updatedtenant.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tenant xmlns="http://docs.openstack.org/idm/api/v1.0"
+ enabled="true" id="1234">
+ <description>A NEW description...</description>
+</tenant>
diff --git a/docs/guide/src/docbkx/samples/validatetoken.json b/docs/guide/src/docbkx/samples/validatetoken.json
new file mode 100644
index 00000000..c4721873
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/validatetoken.json
@@ -0,0 +1,19 @@
+{
+ "auth" : {
+ "token": {
+ "id": "ab48a9efdfedb23ty3494",
+ "expires": "2010-11-01T03:32:15-05:00"
+ },
+ "user" : {
+ "groups": {
+ "group": [
+ {
+ "tenantId" : "1234",
+ "name": "Admin"
+ }
+ ]},
+ "username": "jqsmith",
+ "tenantId": "1234",
+ }
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/validatetoken.xml b/docs/guide/src/docbkx/samples/validatetoken.xml
new file mode 100644
index 00000000..b091c7af
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/validatetoken.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<auth xmlns="http://docs.openstack.org/idm/api/v1.0">
+ <token expires="2010-11-01T03:32:15-05:00"
+ id="ab48a9efdfedb23ty3494"/>
+ <user tenantId="1245" username="jqsmith">
+ <groups>
+ <group tenantId="1245" name="Admin"/>
+ </groups>
+ </user>
+</auth>
diff --git a/docs/guide/src/docbkx/samples/version-atom.xml b/docs/guide/src/docbkx/samples/version-atom.xml
new file mode 100644
index 00000000..8cf3dc88
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/version-atom.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <title type="text">About This Version</title>
+ <updated>2011-01-21T11:33:21-06:00</updated>
+ <id>http://idm.api.openstack.org/v1.0/</id>
+ <author><name>Rackspace</name><uri>http://www.rackspace.com/</uri></author>
+ <link rel="self" href="http://idm.api.openstack.org/v1.0/"/>
+ <entry>
+ <id>http://idm.api.openstack.org/v1.0/</id>
+ <title type="text">Version v1.0</title>
+ <updated>2011-01-21T11:33:21-06:00</updated>
+ <link rel="self" href="http://idm.api.openstack.org/v1.0/"/>
+ <link rel="describedby" type="application/pdf"
+ href="http://docs.rackspacecloud.com/idm/api/v1.0/idm-devguide-20110125.pdf"/>
+ <link rel="describedby" type="application/vnd.sun.wadl+xml"
+ href="http://docs.rackspacecloud.com/idm/api/v1.0/application.wadl"/>
+ <content type="text">Version v1.0 CURRENT (2011-01-21T11:33:21-06:00)</content>
+ </entry>
+</feed>
diff --git a/docs/guide/src/docbkx/samples/version.json b/docs/guide/src/docbkx/samples/version.json
new file mode 100644
index 00000000..74bae5bd
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/version.json
@@ -0,0 +1,33 @@
+{
+ "version" : {
+ "id" : "v1.0",
+ "status" : "CURRENT",
+ "updated" : "2011-01-21T11:33:21-06:00",
+ "links": [
+ {
+ "rel" : "self",
+ "href" : "http://idm.api.openstack.org/v1.0/"
+ },
+ {
+ "rel" : "describedby",
+ "type" : "application/pdf",
+ "href" : "http://docs.rackspacecloud.com/idm/api/v1.0/idm-devguide-20110125.pdf"
+ },
+ {
+ "rel" : "describedby",
+ "type" : "application/vnd.sun.wadl+xml",
+ "href" : "http://docs.rackspacecloud.com/idm/api/v1.0/application.wadl"
+ }
+ ],
+ "media-types": [
+ {
+ "base" : "application/xml",
+ "type" : "application/vnd.openstack.idm-v1.0+xml"
+ },
+ {
+ "base" : "application/json",
+ "type" : "application/vnd.openstack.idm-v1.0+json"
+ }
+ ]
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/version.xml b/docs/guide/src/docbkx/samples/version.xml
new file mode 100644
index 00000000..4f499322
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/version.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<version xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ id="v1.0" status="CURRENT" updated="2011-01-21T11:33:21-06:00">
+
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.idm-v1.0+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.idm-v1.0+json"/>
+ </media-types>
+
+ <atom:link rel="self"
+ href="http://idm.api.openstack.org/v1.0/"/>
+
+ <atom:link rel="describedby"
+ type="application/pdf"
+ href="http://docs.rackspacecloud.com/idm/api/v1.0/idm-devguide-20110125.pdf" />
+
+ <atom:link rel="describedby"
+ type="application/vnd.sun.wadl+xml"
+ href="http://docs.rackspacecloud.com/idm/api/v1.0/application.wadl" />
+</version>
diff --git a/docs/guide/src/docbkx/samples/versions-atom.xml b/docs/guide/src/docbkx/samples/versions-atom.xml
new file mode 100644
index 00000000..e75cf416
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/versions-atom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <title type="text">Available API Versions</title>
+ <updated>2010-12-12T18:30:02.25Z</updated>
+ <id>http://idm.api.openstack.org/</id>
+ <author><name>Rackspace</name><uri>http://www.rackspace.com/</uri></author>
+ <link rel="self" href="http://idm.api.openstack.org/"/>
+ <entry>
+ <id>http://idm.api.openstack.org/v1.1/</id>
+ <title type="text">Version v1.1</title>
+ <updated>2010-12-12T18:30:02.25Z</updated>
+ <link rel="self" href="http://idm.api.openstack.org/v1.1/"/>
+ <content type="text">Version v1.1 CURRENT (2010-12-12T18:30:02.25Z)</content>
+ </entry>
+ <entry>
+ <id>http://idm.api.openstack.org/v1.0/</id>
+ <title type="text">Version v1.0</title>
+ <updated>2009-10-09T11:30:00Z</updated>
+ <link rel="self" href="http://idm.api.openstack.org/v1.0/"/>
+ <content type="text">Version v1.0 DEPRECATED (2009-10-09T11:30:00Z)</content>
+ </entry>
+</feed>
diff --git a/docs/guide/src/docbkx/samples/versions.json b/docs/guide/src/docbkx/samples/versions.json
new file mode 100644
index 00000000..330a26e9
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/versions.json
@@ -0,0 +1,28 @@
+{
+ "versions" : {
+ "values" : [
+ {
+ "id" : "v1.0",
+ "status" : "DEPRECATED",
+ "updated" : "2009-10-09T11:30:00Z",
+ "links": [
+ {
+ "rel" : "self",
+ "href" : "http://idm.api.openstack.org/v1.0/"
+ }
+ ]
+ },
+ {
+ "id" : "v1.1",
+ "status" : "CURRENT",
+ "updated" : "2010-12-12T18:30:02.25Z",
+ "links": [
+ {
+ "rel" : "self",
+ "href" : "http://idm.api.openstack.org/v1.1/"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/docs/guide/src/docbkx/samples/versions.xml b/docs/guide/src/docbkx/samples/versions.xml
new file mode 100644
index 00000000..7c3b1535
--- /dev/null
+++ b/docs/guide/src/docbkx/samples/versions.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<versions xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+
+ <version id="v1.0" status="DEPRECATED"
+ updated="2009-10-09T11:30:00Z">
+ <atom:link rel="self"
+ href="http://idm.api.openstack.org/v1.0/"/>
+ </version>
+
+ <version id="v1.1" status="CURRENT"
+ updated="2010-12-12T18:30:02.25Z">
+ <atom:link rel="self"
+ href="http://idm.api.openstack.org/v1.1/"/>
+ </version>
+
+</versions>
diff --git a/docs/guide/src/docbkx/xsd/api-common.xsd b/docs/guide/src/docbkx/xsd/api-common.xsd
new file mode 100644
index 00000000..5e8ebeda
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/api-common.xsd
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?>
+
+<!-- (C) 2009-2011 Rackspace Hosting, All Rights Reserved -->
+
+
+<schema
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:capi="http://docs.openstack.org/common/api/v1.0"
+ xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://docs.openstack.org/common/api/v1.0"
+>
+ <annotation>
+ <xsd:appinfo
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <xsdxt:title>Open Stack Common API Schema Types 1.0</xsdxt:title>
+ <xsdxt:link rev="index" href="extensions.xsd" />
+ <xsdxt:link rev="index" href="limits.xsd" />
+ <xsdxt:link rev="index" href="version.xsd" />
+ </xsd:appinfo>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ This is the main index XML Schema document
+ for Common API Schema Types Version 1.0.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ <include schemaLocation="extensions.xsd">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ Types related to extensions.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </include>
+ <include schemaLocation="version.xsd">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ Types related to API version details.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </include>
+</schema>
diff --git a/docs/guide/src/docbkx/xsd/api.xsd b/docs/guide/src/docbkx/xsd/api.xsd
new file mode 100644
index 00000000..eaa11c17
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/api.xsd
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<schema
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:idm="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://docs.openstack.org/idm/api/v1.0"
+>
+ <include schemaLocation="token.xsd"/>
+ <include schemaLocation="tenant.xsd"/>
+ <include schemaLocation="fault.xsd"/>
+</schema>
diff --git a/docs/guide/src/docbkx/xsd/atom/atom.xsd b/docs/guide/src/docbkx/xsd/atom/atom.xsd
new file mode 100644
index 00000000..a619efaa
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/atom/atom.xsd
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified"
+ targetNamespace="http://www.w3.org/2005/Atom"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ xmlns:xml="http://www.w3.org/XML/1998/namespace"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>
+
+ <xs:simpleType name="relation">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="alternate" />
+ <xs:enumeration value="appendix" />
+ <xs:enumeration value="archives" />
+ <xs:enumeration value="author" />
+ <xs:enumeration value="bookmark" />
+ <xs:enumeration value="chapter" />
+ <xs:enumeration value="contents" />
+ <xs:enumeration value="copyright" />
+ <xs:enumeration value="current" />
+ <xs:enumeration value="describedby" />
+ <xs:enumeration value="edit" />
+ <xs:enumeration value="edit-media" />
+ <xs:enumeration value="first" />
+ <xs:enumeration value="glossary" />
+ <xs:enumeration value="help" />
+ <xs:enumeration value="hub" />
+ <xs:enumeration value="icon" />
+ <xs:enumeration value="index" />
+ <xs:enumeration value="last" />
+ <xs:enumeration value="latest-version" />
+ <xs:enumeration value="license" />
+ <xs:enumeration value="monitor" />
+ <xs:enumeration value="monitor-group" />
+ <xs:enumeration value="next" />
+ <xs:enumeration value="next-arvhice" />
+ <xs:enumeration value="nofollow" />
+ <xs:enumeration value="payment" />
+ <xs:enumeration value="predecessor-version" />
+ <xs:enumeration value="prefetch" />
+ <xs:enumeration value="prev" />
+ <xs:enumeration value="previous" />
+ <xs:enumeration value="prev-archive" />
+ <xs:enumeration value="replies" />
+ <xs:enumeration value="search" />
+ <xs:enumeration value="section" />
+ <xs:enumeration value="self" />
+ <xs:enumeration value="service" />
+ <xs:enumeration value="start" />
+ <xs:enumeration value="stylesheet" />
+ <xs:enumeration value="subsection" />
+ <xs:enumeration value="successor-version" />
+ <xs:enumeration value="up" />
+ <xs:enumeration value="version-history" />
+ <xs:enumeration value="via" />
+ <xs:enumeration value="working-copy" />
+ <xs:enumeration value="working-copy-of" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:element name="link" type="atom:link" />
+
+ <xs:complexType name="link">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>See section 3.4 of the ATOM RFC <html:a href="http://tools.ietf.org/html/rfc4287">RFC4287</html:a></html:p>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="rel" use="required" type="atom:relation">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="type" use="optional" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="href" use="required" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="hreflang" use="optional" type="xs:NMTOKEN">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="title" use="optional" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute ref="xml:base" />
+ <xs:attribute ref="xml:lang" />
+ </xs:complexType>
+</xs:schema>
diff --git a/docs/guide/src/docbkx/xsd/atom/xml.xsd b/docs/guide/src/docbkx/xsd/atom/xml.xsd
new file mode 100644
index 00000000..aea7d0db
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/atom/xml.xsd
@@ -0,0 +1,287 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns ="http://www.w3.org/1999/xhtml"
+ xml:lang="en">
+
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h1>About the XML namespace</h1>
+
+ <div class="bodytext">
+ <p>
+ This schema document describes the XML namespace, in a form
+ suitable for import by other schema documents.
+ </p>
+ <p>
+ See <a href="http://www.w3.org/XML/1998/namespace.html">
+ http://www.w3.org/XML/1998/namespace.html</a> and
+ <a href="http://www.w3.org/TR/REC-xml">
+ http://www.w3.org/TR/REC-xml</a> for information
+ about this namespace.
+ </p>
+ <p>
+ Note that local names in this namespace are intended to be
+ defined only by the World Wide Web Consortium or its subgroups.
+ The names currently defined in this namespace are listed below.
+ They should not be used with conflicting semantics by any Working
+ Group, specification, or document instance.
+ </p>
+ <p>
+ See further below in this document for more information about <a
+ href="#usage">how to refer to this schema document from your own
+ XSD schema documents</a> and about <a href="#nsversioning">the
+ namespace-versioning policy governing this schema document</a>.
+ </p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>lang (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ is a language code for the natural language of the content of
+ any element; its value is inherited. This name is reserved
+ by virtue of its definition in the XML specification.</p>
+
+ </div>
+ <div>
+ <h4>Notes</h4>
+ <p>
+ Attempting to install the relevant ISO 2- and 3-letter
+ codes as the enumerated possible values is probably never
+ going to be a realistic possibility.
+ </p>
+ <p>
+ See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+ http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+ and the IANA language subtag registry at
+ <a href="http://www.iana.org/assignments/language-subtag-registry">
+ http://www.iana.org/assignments/language-subtag-registry</a>
+ for further information.
+ </p>
+ <p>
+ The union allows for the 'un-declaration' of xml:lang with
+ the empty string.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:union memberTypes="xs:language">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value=""/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>space (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose
+ value is a keyword indicating what whitespace processing
+ discipline is intended for the content of the element; its
+ value is inherited. This name is reserved by virtue of its
+ definition in the XML specification.</p>
+
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:NCName">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>base (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ provides a URI to be used as the base for interpreting any
+ relative URIs in the scope of the element on which it
+ appears; its value is inherited. This name is reserved
+ by virtue of its definition in the XML Base specification.</p>
+
+ <p>
+ See <a
+ href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="id" type="xs:ID">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>id (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ should be interpreted as if declared to be of type ID.
+ This name is reserved by virtue of its definition in the
+ xml:id specification.</p>
+
+ <p>
+ See <a
+ href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+ <xs:attribute ref="xml:base"/>
+ <xs:attribute ref="xml:lang"/>
+ <xs:attribute ref="xml:space"/>
+ <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>Father (in any context at all)</h3>
+
+ <div class="bodytext">
+ <p>
+ denotes Jon Bosak, the chair of
+ the original XML Working Group. This name is reserved by
+ the following decision of the W3C XML Plenary and
+ XML Coordination groups:
+ </p>
+ <blockquote>
+ <p>
+ In appreciation for his vision, leadership and
+ dedication the W3C XML Plenary on this 10th day of
+ February, 2000, reserves for Jon Bosak in perpetuity
+ the XML name "xml:Father".
+ </p>
+ </blockquote>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div xml:id="usage" id="usage">
+ <h2><a name="usage">About this schema document</a></h2>
+
+ <div class="bodytext">
+ <p>
+ This schema defines attributes and an attribute group suitable
+ for use by schemas wishing to allow <code>xml:base</code>,
+ <code>xml:lang</code>, <code>xml:space</code> or
+ <code>xml:id</code> attributes on elements they define.
+ </p>
+ <p>
+ To enable this, such a schema must import this schema for
+ the XML namespace, e.g. as follows:
+ </p>
+ <pre>
+ &lt;schema . . .>
+ . . .
+ &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+ </pre>
+ <p>
+ or
+ </p>
+ <pre>
+ &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+ </pre>
+ <p>
+ Subsequently, qualified reference to any of the attributes or the
+ group defined below will have the desired effect, e.g.
+ </p>
+ <pre>
+ &lt;type . . .>
+ . . .
+ &lt;attributeGroup ref="xml:specialAttrs"/>
+ </pre>
+ <p>
+ will define a type which will schema-validate an instance element
+ with any of those attributes.
+ </p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div id="nsversioning" xml:id="nsversioning">
+ <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
+ <div class="bodytext">
+ <p>
+ In keeping with the XML Schema WG's standard versioning
+ policy, this schema document will persist at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd</a>.
+ </p>
+ <p>
+ At the date of issue it can also be found at
+ <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd</a>.
+ </p>
+ <p>
+ The schema document at that URI may however change in the future,
+ in order to remain compatible with the latest version of XML
+ Schema itself, or with the XML namespace itself. In other words,
+ if the XML Schema or XML namespaces change, the version of this
+ document at <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd
+ </a>
+ will change accordingly; the version at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd
+ </a>
+ will not change.
+ </p>
+ <p>
+ Previous dated (and unchanging) versions of this schema
+ document are at:
+ </p>
+ <ul>
+ <li><a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2007/08/xml.xsd">
+ http://www.w3.org/2007/08/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2004/10/xml.xsd">
+ http://www.w3.org/2004/10/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2001/03/xml.xsd">
+ http://www.w3.org/2001/03/xml.xsd</a></li>
+ </ul>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
+
diff --git a/docs/guide/src/docbkx/xsd/extensions.xsd b/docs/guide/src/docbkx/xsd/extensions.xsd
new file mode 100644
index 00000000..a942f0a1
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/extensions.xsd
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xsd:schema elementFormDefault="qualified" attributeFormDefault="unqualified"
+ targetNamespace="http://docs.openstack.org/common/api/v1.0"
+ xmlns:ext="http://docs.openstack.org/common/api/v1.0"
+ xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <!-- Import ATOM specific schema definitions -->
+ <xsd:import namespace="http://www.w3.org/2005/Atom" schemaLocation="atom/atom.xsd" />
+
+ <xsd:element name="extensions" type="ext:Extensions"/>
+ <xsd:element name="extension" type="ext:Extension"/>
+
+ <xsd:complexType name="Extensions">
+ <xsd:sequence>
+ <xsd:element name="extension" type="ext:Extension" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ <xsd:anyAttribute namespace="##other" processContents="lax"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="Extension">
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="1" />
+ <xsd:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="namespace" type="xsd:anyURI" use="required"/>
+ <xsd:attribute name="alias" type="ext:Alias" use="required"/>
+ <xsd:attribute name="updated" type="xsd:dateTime" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="lax"/>
+ <xsd:assert vc:minVersion="1.1" test="atom:link[@rel='describedby']">
+ <xsd:annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ There should be at least one atom link
+ with a describedby relation.
+ </p>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:assert>
+ </xsd:complexType>
+
+ <xsd:simpleType name="Alias">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="\w+\-\w+" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+</xsd:schema>
diff --git a/docs/guide/src/docbkx/xsd/fault.xsd b/docs/guide/src/docbkx/xsd/fault.xsd
new file mode 100644
index 00000000..8701b9f7
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/fault.xsd
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<schema
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:idm="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://docs.openstack.org/idm/api/v1.0"
+>
+ <!-- Fault Elements -->
+ <element name="idmFault" type="idm:IDMFault"/>
+ <element name="serviceUnavailable" type="idm:ServiceUnavailableFault"/>
+ <element name="badRequest" type="idm:BadRequestFault"/>
+ <element name="unauthorized" type="idm:UnauthorizedFault"/>
+ <element name="overLimit" type="idm:OverLimitFault"/>
+ <element name="userDisabled" type="idm:UserDisabledFault"/>
+ <element name="forbidden" type="idm:ForbiddenFault"/>
+ <element name="itemNotFound" type="idm:ItemNotFoundFault"/>
+ <element name="tenantConflict" type="idm:TenantConflictFault"/>
+
+ <!-- Fault Types -->
+ <complexType name="IDMFault">
+ <sequence>
+ <element name="message" type="xsd:string">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ A human readable message that is appropriate for display
+ to the end user.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </element>
+ <element name="details" type="xsd:string" minOccurs="0">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ The optional &lt;details&gt; element may contain useful
+ information for tracking down errors (e.g a stack
+ trace). This information may or may not be appropriate
+ for display to an end user.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </element>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="code" type="xsd:int" use="required">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ The HTTP status code associated with the current fault.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+
+ <complexType name="ServiceUnavailableFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="BadRequestFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="UnauthorizedFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="UserDisabledFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="ForbiddenFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="ItemNotFoundFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="TenantConflictFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="OverLimitFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ <attribute name="retryAt" type="xsd:dateTime" use="optional">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ An optional dateTime denoting when an operation should
+ be retried.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ </extension>
+ </complexContent>
+ </complexType>
+
+</schema>
diff --git a/docs/guide/src/docbkx/xsd/tenant.xsd b/docs/guide/src/docbkx/xsd/tenant.xsd
new file mode 100644
index 00000000..2e4854fb
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/tenant.xsd
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<schema
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:idm="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ targetNamespace="http://docs.openstack.org/idm/api/v1.0"
+>
+ <!-- Import ATOM specific schema definitions -->
+ <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom"
+ schemaLocation="atom/atom.xsd" />
+
+ <!-- Elements -->
+ <element name="tenant" type="idm:Tenant" />
+ <element name="tenants" type="idm:Tenants" />
+
+ <!-- Complex Types -->
+ <complexType name="Tenants">
+ <sequence>
+ <element name="tenant" type="idm:Tenant" maxOccurs="1000"/>
+ <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" />
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+
+ <complexType name="Tenant">
+ <sequence>
+ <element name="description" type="xsd:string"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="enabled" type="xsd:boolean" use="optional" default="true"/>
+ <attribute name="id" type="xsd:string" use="optional"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+</schema>
diff --git a/docs/guide/src/docbkx/xsd/token.xsd b/docs/guide/src/docbkx/xsd/token.xsd
new file mode 100644
index 00000000..80bcb029
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/token.xsd
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<schema
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:idm="http://docs.openstack.org/idm/api/v1.0"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://docs.openstack.org/idm/api/v1.0"
+>
+ <!-- Elements -->
+ <element name="passwordCredentials" type="idm:PasswordCredentials"/>
+ <element name="auth" type="idm:AuthData"/>
+
+ <!-- Complex Types -->
+ <complexType name="Credentials" abstract="true" />
+ <complexType name="PasswordCredentials">
+ <complexContent>
+ <extension base="idm:Credentials">
+ <sequence>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="password" type="xsd:string" use="required" />
+ <attribute name="username" type="xsd:string" use="required" />
+ <attribute name="tenantId" type="xsd:string" use="optional" />
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="AuthData">
+ <sequence>
+ <element name="token" type="idm:Token"/>
+ <element name="user" type="idm:User"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+
+ <complexType name="Token">
+ <sequence>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="expires" type="xsd:dateTime" use="required"/>
+ <attribute name="id" type="xsd:string" use="required"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+
+ <complexType name="User">
+ <sequence>
+ <element name="groups" type="idm:Groups" />
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <attribute name="tenantId" type="xsd:string"/>
+ <attribute name="username" type="xsd:string"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+
+ <complexType name="Groups">
+ <sequence>
+ <element name="group" type="idm:Group" maxOccurs="1000"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </sequence>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+
+ <complexType name="Group">
+ <attribute name="id" type="xsd:string" use="required"/>
+ <attribute name="tenantId" type="xsd:string" use="optional"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+</schema>
diff --git a/docs/guide/src/docbkx/xsd/version.xsd b/docs/guide/src/docbkx/xsd/version.xsd
new file mode 100644
index 00000000..6b2403bd
--- /dev/null
+++ b/docs/guide/src/docbkx/xsd/version.xsd
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified"
+ targetNamespace="http://docs.openstack.org/common/api/v1.0"
+ xmlns:vers="http://docs.openstack.org/common/api/v1.0"
+ xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <!-- Import ATOM specific schema definitions -->
+ <xs:import namespace="http://www.w3.org/2005/Atom" schemaLocation="atom/atom.xsd" />
+
+ <!-- Multiple choices -->
+ <xs:element name="choices" type="vers:VersionChoiceList" />
+
+ <!-- Versioning -->
+ <xs:element name="versions" type="vers:VersionChoiceList" />
+ <xs:element name="version" type="vers:VersionChoice" vc:minVersion="1.0" vc:maxVersion="1.1"/>
+ <xs:element name="version" type="vers:VersionChoiceRoot" vc:minVersion="1.1"/>
+
+ <!-- Types -->
+ <xs:simpleType name="VersionStatus">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ The VersionStatus type describes a service's operational status.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="DEPRECATED" />
+ <xs:enumeration value="ALPHA" />
+ <xs:enumeration value="BETA" />
+ <xs:enumeration value="CURRENT" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="VersionChoiceList">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ A version choice list outlines a collection of service version choices.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="version" type="vers:VersionChoice" minOccurs="1" maxOccurs="unbounded" />
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ <xs:assert vc:minVersion="1.1" test="every $v in vers:version satisfies $v/atom:link[@rel='self']">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ In version lists, every single version must
+ contain at least one self link.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:assert>
+ </xs:complexType>
+
+ <xs:complexType name="VersionChoiceRoot" vc:minVersion="1.1">
+ <xs:complexContent>
+ <xs:extension base="vers:VersionChoice">
+ <xs:assert test="atom:link[@rel='describedby']">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ When used as a root element, a version choice
+ must contain at least one describedby link.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:assert>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="VersionChoice">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ A version choice contains relevant information about an available service
+ that a user can then use to target a specific version of the service. Note
+ that both the descriptive media types and the atom link references are
+ not manditory and are offered as message enrichment elements rather
+ than message requirements.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="media-types" type="vers:MediaTypeList" minOccurs="0" maxOccurs="1" />
+ <xs:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" />
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+
+ <xs:attribute name="id" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ The ID of a version choice represents the service version's unique
+ identifier. This ID is guaranteed to be unique only among the
+ service version choices outlined in the VersionChoiceList.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="status" type="vers:VersionStatus" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ A version choice's status describes the current operational state of
+ the given service version. The operational status is captured in a
+ simple type enumeration called VersionStatus.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="updated" type="xs:dateTime" use="optional">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ A version choice's updated attribute describes
+ the time when the version was updated. The
+ time should be updated anytime
+ <html:strong>anything</html:strong> in the
+ version has changed: documentation,
+ extensions, bug fixes.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+
+ <xs:complexType name="MediaTypeList">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ A MediaTypeList outlines a collection of valid media types for a given
+ service version.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="media-type" type="vers:MediaType" minOccurs="1" maxOccurs="unbounded" />
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+
+ <xs:complexType name="MediaType">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ A MediaType describes what content types the service version understands.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:attribute name="base" type="xs:string" use="optional" default="">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ The base of a given media type describes the simple MIME type
+ that then a more complicated media type can be derived from. These
+ types are basic and provide no namespace or version specific
+ data are are only provided as a convenience. Because of this the
+ base attribute is declared as optional.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ The type attribute of a MediaType describes the MIME specific
+ identifier of the media type in question. This identifier should include
+ a vendor namespace (
+ <html:a href="http://tools.ietf.org/html/rfc2048">See RFC 2048</html:a>)
+ as well as a version suffix.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+</xs:schema>
diff --git a/echo/echo/__init__.py b/echo/echo/__init__.py
new file mode 100644
index 00000000..52bcde04
--- /dev/null
+++ b/echo/echo/__init__.py
@@ -0,0 +1 @@
+from echo import app_factory
diff --git a/echo/echo/echo.ini b/echo/echo/echo.ini
new file mode 100644
index 00000000..b81174d0
--- /dev/null
+++ b/echo/echo/echo.ini
@@ -0,0 +1,36 @@
+[DEFAULT]
+;delegated means we still allow unauthenticated requests through so the
+;service can make the final decision on authentication
+delay_auth_decision = 0
+
+;where to find the OpenStack service (if not in local WSGI chain)
+service_protocol = http
+service_host = 127.0.0.1
+service_port = 8090
+;used to verify this component with the OpenStack service (or PAPIAuth)
+service_pass = dTpw
+
+
+[app:echo]
+paste.app_factory = echo:app_factory
+
+[pipeline:main]
+pipeline =
+ tokenauth
+ echo
+
+[filter:tokenauth]
+paste.filter_factory = keystone:tokenauth_factory
+;where to find the token auth service
+auth_host = 127.0.0.1
+auth_port = 8080
+auth_protocol = http
+;how to authenticate to the auth service for priviledged operations
+;like validate token
+admin_token = 999888777666
+
+[filter:basicauth]
+paste.filter_factory = keystone:basicauth_factory
+
+[filter:openidauth]
+paste.filter_factory = keystone:openidauth_factory
diff --git a/echo/echo/echo.py b/echo/echo/echo.py
new file mode 100644
index 00000000..ee950b37
--- /dev/null
+++ b/echo/echo/echo.py
@@ -0,0 +1,121 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+import eventlet
+from eventlet import wsgi
+from lxml import etree
+from paste.deploy import loadapp
+
+# If ../echo/__init__.py exists, add ../ to Python search path, so that
+# it will override what happens to be installed in /usr/(local/)lib/python...
+POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+ os.pardir,
+ os.pardir))
+if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'echo', '__init__.py')):
+ # also use the local keystone
+ KEYSTONE_TOPDIR = os.path.normpath(os.path.join(POSSIBLE_TOPDIR,
+ os.pardir))
+ if os.path.exists(os.path.join(KEYSTONE_TOPDIR,
+ 'keystone',
+ '__init__.py')):
+ sys.path.insert(0, KEYSTONE_TOPDIR)
+ sys.path.insert(0, POSSIBLE_TOPDIR)
+
+
+"""
+Echo: a dummy service for OpenStack auth testing. It returns request info.
+"""
+
+
+class EchoApp(object):
+ def __init__(self, environ, start_response):
+ self.envr = environ
+ self.start = start_response
+ self.dom = self.toDOM(environ)
+ echo_xsl = os.path.join(os.path.abspath(\
+ os.path.dirname(__file__)), "xsl/echo.xsl")
+ self.transform = etree.XSLT(etree.parse(echo_xsl))
+
+ def __iter__(self):
+ if 'HTTP_X_AUTHORIZATION' not in self.envr:
+ return HTTPUnauthorized(self.envr, start_response)
+
+ print ' Received:'
+ if 'HTTP_X_IDENTITY_STATUS' in self.envr: print ' Auth Status:', self.envr['HTTP_X_IDENTITY_STATUS']
+ if 'HTTP_X_AUTHORIZATION' in self.envr: print ' Identity :', self.envr['HTTP_X_AUTHORIZATION']
+ if 'HTTP_X_TENANT' in self.envr: print ' Tenant :', self.envr['HTTP_X_TENANT']
+ if 'HTTP_X_GROUP' in self.envr: print ' Group :', self.envr['HTTP_X_GROUP']
+
+ accept = self.envr.get("HTTP_ACCEPT", "application/json")
+ if accept == "application/xml":
+ return self.toXML()
+ else:
+ return self.toJSON()
+
+ def toJSON(self):
+ self.start('200 OK', [('Content-Type', 'application/json')])
+ yield str(self.transform(self.dom))
+
+ def toXML(self):
+ self.start('200 OK', [('Content-Type', 'application/xml')])
+ yield etree.tostring(self.dom)
+
+ def toDOM(self, environ):
+ echo = etree.Element("{http://docs.openstack.org/echo/api/v1.0}echo",
+ method=environ["REQUEST_METHOD"],
+ pathInfo=environ["PATH_INFO"],
+ queryString=environ.get('QUERY_STRING', ""),
+ caller_identity=self.envr['HTTP_X_AUTHORIZATION'])
+ content = etree.Element(
+ "{http://docs.openstack.org/echo/api/v1.0}content")
+ content.set("type", environ["CONTENT_TYPE"])
+ content.text = ""
+ inReq = environ["wsgi.input"]
+ for line in inReq:
+ content.text = content.text + line
+ echo.append(content)
+ return echo
+
+
+def app_factory(global_conf, **local_conf):
+ return EchoApp
+
+if __name__ == "__main__":
+ remote_auth = False
+ if len(sys.argv) > 1:
+ remote_auth = sys.argv[1] == '--remote'
+
+ if remote_auth:
+ # running auth remotely
+ print "Running for use with remote auth"
+
+ app = loadapp("config:" + \
+ os.path.join(os.path.abspath(os.path.dirname(__file__)),
+ "echo_remote.ini"), global_conf={"log_name": "echo.log"})
+
+ wsgi.server(eventlet.listen(('', 8100)), app)
+
+ else:
+ print "Running all components locally."
+ print "Use --remote option to run with remote auth proxy"
+ app = loadapp("config:" + \
+ os.path.join(os.path.abspath(os.path.dirname(__file__)),
+ "echo.ini"), global_conf={"log_name": "echo.log"})
+
+ wsgi.server(eventlet.listen(('', 8090)), app)
diff --git a/echo/echo/echo.wadl b/echo/echo/echo.wadl
new file mode 100644
index 00000000..b9572999
--- /dev/null
+++ b/echo/echo/echo.wadl
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<application xmlns="http://wadl.dev.java.net/2009/02"
+ xmlns:echo="http://docs.openstack.org/echo/api/v1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xsi:schemaLocation="http://docs.openstack.org/echo/api/v1.0
+ xsd/echo.xsd
+ ">
+ <grammars>
+ <include href="xsd/echo.xsd"/>
+ </grammars>
+
+ <resources base="http://localhost:8090">
+ <resource id="root" path="/">
+ <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/>
+ <method href="#get" />
+ <method href="#put" />
+ <method href="#post" />
+ <method href="#delete" />
+ <method href="#head" />
+ <method href="#options" />
+ </resource>
+ </resources>
+
+ <method name="GET" id="get">
+ <request>
+ <representation mediaType="*/*" />
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="echo:echo"/>
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="PUT" id="put">
+ <request>
+ <representation mediaType="*/*" />
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="echo:echo"/>
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="POST" id="post">
+ <request>
+ <representation mediaType="*/*" />
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="echo:echo"/>
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="DELETE" id="delete">
+ <request>
+ <representation mediaType="*/*" />
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="echo:echo"/>
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="HEAD" id="head">
+ <request>
+ <representation mediaType="*/*" />
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="echo:echo"/>
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="OPTIONS" id="options">
+ <request>
+ <representation mediaType="*/*" />
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="echo:echo"/>
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+</application>
diff --git a/echo/echo/echo_remote.ini b/echo/echo/echo_remote.ini
new file mode 100644
index 00000000..c27b2365
--- /dev/null
+++ b/echo/echo/echo_remote.ini
@@ -0,0 +1,19 @@
+[DEFAULT]
+
+[app:echo]
+paste.app_factory = echo:app_factory
+
+[pipeline:main]
+pipeline =
+ remoteauth
+ echo
+
+[filter:remoteauth]
+paste.filter_factory = keystone:remoteauth_factory
+;password which will tell us call is coming from a trusted auth proxy
+; (otherwise we redirect call)
+remote_auth_pass = dTpw
+;where to redirect untrusted calls to
+auth_location = http://127.0.0.1:8080/
+
+
diff --git a/echo/echo/samples/echo.json b/echo/echo/samples/echo.json
new file mode 100644
index 00000000..05b60ef4
--- /dev/null
+++ b/echo/echo/samples/echo.json
@@ -0,0 +1,11 @@
+{
+ "echo" : {
+ "method" : "GET",
+ "pathInfo" : "/bla/bla",
+ "queryString" : "hello",
+ "content" : {
+ "type" : "application/xml",
+ "value" : " This is some content. "
+ }
+ }
+}
diff --git a/echo/echo/samples/echo.xml b/echo/echo/samples/echo.xml
new file mode 100644
index 00000000..8cf4b984
--- /dev/null
+++ b/echo/echo/samples/echo.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<echo xmlns="http://docs.openstack.org/echo/api/v1.0"
+ method="GET" pathInfo="/bla/bla" queryString="hello">
+ <content type="application/xml">
+ This is some content.
+ </content>
+</echo>
diff --git a/echo/echo/xsd/echo.xsd b/echo/echo/xsd/echo.xsd
new file mode 100644
index 00000000..3d5c2a51
--- /dev/null
+++ b/echo/echo/xsd/echo.xsd
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<schema
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:echo="http://docs.openstack.org/echo/api/v1.0"
+ targetNamespace="http://docs.openstack.org/echo/api/v1.0">
+
+ <!-- Elements -->
+ <element name="echo" type="echo:Echo" />
+
+ <!-- Complex Type -->
+ <complexType name="Echo">
+ <sequence>
+ <element name="content" type="echo:Content" />
+ </sequence>
+ <attribute name="method" type="echo:Method" use="required"/>
+ <attribute name="pathInfo" type="xsd:string" use="required"/>
+ <attribute name="queryString" type="xsd:string" use="optional"/>
+ </complexType>
+
+ <complexType name="Content">
+ <simpleContent>
+ <extension base="xsd:string">
+ <attribute name="type" type="xsd:string" use="required"/>
+ </extension>
+ </simpleContent>
+ </complexType>
+
+ <!-- Simple Types -->
+ <simpleType name="Method">
+ <restriction base="xsd:string">
+ <enumeration value="GET" />
+ <enumeration value="DELETE" />
+ <enumeration value="POST" />
+ <enumeration value="PUT" />
+ <enumeration value="HEAD" />
+ <enumeration value="OPTIONS" />
+ <enumeration value="CONNECT" />
+ <enumeration value="TRACE" />
+
+ <enumeration value="ALL" />
+ </restriction>
+ </simpleType>
+
+</schema>
diff --git a/echo/echo/xsl/echo.xsl b/echo/echo/xsl/echo.xsl
new file mode 100644
index 00000000..dbe8b5dd
--- /dev/null
+++ b/echo/echo/xsl/echo.xsl
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<transform xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:echo="http://docs.openstack.org/echo/api/v1.0"
+ version="1.0">
+ <output method="text" encoding="UTF-8"/>
+
+ <template match="echo:echo">
+ <text>{ "echo" : { </text>
+ <apply-templates select="@*"/>
+ <text>,</text>
+ <apply-templates />
+ <text>}}</text>
+ </template>
+
+ <template match="echo:content">
+ <text>"content" : {</text>
+ <apply-templates select="@*"/>
+ <text>, "value" : "</text>
+ <apply-templates />
+ <text>" }</text>
+ </template>
+
+ <template match="@*">
+ <if test="position() != 1">
+ <text>,</text>
+ </if>
+ <text>"</text>
+ <value-of select="name()"/>
+ <text>" : "</text>
+ <value-of select="."/>
+ <text>"</text>
+ </template>
+
+ <template match="text()">
+ <variable name="noeol" select="translate(string(.),'&#x000a;','')"/>
+ <variable name="noquote" select="translate($noeol,'&quot;',&quot;&apos;&quot;)"/>
+ <value-of select="$noquote"/>
+ </template>
+
+</transform>
diff --git a/echo/echo_client.py b/echo/echo_client.py
new file mode 100644
index 00000000..677e8ab9
--- /dev/null
+++ b/echo/echo_client.py
@@ -0,0 +1,72 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Implement a client for Echo service using Identity service
+"""
+
+import httplib
+import json
+
+
+def get_auth_token(username, password, tenant):
+ headers = {"Content-type": "application/json", "Accept": "text/json"}
+ params = {"passwordCredentials": {"username": username,
+ "password": password,
+ "tenantId": "1"}}
+ conn = httplib.HTTPConnection("localhost:8080")
+ conn.request("POST", "/v1.0/token", json.dumps(params), headers=headers)
+ response = conn.getresponse()
+ data = response.read()
+ ret = data
+ return ret
+
+
+def call_service(token):
+ headers = {"X-Auth-Token": token,
+ "Content-type": "application/json",
+ "Accept": "text/json"}
+ params = '{"ping": "abcdefg"}'
+ conn = httplib.HTTPConnection("localhost:8090")
+ conn.request("POST", "/", params, headers=headers)
+ response = conn.getresponse()
+ data = response.read()
+ ret = data
+ return ret
+
+if __name__ == '__main__':
+ # Call the keystone service to get a token
+ # NOTE: assumes the test_setup.sql script has loaded this user
+ print "\033[91mTrying with valid test credentials...\033[0m"
+ auth = get_auth_token("joeuser", "secrete", "1")
+ obj = json.loads(auth)
+ token = obj["auth"]["token"]["id"]
+ print "Token obtained:", token
+
+ # Use that token to call an OpenStack service (echo)
+ data = call_service(token)
+ print "Response received:", data
+ print
+
+ # Use bad token to call an OpenStack service (echo)
+ print "\033[91mTrying with bad token...\033[0m"
+ data = call_service("xxxx_invalid_token_xxxx")
+ print "Response received:", data
+ print
+
+ #Supply bad credentials
+ print "\033[91mTrying with bad credentials...\033[0m"
+ auth = get_auth_token("joeuser", "wrongpass", "1")
+ print "Response:", auth
diff --git a/echo/setup.py b/echo/setup.py
new file mode 100644
index 00000000..045df550
--- /dev/null
+++ b/echo/setup.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from setuptools import setup, find_packages
+
+version = '1.0'
+
+setup(
+ name='echo',
+ version=version,
+ description="",
+ license='Apache License (2.0)',
+ classifiers=["Programming Language :: Python"],
+ keywords='',
+ author='OpenStack, LLC.',
+ include_package_data=True,
+ packages=find_packages(exclude=['test', 'bin']),
+ zip_safe=False,
+ install_requires=['setuptools', 'keystone'],
+ entry_points={
+ 'paste.app_factory': ['main=echo:app_factory'],
+ 'paste.filter_factory': [
+ 'papiauth=keystone:papiauth_factory',
+ ],
+ },
+ )
diff --git a/keystone/__init__.py b/keystone/__init__.py
new file mode 100644
index 00000000..aec9d44f
--- /dev/null
+++ b/keystone/__init__.py
@@ -0,0 +1,15 @@
+#TOKEN AUTH
+from auth_protocols.auth_token \
+ import filter_factory as tokenauth_factory
+
+#BASIC AUTH
+from auth_protocols.auth_basic \
+ import filter_factory as basicauth_factory
+
+#OPENID AUTH
+from auth_protocols.auth_openid \
+ import filter_factory as openidauth_factory
+
+#Remote Auth handler
+from middleware.remoteauth \
+ import filter_factory as remoteauth_factory
diff --git a/keystone/auth_protocols/__init__.py b/keystone/auth_protocols/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystone/auth_protocols/__init__.py
diff --git a/keystone/auth_protocols/auth_basic.py b/keystone/auth_protocols/auth_basic.py
new file mode 100644
index 00000000..2bc967ff
--- /dev/null
+++ b/keystone/auth_protocols/auth_basic.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Not Yet PEP8 standardized
+
+
+"""
+BASIC AUTH MIDDLEWARE - STUB
+
+This WSGI component should perform multiple jobs:
+- validate incoming basic claims
+- perform all basic auth interactions with clients
+- collect and forward identity information from the authentication process
+ such as user name, groups, etc...
+
+This is an Auth component as per: http://wiki.openstack.org/openstack-authn
+
+"""
+
+
+PROTOCOL_NAME = "Basic Authentication"
+
+
+class AuthProtocol(object):
+ """Auth Middleware that handles authenticating client calls"""
+
+ def __init__(self, app, conf):
+ print "Starting the %s component" % PROTOCOL_NAME
+
+ self.conf = conf
+ self.app = app
+ #if app is set, then we are in a WSGI pipeline and requests get passed
+ # on to app. If it is not set, this component should forward requests
+
+ # where to find the OpenStack service (if not in local WSGI chain)
+ # these settings are only used if this component is acting as a proxy
+ # and the OpenSTack service is running remotely
+ self.service_protocol = conf.get('service_protocol', 'https')
+ self.service_host = conf.get('service_host')
+ self.service_port = int(conf.get('service_port'))
+ self.service_url = '%s://%s:%s' % (self.service_protocol,
+ self.service_host,
+ self.service_port)
+ # used to verify this component with the OpenStack service or PAPIAuth
+ self.service_pass = conf.get('service_pass')
+
+ # delay_auth_decision means we still allow unauthenticated requests
+ # through and we let the downstream service make the final decision
+ self.delay_auth_decision = int(conf.get('delay_auth_decision', 0))
+
+ def __call__(self, env, start_response):
+ def custom_start_response(status, headers):
+ if self.delay_auth_decision:
+ headers.append(('WWW-Authenticate', "Basic realm='API Realm'"))
+ return start_response(status, headers)
+
+ #TODO(Ziad): PERFORM BASIC AUTH
+
+ #Auth processed, headers added now decide how to pass on the call
+ if self.app:
+ # Pass to downstream WSGI component
+ env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
+ return self.app(env, custom_start_response)
+
+ proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
+ # We are forwarding to a remote service (no downstream WSGI app)
+ req = Request(proxy_headers)
+ parsed = urlparse(req.url)
+ conn = http_connect(self.service_host, self.service_port, \
+ req.method, parsed.path, \
+ proxy_headers,\
+ ssl=(self.service_protocol == 'https'))
+ resp = conn.getresponse()
+ data = resp.read()
+ #TODO: use a more sophisticated proxy
+ # we are rewriting the headers now
+ return Response(status=resp.status, body=data)(env, start_response)
+
+
+def filter_factory(global_conf, **local_conf):
+ """Returns a WSGI filter app for use with paste.deploy."""
+ conf = global_conf.copy()
+ conf.update(local_conf)
+
+ def auth_filter(app):
+ return AuthProtocol(app, conf)
+ return auth_filter
+
+
+def app_factory(global_conf, **local_conf):
+ conf = global_conf.copy()
+ conf.update(local_conf)
+ return AuthProtocol(None, conf)
+
+if __name__ == "__main__":
+ app = loadapp("config:" + \
+ os.path.join(os.path.abspath(os.path.dirname(__file__)),
+ "auth_basic.ini"),
+ global_conf={"log_name": "auth_basic.log"})
+ wsgi.server(eventlet.listen(('', 8090)), app)
diff --git a/keystone/auth_protocols/auth_openid.py b/keystone/auth_protocols/auth_openid.py
new file mode 100644
index 00000000..ac9121f7
--- /dev/null
+++ b/keystone/auth_protocols/auth_openid.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Not Yet PEP8 standardized
+
+
+"""
+OPENID AUTH MIDDLEWARE - STUB
+
+This WSGI component should perform multiple jobs:
+- validate incoming openid claims
+- perform all openid interactions with clients
+- collect and forward identity information from the openid authentication
+ such as user name, groups, etc...
+
+This is an Auth component as per: http://wiki.openstack.org/openstack-authn
+"""
+
+
+PROTOCOL_NAME = "OpenID Authentication"
+
+
+class AuthProtocol(object):
+ """Auth Middleware that handles authenticating client calls"""
+
+ def __init__(self, app, conf):
+ print "Starting the %s component" % PROTOCOL_NAME
+
+ self.conf = conf
+ self.app = app
+ #if app is set, then we are in a WSGI pipeline and requests get passed
+ # on to app. If it is not set, this component should forward requests
+
+ # where to find the OpenStack service (if not in local WSGI chain)
+ # these settings are only used if this component is acting as a proxy
+ # and the OpenSTack service is running remotely
+ self.service_protocol = conf.get('service_protocol', 'http')
+ self.service_host = conf.get('service_host', '127.0.0.1')
+ self.service_port = int(conf.get('service_port', 8090))
+ self.service_url = '%s://%s:%s' % (self.service_protocol,
+ self.service_host,
+ self.service_port)
+ # used to verify this component with the OpenStack service or PAPIAuth
+ self.service_pass = conf.get('service_pass', 'dTpw')
+
+ # delay_auth_decision means we still allow unauthenticated requests
+ # through and we let the downstream service make the final decision
+ self.delay_auth_decision = int(conf.get('delay_auth_decision', 0))
+
+ def __call__(self, env, start_response):
+ def custom_start_response(status, headers):
+ if self.delay_auth_decision:
+ headers.append(('WWW-Authenticate', "Basic realm='API Realm'"))
+ return start_response(status, headers)
+
+ #TODO(Rasib): PERFORM OPENID AUTH
+
+ #Auth processed, headers added now decide how to pass on the call
+ if self.app:
+ # Pass to downstream WSGI component
+ env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
+ return self.app(env, custom_start_response)
+
+ proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
+ # We are forwarding to a remote service (no downstream WSGI app)
+ req = Request(proxy_headers)
+ parsed = urlparse(req.url)
+ conn = http_connect(self.service_host, self.service_port, \
+ req.method, parsed.path, \
+ proxy_headers,\
+ ssl=(self.service_protocol == 'https'))
+ resp = conn.getresponse()
+ data = resp.read()
+ #TODO: use a more sophisticated proxy
+ # we are rewriting the headers now
+ return Response(status=resp.status, body=data)(env, start_response)
+
+
+def filter_factory(global_conf, **local_conf):
+ """Returns a WSGI filter app for use with paste.deploy."""
+ conf = global_conf.copy()
+ conf.update(local_conf)
+
+ def auth_filter(app):
+ return AuthProtocol(app, conf)
+ return auth_filter
+
+
+def app_factory(global_conf, **local_conf):
+ conf = global_conf.copy()
+ conf.update(local_conf)
+ return AuthProtocol(None, conf)
+
+if __name__ == "__main__":
+ app = loadapp("config:" + \
+ os.path.join(os.path.abspath(os.path.dirname(__file__)),
+ "auth_openid.ini"),
+ global_conf={"log_name": "auth_openid.log"})
+ wsgi.server(eventlet.listen(('', 8090)), app)
diff --git a/keystone/auth_protocols/auth_token.ini b/keystone/auth_protocols/auth_token.ini
new file mode 100644
index 00000000..3154fc69
--- /dev/null
+++ b/keystone/auth_protocols/auth_token.ini
@@ -0,0 +1,18 @@
+[DEFAULT]
+
+[app:main]
+paste.app_factory = auth_token:app_factory
+
+auth_protocol = http
+auth_host = 127.0.0.1
+auth_port = 8080
+admin_token = 999888777666
+
+delay_auth_decision = 0
+
+service_protocol = http
+service_host = 127.0.0.1
+service_port = 8100
+service_pass = dTpw
+
+
diff --git a/keystone/auth_protocols/auth_token.py b/keystone/auth_protocols/auth_token.py
new file mode 100644
index 00000000..cee8e84f
--- /dev/null
+++ b/keystone/auth_protocols/auth_token.py
@@ -0,0 +1,265 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Not Yet PEP8 standardized
+
+
+"""
+TOKEN-BASED AUTH MIDDLEWARE
+
+This WSGI component performs multiple jobs:
+- it verifies that incoming client requests have valid tokens by verifying
+ tokens with the auth service.
+- it will reject unauthenticated requests UNLESS it is in 'delay_auth_decision'
+ mode, which means the final decision is delegated to the downstream WSGI
+ component (usually the OpenStack service)
+- it will collect and forward identity information from a valid token
+ such as user name, groups, etc...
+
+Refer to: http://wiki.openstack.org/openstack-authn
+
+
+HEADERS
+-------
+Headers starting with HTTP_ is a standard http header
+Headers starting with HTTP_X is an extended http header
+
+> Coming in from initial call from client or customer
+HTTP_X_AUTH_TOKEN : the client token being passed in
+HTTP_X_STORAGE_TOKEN: the client token being passed in (legacy Rackspace use)
+ to support cloud files
+> Used for communication between components
+www-authenticate : only used if this component is being used remotely
+HTTP_AUTHORIZATION : basic auth password used to validate the connection
+
+> What we add to the request for use by the OpenStack service
+HTTP_X_AUTHORIZATION: the client identity being passed in
+
+"""
+
+import eventlet
+from eventlet import wsgi
+import json
+import os
+from paste.deploy import loadapp
+from urlparse import urlparse
+from webob.exc import HTTPUnauthorized
+from webob.exc import Request, Response
+import httplib
+
+from keystone.common.bufferedhttp import http_connect_raw as http_connect
+
+PROTOCOL_NAME = "Token Authentication"
+
+
+def _decorate_request_headers(header, value, proxy_headers, env):
+ proxy_headers[header] = value
+ env["HTTP_%s" % header] = value
+
+
+class AuthProtocol(object):
+ """Auth Middleware that handles authenticating client calls"""
+
+ def _init_protocol_common(self, app, conf):
+ """ Common initialization code"""
+ print "Starting the %s component" % PROTOCOL_NAME
+
+ self.conf = conf
+ self.app = app
+ #if app is set, then we are in a WSGI pipeline and requests get passed
+ # on to app. If it is not set, this component should forward requests
+
+ # where to find the OpenStack service (if not in local WSGI chain)
+ # these settings are only used if this component is acting as a proxy
+ # and the OpenSTack service is running remotely
+ self.service_protocol = conf.get('service_protocol', 'https')
+ self.service_host = conf.get('service_host')
+ self.service_port = int(conf.get('service_port'))
+ self.service_url = '%s://%s:%s' % (self.service_protocol,
+ self.service_host,
+ self.service_port)
+ # used to verify this component with the OpenStack service or PAPIAuth
+ self.service_pass = conf.get('service_pass')
+
+ # delay_auth_decision means we still allow unauthenticated requests
+ # through and we let the downstream service make the final decision
+ self.delay_auth_decision = int(conf.get('delay_auth_decision', 0))
+
+ def _init_protocol(self, app, conf):
+ """ Protocol specific initialization """
+
+ # where to find the auth service (we use this to validate tokens)
+ self.auth_host = conf.get('auth_host')
+ self.auth_port = int(conf.get('auth_port'))
+ self.auth_protocol = conf.get('auth_protocol', 'https')
+ self.auth_location = "%s://%s:%s" % (self.auth_protocol, self.auth_host,
+ self.auth_port)
+
+ # Credentials used to verify this component with the Auth service since
+ # validating tokens is a priviledged call
+ self.admin_token = conf.get('admin_token')
+
+ def __init__(self, app, conf):
+ """ Common initialization code """
+
+ #TODO: maybe we rafactor this into a superclass
+ self._init_protocol_common(app, conf) # Applies to all protocols
+ self._init_protocol(app, conf) # Specific to this protocol
+
+ def get_admin_auth_token(self, username, password, tenant):
+ """
+ This function gets an admin auth token to be used by this service to
+ validate a user's token. Validate_token is a priviledged call so
+ it needs to be authenticated by a service that is calling it
+ """
+ headers = {"Content-type": "application/json", "Accept": "text/json"}
+ params = {"passwordCredentials": {"username": username,
+ "password": password,
+ "tenantId": "1"}}
+ conn = httplib.HTTPConnection("%s:%s" \
+ % (self.auth_host, self.auth_port))
+ conn.request("POST", "/v1.0/token", json.dumps(params), \
+ headers=headers)
+ response = conn.getresponse()
+ data = response.read()
+ ret = data
+ return ret
+
+ def __call__(self, env, start_response):
+ def custom_start_response(status, headers):
+ if self.delay_auth_decision:
+ headers.append(('WWW-Authenticate', "Basic realm='API Realm'"))
+ return start_response(status, headers)
+
+ #Prep headers to proxy request to remote service
+ proxy_headers = env.copy()
+ user = ''
+
+ #Look for token in request
+ token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
+ if not token:
+ #No token was provided
+ if self.delay_auth_decision:
+ _decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
+ proxy_headers, env)
+ else:
+ # Redirect client to auth server
+ return HTTPUseProxy(location=self.auth_location)(env,
+ start_response)
+ else:
+ # this request is claiming it has a valid token, let's check
+ # with the auth service
+ # Step 1: We need to auth with the keystone service, so get an
+ # admin token
+ #TODO: Need to properly implement this, where to store creds
+ # for now using token from ini
+ #auth = self.get_admin_auth_token("admin", "secrete", "1")
+ #admin_token = json.loads(auth)["auth"]["token"]["id"]
+
+ # Step 2: validate the user's token with the auth service
+ # since this is a priviledged op,m we need to auth ourselves
+ # by using an admin token
+ headers = {"Content-type": "application/json",
+ "Accept": "text/json",
+ "X-Auth-Token": self.admin_token}
+ ##TODO:we need to figure out how to auth to keystone
+ #since validate_token is a priviledged call
+ #Khaled's version uses creds to get a token
+ # "X-Auth-Token": admin_token}
+ # we're using a test token from the ini file for now
+ conn = http_connect(self.auth_host, self.auth_port, 'GET',
+ '/v1.0/token/%s' % token, headers=headers)
+ resp = conn.getresponse()
+ data = resp.read()
+ conn.close()
+
+ if not str(resp.status).startswith('20'):
+ # Keystone rejected claim
+ if self.delay_auth_decision:
+ # Downstream service will receive call still and decide
+ _decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
+ proxy_headers, env)
+ else:
+ # Reject the response & send back the error
+ # (not delay_auth_decision)
+ return HTTPUnauthorized(headers=headers)(env,
+ start_response)
+ else:
+ # Valid token. Get user data and put it in to the call
+ # so the downstream service can use iot
+ dict_response = json.loads(data)
+ #TODO(Ziad): make this more robust
+ user = dict_response['auth']['user']['username']
+ tenant = dict_response['auth']['user']['tenantId']
+ group = '%s/%s' % (dict_response['auth']['user']['groups']['group'][0]['id'],
+ dict_response['auth']['user']['groups']['group'][0]['tenantId'])
+
+ # TODO(Ziad): add additional details we may need,
+ # like tenant and group info
+ _decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user,
+ proxy_headers, env)
+ _decorate_request_headers("X_IDENTITY_STATUS", "Confirmed",
+ proxy_headers, env)
+ _decorate_request_headers('X_TENANT', tenant,
+ proxy_headers, env)
+ _decorate_request_headers('X_GROUP', group,
+ proxy_headers, env)
+
+ #Token/Auth processed, headers added now decide how to pass on the call
+ _decorate_request_headers('AUTHORIZATION',
+ "Basic %s" % self.service_pass,
+ proxy_headers,
+ env)
+ if self.app:
+ # Pass to downstream WSGI component
+ return self.app(env, custom_start_response)
+ else:
+ # We are forwarding to a remote service (no downstream WSGI app)
+ req = Request(proxy_headers)
+ parsed = urlparse(req.url)
+ conn = http_connect(self.service_host, self.service_port, \
+ req.method, parsed.path, \
+ proxy_headers,\
+ ssl=(self.service_protocol == 'https'))
+ resp = conn.getresponse()
+ data = resp.read()
+ #TODO: use a more sophisticated proxy
+ # we are rewriting the headers now
+ return Response(status=resp.status, body=data)(proxy_headers,
+ start_response)
+
+
+def filter_factory(global_conf, **local_conf):
+ """Returns a WSGI filter app for use with paste.deploy."""
+ conf = global_conf.copy()
+ conf.update(local_conf)
+
+ def auth_filter(app):
+ return AuthProtocol(app, conf)
+ return auth_filter
+
+
+def app_factory(global_conf, **local_conf):
+ conf = global_conf.copy()
+ conf.update(local_conf)
+ return AuthProtocol(None, conf)
+
+if __name__ == "__main__":
+ app = loadapp("config:" + \
+ os.path.join(os.path.abspath(os.path.dirname(__file__)),
+ "auth_token.ini"), global_conf={"log_name": "auth_token.log"})
+ wsgi.server(eventlet.listen(('', 8090)), app)
diff --git a/keystone/common/__init__.py b/keystone/common/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystone/common/__init__.py
diff --git a/keystone/common/bufferedhttp.py b/keystone/common/bufferedhttp.py
new file mode 100644
index 00000000..fdb35ee6
--- /dev/null
+++ b/keystone/common/bufferedhttp.py
@@ -0,0 +1,165 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Monkey Patch httplib.HTTPResponse to buffer reads of headers. This can improve
+performance when making large numbers of small HTTP requests. This module
+also provides helper functions to make HTTP connections using
+BufferedHTTPResponse.
+
+.. warning::
+
+ If you use this, be sure that the libraries you are using do not access
+ the socket directly (xmlrpclib, I'm looking at you :/), and instead
+ make all calls through httplib.
+"""
+
+from urllib import quote
+import logging
+import time
+
+from eventlet.green.httplib import CONTINUE, HTTPConnection, HTTPMessage, \
+ HTTPResponse, HTTPSConnection, _UNKNOWN
+
+
+class BufferedHTTPResponse(HTTPResponse):
+ """HTTPResponse class that buffers reading of headers"""
+
+ def __init__(self, sock, debuglevel=0, strict=0,
+ method=None): # pragma: no cover
+ self.sock = sock
+ self.fp = sock.makefile('rb')
+ self.debuglevel = debuglevel
+ self.strict = strict
+ self._method = method
+
+ self.msg = None
+
+ # from the Status-Line of the response
+ self.version = _UNKNOWN # HTTP-Version
+ self.status = _UNKNOWN # Status-Code
+ self.reason = _UNKNOWN # Reason-Phrase
+
+ self.chunked = _UNKNOWN # is "chunked" being used?
+ self.chunk_left = _UNKNOWN # bytes left to read in current chunk
+ self.length = _UNKNOWN # number of bytes left in response
+ self.will_close = _UNKNOWN # conn will close at end of response
+
+ def expect_response(self):
+ self.fp = self.sock.makefile('rb', 0)
+ version, status, reason = self._read_status()
+ if status != CONTINUE:
+ self._read_status = lambda: (version, status, reason)
+ self.begin()
+ else:
+ self.status = status
+ self.reason = reason.strip()
+ self.version = 11
+ self.msg = HTTPMessage(self.fp, 0)
+ self.msg.fp = None
+
+
+class BufferedHTTPConnection(HTTPConnection):
+ """HTTPConnection class that uses BufferedHTTPResponse"""
+ response_class = BufferedHTTPResponse
+
+ def connect(self):
+ self._connected_time = time.time()
+ return HTTPConnection.connect(self)
+
+ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
+ self._method = method
+ self._path = url
+ return HTTPConnection.putrequest(self, method, url, skip_host,
+ skip_accept_encoding)
+
+ def getexpect(self):
+ response = BufferedHTTPResponse(self.sock, strict=self.strict,
+ method=self._method)
+ response.expect_response()
+ return response
+
+ def getresponse(self):
+ response = HTTPConnection.getresponse(self)
+ logging.debug(("HTTP PERF: %(time).5f seconds to %(method)s "
+ "%(host)s:%(port)s %(path)s)"),
+ {'time': time.time() - self._connected_time, 'method': self._method,
+ 'host': self.host, 'port': self.port, 'path': self._path})
+ return response
+
+
+def http_connect(ipaddr, port, device, partition, method, path,
+ headers=None, query_string=None, ssl=False):
+ """
+ Helper function to create an HTTPConnection object. If ssl is set True,
+ HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
+ will be used, which is buffered for backend Swift services.
+
+ :param ipaddr: IPv4 address to connect to
+ :param port: port to connect to
+ :param device: device of the node to query
+ :param partition: partition on the device
+ :param method: HTTP method to request ('GET', 'PUT', 'POST', etc.)
+ :param path: request path
+ :param headers: dictionary of headers
+ :param query_string: request query string
+ :param ssl: set True if SSL should be used (default: False)
+ :returns: HTTPConnection object
+ """
+ if ssl:
+ conn = HTTPSConnection('%s:%s' % (ipaddr, port))
+ else:
+ conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port))
+ path = quote('/' + device + '/' + str(partition) + path)
+ if query_string:
+ path += '?' + query_string
+ conn.path = path
+ conn.putrequest(method, path)
+ if headers:
+ for header, value in headers.iteritems():
+ conn.putheader(header, value)
+ conn.endheaders()
+ return conn
+
+
+def http_connect_raw(ipaddr, port, method, path, headers=None,
+ query_string=None, ssl=False):
+ """
+ Helper function to create an HTTPConnection object. If ssl is set True,
+ HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
+ will be used, which is buffered for backend Swift services.
+
+ :param ipaddr: IPv4 address to connect to
+ :param port: port to connect to
+ :param method: HTTP method to request ('GET', 'PUT', 'POST', etc.)
+ :param path: request path
+ :param headers: dictionary of headers
+ :param query_string: request query string
+ :param ssl: set True if SSL should be used (default: False)
+ :returns: HTTPConnection object
+ """
+ if ssl:
+ conn = HTTPSConnection('%s:%s' % (ipaddr, port))
+ else:
+ conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port))
+ if query_string:
+ path += '?' + query_string
+ conn.path = path
+ conn.putrequest(method, path)
+ if headers:
+ for header, value in headers.iteritems():
+ conn.putheader(header, value)
+ conn.endheaders()
+ return conn
diff --git a/keystone/content/extensions.json b/keystone/content/extensions.json
new file mode 100644
index 00000000..9e1c96d0
--- /dev/null
+++ b/keystone/content/extensions.json
@@ -0,0 +1 @@
+{ "extensions" : { "values" : []}} \ No newline at end of file
diff --git a/keystone/content/extensions.xml b/keystone/content/extensions.xml
new file mode 100644
index 00000000..ed5ee9c6
--- /dev/null
+++ b/keystone/content/extensions.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<extensions xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+</extensions>
diff --git a/keystone/content/idmdevguide.pdf b/keystone/content/idmdevguide.pdf
new file mode 100644
index 00000000..3c37c310
--- /dev/null
+++ b/keystone/content/idmdevguide.pdf
Binary files differ
diff --git a/keystone/content/version.json.tpl b/keystone/content/version.json.tpl
new file mode 100644
index 00000000..8fe0e2e1
--- /dev/null
+++ b/keystone/content/version.json.tpl
@@ -0,0 +1,33 @@
+{
+ "version" : {
+ "id" : "v1.0",
+ "status" : "{{VERSION_STATUS}}",
+ "updated" : "{{VERSION_DATE}}",
+ "links": [
+ {
+ "rel" : "self",
+ "href" : "http://{{HOST}}:{{PORT}}/v1.0/"
+ },
+ {
+ "rel" : "describedby",
+ "type" : "application/pdf",
+ "href" : "http://{{HOST}}:{{PORT}}/v1.0/idmdevguide.pdf"
+ },
+ {
+ "rel" : "describedby",
+ "type" : "application/vnd.sun.wadl+xml",
+ "href" : "http://{{HOST}}:{{PORT}}/v1.0/identity.wadl"
+ }
+ ],
+ "media-types": [
+ {
+ "base" : "application/xml",
+ "type" : "application/vnd.openstack.idm-v1.0+xml"
+ },
+ {
+ "base" : "application/json",
+ "type" : "application/vnd.openstack.idm-v1.0+json"
+ }
+ ]
+ }
+}
diff --git a/keystone/content/version.xml.tpl b/keystone/content/version.xml.tpl
new file mode 100644
index 00000000..a7ee96b9
--- /dev/null
+++ b/keystone/content/version.xml.tpl
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<version xmlns="http://docs.openstack.org/common/api/v1.0"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ id="v1.0" status="{{VERSION_STATUS}}" updated="{{VERSION_DATE}}">
+
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.idm-v1.0+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.idm-v1.0+json"/>
+ </media-types>
+
+ <atom:link rel="self"
+ href="http://{{HOST}}:{{PORT}}/v1.0/"/>
+
+ <atom:link rel="describedby"
+ type="application/pdf"
+ href="http://{{HOST}}:{{PORT}}/v1.0/idmdevguide.pdf" />
+
+ <atom:link rel="describedby"
+ type="application/vnd.sun.wadl+xml"
+ href="http://{{HOST}}:{{PORT}}/v1.0/identity.wadl" />
+</version>
diff --git a/keystone/db/__init__.py b/keystone/db/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystone/db/__init__.py
diff --git a/keystone/db/sqlalchemy/__init__.py b/keystone/db/sqlalchemy/__init__.py
new file mode 100644
index 00000000..cffaa881
--- /dev/null
+++ b/keystone/db/sqlalchemy/__init__.py
@@ -0,0 +1,24 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Not Yet PEP8 standardized
+
+
+from models import register_models
+from session import get_session, get_engine
+
+session = get_session()
+engine = get_engine()
+register_models(session, engine)
diff --git a/keystone/db/sqlalchemy/api.py b/keystone/db/sqlalchemy/api.py
new file mode 100644
index 00000000..a0910778
--- /dev/null
+++ b/keystone/db/sqlalchemy/api.py
@@ -0,0 +1,179 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Not Yet PEP8 standardized
+
+
+from session import get_session
+from sqlalchemy.orm import joinedload
+import models
+
+
+def tenant_create(values):
+ tenant_ref = models.Tenant()
+ tenant_ref.update(values)
+ tenant_ref.save()
+ return tenant_ref
+
+
+def tenant_get(id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.Tenant).filter_by(id=id).first()
+ return result
+
+
+def tenant_get_all(session=None):
+ if not session:
+ session = get_session()
+ return session.query(models.Tenant).all()
+
+
+def tenant_is_empty(id, session=None):
+ if not session:
+ session = get_session()
+ a_user = session.query(models.UserTenantAssociation).filter_by(
+ tenant_id=id).first()
+ if a_user != None:
+ return False
+ a_group = session.query(models.Group).filter_by(tenant_id=id).first()
+ if a_group != None:
+ return False
+ return True
+
+
+def tenant_update(id, values, session=None):
+ if not session:
+ session = get_session()
+ with session.begin():
+ tenant_ref = tenant_get(id, session)
+ tenant_ref.update(values)
+ tenant_ref.save(session=session)
+
+
+def tenant_delete(id, session=None):
+ if not session:
+ session = get_session()
+ with session.begin():
+ tenant_ref = tenant_get(id, session)
+ session.delete(tenant_ref)
+
+
+def user_create(values):
+ user_ref = models.User()
+ user_ref.update(values)
+ user_ref.save()
+ return user_ref
+
+
+def user_get(id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.User).options(joinedload('groups')).options(
+ joinedload('tenants')).filter_by(id=id).first()
+ return result
+
+
+def user_get_by_tenant(tenant_id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.UserTenantAssociation).filter_by(
+ tenant_id=tenant_id)
+ return result
+
+
+def user_groups(id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.Group).filter_by(
+ user_id=id)
+ return result
+
+
+def user_update(id, values, session=None):
+ if not session:
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(id, session)
+ user_ref.update(values)
+ user_ref.save(session=session)
+
+
+def user_delete(id, session=None):
+ if not session:
+ session = get_session()
+ with session.begin():
+ user_ref = user_get(id, session)
+ session.delete(user_ref)
+
+
+def group_get(id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.Group).filter_by(id=id).first()
+ return result
+
+
+def group_users(id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.Users).filter_by(
+ group_id=id)
+ return result
+
+
+def group_get_all(session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.Group)
+ return result
+
+
+def group_delete(id, session=None):
+ if not session:
+ session = get_session()
+ with session.begin():
+ group_ref = group_get(id, session)
+ session.delete(group_ref)
+
+
+def token_create(values):
+ token_ref = models.Token()
+ token_ref.update(values)
+ token_ref.save()
+ return token_ref
+
+
+def token_get(id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.Token).filter_by(token_id=id).first()
+ return result
+
+
+def token_delete(id, session=None):
+ if not session:
+ session = get_session()
+ with session.begin():
+ token_ref = token_get(id, session)
+ session.delete(token_ref)
+
+
+def token_for_user(user_id, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.Token).filter_by(
+ user_id=user_id).order_by("expires desc").first()
+ return result
diff --git a/keystone/db/sqlalchemy/models.py b/keystone/db/sqlalchemy/models.py
new file mode 100644
index 00000000..f8050377
--- /dev/null
+++ b/keystone/db/sqlalchemy/models.py
@@ -0,0 +1,135 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Not Yet PEP8 standardized
+
+from sqlalchemy import create_engine, Column, String, Integer, ForeignKey
+from sqlalchemy import DateTime
+from sqlalchemy.exc import IntegrityError
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import relationship, object_mapper
+
+from session import get_session
+
+Base = declarative_base()
+
+
+class KeystoneBase(object):
+ """Base class for Keystone Models."""
+
+ def save(self, session=None):
+ """Save this object."""
+ if not session:
+ session = get_session()
+ session.add(self)
+ try:
+ session.flush()
+ except IntegrityError:
+ raise
+
+ def delete(self, session=None):
+ """Delete this object."""
+ self.save(session=session)
+
+ def __setitem__(self, key, value):
+ setattr(self, key, value)
+
+ def __getitem__(self, key):
+ return getattr(self, key)
+
+ def get(self, key, default=None):
+ return getattr(self, key, default)
+
+ def __iter__(self):
+ self._i = iter(object_mapper(self).columns)
+ return self
+
+ def next(self):
+ n = self._i.next().name
+ return n, getattr(self, n)
+
+ def update(self, values):
+ """Make the model object behave like a dict"""
+ for k, v in values.iteritems():
+ setattr(self, k, v)
+
+ def iteritems(self):
+ """Make the model object behave like a dict.
+
+ Includes attributes from joins."""
+ local = dict(self)
+ joined = dict([(k, v) for k, v in self.__dict__.iteritems()
+ if not k[0] == '_'])
+ local.update(joined)
+ return local.iteritems()
+
+
+class UserTenantAssociation(Base, KeystoneBase):
+ __tablename__ = 'user_tenant_association'
+
+ user_id = Column(String(255), ForeignKey('users.id'), primary_key=True)
+ tenant_id = Column(String(255), ForeignKey('tenants.id'), primary_key=True)
+
+
+class UserGroupAssociation(Base, KeystoneBase):
+ __tablename__ = 'user_group_association'
+
+ user_id = Column(String(255), ForeignKey('users.id'), primary_key=True)
+ group_id = Column(String(255), ForeignKey('groups.id'), primary_key=True)
+
+
+class User(Base, KeystoneBase):
+ __tablename__ = 'users'
+
+ id = Column(String(255), primary_key=True, unique=True)
+ password = Column(String(255))
+ email = Column(String(255))
+ enabled = Column(Integer)
+ groups = relationship(UserGroupAssociation, backref='users')
+ tenants = relationship(UserTenantAssociation, backref='user')
+
+
+class Tenant(Base, KeystoneBase):
+ __tablename__ = 'tenants'
+
+ id = Column(String(255), primary_key=True, unique=True)
+ desc = Column(String(255))
+ enabled = Column(Integer)
+ groups = relationship('Group', backref='tenants')
+
+
+class Group(Base, KeystoneBase):
+ __tablename__ = 'groups'
+
+ id = Column(String(255), primary_key=True, unique=True)
+ desc = Column(String(255))
+ tenant_id = Column(String(255), ForeignKey('tenants.id'))
+
+
+class Token(Base, KeystoneBase):
+ __tablename__ = 'token'
+
+ token_id = Column(String(255), primary_key=True, unique=True)
+ user_id = Column(String(255))
+ tenant_id = Column(String(255))
+ expires = Column(DateTime)
+
+
+def register_models(session, engine):
+ models = (User, Tenant, Group, Token, UserGroupAssociation,
+ UserTenantAssociation)
+ for model in models:
+ model.metadata.create_all(engine)
+ session.flush()
diff --git a/keystone/db/sqlalchemy/session.py b/keystone/db/sqlalchemy/session.py
new file mode 100644
index 00000000..10e727a7
--- /dev/null
+++ b/keystone/db/sqlalchemy/session.py
@@ -0,0 +1,64 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+"""
+Session Handling for SQLAlchemy backend
+"""
+import logging
+import os
+
+from sqlalchemy import create_engine
+from sqlalchemy import pool
+from sqlalchemy.orm import sessionmaker
+
+
+_ENGINE = None
+_MAKER = None
+
+
+def get_connection_string():
+ path = os.path.realpath(__file__)
+ dbpath = os.path.normpath(os.path.join(path,
+ os.pardir, # sqlalchemy
+ os.pardir, # db
+ os.pardir # keystone
+ ))
+ connection_string = "sqlite:///%s/keystone.db" % dbpath
+ logging.debug('SQL ALchemy connection string: %s', connection_string)
+ return connection_string
+
+
+def get_session(autocommit=True, expire_on_commit=False):
+ """Helper method to grab session"""
+ global _ENGINE
+ global _MAKER
+ if not _MAKER:
+ if not _ENGINE:
+ kwargs = {'pool_recycle': 30, 'echo': False}
+ kwargs['poolclass'] = pool.NullPool # for SQLite3
+ _ENGINE = create_engine(get_connection_string(), **kwargs)
+ _MAKER = (sessionmaker(bind=_ENGINE,
+ autocommit=autocommit,
+ expire_on_commit=expire_on_commit))
+ session = _MAKER()
+ return session
+
+
+def get_engine():
+ if not _ENGINE:
+ raise
+ return _ENGINE
diff --git a/keystone/identity.py b/keystone/identity.py
new file mode 100644
index 00000000..8bb6a4f7
--- /dev/null
+++ b/keystone/identity.py
@@ -0,0 +1,302 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""
+Service that stores identities and issues and manages tokens
+
+HEADERS
+-------
+HTTP_ is a standard http header
+HTTP_X is an extended http header
+
+> Coming in from initial call
+HTTP_X_AUTH_TOKEN : the client token being passed in
+HTTP_X_STORAGE_TOKEN: the client token being passed in (legacy Rackspace use)
+ to support cloud files
+> Used for communication between components
+www-authenticate : only used if this component is being used remotely
+HTTP_AUTHORIZATION : basic auth password used to validate the connection
+
+> What we add to the request for use by the OpenStack service
+HTTP_X_AUTHORIZATION: the client identity being passed in
+
+"""
+
+import functools
+import logging
+import os
+import sys
+import eventlet
+from eventlet import wsgi
+
+import bottle
+from bottle import request
+from bottle import response
+
+# If ../keystone/__init__.py exists, add ../ to Python search path, so that
+# it will override what happens to be installed in /usr/(local/)lib/python...
+POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+ os.pardir,
+ os.pardir))
+if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'keystone', '__init__.py')):
+ sys.path.insert(0, POSSIBLE_TOPDIR)
+
+import keystone.logic.service as serv
+import keystone.logic.types.auth as auth
+import keystone.logic.types.tenant as tenants
+import keystone.logic.types.fault as fault
+
+VERSION_STATUS = "ALPHA"
+VERSION_DATE = "2011-04-23T00:00:00Z"
+
+bottle.debug(True)
+
+service = serv.IDMService()
+
+##
+## Override error pages
+##
+
+
+@bottle.error(400)
+@bottle.error(401)
+@bottle.error(403)
+@bottle.error(404)
+@bottle.error(409)
+@bottle.error(415)
+@bottle.error(500)
+@bottle.error(503)
+def error_handler(err):
+ return err.output
+
+
+def is_xml_response():
+ if not "Accept" in request.header:
+ return False
+ return request.header["Accept"] == "application/xml"
+
+
+def get_app_root():
+ return os.path.abspath(os.path.dirname(__file__))
+
+
+def send_result(code, result):
+ content = None
+ response.content_type = None
+ if result:
+ if is_xml_response():
+ content = result.to_xml()
+ response.content_type = "application/xml"
+ else:
+ content = result.to_json()
+ response.content_type = "application/json"
+ response.status = code
+ if code > 399:
+ return bottle.abort(code, content)
+ return content
+
+
+def get_normalized_request_content(model):
+ """initialize a model from json/xml contents of request body"""
+
+ ctype = request.environ.get("CONTENT_TYPE")
+ if ctype == "application/xml":
+ ret = model.from_xml(request.body.read())
+ elif ctype == "application/json":
+ ret = model.from_json(request.body.read())
+ else:
+ raise fault.IDMFault("I don't understand the content type ", code=415)
+ return ret
+
+
+def get_auth_token():
+ auth_token = None
+ if "X-Auth-Token" in request.header:
+ auth_token = request.header["X-Auth-Token"]
+ return auth_token
+
+
+def wrap_error(func):
+ @functools.wraps(func)
+ def check_error(*args, **kwargs):
+ try:
+ return func(*args, **kwargs)
+ except Exception as err:
+ if isinstance(err, fault.IDMFault):
+ send_result(err.code, err)
+ else:
+ logging.exception(err)
+ send_result(500, fault.IDMFault("Unhandled error", str(err)))
+ return check_error
+
+
+@bottle.route('/v1.0', method='GET')
+@bottle.route('/v1.0/', method='GET')
+@wrap_error
+def get_version_info():
+ if is_xml_response():
+ resp_file = "content/version.xml"
+ response.content_type = "application/xml"
+ else:
+ resp_file = "content/version.json"
+ response.content_type = "application/json"
+ hostname = request.environ.get("SERVER_NAME")
+ port = request.environ.get("SERVER_PORT")
+ return bottle.template(resp_file, HOST=hostname, PORT=port,
+ VERSION_STATUS=VERSION_STATUS,
+ VERSION_DATE=VERSION_DATE)
+
+##
+## Version links:
+##
+
+
+@bottle.route('/v1.0/idmdevguide.pdf', method='GET')
+@wrap_error
+def get_pdf_contract():
+ return bottle.static_file("content/idmdevguide.pdf",
+ root=get_app_root(),
+ mimetype="application/pdf")
+
+
+@bottle.route('/v1.0/identity.wadl', method='GET')
+@wrap_error
+def get_wadl_contract():
+ return bottle.static_file("identity.wadl",
+ root=get_app_root(),
+ mimetype="application/vnd.sun.wadl+xml")
+
+
+@bottle.route('/v1.0/xsd/:xsd', method='GET')
+@wrap_error
+def get_xsd_contract(xsd):
+ return bottle.static_file("/xsd/" + xsd,
+ root=get_app_root(),
+ mimetype="application/xml")
+
+
+@bottle.route('/v1.0/xsd/atom/:xsd', method='GET')
+@wrap_error
+def get_xsd_atom_contract(xsd):
+ return bottle.static_file("/xsd/atom/" + xsd,
+ root=get_app_root(),
+ mimetype="application/xml")
+
+##
+## Token Operations
+##
+
+
+@bottle.route('/v1.0/token', method='POST')
+@wrap_error
+def authenticate():
+ creds = get_normalized_request_content(auth.PasswordCredentials)
+ return send_result(200, service.authenticate(creds))
+
+
+@bottle.route('/v1.0/token/:token_id', method='GET')
+@wrap_error
+def validate_token(token_id):
+ belongs_to = None
+ if "belongsTo" in request.GET:
+ belongs_to = request.GET["belongsTo"]
+ rval = service.validate_token(get_auth_token(), token_id, belongs_to)
+ return send_result(200, rval)
+
+
+@bottle.route('/v1.0/token/:token_id', method='DELETE')
+@wrap_error
+def delete_token(token_id):
+ return send_result(204,
+ service.revoke_token(get_auth_token(), token_id))
+
+##
+## Tenant Operations
+##
+
+@bottle.route('/v1.0/tenants', method='POST')
+@wrap_error
+def create_tenant():
+ tenant = get_normalized_request_content(tenants.Tenant)
+ return send_result(201,
+ service.create_tenant(get_auth_token(), tenant))
+
+
+@bottle.route('/v1.0/tenants', method='GET')
+@wrap_error
+def get_tenants():
+ marker = None
+ if "marker" in request.GET:
+ marker = request.GET["marker"]
+ limit = None
+ if "limit" in request.GET:
+ limit = request.GET["limit"]
+ tenants = service.get_tenants(get_auth_token(), marker, limit)
+ return send_result(200, tenants)
+
+
+@bottle.route('/v1.0/tenants/:tenant_id', method='GET')
+@wrap_error
+def get_tenant(tenant_id):
+ tenant = service.get_tenant(get_auth_token(), tenant_id)
+ return send_result(200, tenant)
+
+
+@bottle.route('/v1.0/tenants/:tenant_id', method='PUT')
+@wrap_error
+def update_tenant(tenant_id):
+ tenant = get_normalized_request_content(tenants.Tenant)
+ rval = service.update_tenant(get_auth_token(), tenant_id, tenant)
+ return send_result(200, rval)
+
+
+@bottle.route('/v1.0/tenants/:tenant_id', method='DELETE')
+@wrap_error
+def delete_tenant(tenant_id):
+ rval = service.delete_tenant(get_auth_token(), tenant_id)
+ return send_result(204, rval)
+
+
+##
+## Extensions
+##
+
+@bottle.route('/v1.0/extensions', method='GET')
+@wrap_error
+def get_extensions():
+ if is_xml_response():
+ resp_file = "content/extensions.xml"
+ mimetype = "application/xml"
+ else:
+ resp_file = "content/extensions.json"
+ mimetype = "application/json"
+ return bottle.static_file(resp_file,
+ root=get_app_root(),
+ mimetype=mimetype)
+
+
+@bottle.route('/v1.0/extensions/:ext_alias', method='GET')
+@wrap_error
+def get_extension(ext_alias):
+ #
+ # Todo: Define some extensions :-)
+ #
+ raise fault.ItemNotFoundFault("The extension is not found")
+
+
+if __name__ == "__main__":
+ wsgi.server(eventlet.listen(('', 8080)), bottle.default_app())
diff --git a/keystone/identity.wadl b/keystone/identity.wadl
new file mode 120000
index 00000000..4c93e9e5
--- /dev/null
+++ b/keystone/identity.wadl
@@ -0,0 +1 @@
+../docs/guide/src/docbkx/idm.wadl \ No newline at end of file
diff --git a/keystone/keystone.ini b/keystone/keystone.ini
new file mode 100644
index 00000000..626ca1ed
--- /dev/null
+++ b/keystone/keystone.ini
@@ -0,0 +1,16 @@
+;
+; This file not used
+;
+[DEFAULT]
+
+[composite:main]
+use = egg:Paste#urlmap
+/ = home
+/tenants = admin
+
+[app:home]
+use = egg:keystone
+
+[app:admin]
+use = egg:keystone
+
diff --git a/keystone/logic/__init__.py b/keystone/logic/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystone/logic/__init__.py
diff --git a/keystone/logic/service.py b/keystone/logic/service.py
new file mode 100644
index 00000000..cd0b5237
--- /dev/null
+++ b/keystone/logic/service.py
@@ -0,0 +1,216 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import datetime
+from datetime import timedelta
+
+import keystone.logic.types.auth as auth
+import keystone.logic.types.tenant as tenants
+import keystone.logic.types.atom as atom
+import keystone.logic.types.fault as fault
+
+import keystone.db.sqlalchemy.api as db_api
+import keystone.db.sqlalchemy.models as db_models
+
+import uuid
+
+
+class IDMService(object):
+ "This is the logical implemenation of the IDM service"
+
+ #
+ # Token Operations
+ #
+ def authenticate(self, credentials):
+ if not isinstance(credentials, auth.PasswordCredentials):
+ raise fault.BadRequestFault("Expecting Password Credentials!")
+
+ duser = db_api.user_get(credentials.username)
+ if duser == None:
+ raise fault.UnauthorizedFault("Unauthorized")
+ if not duser.enabled:
+ raise fault.UserDisabledFault("Your account has been disabled")
+ if duser.password != credentials.password:
+ raise fault.UnauthorizedFault("Unauthorized")
+
+ #
+ # Look for an existing token, or create one,
+ # TODO: Handle tenant/token search
+ #
+ dtoken = db_api.token_for_user(duser.id)
+ if not dtoken or dtoken.expires < datetime.now():
+ dtoken = db_models.Token()
+ dtoken.token_id = str(uuid.uuid4())
+ dtoken.user_id = duser.id
+ if not duser.tenants:
+ raise fault.IDMFault("Strange: user %s is not associated "
+ "with a tenant!" % duser.id)
+ dtoken.tenant_id = duser.tenants[0].tenant_id
+ dtoken.expires = datetime.now() + timedelta(days=1)
+
+ db_api.token_create(dtoken)
+
+ return self.__get_auth_data(dtoken, duser)
+
+ def validate_token(self, admin_token, token_id, belongs_to=None):
+ self.__validate_token(admin_token)
+
+ (dtoken, duser) = self.__get_dauth_data(token_id)
+
+ if not dtoken:
+ raise fault.ItemNotFoundFault("Token not found")
+
+ if dtoken.expires < datetime.now():
+ raise fault.ItemNotFoundFault("Token not found")
+
+ if belongs_to != None and dtoken.tenant_id != belongs_to:
+ raise fault.ItemNotFoundFault("Token not found")
+
+ return self.__get_auth_data(dtoken, duser)
+
+ def revoke_token(self, admin_token, token_id):
+ self.__validate_token(admin_token)
+
+ dtoken = db_api.token_get(token_id)
+ if not dtoken:
+ raise fault.ItemNotFoundFault("Token not found")
+
+ db_api.token_delete(token_id)
+
+ #
+ # Tenant Operations
+ #
+ def create_tenant(self, admin_token, tenant):
+ self.__validate_token(admin_token)
+
+ if not isinstance(tenant, tenants.Tenant):
+ raise fault.BadRequestFault("Expecting a Tenant")
+
+ if tenant.tenant_id == None:
+ raise fault.BadRequestFault("Expecting a unique Tenant Id")
+
+ if db_api.tenant_get(tenant.tenant_id) != None:
+ raise fault.TenantConflictFault(
+ "A tenant with that id already exists")
+
+ dtenant = db_models.Tenant()
+ dtenant.id = tenant.tenant_id
+ dtenant.desc = tenant.description
+ dtenant.enabled = tenant.enabled
+
+ db_api.tenant_create(dtenant)
+
+ return tenant
+
+ def get_tenants(self, admin_token, marker, limit):
+ self.__validate_token(admin_token)
+
+ ts = []
+ dtenants = db_api.tenant_get_all()
+ for dtenant in dtenants:
+ ts.append(tenants.Tenant(dtenant.id,
+ dtenant.desc, dtenant.enabled))
+
+ return tenants.Tenants(ts, [])
+
+ def get_tenant(self, admin_token, tenant_id):
+ self.__validate_token(admin_token)
+
+ dtenant = db_api.tenant_get(tenant_id)
+ if not dtenant:
+ raise fault.ItemNotFoundFault("The tenant could not be found")
+
+ return tenants.Tenant(dtenant.id, dtenant.desc, dtenant.enabled)
+
+ def update_tenant(self, admin_token, tenant_id, tenant):
+ self.__validate_token(admin_token)
+
+ if not isinstance(tenant, tenants.Tenant):
+ raise fault.BadRequestFault("Expecting a Tenant")
+ True
+
+ dtenant = db_api.tenant_get(tenant_id)
+ if dtenant == None:
+ raise fault.ItemNotFoundFault("The tenant cloud not be found")
+
+ values = {'desc': tenant.description, 'enabled': tenant.enabled}
+
+ db_api.tenant_update(tenant_id, values)
+
+ return tenants.Tenant(dtenant.id, tenant.description, tenant.enabled)
+
+ def delete_tenant(self, admin_token, tenant_id):
+ self.__validate_token(admin_token)
+
+ dtenant = db_api.tenant_get(tenant_id)
+ if dtenant == None:
+ raise fault.ItemNotFoundFault("The tenant cloud not be found")
+
+ if not db_api.tenant_is_empty(tenant_id):
+ raise fault.ForbiddenFault("You may not delete a tenant that "
+ "contains users or groups")
+
+ db_api.tenant_delete(dtenant.id)
+ return None
+
+ #
+ # Private Operations
+ #
+ def __get_dauth_data(self, token_id):
+ """return token and user object for a token_id"""
+
+ token = None
+ user = None
+ if token_id:
+ token = db_api.token_get(token_id)
+ if token:
+ user = db_api.user_get(token.user_id)
+ return (token, user)
+
+ def __get_auth_data(self, dtoken, duser):
+ """return AuthData object for a token/user pair"""
+
+ token = auth.Token(dtoken.expires, dtoken.token_id)
+
+ gs = []
+ for ug in duser.groups:
+ dgroup = db_api.group_get(ug.group_id)
+ gs.append(auth.Group(dgroup.id, dgroup.tenant_id))
+ groups = auth.Groups(gs, [])
+ if len(duser.tenants) == 0:
+ raise fault.IDMFault("Strange: user %s is not associated "
+ "with a tenant!" % duser.id)
+ user = auth.User(duser.id, duser.tenants[0].tenant_id, groups)
+ return auth.AuthData(token, user)
+
+ def __validate_token(self, token_id, admin=True):
+ if not token_id:
+ raise fault.UnauthorizedFault("Missing token")
+ (token, user) = self.__get_dauth_data(token_id)
+
+ if not token:
+ raise fault.UnauthorizedFault("Bad token, please reauthenticate")
+ if token.expires < datetime.now():
+ raise fault.UnauthorizedFault("Token expired, please renew")
+ if not user.enabled:
+ raise fault.UserDisabledFault("The user %s has been disabled!"
+ % user.id)
+ if admin:
+ for ug in user.groups:
+ if ug.group_id == "Admin":
+ return (token, user)
+ raise fault.ForbiddenFault("You are not authorized "
+ "to make this call")
+ return (token, user)
diff --git a/keystone/logic/types/__init__.py b/keystone/logic/types/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystone/logic/types/__init__.py
diff --git a/keystone/logic/types/atom.py b/keystone/logic/types/atom.py
new file mode 100644
index 00000000..04431ce2
--- /dev/null
+++ b/keystone/logic/types/atom.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class Link(object):
+ "An atom link"
+
+ def __init__(self, rel, href, link_type=None, hreflang=None, title=None):
+ self.rel = rel
+ self.href = href
+ self.link_type = link_type
+ self.hreflang = hreflang
+ self.title = title
diff --git a/keystone/logic/types/auth.py b/keystone/logic/types/auth.py
new file mode 100644
index 00000000..fc5be63a
--- /dev/null
+++ b/keystone/logic/types/auth.py
@@ -0,0 +1,157 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import datetime
+
+from abc import ABCMeta
+import json
+import keystone.logic.types.fault as fault
+from lxml import etree
+
+
+class PasswordCredentials(object):
+ "Credentials based on username, password, and (optional) tenant_id."
+
+ def __init__(self, username, password, tenant_id):
+ self.username = username
+ self.password = password
+ self.tenant_id = tenant_id
+
+ @staticmethod
+ def from_xml(xml_str):
+ try:
+ dom = etree.Element("root")
+ dom.append(etree.fromstring(xml_str))
+ root = dom.find("{http://docs.openstack.org/idm/api/v1.0}"
+ "passwordCredentials")
+ if root == None:
+ raise fault.BadRequestFault("Expecting passwordCredentials")
+ username = root.get("username")
+ if username == None:
+ raise fault.BadRequestFault("Expecting a username")
+ password = root.get("password")
+ if password == None:
+ raise fault.BadRequestFault("Expecting a password")
+ tenant_id = root.get("tenantId")
+ return PasswordCredentials(username, password, tenant_id)
+ except etree.LxmlError as e:
+ raise fault.BadRequestFault("Cannot parse password credentials",
+ str(e))
+
+ @staticmethod
+ def from_json(json_str):
+ try:
+ obj = json.loads(json_str)
+ if not "passwordCredentials" in obj:
+ raise fault.BadRequestFault("Expecting passwordCredentials")
+ cred = obj["passwordCredentials"]
+ if not "username" in cred:
+ raise fault.BadRequestFault("Expecting a username")
+ username = cred["username"]
+ if not "password" in cred:
+ raise fault.BadRequestFault("Expecting a password")
+ password = cred["password"]
+ if "tenantId" in cred:
+ tenant_id = cred["tenantId"]
+ else:
+ tenant_id = None
+ return PasswordCredentials(username, password, tenant_id)
+ except (ValueError, TypeError) as e:
+ raise fault.BadRequestFault("Cannot parse password credentials",
+ str(e))
+
+
+class Token(object):
+ "An auth token."
+
+ def __init__(self, expires, token_id):
+ self.expires = expires
+ self.token_id = token_id
+
+
+class Group(object):
+ "A group, optionally belonging to a tenant."
+
+ def __init__(self, group_id, tenant_id):
+ self.tenant_id = tenant_id
+ self.group_id = group_id
+
+
+class Groups(object):
+ "A collection of groups."
+
+ def __init__(self, values, links):
+ self.values = values
+ self.links = links
+
+
+class User(object):
+ "A user."
+
+ def __init__(self, username, tenant_id, groups):
+ self.username = username
+ self.tenant_id = tenant_id
+ self.groups = groups
+
+
+class AuthData(object):
+ "Authentation Infor returned upon successful login."
+
+ def __init__(self, token, user):
+ self.token = token
+ self.user = user
+
+ def to_xml(self):
+ dom = etree.Element("auth",
+ xmlns="http://docs.openstack.org/idm/api/v1.0")
+ token = etree.Element("token",
+ expires=self.token.expires.isoformat())
+ token.set("id", self.token.token_id)
+ user = etree.Element("user",
+ username=self.user.username,
+ tenantId=self.user.tenant_id)
+ groups = etree.Element("groups")
+ for group in self.user.groups.values:
+ g = etree.Element("group",
+ tenantId=group.tenant_id)
+ g.set("id", group.group_id)
+ groups.append(g)
+ user.append(groups)
+ dom.append(token)
+ dom.append(user)
+ return etree.tostring(dom)
+
+ def to_json(self):
+ token = {}
+ token["id"] = self.token.token_id
+ token["expires"] = self.token.expires.isoformat()
+ user = {}
+ user["username"] = self.user.username
+ user["tenantId"] = self.user.tenant_id
+ group = []
+ for g in self.user.groups.values:
+ grp = {}
+ grp["tenantId"] = g.tenant_id
+ grp["id"] = g.group_id
+ group.append(grp)
+ groups = {}
+ groups["group"] = group
+ user["groups"] = groups
+ auth = {}
+ auth["token"] = token
+ auth["user"] = user
+ ret = {}
+ ret["auth"] = auth
+ return json.dumps(ret)
diff --git a/keystone/logic/types/fault.py b/keystone/logic/types/fault.py
new file mode 100644
index 00000000..29bd2dc9
--- /dev/null
+++ b/keystone/logic/types/fault.py
@@ -0,0 +1,121 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+from lxml import etree
+
+
+class IDMFault(Exception):
+ "Base Exception type for all auth exceptions"
+
+ def __init__(self, msg, details=None, code=500):
+ self.args = (code, msg, details)
+ self.code = code
+ self.msg = msg
+ self.details = details
+ self.key = "idmFault"
+
+ @property
+ def message(self):
+ return self.msg
+
+ def to_xml(self):
+ dom = etree.Element(self.key,
+ xmlns="http://docs.openstack.org/idm/api/v1.0")
+ dom.set("code", str(self.code))
+ msg = etree.Element("message")
+ msg.text = self.msg
+ dom.append(msg)
+ if self.details != None:
+ desc = etree.Element("details")
+ desc.text = self.details
+ dom.append(desc)
+ return etree.tostring(dom)
+
+ def to_json(self):
+ fault = {}
+ fault["message"] = self.msg
+ fault["code"] = str(self.code)
+ if self.details != None:
+ fault["details"] = self.details
+ ret = {}
+ ret[self.key] = fault
+ return json.dumps(ret)
+
+
+class ServiceUnavailableFault(IDMFault):
+ "The auth service is unavailable"
+
+ def __init__(self, msg, details=None, code=503):
+ super(ServiceUnavailableFault, self).__init__(msg, details, code)
+ self.key = "serviceUnavailable"
+
+
+class BadRequestFault(IDMFault):
+ "Bad user request"
+
+ def __init__(self, msg, details=None, code=400):
+ super(BadRequestFault, self).__init__(msg, details, code)
+ self.key = "badRequest"
+
+
+class UnauthorizedFault(IDMFault):
+ "User is unauthorized"
+
+ def __init__(self, msg, details=None, code=401):
+ super(UnauthorizedFault, self).__init__(msg, details, code)
+ self.key = "unauthorized"
+
+
+class UserDisabledFault(IDMFault):
+ "The user is disabled"
+
+ def __init__(self, msg, details=None, code=403):
+ super(UserDisabledFault, self).__init__(msg, details, code)
+ self.key = "userDisabled"
+
+
+class ForbiddenFault(IDMFault):
+ "The user is forbidden"
+
+ def __init__(self, msg, details=None, code=403):
+ super(ForbiddenFault, self).__init__(msg, details, code)
+ self.key = "forbidden"
+
+
+class ItemNotFoundFault(IDMFault):
+ "The item is not found"
+
+ def __init__(self, msg, details=None, code=404):
+ super(ItemNotFoundFault, self).__init__(msg, details, code)
+ self.key = "itemNotFound"
+
+
+class TenantConflictFault(IDMFault):
+ "The tenant already exists?"
+
+ def __init__(self, msg, details=None, code=409):
+ super(TenantConflictFault, self).__init__(msg, details, code)
+ self.key = "tenantConflict"
+
+
+class OverlimitFault(IDMFault):
+ "A limit has been exceeded"
+
+ def __init__(self, msg, details=None, code=409, retry_at=None):
+ super(OverlimitFault, self).__init__(msg, details, code)
+ self.args = (code, msg, details, retry_at)
+ self.retry_at = retry_at
+ self.key = "overLimit"
diff --git a/keystone/logic/types/tenant.py b/keystone/logic/types/tenant.py
new file mode 100644
index 00000000..b161476b
--- /dev/null
+++ b/keystone/logic/types/tenant.py
@@ -0,0 +1,119 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import keystone.logic.types.fault as fault
+from lxml import etree
+import string
+
+
+class Tenant(object):
+ "Describes a tenant in the auth system"
+
+ def __init__(self, tenant_id, description, enabled):
+ self.tenant_id = tenant_id
+ self.description = description
+ self.enabled = enabled and True or False
+
+ @staticmethod
+ def from_xml(xml_str):
+ try:
+ dom = etree.Element("root")
+ dom.append(etree.fromstring(xml_str))
+ root = dom.find("{http://docs.openstack.org/idm/api/v1.0}tenant")
+ if root == None:
+ raise fault.BadRequestFault("Expecting Tenant")
+ tenant_id = root.get("id")
+ enabled = root.get("enabled")
+ if enabled == None or enabled == "true" or enabled == "yes":
+ set_enabled = True
+ elif enabled == "false" or enabled == "no":
+ set_enabled = False
+ else:
+ raise fault.BadRequestFault("Bad enabled attribute!")
+ desc = root.find("{http://docs.openstack.org/idm/api/v1.0}"
+ "description")
+ if desc == None:
+ raise fault.BadRequestFault("Expecting Tenant Description")
+ return Tenant(tenant_id, desc.text, set_enabled)
+ except etree.LxmlError as e:
+ raise fault.BadRequestFault("Cannot parse Tenant", str(e))
+
+ @staticmethod
+ def from_json(json_str):
+ try:
+ obj = json.loads(json_str)
+ if not "tenant" in obj:
+ raise fault.BadRequestFault("Expecting tenant")
+ tenant = obj["tenant"]
+ if not "id" in tenant:
+ tenant_id = None
+ else:
+ tenant_id = tenant["id"]
+ set_enabled = True
+ if "enabled" in tenant:
+ set_enabled = tenant["enabled"]
+ if not isinstance(set_enabled, bool):
+ raise fault.BadRequestFault("Bad enabled attribute!")
+ if not "description" in tenant:
+ raise fault.BadRequestFault("Expecting Tenant Description")
+ description = tenant["description"]
+ return Tenant(tenant_id, description, set_enabled)
+ except (ValueError, TypeError) as e:
+ raise fault.BadRequestFault("Cannot parse Tenant", str(e))
+
+ def to_dom(self):
+ dom = etree.Element("tenant",
+ xmlns="http://docs.openstack.org/idm/api/v1.0",
+ enabled=string.lower(str(self.enabled)))
+ if self.tenant_id:
+ dom.set("id", self.tenant_id)
+ desc = etree.Element("description")
+ desc.text = self.description
+ dom.append(desc)
+ return dom
+
+ def to_xml(self):
+ return etree.tostring(self.to_dom())
+
+ def to_dict(self):
+ tenant = {}
+ if self.tenant_id:
+ tenant["id"] = self.tenant_id
+ tenant["description"] = self.description
+ tenant["enabled"] = self.enabled
+ return {'tenant': tenant}
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+
+class Tenants(object):
+ "A collection of tenants."
+
+ def __init__(self, values, links):
+ self.values = values
+ self.links = links
+
+ def to_xml(self):
+ dom = etree.Element("tenants",
+ xmlns="http://docs.openstack.org/idm/api/v1.0")
+ for t in self.values:
+ dom.append(t.to_dom())
+ return etree.tostring(dom)
+
+ def to_json(self):
+ values = [t.to_dict()["tenant"] for t in self.values]
+ return json.dumps({"tenants": {"values": values}})
diff --git a/keystone/middleware/__init__.py b/keystone/middleware/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/keystone/middleware/__init__.py
diff --git a/keystone/middleware/remoteauth.py b/keystone/middleware/remoteauth.py
new file mode 100644
index 00000000..5919abd6
--- /dev/null
+++ b/keystone/middleware/remoteauth.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (c) 2010 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""
+Auth Middleware that handles auth for a service
+
+This module can be installed as a filter in front of your service to validate
+that requests are coming from a trusted component that has handled
+authenticating the call. If a call comes from an untrusted source, it will
+redirect it back to be properly authenticated. This is done by sending our a
+305 proxy redirect response with the URL for the auth service.
+
+The auth service settings are specified in the INI file (keystone.ini). The ini
+file is passed in as the WSGI config file when starting the service. For this
+proof of concept, the ini file is in echo/echo/echo.ini.
+
+In the current implementation use a basic auth password to verify that the
+request is coming from a valid auth component or service
+
+Refer to: http://wiki.openstack.org/openstack-authn
+
+
+HEADERS
+-------
+HTTP_ is a standard http header
+HTTP_X is an extended http header
+
+> Coming in from initial call
+HTTP_X_AUTH_TOKEN : the client token being passed in
+HTTP_X_STORAGE_TOKEN: the client token being passed in (legacy Rackspace use)
+ to support cloud files
+> Used for communication between components
+www-authenticate : only used if this component is being used remotely
+HTTP_AUTHORIZATION : basic auth password used to validate the connection
+
+> What we add to the request for use by the OpenStack service
+HTTP_X_AUTHORIZATION: the client identity being passed in
+
+
+"""
+
+from webob.exc import HTTPUseProxy, HTTPUnauthorized
+
+
+class RemoteAuth(object):
+
+ # app is the downstream WSGI component, usually the OpenStack service
+ #
+ # if app is not provided, the assumption is this filter is being run
+ # from a separate server.
+
+ def __init__(self, app, conf):
+ # app is the next app in WSGI chain - eventually the OpenStack service
+ self.app = app
+ self.conf = conf
+ # where to redirect untrusted requests to go and auth
+ self.auth_location = conf.get('auth_location')
+ # secret that will tell us a request is coming from a trusted auth
+ # component
+ self.remote_auth_pass = conf.get('remote_auth_pass')
+ print 'Starting Remote Auth middleware'
+
+ def __call__(self, env, start_response):
+ # Validate the request is trusted
+ # Authenticate the Auth component itself.
+ headers = [('www-authenticate', 'Basic realm="API Auth"')]
+ if 'HTTP_AUTHORIZATION' not in env:
+ return HTTPUnauthorized(headers=headers)(env, start_response)
+ else:
+ auth_type, encoded_creds = env['HTTP_AUTHORIZATION'].split(None, 1)
+ if encoded_creds != self.remote_auth_pass:
+ return HTTPUnauthorized(headers=headers)(env, start_response)
+
+ # Make sure that the user has been authenticated by the Auth Service
+ if 'HTTP_X_AUTHORIZATION' not in env:
+ return HTTPUseProxy(location=self.auth_location)(env,
+ start_response)
+
+ return self.app(env, start_response)
+
+
+def filter_factory(global_conf, **local_conf):
+ """Returns a WSGI filter app for use with paste.deploy."""
+ conf = global_conf.copy()
+ conf.update(local_conf)
+
+ def auth_filter(app):
+ return RemoteAuth(app, conf)
+ return auth_filter
diff --git a/keystone/samples b/keystone/samples
new file mode 120000
index 00000000..d6685fde
--- /dev/null
+++ b/keystone/samples
@@ -0,0 +1 @@
+../docs/guide/src/docbkx/samples \ No newline at end of file
diff --git a/keystone/xsd b/keystone/xsd
new file mode 120000
index 00000000..d63d56ef
--- /dev/null
+++ b/keystone/xsd
@@ -0,0 +1 @@
+../docs/guide/src/docbkx/xsd \ No newline at end of file
diff --git a/management/delgroup.py b/management/delgroup.py
new file mode 100644
index 00000000..9ef2d733
--- /dev/null
+++ b/management/delgroup.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog group_id"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 1:
+ parser.error("Incorrect number of arguments")
+ else:
+ index = args[0]
+
+ try:
+ o = db_api.group_get(index)
+ if o == None:
+ raise IndexError("Group %s not found", index)
+ else:
+ db_api.group_delete(index)
+ print 'Group', index, 'deleted.'
+
+ except Exception, e:
+ print 'Error deleting group', index, str(e)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/management/getgroup.py b/management/getgroup.py
new file mode 100644
index 00000000..38a6b8f2
--- /dev/null
+++ b/management/getgroup.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog group_id"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 1:
+ parser.error("Incorrect number of arguments")
+ else:
+ index = args[0]
+
+ try:
+ o = db_api.group_get(index)
+ if o == None:
+ raise IndexError("Group %s not found", index)
+
+ print o.id, o.desc
+ except Exception, e:
+ print 'Error getting group', index, str(e)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/management/getgroups.py b/management/getgroups.py
new file mode 100644
index 00000000..501d948f
--- /dev/null
+++ b/management/getgroups.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog "
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 0:
+ parser.error("Incorrect number of arguments")
+ else:
+ try:
+ u = db_api.group_get_all()
+ if u == None:
+ raise IndexError("Groups not found")
+ for row in u:
+ print row.id
+ except Exception, e:
+ print 'Error getting groups:', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/getgroupusers.py b/management/getgroupusers.py
new file mode 100644
index 00000000..0fc7d231
--- /dev/null
+++ b/management/getgroupusers.py
@@ -0,0 +1,39 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog group_id"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 1:
+ parser.error("Incorrect number of arguments")
+ else:
+ group_id = args[0]
+
+ try:
+ g = db_api.group_users(group_id)
+ if g == None:
+ raise IndexError("Group users not found")
+ for row in g:
+ print row
+ except Exception, e:
+ print 'Error getting group users for group', group_id, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/getuser.py b/management/getuser.py
new file mode 100644
index 00000000..036f10f4
--- /dev/null
+++ b/management/getuser.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog username"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 1:
+ parser.error("Incorrect number of arguments")
+ else:
+ username = args[0]
+ try:
+ u = db_api.user_get(username)
+ if u == None:
+ raise IndexError("User not found")
+ print u.id, u.email, u.enabled
+ except Exception, e:
+ print 'Error finding user', username, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/getusergroups.py b/management/getusergroups.py
new file mode 100644
index 00000000..3fd7d3f9
--- /dev/null
+++ b/management/getusergroups.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog user_id"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 1:
+ parser.error("Incorrect number of arguments")
+ else:
+ username = args[0]
+ try:
+ g = db_api.user_groups(username)
+ if g == None:
+ raise IndexError("User groups not found")
+ for row in g:
+ print row
+ except Exception, e:
+ print 'Error getting user groups for user', user_id, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/getusers.py b/management/getusers.py
new file mode 100644
index 00000000..18a5de56
--- /dev/null
+++ b/management/getusers.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog tenant_id"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 1:
+ parser.error("Incorrect number of arguments")
+ else:
+ tenant_id = args[0]
+ try:
+ u = db_api.user_get_by_tenant(tenant_id)
+ if u == None:
+ raise IndexError("Users not found")
+ for row in u:
+ print row
+ except Exception, e:
+ print 'Error getting users for tenant', tenant_id, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/groupadd.py b/management/groupadd.py
new file mode 100644
index 00000000..adec58dc
--- /dev/null
+++ b/management/groupadd.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+from keystone.db.sqlalchemy import models
+
+
+def main():
+ usage = "usage: %prog group_id group_desc"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("Incorrect number of arguments")
+ else:
+ group_id = args[0]
+ group_desc = args[1]
+ try:
+ g = models.Group()
+ g.id = group_id
+ g.desc = group_desc
+ db_api.group_create(g)
+ print 'Group', g.id, 'created.'
+ except Exception, e:
+ print 'Error creating group', group_id, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/setuserlock.py b/management/setuserlock.py
new file mode 100644
index 00000000..622d876b
--- /dev/null
+++ b/management/setuserlock.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog username enabled"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("Incorrect number of arguments")
+ else:
+ username = args[0]
+ enabled = args[1].capitalize().strip()
+
+ if enabled == 'True' or enabled == '1':
+ enabled = 1
+ elif enabled == 'False' or enabled == '0':
+ enabled = 0
+ else:
+ parser.error("Incorrect arguments value")
+
+ try:
+ u = db_api.user_get(username)
+ if u == None:
+ raise IndexError("User not found")
+ else:
+ values = {'enabled': enabled}
+ db_api.user_update(username, values)
+ print 'User', u.id, 'updated. Enabled =', enabled
+ except Exception, e:
+ print 'Error updating user', username, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/setuserpswd.py b/management/setuserpswd.py
new file mode 100644
index 00000000..6283bf73
--- /dev/null
+++ b/management/setuserpswd.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog username password"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("Incorrect number of arguments")
+ else:
+ username = args[0]
+ password = args[1]
+ try:
+ u = db_api.user_get(username)
+ if u == None:
+ raise IndexError("User not found")
+ else:
+ values = {'password': password}
+ db_api.user_update(username, values)
+ print 'User', u.id, 'updated.'
+ except Exception, e:
+ print 'Error updating user', username, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/updategroup.py b/management/updategroup.py
new file mode 100644
index 00000000..598d38ef
--- /dev/null
+++ b/management/updategroup.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+from keystone.db.sqlalchemy import models
+
+
+def main():
+ usage = "usage: %prog group_id group_desc"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("Incorrect number of arguments")
+ else:
+ group = args[0]
+ desc = args[1]
+ try:
+ g = db_api.group_get(group)
+ if g == None:
+ raise IndexError("Group not found")
+ else:
+ values = {'desc': desc}
+ db_api.group_update(group, values)
+ print 'Group', g.id, 'updated.'
+ except Exception, e:
+ print 'Error updating user', group, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/useradd.py b/management/useradd.py
new file mode 100644
index 00000000..fc3fc8b6
--- /dev/null
+++ b/management/useradd.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+from keystone.db.sqlalchemy import models
+
+
+def main():
+ usage = "usage: %prog username password"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("Incorrect number of arguments")
+ else:
+ username = args[0]
+ password = args[1]
+ try:
+ u = models.User()
+ u.id = username
+ u.email = username
+ u.password = password
+ u.enabled = True
+ db_api.user_create(u)
+ print 'User', u.id, 'created.'
+ except Exception, e:
+ print 'Error creating user', username, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/userdel.py b/management/userdel.py
new file mode 100644
index 00000000..928dfc55
--- /dev/null
+++ b/management/userdel.py
@@ -0,0 +1,39 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog username"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 1:
+ parser.error("Incorrect number of arguments")
+ else:
+ username = args[0]
+ try:
+ u = db_api.user_get(username)
+ if u == None:
+ raise IndexError("User not found")
+ else:
+ db_api.user_delete(username)
+ print 'User', username, 'deleted.'
+ except Exception, e:
+ print 'Error deleting user', username, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/management/userupdate.py b/management/userupdate.py
new file mode 100644
index 00000000..89174d04
--- /dev/null
+++ b/management/userupdate.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import optparse
+import keystone.db.sqlalchemy.api as db_api
+
+
+def main():
+ usage = "usage: %prog username email"
+ parser = optparse.OptionParser(usage)
+ options, args = parser.parse_args()
+ if len(args) != 2:
+ parser.error("Incorrect number of arguments")
+ else:
+ username = args[0]
+ email = args[1]
+ try:
+ u = db_api.user_get(username)
+ if u == None:
+ raise IndexError("User not found")
+ else:
+ values = {'email': email}
+ db_api.user_update(username, values)
+ print 'User', u.id, 'updated.'
+ except Exception, e:
+ print 'Error updating user', username, ':', str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/pip-requires b/pip-requires
new file mode 100644
index 00000000..1ef8484b
--- /dev/null
+++ b/pip-requires
@@ -0,0 +1,9 @@
+bottle
+eventlet
+lxml
+paste
+pastedeploy
+pastescript
+pysqlite
+sqlalchemy
+webob
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..83ead0ca
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+# Copyright (c) 2010-2011 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from setuptools import setup, find_packages
+
+version = '1.0'
+
+setup(
+ name='keystone',
+ version=version,
+ description="Authentication service - proposed for OpenStack",
+ license='Apache License (2.0)',
+ classifiers=["Programming Language :: Python"],
+ keywords='identity auth authentication openstack',
+ author='OpenStack, LLC.',
+ author_email='openstack@lists.launchpad.net',
+ url='http://www.openstack.org',
+ include_package_data=True,
+ packages=find_packages(exclude=['test', 'bin']),
+ zip_safe=False,
+ install_requires=['setuptools'],
+ entry_points={
+ 'paste.app_factory': ['main=identity:app_factory'],
+ 'paste.filter_factory': [
+ 'papiauth=keystone:papiauth_factory',
+ 'tokenauth=keystone:tokenauth_factory',
+ ],
+ },
+ )
diff --git a/test b/test
deleted file mode 100644
index 257cc564..00000000
--- a/test
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/test/EchoSOAPUI.xml b/test/EchoSOAPUI.xml
new file mode 100644
index 00000000..8a74eee0
--- /dev/null
+++ b/test/EchoSOAPUI.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<con:soapui-project name="Echo" resourceRoot="" soapui-version="3.6.1" abortOnError="false" runType="SEQUENTIAL" xmlns:con="http://eviware.com/soapui/config"><con:settings/><con:interface xsi:type="con:RestService" wadlVersion="http://wadl.dev.java.net/2009/02" name="Echo" type="rest" basePath="" definitionUrl="file:/Users/jorgew/projects/keystone/echo/echo.wadl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:settings/><con:definitionCache/><con:endpoints><con:endpoint>http://localhost:8090</con:endpoint></con:endpoints><con:resource name="/" path="/"><con:settings/><con:parameters><con:parameter required="true"><con:name>X-Auth-Token</con:name><con:value xsi:nil="true"/><con:style>HEADER</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - get" method="GET"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="*/*"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8090</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method><con:method name="PUT - put" method="PUT"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="POST - post" method="POST"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="DELETE - delete" method="DELETE"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="HEAD - head" method="HEAD"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="OPTIONS - options" method="OPTIONS"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method></con:resource></con:interface><con:properties/><con:wssContainer/><con:databaseConnectionContainer/><con:reporting><con:xmlTemplates/><con:parameters/></con:reporting></con:soapui-project> \ No newline at end of file
diff --git a/test/IdentitySOAPUI.xml b/test/IdentitySOAPUI.xml
new file mode 100644
index 00000000..3ed02484
--- /dev/null
+++ b/test/IdentitySOAPUI.xml
@@ -0,0 +1,1355 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<con:soapui-project name="Keystone" resourceRoot="" soapui-version="3.6.1" abortOnError="false" runType="SEQUENTIAL" xmlns:con="http://eviware.com/soapui/config"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.actions.iface.tools.soapui.TestRunnerAction@values-local"><![CDATA[<xml-fragment xmlns:con="http://eviware.com/soapui/config">
+ <con:entry key="Global Properties" value=""/>
+ <con:entry key="TestSuite" value="Keystone Tests"/>
+ <con:entry key="Report to Generate" value=""/>
+ <con:entry key="Password" value=""/>
+ <con:entry key="soapui-setings.xml Password" value=""/>
+ <con:entry key="TestRunner Path" value=""/>
+ <con:entry key="Tool Args" value=""/>
+ <con:entry key="Ignore Errors" value="false"/>
+ <con:entry key="Host:Port" value=""/>
+ <con:entry key="WSS Password Type" value=""/>
+ <con:entry key="Save Project" value="false"/>
+ <con:entry key="Enable UI" value="true"/>
+ <con:entry key="System Properties" value=""/>
+ <con:entry key="Domain" value=""/>
+ <con:entry key="Coverage Report" value="false"/>
+ <con:entry key="Export JUnit Results" value="false"/>
+ <con:entry key="Open Report" value="false"/>
+ <con:entry key="Project Properties" value=""/>
+ <con:entry key="Project Password" value=""/>
+ <con:entry key="Export All" value="false"/>
+ <con:entry key="Report Format(s)" value=""/>
+ <con:entry key="TestCase" value="&lt;all>"/>
+ <con:entry key="Print Report" value="false"/>
+ <con:entry key="Username" value=""/>
+ <con:entry key="Root Folder" value=""/>
+ <con:entry key="Save After" value="false"/>
+ <con:entry key="Add Settings" value="false"/>
+ <con:entry key="Endpoint" value=""/>
+</xml-fragment>]]></con:setting></con:settings><con:interface xsi:type="con:RestService" wadlVersion="http://wadl.dev.java.net/2009/02" name="Keystone" type="rest" basePath="" definitionUrl="file:/Users/jorgew/projects/keystone/keystone/identity.wadl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:settings/><con:definitionCache type="TEXT" rootPart="file:/Users/jorgew/projects/keystone/keystone/identity.wadl"><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/identity.wadl</con:url><con:content><![CDATA[<application xsi:schemaLocation="http://docs.openstack.org/idm/api/v1.0 xsd/api.xsd http://docs.openstack.org/common/api/v1.0 xsd/api-common.xsd " xmlns="http://wadl.dev.java.net/2009/02" xmlns:idm="http://docs.openstack.org/idm/api/v1.0" xmlns:capi="http://docs.openstack.org/common/api/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <grammars>
+ <include href="xsd/api.xsd"/>
+ <include href="xsd/api-common.xsd"/>
+ </grammars>
+ <!--We should use SSL in production-->
+ <resources base="http://localhost:8080">
+ <resource id="version" path="v1.0">
+ <method href="#getVersionInfo"/>
+ <resource id="extensions" path="extensions">
+ <method href="#getExtensions"/>
+ <resource id="alias" path="{alias}">
+ <param name="alias" style="template" type="xsd:string"/>
+ <method href="#getExtension"/>
+ </resource>
+ </resource>
+ <resource id="token" path="token">
+ <method href="#authenticate"/>
+ <resource id="tokenId" path="{tokenId}">
+ <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/>
+ <param name="tokenId" style="template" type="xsd:string"/>
+ <method href="#validateToken"/>
+ <method href="#revokeToken"/>
+ </resource>
+ </resource>
+ <resource id="tenants" path="tenants">
+ <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/>
+ <method href="#getTenants"/>
+ <method href="#createTenant"/>
+ <resource id="tenantId" path="{tenantId}">
+ <param name="tenantId" style="template" type="xsd:string"/>
+ <method href="#getTenant"/>
+ <method href="#updateTenant"/>
+ <method href="#deleteTenant"/>
+ </resource>
+ </resource>
+ </resource>
+ </resources>
+ <!--Extensions-->
+ <method name="GET" id="getExtensions">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="capi:extensions"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="GET" id="getExtension">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="capi:extension"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <!--Version Info-->
+ <method name="GET" id="getVersionInfo">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="capi:version"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <!--Token Operations-->
+ <method name="POST" id="authenticate">
+ <request>
+ <representation mediaType="application/xml" element="idm:passwordCredentials"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:auth"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:userDisabled"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="401 403 400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="GET" id="validateToken">
+ <request>
+ <param name="belongsTo" style="query" required="false" type="xsd:string"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:auth"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ <representation mediaType="application/xml" element="idm:userDisabled"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="DELETE" id="revokeToken">
+ <response status="204"/>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <!--Tenant Operations-->
+ <method name="GET" id="getTenants">
+ <request>
+ <param name="marker" style="query" required="false" type="xsd:string"/>
+ <param name="limit" style="query" required="false" type="xsd:int"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:tenants"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="POST" id="createTenant">
+ <request>
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="201">
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="409">
+ <representation mediaType="application/xml" element="idm:tenantConflict"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="401 403 400 409 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="GET" id="getTenant">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="PUT" id="updateTenant">
+ <request>
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="200">
+ <representation mediaType="application/xml" element="idm:tenant"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="401 403 404 400 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="DELETE" id="deleteTenant">
+ <response status="204"/>
+ <response status="401">
+ <representation mediaType="application/xml" element="idm:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="idm:forbidden"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="idm:badRequest"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="idm:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="idm:idmFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="idm:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+</application>]]></con:content><con:type>http://wadl.dev.java.net/2009/02</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/api.xsd</con:url><con:content>&lt;schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/idm/api/v1.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:idm="http://docs.openstack.org/idm/api/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ &lt;include schemaLocation="token.xsd"/>
+ &lt;include schemaLocation="tenant.xsd"/>
+ &lt;include schemaLocation="fault.xsd"/>
+&lt;/schema></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/token.xsd</con:url><con:content><![CDATA[<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/idm/api/v1.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:idm="http://docs.openstack.org/idm/api/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <!--Elements-->
+ <element name="passwordCredentials" type="idm:PasswordCredentials"/>
+ <element name="auth" type="idm:AuthData"/>
+ <!--Complex Types-->
+ <complexType name="Credentials" abstract="true"/>
+ <complexType name="PasswordCredentials">
+ <complexContent>
+ <extension base="idm:Credentials">
+ <sequence>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="password" type="xsd:string" use="required"/>
+ <attribute name="username" type="xsd:string" use="required"/>
+ <attribute name="tenantId" type="xsd:string" use="optional"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </extension>
+ </complexContent>
+ </complexType>
+ <complexType name="AuthData">
+ <sequence>
+ <element name="token" type="idm:Token"/>
+ <element name="user" type="idm:User"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="Token">
+ <sequence>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="expires" type="xsd:dateTime" use="required"/>
+ <attribute name="id" type="xsd:string" use="required"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="User">
+ <sequence>
+ <element name="groups" type="idm:Groups"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="tenantId" type="xsd:string"/>
+ <attribute name="username" type="xsd:string"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="Groups">
+ <sequence>
+ <element name="group" type="idm:Group" maxOccurs="1000"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="Group">
+ <attribute name="id" type="xsd:string" use="required"/>
+ <attribute name="tenantId" type="xsd:string" use="optional"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/tenant.xsd</con:url><con:content><![CDATA[<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/idm/api/v1.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:idm="http://docs.openstack.org/idm/api/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" xmlns:atom="http://www.w3.org/2005/Atom">
+ <!--Import ATOM specific schema definitions-->
+ <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" schemaLocation="./atom/atom.xsd"/>
+ <!--Elements-->
+ <element name="tenant" type="idm:Tenant"/>
+ <element name="tenants" type="idm:Tenants"/>
+ <!--Complex Types-->
+ <complexType name="Tenants">
+ <sequence>
+ <element name="tenant" type="idm:Tenant" maxOccurs="1000"/>
+ <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="Tenant">
+ <sequence>
+ <element name="description" type="xsd:string"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="enabled" type="xsd:boolean" use="optional" default="true"/>
+ <attribute name="id" type="xsd:string" use="optional"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/atom/atom.xsd</con:url><con:content><![CDATA[<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://www.w3.org/2005/Atom" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>
+ <xs:simpleType name="relation">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="alternate"/>
+ <xs:enumeration value="appendix"/>
+ <xs:enumeration value="archives"/>
+ <xs:enumeration value="author"/>
+ <xs:enumeration value="bookmark"/>
+ <xs:enumeration value="chapter"/>
+ <xs:enumeration value="contents"/>
+ <xs:enumeration value="copyright"/>
+ <xs:enumeration value="current"/>
+ <xs:enumeration value="describedby"/>
+ <xs:enumeration value="edit"/>
+ <xs:enumeration value="edit-media"/>
+ <xs:enumeration value="first"/>
+ <xs:enumeration value="glossary"/>
+ <xs:enumeration value="help"/>
+ <xs:enumeration value="hub"/>
+ <xs:enumeration value="icon"/>
+ <xs:enumeration value="index"/>
+ <xs:enumeration value="last"/>
+ <xs:enumeration value="latest-version"/>
+ <xs:enumeration value="license"/>
+ <xs:enumeration value="monitor"/>
+ <xs:enumeration value="monitor-group"/>
+ <xs:enumeration value="next"/>
+ <xs:enumeration value="next-arvhice"/>
+ <xs:enumeration value="nofollow"/>
+ <xs:enumeration value="payment"/>
+ <xs:enumeration value="predecessor-version"/>
+ <xs:enumeration value="prefetch"/>
+ <xs:enumeration value="prev"/>
+ <xs:enumeration value="previous"/>
+ <xs:enumeration value="prev-archive"/>
+ <xs:enumeration value="replies"/>
+ <xs:enumeration value="search"/>
+ <xs:enumeration value="section"/>
+ <xs:enumeration value="self"/>
+ <xs:enumeration value="service"/>
+ <xs:enumeration value="start"/>
+ <xs:enumeration value="stylesheet"/>
+ <xs:enumeration value="subsection"/>
+ <xs:enumeration value="successor-version"/>
+ <xs:enumeration value="up"/>
+ <xs:enumeration value="version-history"/>
+ <xs:enumeration value="via"/>
+ <xs:enumeration value="working-copy"/>
+ <xs:enumeration value="working-copy-of"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:element name="link" type="atom:link"/>
+ <xs:complexType name="link">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ See section 3.4 of the ATOM RFC
+ <html:a href="http://tools.ietf.org/html/rfc4287">RFC4287</html:a>
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="rel" use="required" type="atom:relation">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" use="optional" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="href" use="required" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="hreflang" use="optional" type="xs:NMTOKEN">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="title" use="optional" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>TODO</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute ref="xml:base"/>
+ <xs:attribute ref="xml:lang"/>
+ </xs:complexType>
+</xs:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/atom/xml.xsd</con:url><con:content><![CDATA[<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xml:lang="en" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/1999/xhtml">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h1>About the XML namespace</h1>
+ <div class="bodytext">
+ <p>This schema document describes the XML namespace, in a form
+ suitable for import by other schema documents.</p>
+ <p>
+ See
+ <a href="http://www.w3.org/XML/1998/namespace.html">http://www.w3.org/XML/1998/namespace.html</a>
+ and
+ <a href="http://www.w3.org/TR/REC-xml">http://www.w3.org/TR/REC-xml</a>
+ for information
+ about this namespace.
+ </p>
+ <p>Note that local names in this namespace are intended to be
+ defined only by the World Wide Web Consortium or its subgroups.
+ The names currently defined in this namespace are listed below.
+ They should not be used with conflicting semantics by any Working
+ Group, specification, or document instance.</p>
+ <p>
+ See further below in this document for more information about
+ <a href="#usage">how to refer to this schema document from your own
+ XSD schema documents</a>
+ and about
+ <a href="#nsversioning">the
+ namespace-versioning policy governing this schema document</a>
+ .
+ </p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="lang">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h3>lang (as an attribute name)</h3>
+ <p>denotes an attribute whose value
+ is a language code for the natural language of the content of
+ any element; its value is inherited. This name is reserved
+ by virtue of its definition in the XML specification.</p>
+ </div>
+ <div>
+ <h4>Notes</h4>
+ <p>Attempting to install the relevant ISO 2- and 3-letter
+ codes as the enumerated possible values is probably never
+ going to be a realistic possibility.</p>
+ <p>
+ See BCP 47 at
+ <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+ and the IANA language subtag registry at
+ <a href="http://www.iana.org/assignments/language-subtag-registry">http://www.iana.org/assignments/language-subtag-registry</a>
+ for further information.
+ </p>
+ <p>The union allows for the 'un-declaration' of xml:lang with
+ the empty string.</p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:union memberTypes="xs:language">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value=""/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="space">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h3>space (as an attribute name)</h3>
+ <p>denotes an attribute whose
+ value is a keyword indicating what whitespace processing
+ discipline is intended for the content of the element; its
+ value is inherited. This name is reserved by virtue of its
+ definition in the XML specification.</p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:NCName">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="base" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h3>base (as an attribute name)</h3>
+ <p>denotes an attribute whose value
+ provides a URI to be used as the base for interpreting any
+ relative URIs in the scope of the element on which it
+ appears; its value is inherited. This name is reserved
+ by virtue of its definition in the XML Base specification.</p>
+ <p>
+ See
+ <a href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="id" type="xs:ID">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h3>id (as an attribute name)</h3>
+ <p>denotes an attribute whose value
+ should be interpreted as if declared to be of type ID.
+ This name is reserved by virtue of its definition in the
+ xml:id specification.</p>
+ <p>
+ See
+ <a href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup name="specialAttrs">
+ <xs:attribute ref="xml:base"/>
+ <xs:attribute ref="xml:lang"/>
+ <xs:attribute ref="xml:space"/>
+ <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h3>Father (in any context at all)</h3>
+ <div class="bodytext">
+ <p>denotes Jon Bosak, the chair of
+ the original XML Working Group. This name is reserved by
+ the following decision of the W3C XML Plenary and
+ XML Coordination groups:</p>
+ <blockquote>
+ <p>In appreciation for his vision, leadership and
+ dedication the W3C XML Plenary on this 10th day of
+ February, 2000, reserves for Jon Bosak in perpetuity
+ the XML name "xml:Father".</p>
+ </blockquote>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:annotation>
+ <xs:documentation>
+ <div xml:id="usage" id="usage">
+ <h2>
+ <a name="usage">About this schema document</a>
+ </h2>
+ <div class="bodytext">
+ <p>
+ This schema defines attributes and an attribute group suitable
+ for use by schemas wishing to allow
+ <code>xml:base</code>
+ ,
+ <code>xml:lang</code>
+ ,
+ <code>xml:space</code>
+ or
+ <code>xml:id</code>
+ attributes on elements they define.
+ </p>
+ <p>To enable this, such a schema must import this schema for
+ the XML namespace, e.g. as follows:</p>
+ <pre>&lt;schema . . .>
+ . . .
+ &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/></pre>
+ <p>or</p>
+ <pre>&lt;import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2009/01/xml.xsd"/></pre>
+ <p>Subsequently, qualified reference to any of the attributes or the
+ group defined below will have the desired effect, e.g.</p>
+ <pre>&lt;type . . .>
+ . . .
+ &lt;attributeGroup ref="xml:specialAttrs"/></pre>
+ <p>will define a type which will schema-validate an instance element
+ with any of those attributes.</p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:annotation>
+ <xs:documentation>
+ <div id="nsversioning" xml:id="nsversioning">
+ <h2>
+ <a name="nsversioning">Versioning policy for this schema document</a>
+ </h2>
+ <div class="bodytext">
+ <p>
+ In keeping with the XML Schema WG's standard versioning
+ policy, this schema document will persist at
+ <a href="http://www.w3.org/2009/01/xml.xsd">http://www.w3.org/2009/01/xml.xsd</a>
+ .
+ </p>
+ <p>
+ At the date of issue it can also be found at
+ <a href="http://www.w3.org/2001/xml.xsd">http://www.w3.org/2001/xml.xsd</a>
+ .
+ </p>
+ <p>
+ The schema document at that URI may however change in the future,
+ in order to remain compatible with the latest version of XML
+ Schema itself, or with the XML namespace itself. In other words,
+ if the XML Schema or XML namespaces change, the version of this
+ document at
+ <a href="http://www.w3.org/2001/xml.xsd">http://www.w3.org/2001/xml.xsd</a>
+ will change accordingly; the version at
+ <a href="http://www.w3.org/2009/01/xml.xsd">http://www.w3.org/2009/01/xml.xsd</a>
+ will not change.
+ </p>
+ <p>Previous dated (and unchanging) versions of this schema
+ document are at:</p>
+ <ul>
+ <li>
+ <a href="http://www.w3.org/2009/01/xml.xsd">http://www.w3.org/2009/01/xml.xsd</a>
+ </li>
+ <li>
+ <a href="http://www.w3.org/2007/08/xml.xsd">http://www.w3.org/2007/08/xml.xsd</a>
+ </li>
+ <li>
+ <a href="http://www.w3.org/2004/10/xml.xsd">http://www.w3.org/2004/10/xml.xsd</a>
+ </li>
+ <li>
+ <a href="http://www.w3.org/2001/03/xml.xsd">http://www.w3.org/2001/03/xml.xsd</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+</xs:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/fault.xsd</con:url><con:content><![CDATA[<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/idm/api/v1.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:idm="http://docs.openstack.org/idm/api/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <!--Fault Elements-->
+ <element name="idmFault" type="idm:IDMFault"/>
+ <element name="serviceUnavailable" type="idm:ServiceUnavailableFault"/>
+ <element name="badRequest" type="idm:BadRequestFault"/>
+ <element name="unauthorized" type="idm:UnauthorizedFault"/>
+ <element name="overLimit" type="idm:OverLimitFault"/>
+ <element name="userDisabled" type="idm:UserDisabledFault"/>
+ <element name="forbidden" type="idm:ForbiddenFault"/>
+ <element name="itemNotFound" type="idm:ItemNotFoundFault"/>
+ <element name="tenantConflict" type="idm:TenantConflictFault"/>
+ <!--Fault Types-->
+ <complexType name="IDMFault">
+ <sequence>
+ <element name="message" type="xsd:string">
+ <annotation>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <p>A human readable message that is appropriate for display
+ to the end user.</p>
+ </xsd:documentation>
+ </annotation>
+ </element>
+ <element name="details" type="xsd:string" minOccurs="0">
+ <annotation>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <p>The optional &lt;details> element may contain useful
+ information for tracking down errors (e.g a stack
+ trace). This information may or may not be appropriate
+ for display to an end user.</p>
+ </xsd:documentation>
+ </annotation>
+ </element>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="code" type="xsd:int" use="required">
+ <annotation>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <p>The HTTP status code associated with the current fault.</p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="ServiceUnavailableFault">
+ <complexContent>
+ <extension base="idm:IDMFault"></extension>
+ </complexContent>
+ </complexType>
+ <complexType name="BadRequestFault">
+ <complexContent>
+ <extension base="idm:IDMFault"></extension>
+ </complexContent>
+ </complexType>
+ <complexType name="UnauthorizedFault">
+ <complexContent>
+ <extension base="idm:IDMFault"></extension>
+ </complexContent>
+ </complexType>
+ <complexType name="UserDisabledFault">
+ <complexContent>
+ <extension base="idm:IDMFault"></extension>
+ </complexContent>
+ </complexType>
+ <complexType name="ForbiddenFault">
+ <complexContent>
+ <extension base="idm:IDMFault"></extension>
+ </complexContent>
+ </complexType>
+ <complexType name="ItemNotFoundFault">
+ <complexContent>
+ <extension base="idm:IDMFault"></extension>
+ </complexContent>
+ </complexType>
+ <complexType name="TenantConflictFault">
+ <complexContent>
+ <extension base="idm:IDMFault"></extension>
+ </complexContent>
+ </complexType>
+ <complexType name="OverLimitFault">
+ <complexContent>
+ <extension base="idm:IDMFault">
+ <attribute name="retryAt" type="xsd:dateTime" use="optional">
+ <annotation>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <p>An optional dateTime denoting when an operation should
+ be retried.</p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ </extension>
+ </complexContent>
+ </complexType>
+</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/api-common.xsd</con:url><con:content><![CDATA[<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?>
+<!--(C) 2009-2011 Rackspace Hosting, All Rights Reserved-->
+<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/common/api/v1.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:capi="http://docs.openstack.org/common/api/v1.0" xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <annotation>
+ <xsd:appinfo xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xsdxt:title>Open Stack Common API Schema Types 1.0</xsdxt:title>
+ <xsdxt:link rev="index" href="extensions.xsd"/>
+ <xsdxt:link rev="index" href="limits.xsd"/>
+ <xsdxt:link rev="index" href="version.xsd"/>
+ </xsd:appinfo>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <p>This is the main index XML Schema document
+ for Common API Schema Types Version 1.0.</p>
+ </xsd:documentation>
+ </annotation>
+ <include schemaLocation="extensions.xsd">
+ <annotation>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <p>Types related to extensions.</p>
+ </xsd:documentation>
+ </annotation>
+ </include>
+ <include schemaLocation="version.xsd">
+ <annotation>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <p>Types related to API version details.</p>
+ </xsd:documentation>
+ </annotation>
+ </include>
+</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/extensions.xsd</con:url><con:content><![CDATA[<xsd:schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/common/api/v1.0" xmlns:ext="http://docs.openstack.org/common/api/v1.0" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <!--Import ATOM specific schema definitions-->
+ <xsd:import namespace="http://www.w3.org/2005/Atom" schemaLocation="./atom/atom.xsd"/>
+ <xsd:element name="extensions" type="ext:Extensions"/>
+ <xsd:element name="extension" type="ext:Extension"/>
+ <xsd:complexType name="Extensions">
+ <xsd:sequence>
+ <xsd:element name="extension" type="ext:Extension" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:anyAttribute namespace="##other" processContents="lax"/>
+ </xsd:complexType>
+ <xsd:complexType name="Extension">
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="1"/>
+ <xsd:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="namespace" type="xsd:anyURI" use="required"/>
+ <xsd:attribute name="alias" type="ext:Alias" use="required"/>
+ <xsd:attribute name="updated" type="xsd:dateTime" use="optional"/>
+ <xsd:anyAttribute namespace="##other" processContents="lax"/>
+ <xsd:assert vc:minVersion="1.1" test="atom:link[@rel='describedby']">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml">
+ <p>There should be at least one atom link
+ with a describedby relation.</p>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:assert>
+ </xsd:complexType>
+ <xsd:simpleType name="Alias">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="\w+\-\w+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+</xsd:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/version.xsd</con:url><con:content><![CDATA[<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/common/api/v1.0" xmlns:vers="http://docs.openstack.org/common/api/v1.0" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <!--Import ATOM specific schema definitions-->
+ <xs:import namespace="http://www.w3.org/2005/Atom" schemaLocation="./atom/atom.xsd"/>
+ <!--Multiple choices-->
+ <xs:element name="choices" type="vers:VersionChoiceList"/>
+ <!--Versioning-->
+ <xs:element name="versions" type="vers:VersionChoiceList"/>
+ <xs:element name="version" type="vers:VersionChoice" vc:minVersion="1.0" vc:maxVersion="1.1"/>
+ <xs:element name="version" type="vers:VersionChoiceRoot" vc:minVersion="1.1"/>
+ <!--Types-->
+ <xs:simpleType name="VersionStatus">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>The VersionStatus type describes a service's operational status.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="DEPRECATED"/>
+ <xs:enumeration value="ALPHA"/>
+ <xs:enumeration value="BETA"/>
+ <xs:enumeration value="CURRENT"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="VersionChoiceList">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>A version choice list outlines a collection of service version choices.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="version" type="vers:VersionChoice" minOccurs="1" maxOccurs="unbounded"/>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ <xs:assert vc:minVersion="1.1" test="every $v in vers:version satisfies $v/atom:link[@rel='self']">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>In version lists, every single version must
+ contain at least one self link.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:assert>
+ </xs:complexType>
+ <xs:complexType name="VersionChoiceRoot" vc:minVersion="1.1">
+ <xs:complexContent>
+ <xs:extension base="vers:VersionChoice">
+ <xs:assert test="atom:link[@rel='describedby']">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>When used as a root element, a version choice
+ must contain at least one describedby link.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:assert>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="VersionChoice">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>A version choice contains relevant information about an available service
+ that a user can then use to target a specific version of the service. Note
+ that both the descriptive media types and the atom link references are
+ not manditory and are offered as message enrichment elements rather
+ than message requirements.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="media-types" type="vers:MediaTypeList" minOccurs="0" maxOccurs="1"/>
+ <xs:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>The ID of a version choice represents the service version's unique
+ identifier. This ID is guaranteed to be unique only among the
+ service version choices outlined in the VersionChoiceList.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="status" type="vers:VersionStatus" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>A version choice's status describes the current operational state of
+ the given service version. The operational status is captured in a
+ simple type enumeration called VersionStatus.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="updated" type="xs:dateTime" use="optional">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ A version choice's updated attribute describes
+ the time when the version was updated. The
+ time should be updated anytime
+ <html:strong>anything</html:strong>
+ in the
+ version has changed: documentation,
+ extensions, bug fixes.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:complexType name="MediaTypeList">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>A MediaTypeList outlines a collection of valid media types for a given
+ service version.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="media-type" type="vers:MediaType" minOccurs="1" maxOccurs="unbounded"/>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:complexType name="MediaType">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>A MediaType describes what content types the service version understands.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="base" type="xs:string" use="optional" default="">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>The base of a given media type describes the simple MIME type
+ that then a more complicated media type can be derived from. These
+ types are basic and provide no namespace or version specific
+ data are are only provided as a convenience. Because of this the
+ base attribute is declared as optional.</html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ <html:p>
+ The type attribute of a MediaType describes the MIME specific
+ identifier of the media type in question. This identifier should include
+ a vendor namespace (
+ <html:a href="http://tools.ietf.org/html/rfc2048">See RFC 2048</html:a>
+ )
+ as well as a version suffix.
+ </html:p>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+</xs:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part></con:definitionCache><con:endpoints><con:endpoint>http://localhost:8080</con:endpoint></con:endpoints><con:resource name="v1.0" path="v1.0"><con:settings/><con:parameters/><con:resource name="extensions" path="extensions"><con:settings/><con:parameters/><con:resource name="{alias}" path="{alias}"><con:settings/><con:parameters><con:parameter><con:name>alias</con:name><con:value xsi:nil="true"/><con:style>TEMPLATE</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - getExtension" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/common/api/v1.0">v1:extension</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="alias" value="RAX-TEST" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:request></con:method></con:resource><con:method name="GET - getExtensions" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/common/api/v1.0">v1:extensions</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method></con:resource><con:resource name="token" path="token"><con:settings/><con:parameters/><con:resource name="{tokenId}" path="{tokenId}"><con:settings/><con:parameters><con:parameter required="true"><con:name>X-Auth-Token</con:name><con:value xsi:nil="true"/><con:style>HEADER</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter><con:parameter><con:name>tokenId</con:name><con:value xsi:nil="true"/><con:style>TEMPLATE</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - validateToken" method="GET"><con:settings/><con:parameters><con:parameter><con:name>belongsTo</con:name><con:value xsi:nil="true"/><con:style>QUERY</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:auth</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:userDisabled</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="887665443383838"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+ <con:entry key="belongsTo" value="1234"/>
+</con:parameters></con:request></con:method><con:method name="DELETE - revokeToken" method="DELETE"><con:settings/><con:parameters/><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="3u37737"/>
+ <con:entry key="X-Auth-Token" value="3838737726"/>
+</con:parameters></con:request></con:method></con:resource><con:method name="POST - authenticate" method="POST"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>application/xml</con:mediaType><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:passwordCredentials</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="REQUEST" id=""><con:mediaType>application/json</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:auth</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:userDisabled</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>401
+403 400 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials
+password="secrete" username="joeuser"
+xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method></con:resource><con:resource name="tenants" path="tenants"><con:settings/><con:parameters><con:parameter required="true"><con:name>X-Auth-Token</con:name><con:value xsi:nil="true"/><con:style>HEADER</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:resource name="{tenantId}" path="{tenantId}"><con:settings/><con:parameters><con:parameter><con:name>tenantId</con:name><con:value xsi:nil="true"/><con:style>TEMPLATE</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - getTenant" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400
+401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="1234"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:request></con:method><con:method name="PUT - updateTenant" method="PUT"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>application/xml</con:mediaType><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="REQUEST" id=""><con:mediaType>application/json</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>401
+403 404 400 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" postQueryString="false"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">
+ &lt;v1:description>New Description&lt;/v1:description>
+&lt;/v1:tenant></con:request><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="1234"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:request></con:method><con:method name="DELETE - deleteTenant" method="DELETE"><con:settings/><con:parameters/><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400
+401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="0000"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:request></con:method></con:resource><con:method name="GET - getTenants" method="GET"><con:settings/><con:parameters><con:parameter><con:name>marker</con:name><con:value xsi:nil="true"/><con:style>QUERY</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter><con:parameter><con:name>limit</con:name><con:value xsi:nil="true"/><con:style>QUERY</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:int</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:tenants</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400
+401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:request></con:method><con:method name="POST - createTenant" method="POST"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>application/xml</con:mediaType><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="REQUEST" id=""><con:mediaType>application/json</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>201</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>201</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType>
+<con:status>409</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:tenantConflict</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType>
+<con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>401
+403 400 409 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant
+enabled="true" id="my_new_tenant"
+xmlns:v1="http://docs.openstack.org/idm/api/v1.0">&lt;v1:description>This
+is a description of my tenant. Thank you very
+much.&lt;/v1:description>&lt;/v1:tenant></con:request><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:request></con:method></con:resource><con:method name="GET - getVersionInfo" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/common/api/v1.0">v1:version</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200
+203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:idmFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/idm/api/v1.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400
+500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method></con:resource></con:interface><con:testSuite name="Keystone Tests"><con:settings/><con:runType>SEQUENTIAL</con:runType><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Admin Credential Check" searchProperties="true" id="29b2fa4b-e1c3-49c4-a7e6-334724e74bb9"><con:settings/><con:testStep type="restrequest" name="GET - validateToken - Valid Token"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>auth</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="887665443383838"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+ <con:entry key="belongsTo" value="1234"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Expired Admin Token"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Expired Admin Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="377372"/>
+ <con:entry key="X-Auth-Token" value="000999"/>
+ <con:entry key="belongsTo" value="3334"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Missing Admin Token"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Missing Admin Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="377372"/>
+ <con:entry key="X-Auth-Token" value=" "/>
+ <con:entry key="belongsTo" value="3334"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Bad Admin Token"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Bad Admin Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="377372"/>
+ <con:entry key="X-Auth-Token" value="976BAD"/>
+ <con:entry key="belongsTo" value="3334"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Joe User Makes Admin Call"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Joe User Makes Admin Call" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>forbidden</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="377372"/>
+ <con:entry key="X-Auth-Token" value="887665443383838"/>
+ <con:entry key="belongsTo" value="3334"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Disabled User Makes Admin Call"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Disabled User Makes Admin Call" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>userDisabled</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="377372"/>
+ <con:entry key="X-Auth-Token" value="999888777"/>
+ <con:entry key="belongsTo" value="3334"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Validate Token" searchProperties="true" id="e4d9f6ba-f392-4c78-bddc-47fca1fc9b0e"><con:settings/><con:testStep type="restrequest" name="GET - validateToken - Valid Token"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>auth</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="887665443383838"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+ <con:entry key="belongsTo" value="1234"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Valid Token no belongsTo"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token no belongsTo" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>auth</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="887665443383838"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Valid Token bad belongsTo"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token bad belongsTo" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="887665443383838"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+ <con:entry key="belongsTo" value="998877665"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Bad Token"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Bad Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="0009991919"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Expired Token"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Expired Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="000999"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Authenticate" searchProperties="true" id="6daa3ca5-8855-4181-afd4-151833f786ff"><con:settings/><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, user doesn't exist"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, user doesn't exist" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials password="P@ssword1" username="testuser" xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Disabled User"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Disabled User" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials password="1234" username="disabled" xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>userDisabled</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, bad password"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, bad password" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials password="123774" username="joeuser" xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Admin"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Admin" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials password="secrete" username="admin" xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/idm/api/v1.0';
+/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/idm/api/v1.0';
+/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Authenticate (JSON)" searchProperties="true" id="686eabb3-4d04-4f63-a46c-fab41cd1ea62"><con:settings/><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, user doesn't exist"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, user doesn't exist" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{
+ "passwordCredentials" : {
+ "username" : "testuser",
+ "password" : "P@ssword1"
+ }
+}</con:request><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Disabled User"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Disabled User" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{
+ "passwordCredentials" : {
+ "username" : "disabled",
+ "password" : "1234"
+ }
+}</con:request><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>userDisabled</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, bad password"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, bad password" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{
+ "passwordCredentials" : {
+ "username" : "joeuser",
+ "password" : "123774"
+ }
+}</con:request><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Admin"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Admin" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{
+ "passwordCredentials" : {
+ "username" : "admin",
+ "password" : "secrete"
+ }
+}</con:request><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{
+ "passwordCredentials" : {
+ "username" : "joeuser",
+ "password" : "secrete"
+ }
+}</con:request><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Revoke Token" searchProperties="true" id="f10219b1-055a-4cf4-aeb3-4172bd596979"><con:settings/><con:testStep type="restrequest" name="DELETE - revokeToken - token doesn't exist"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="DELETE - revokeToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - revokeToken - token doesn't exist" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="3u37737"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User, old Token?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User, old Token?" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/idm/api/v1.0';
+/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/idm/api/v1.0';
+/auth:auth/auth:token/@id</path><content>887665443383838</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - revokeToken - Joe User"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="DELETE - revokeToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - revokeToken - Joe User" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="GroovyScriptAssertion"><con:configuration><scriptText>assert(context.response==null)</scriptText></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="887665443383838"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - old Token, Bad?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - old Token, Bad?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tokenId" value="887665443383838"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User, new Token?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/token" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User, new Token?" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/idm/api/v1.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/idm/api/v1.0';
+/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/idm/api/v1.0';
+/auth:auth/auth:token/@id="887665443383838"</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Tenant Create" searchProperties="true" id="ec537986-d739-4ade-a2f6-f0fca19b876e"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">&lt;v1:description>This is a description of my tenant. Thank you very much.&lt;/v1:description>&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="my_new_tenant" and /ns1:tenant/ns1:description = "This is a description of my tenant. Thank you very much."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant, same id"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant, same id" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">&lt;v1:description>This is a description of my tenant. Thank you very much.&lt;/v1:description>&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>tenantConflict</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>409</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Disabled Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Disabled Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="false" id="mt2" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">&lt;v1:description>New Disabled Tenant&lt;/v1:description>&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@enabled = "false" and /ns1:tenant/@id="mt2" and /ns1:tenant/ns1:description = "New Disabled Tenant"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No Enable"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No Enable" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant id="mt3" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">&lt;v1:description>New Tenant 3&lt;/v1:description>&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="mt3" and /ns1:tenant/ns1:description = "New Tenant 3"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No ID"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No ID" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">&lt;v1:description>New Tenant No ID&lt;/v1:description>&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant no Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant no Description" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Tenant Create (JSON)" searchProperties="true" id="8e95cb90-ef1b-4f0e-a882-09a4a43dd01f"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant":
+ {
+ "id": "JGroup",
+ "description": "A description ...",
+ "enabled": true
+ }
+}
+</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants';
+ns1:Response/ns1:tenant/ns1:id="JGroup" and ns1:Response/ns1:tenant/ns1:enabled="true" and ns1:Response/ns1:tenant/ns1:description="A description ..."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant, same id"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant, same id" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant":
+ {
+ "id": "JGroup",
+ "description": "A description ...",
+ "enabled": true
+ }
+}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>tenantConflict</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>409</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Disabled Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Disabled Tenant" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant":
+ {
+ "id": "JGroup33",
+ "description": "A description...",
+ "enabled": false
+ }
+}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants';
+ns1:Response/ns1:tenant/ns1:id = "JGroup33" and ns1:Response/ns1:tenant/ns1:enabled="false" and ns1:Response/ns1:tenant/ns1:description="A description..."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No Enable"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No Enable" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant":
+ {
+ "id": "JGroup65",
+ "description": "A description..."
+ }
+}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants';
+ns1:Response/ns1:tenant/ns1:id = "JGroup65" and ns1:Response/ns1:tenant/ns1:description = "A description..."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No ID"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No ID" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant":
+ {
+ "description": "A description...",
+ "enabled" : true
+ }
+}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant no Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant no Description" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant":
+ {
+ "id": "JGroup95",
+ "enabled": true
+ }
+}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant Bad Enabled"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant Bad Enabled" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant":
+ {
+ "id": "JGroup95",
+ "description" : "A description...",
+ "enabled": "true"
+ }
+}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Get Tenants" searchProperties="true" id="3465716c-ff9c-4dea-8444-ae70e8144571"><con:settings/><con:testStep type="restrequest" name="GET - getTenants"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="GET - getTenants" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenants" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+count(//ns1:tenant)</path><content>8</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenants (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="GET - getTenants" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenants (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants';
+count(//ns1:e)</path><content>8</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Get Tenant" searchProperties="true" id="e66665ad-46b4-45d2-a8e4-761dbb12fd1c"><con:settings/><con:testStep type="restrequest" name="GET - getTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+ns1:tenant/@id</path><content>1234</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath (enabled and description)"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@enabled and /ns1:tenant/ns1:description</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="1234"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants/1234';
+ns1:Response/ns1:tenant/ns1:id</path><content>1234</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath (enabled and description)"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants/1234';
+/ns1:Response/ns1:tenant/ns1:enabled and /ns1:Response/ns1:tenant/ns1:description</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="1234"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant, Not Found"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant, Not Found" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="88273666219200"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Delete Tenant" searchProperties="true" id="65633524-fcae-4006-b838-e02a1077e3f7"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" id="to_delete"
+ xmlns:v1="http://docs.openstack.org/idm/api/v1.0">
+ &lt;v1:description>To Be Deleted&lt;/v1:description>
+&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_delete" and /ns1:tenant/ns1:description = "To Be Deleted"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="Copy of GET - getTenant" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+ns1:tenant/@id</path><content>to_delete</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath (enabled and description)"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@enabled and /ns1:tenant/ns1:description</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_delete"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant - Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="GroovyScriptAssertion" name="Script Assertion"><con:configuration><scriptText>assert(context.response == null)</scriptText></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_delete"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant, Not Found"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="Copy of GET - getTenant, Not Found" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_delete"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant, not empty - user"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant, not empty - user" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains" name="Contains - forbidden"><con:configuration><token>forbidden</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains - 403"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="1234"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant, not empty - group"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant, not empty - group" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains" name="Contains - forbidden"><con:configuration><token>forbidden</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains - 403"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="0000"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Update Tenant" searchProperties="true" id="60b728bc-d3d5-46f0-84bf-a12d6a443d1a"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" id="to_update" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">
+ &lt;v1:description>ToUpdate&lt;/v1:description>
+&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - Description" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">
+ &lt;v1:description>ToUpdate2&lt;/v1:description>
+&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New Description?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New Description?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - Enabled"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - Enabled" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="false" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">
+ &lt;v1:description>ToUpdate2&lt;/v1:description>
+&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New Enable?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New Enable?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - ID, should ignore"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - ID, should ignore" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant id="boogabooga" enabled="false" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">
+ &lt;v1:description>ToUpdate2&lt;/v1:description>
+&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New ID ignored?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New ID ignored?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - Enabled and Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - Enabled and Description" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>&lt;v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0">
+ &lt;v1:description>ToUpdate3&lt;/v1:description>
+&lt;/v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/ns1:description = "ToUpdate3"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New Changes?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New Changes?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/idm/api/v1.0';
+/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate3"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="GroovyScriptAssertion" name="Script Assertion"><con:configuration><scriptText>assert(context.response == null)</scriptText></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters>
+ <con:entry key="tenantId" value="to_update"/>
+ <con:entry key="X-Auth-Token" value="999888777666"/>
+</con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Extensions" searchProperties="true" id="038f320e-bf1f-42e6-a953-33c166d7619e"><con:settings/><con:testStep type="restrequest" name="GET - getExtensions"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions" methodName="GET - getExtensions" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtensions" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/common/api/v1.0';
+count(/ns1:extensions)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getExtensions (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions" methodName="GET - getExtensions" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtensions (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/extensions';
+count(//ns1:extensions)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getExtension"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions/{alias}" methodName="GET - getExtension" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtension - Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="alias" value="RAX-TEST" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getExtension (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions/{alias}" methodName="GET - getExtension" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtension (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="alias" value="RAX-TEST" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Version Info" searchProperties="true" id="99db3dab-53de-44f5-93cd-099c8850fd97"><con:settings/><con:testStep type="restrequest" name="GET - getVersionInfo"><con:settings/><con:config service="Keystone" resourcePath="/v1.0" methodName="GET - getVersionInfo" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getVersionInfo - Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/common/api/v1.0';
+count(//ns1:version)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getVersionInfo (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0" methodName="GET - getVersionInfo" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getVersionInfo (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0';
+count(//ns1:version)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:properties/><con:reportParameters/></con:testSuite><con:requirements/><con:properties/><con:wssContainer/><con:databaseConnectionContainer/><con:reporting><con:xmlTemplates/><con:parameters/></con:reporting></con:soapui-project> \ No newline at end of file
diff --git a/test/kill.sql b/test/kill.sql
new file mode 100644
index 00000000..66e3733f
--- /dev/null
+++ b/test/kill.sql
@@ -0,0 +1,10 @@
+--
+-- Clean up the DB
+--
+
+delete from users;
+delete from tenants;
+delete from groups;
+delete from user_group_association;
+delete from user_tenant_association;
+delete from token;
diff --git a/test/test_setup.sql b/test/test_setup.sql
new file mode 100644
index 00000000..a15f6d8e
--- /dev/null
+++ b/test/test_setup.sql
@@ -0,0 +1,71 @@
+--
+-- Test Setup
+--
+
+-- Users
+
+insert into users (id, password, email, enabled) values
+ ("joeuser", "secrete", "joe@rackspace.com", 1);
+
+insert into users (id, password, email, enabled) values
+ ("admin", "secrete", "admin@rackspace.com", 1);
+
+insert into users (id, password, email, enabled) values
+ ("disabled", "secrete", "disable@rackspace.com", 0);
+
+-- Tenants
+
+insert into tenants (id, "desc", enabled) values
+ ("1234", "This is a tenant", 1);
+
+insert into tenants (id, "desc", enabled) values
+ ("0000", "This one is disabled", 0);
+
+-- Groups
+
+insert into groups (id, "desc", tenant_id) values
+ ("Admin", "Andmin users", "1234");
+
+insert into groups (id, "desc", tenant_id) values
+ ("Default", "Standard users", "1234");
+
+insert into groups (id, "desc", tenant_id) values
+ ("Empty", "Group on disabled tenant", "0000");
+
+
+-- User Group Associations
+
+insert into user_group_association values
+ ("joeuser", "Default");
+
+insert into user_group_association values
+ ("disabled", "Default");
+
+insert into user_group_association values
+ ("admin", "Admin");
+
+-- User Tenant Associations
+
+insert into user_tenant_association values
+ ("joeuser", "1234");
+
+insert into user_tenant_association values
+ ("disabled", "1234");
+
+insert into user_tenant_association values
+ ("admin", "1234");
+
+-- Token
+
+insert into token values
+ ("887665443383838", "joeuser", "1234", datetime("2012-02-05T00:00"));
+
+insert into token values
+ ("999888777666", "admin", "1234", datetime("2015-02-05T00:00"));
+
+insert into token values
+ ("000999", "admin", "1234", datetime("2010-02-05T00:00"));
+
+insert into token values
+ ("999888777", "disabled", "1234", datetime("2015-02-05T00:00"));
+
diff --git a/test/unit/test_identity.py b/test/unit/test_identity.py
new file mode 100644
index 00000000..95a91afc
--- /dev/null
+++ b/test/unit/test_identity.py
@@ -0,0 +1,966 @@
+import httplib2
+import json
+from lxml import etree
+import unittest
+from webtest import TestApp
+
+URL = 'http://localhost:8080/v1.0/'
+
+
+def get_token(user, pswd, kind=''):
+ h = httplib2.Http(".cache")
+ url = '%stoken' % URL
+ body = {"passwordCredentials": {"username": user,
+ "password": pswd}}
+ resp, content = h.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json"})
+ content = json.loads(content)
+ token = str(content['auth']['token']['id'])
+ if kind == 'token':
+ return token
+ else:
+ return (resp, content)
+
+
+def get_token_xml(user, pswd, type=''):
+ h = httplib2.Http(".cache")
+ url = '%stoken' % URL
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <passwordCredentials \
+ xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ password="%s" username="%s" \
+ tenantId="77654"/> ' % (pswd, user)
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",
+ "ACCEPT": "application/xml"})
+ dom = etree.fromstring(content)
+ root = dom.find("{http://docs.openstack.org/idm/api/v1.0}token")
+ token_root = root.attrib
+ token = str(token_root['id'])
+ if type == 'token':
+ return token
+ else:
+ return (resp, content)
+
+
+def delete_token(token, auth_token):
+ h = httplib2.Http(".cache")
+ url = '%stoken/%s' % (URL, token)
+ resp, content = h.request(url, "DELETE", body='', \
+ headers={"Content-Type": "application/json", \
+ "X-Auth-Token": auth_token})
+ return (resp, content)
+
+
+def delete_token_xml(token, auth_token):
+ h = httplib2.Http(".cache")
+ url = '%stoken/%s' % (URL, token)
+ resp, content = h.request(url, "DELETE", body='',\
+ headers={"Content-Type": "application/xml", \
+ "X-Auth-Token": auth_token,
+ "ACCEPT": "application/xml"})
+ return (resp, content)
+
+
+def create_tenant(tenantid, auth_token):
+ h = httplib2.Http(".cache")
+
+ url = '%stenants' % (URL)
+ body = {"tenant": {"id": tenantid,
+ "description": "A description ...",
+ "enabled": True}}
+ resp, content = h.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": auth_token})
+ return (resp, content)
+
+
+def create_tenant_xml(tenantid, auth_token):
+ h = httplib2.Http(".cache")
+ url = '%stenants' % (URL)
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true" id="%s"> \
+ <description>A description...</description> \
+ </tenant>' % tenantid
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": auth_token,
+ "ACCEPT": "application/xml"})
+ return (resp, content)
+
+
+def delete_tenant(tenantid, auth_token):
+ h = httplib2.Http(".cache")
+ url = '%stenants/%s' % (URL, tenantid)
+ resp, content = h.request(url, "DELETE", body='{}',\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": auth_token})
+ return (resp, content)
+
+
+def delete_tenant_xml(tenantid, auth_token):
+ h = httplib2.Http(".cache")
+ url = '%stenants/%s' % (URL, tenantid)
+ resp, content = h.request(url, "DELETE", body='',\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": auth_token,
+ "ACCEPT": "application/xml"})
+ return (resp, content)
+
+
+def get_tenant():
+ return '1234'
+
+
+def get_user():
+ return '1234'
+
+
+def get_userdisabled():
+ return '1234'
+
+
+def get_auth_token():
+ return '999888777666'
+
+
+def get_exp_auth_token():
+ return '000999'
+
+
+def get_disabled_token():
+ return '999888777'
+
+
+class identity_test(unittest.TestCase):
+
+ #Given _a_ to make inherited test cases in an order.
+ #here to call below method will call as last test case
+
+ def test_a_get_version(self):
+ h = httplib2.Http(".cache")
+ url = URL
+ resp, content = h.request(url, "GET", body="",
+ headers={"Content-Type": "application/json"})
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual('application/json', resp['content-type'])
+
+ def test_a_get_version(self):
+ h = httplib2.Http(".cache")
+ url = URL
+ resp, content = h.request(url, "GET", body="",
+ headers={"Content-Type": "application/xml",
+ "ACCEPT": "application/xml"})
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual('application/xml', resp['content-type'])
+
+
+class authorize_test(identity_test):
+
+ def setUp(self):
+ self.token = get_token('joeuser', 'secrete', 'token')
+ self.tenant = get_tenant()
+ self.user = get_user()
+ self.userdisabled = get_userdisabled()
+ self.auth_token = get_auth_token()
+ self.exp_auth_token = get_exp_auth_token()
+ self.disabled_token = get_disabled_token()
+
+ def tearDown(self):
+ delete_token(self.token, self.auth_token)
+
+ def test_a_authorize(self):
+ resp, content = get_token('joeuser', 'secrete')
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual('application/json', resp['content-type'])
+
+ def test_a_authorize_xml(self):
+ resp, content = get_token_xml('joeuser', 'secrete')
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual('application/xml', resp['content-type'])
+
+ def test_a_authorize_user_disaabled(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken' % URL
+ body = {"passwordCredentials": {"username": "disabled",
+ "password": "secrete"}}
+ resp, content = h.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json"})
+ content = json.loads(content)
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_a_authorize_user_disaabled_xml(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken' % URL
+
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <passwordCredentials \
+ xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ password="secrete" username="disabled" \
+ />'
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",
+ "ACCEPT": "application/xml"})
+ content = etree.fromstring(content)
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_a_authorize_user_wrong(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken' % URL
+ body = {"passwordCredentials": {"username-w": "disabled",
+ "password": "secrete"}}
+ resp, content = h.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json"})
+ content = json.loads(content)
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(400, int(resp['status']))
+
+ def test_a_authorize_user_wrong_xml(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken' % URL
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <passwordCredentials \
+ xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ password="secrete" username-w="disabled" \
+ />'
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",
+ "ACCEPT": "application/xml"})
+ content = etree.fromstring(content)
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(400, int(resp['status']))
+
+
+class validate_token(authorize_test):
+
+ def test_validate_token_true(self):
+ h = httplib2.Http(".cache")
+
+ url = '%stoken/%s?belongsTo=%s' % (URL, self.token, self.tenant)
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/json", \
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual('application/json', resp['content-type'])
+
+ def test_validate_token_true_xml(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken/%s?belongsTo=%s' % (URL, self.token, self.tenant)
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/xml", \
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual('application/xml', resp['content-type'])
+
+ def test_validate_token_expired(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken/%s?belongsTo=%s' % (URL, self.exp_auth_token, \
+ self.tenant)
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/json", \
+ "X-Auth-Token": self.exp_auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+ self.assertEqual('application/json', resp['content-type'])
+
+ def test_validate_token_expired_xml(self):
+ h = httplib2.Http(".cache")
+
+ url = '%stoken/%s?belongsTo=%s' % (URL, self.exp_auth_token, \
+ self.tenant)
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/xml", \
+ "X-Auth-Token": self.exp_auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+ self.assertEqual('application/xml', resp['content-type'])
+
+ def test_validate_token_invalid(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken/%s?belongsTo=%s' % (URL, 'NonExistingToken', \
+ self.tenant)
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/json", \
+ "X-Auth-Token": self.auth_token})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+ self.assertEqual('application/json', resp['content-type'])
+
+ def test_validate_token_invalid_xml(self):
+ h = httplib2.Http(".cache")
+ url = '%stoken/%s?belongsTo=%s' % (URL, 'NonExistingToken', \
+ self.tenant)
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/json", \
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+ self.assertEqual('application/json', resp['content-type'])
+
+
+class tenant_test(unittest.TestCase):
+
+ def setUp(self):
+ self.token = get_token('joeuser', 'secrete', 'token')
+ self.tenant = get_tenant()
+ self.user = get_user()
+ self.userdisabled = get_userdisabled()
+ self.auth_token = get_auth_token()
+ self.exp_auth_token = get_exp_auth_token()
+ self.disabled_token = get_disabled_token()
+
+ def tearDown(self):
+ resp, content = delete_tenant(self.tenant, self.auth_token)
+
+
+class create_tenant_test(tenant_test):
+
+ def test_tenant_create(self):
+ resp, content = delete_tenant('test_tenant', str(self.auth_token))
+ resp, content = create_tenant('test_tenant', str(self.auth_token))
+ self.tenant = 'test_tenant'
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+
+ if int(resp['status']) not in (200, 201):
+
+ self.fail('Failed due to %d' % int(resp['status']))
+
+ def test_tenant_create_xml(self):
+ resp, content = delete_tenant_xml('test_tenant', str(self.auth_token))
+ resp, content = create_tenant_xml('test_tenant', str(self.auth_token))
+ self.tenant = 'test_tenant'
+ content = etree.fromstring(content)
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+
+ if int(resp['status']) not in (200, 201):
+
+ self.fail('Failed due to %d' % int(resp['status']))
+
+ def test_tenant_create_again(self):
+
+ resp, content = create_tenant("test_tenant", str(self.auth_token))
+ resp, content = create_tenant("test_tenant", str(self.auth_token))
+ if int(resp['status']) == 200:
+ self.tenant = content['tenant']['id']
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(409, int(resp['status']))
+ if int(resp['status']) == 200:
+ self.tenant = content['tenant']['id']
+
+ def test_tenant_create_again_xml(self):
+
+ resp, content = create_tenant_xml("test_tenant", str(self.auth_token))
+ resp, content = create_tenant_xml("test_tenant", str(self.auth_token))
+ content = etree.fromstring(content)
+ if int(resp['status']) == 200:
+ self.tenant = content.get("id")
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(409, int(resp['status']))
+ if int(resp['status']) == 200:
+ self.tenant = content.get("id")
+
+ def test_tenant_create_forbidden_token(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant("test_tenant", str(self.auth_token))
+ if int(resp['status']) == 200:
+ self.tenant = content['tenant']['id']
+
+ url = '%stenants' % (URL)
+ body = {"tenant": {"id": self.tenant,
+ "description": "A description ...",
+ "enabled": True}}
+ resp, content = h.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": self.token})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_tenant_create_forbidden_token_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant_xml("test_tenant", str(self.auth_token))
+ content = etree.fromstring(content)
+ if int(resp['status']) == 200:
+ self.tenant = content.get('id')
+
+ url = '%stenants' % (URL)
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true" id="%s"> \
+ <description>A description...</description> \
+ </tenant>' % self.tenant
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.token,
+ "ACCEPT": "application/xml"})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_tenant_create_expired_token(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant("test_tenant", str(self.auth_token))
+ if int(resp['status']) == 200:
+ self.tenant = content['tenant']['id']
+
+ url = '%stenants' % (URL)
+ body = {"tenant": {"id": self.tenant,
+ "description": "A description ...",
+ "enabled": True}}
+ resp, content = h.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": self.exp_auth_token})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+ def test_tenant_create_expired_token_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant_xml("test_tenant", str(self.auth_token))
+ content = etree.fromstring(content)
+ if int(resp['status']) == 200:
+ self.tenant = content.get('id')
+
+ url = '%stenants' % (URL)
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true" id="%s"> \
+ <description>A description...</description> \
+ </tenant>' % self.tenant
+
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.exp_auth_token,
+ "ACCEPT": "application/xml"})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+ def test_tenant_create_missing_token(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant("test_tenant", str(self.auth_token))
+ if int(resp['status']) == 200:
+ self.tenant = content['tenant']['id']
+
+ url = '%stenants' % (URL)
+ body = {"tenant": {"id": self.tenant,
+ "description": "A description ...",
+ "enabled": True}}
+ resp, content = h.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json"})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+ def test_tenant_create_missing_token_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant_xml("test_tenant", str(self.auth_token))
+ content = etree.fromstring(content)
+ if int(resp['status']) == 200:
+ self.tenant = content.get('id')
+
+ url = '%stenants' % (URL)
+
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true" id="%s"> \
+ <description>A description...</description> \
+ </tenant>' % self.tenant
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",
+ "ACCEPT": "application/xml"})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+ def test_tenant_create_disabled_token(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant("test_tenant", str(self.auth_token))
+ if int(resp['status']) == 200:
+ self.tenant = content['tenant']['id']
+
+ url = '%stenants' % (URL)
+ body = '{"tenant": { "id": "%s", \
+ "description": "A description ...", "enabled"\
+ :true } }' % self.tenant
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.disabled_token})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_tenant_create_disabled_token_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant_xml("test_tenant", str(self.auth_token))
+ content = etree.fromstring(content)
+ if int(resp['status']) == 200:
+ self.tenant = content.get('id')
+
+ url = '%stenants' % (URL)
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true" id="%s"> \
+ <description>A description...</description> \
+ </tenant>' % self.tenant
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",
+ "X-Auth-Token": self.disabled_token,
+ "ACCEPT": "application/xml"})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_tenant_create_invalid_token(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant("test_tenant", str(self.auth_token))
+ if int(resp['status']) == 200:
+ self.tenant = content['tenant']['id']
+
+ url = '%stenants' % (URL)
+ body = '{"tenant": { "id": "%s", \
+ "description": "A description ...", "enabled"\
+ :true } }' % self.tenant
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": 'nonexsitingtoken'})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+ def test_tenant_create_invalid_token_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant_xml("test_tenant", str(self.auth_token))
+ content = etree.fromstring(content)
+ if int(resp['status']) == 200:
+ self.tenant = content.get('id')
+
+ url = '%stenants' % (URL)
+ body = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true" id="%s"> \
+ <description>A description...</description> \
+ </tenant>' % self.tenant
+ resp, content = h.request(url, "POST", body=body,\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": 'nonexsitingtoken',
+ "ACCEPT": "application/xml"})
+
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+
+class get_tenants_test(tenant_test):
+
+ def test_get_tenants(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='{}',\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_tenants_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_tenants_forbidden_token(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='{}',\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_get_tenants_forbidden_token_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_get_tenants_exp_token(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='{}',\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.exp_auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+ def test_get_tenants_exp_token_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.exp_auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(401, int(resp['status']))
+
+
+class get_tenant_test(tenant_test):
+
+ def test_get_tenant(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, self.tenant)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='{}',\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_tenant_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, self.tenant)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_tenant_bad(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, 'tenant_bad')
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='{',\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+ def test_get_tenant_bad_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, 'tenant_bad')
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='{',\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+ def test_get_tenant_not_found(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/NonexistingID' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='{}',\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+ def test_get_tenant_not_found_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/NonexistingID' % (URL)
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body='',\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+
+class update_tenant_test(tenant_test):
+
+ def test_update_tenant(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, self.tenant)
+ data = '{"tenant": { "description": "A NEW description..." ,\
+ "enabled":true }}'
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "PUT", body=data,\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.auth_token})
+ body = json.loads(content)
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual(int(self.tenant), int(body['tenant']['id']))
+ self.assertEqual('A NEW description...', \
+ body['tenant']['description'])
+
+ def test_update_tenant_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant_xml(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, self.tenant)
+ data = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true"> \
+ <description>A NEW description...</description> \
+ </tenant>'
+
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "PUT", body=data,\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ body = etree.fromstring(content)
+ desc = body.find("{http://docs.openstack.org/idm/api/v1.0}description")
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+ self.assertEqual(int(self.tenant), int(body.get('id')))
+ self.assertEqual('A NEW description...', \
+ desc.text)
+
+ def test_update_tenant_bad(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, self.tenant)
+ data = '{"tenant": { "description_bad": "A NEW description...",\
+ "enabled":true }}'
+ #test for Content-Type = application/json
+
+ resp, content = h.request(url, "PUT", body=data,\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(400, int(resp['status']))
+
+ def test_update_tenant_bad_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/%s' % (URL, self.tenant)
+ data = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true"> \
+ <description_bad>A NEW description...</description> \
+ </tenant>'
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "PUT", body=data,\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(400, int(resp['status']))
+
+ def test_update_tenant_not_found(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/NonexistingID' % (URL)
+ data = '{"tenant": { "description": "A NEW description...",\
+ "enabled":true }}'
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body=data,\
+ headers={"Content-Type": "application/json",\
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+ def test_update_tenant_not_found_xml(self):
+ h = httplib2.Http(".cache")
+ resp, content = create_tenant(self.tenant, str(self.auth_token))
+ url = '%stenants/NonexistingID' % (URL)
+ data = '<?xml version="1.0" encoding="UTF-8"?> \
+ <tenant xmlns="http://docs.openstack.org/idm/api/v1.0" \
+ enabled="true"> \
+ <description_bad>A NEW description...</description> \
+ </tenant>'
+ #test for Content-Type = application/json
+ resp, content = h.request(url, "GET", body=data,\
+ headers={"Content-Type": "application/xml",\
+ "X-Auth-Token": self.auth_token,
+ "ACCEPT": "application/xml"})
+ if int(resp['status']) == 500:
+ self.fail('IDM fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+
+class delete_tenant_test(tenant_test):
+
+ def test_delete_tenant_not_found(self):
+ #resp,content=create_tenant("test_tenant_delete", str(self.auth_token))
+ resp, content = delete_tenant("test_tenant_delete111", \
+ str(self.auth_token))
+ self.assertEqual(404, int(resp['status']))
+
+ def test_delete_tenant_not_found_xml(self):
+ #resp,content=create_tenant("test_tenant_delete", str(self.auth_token))
+ resp, content = delete_tenant_xml("test_tenant_delete111", \
+ str(self.auth_token))
+ self.assertEqual(404, int(resp['status']))
+
+ def test_delete_tenant(self):
+ resp, content = create_tenant("test_tenant_delete", \
+ str(self.auth_token))
+ resp, content = delete_tenant("test_tenant_delete", \
+ str(self.auth_token))
+ self.assertEqual(204, int(resp['status']))
+
+ def test_delete_tenant_xml(self):
+ resp, content = create_tenant_xml("test_tenant_delete", \
+ str(self.auth_token))
+ resp, content = delete_tenant_xml("test_tenant_delete", \
+ str(self.auth_token))
+ self.assertEqual(204, int(resp['status']))
+
+if __name__ == '__main__':
+ unittest.main()