diff options
| author | Sai Krishna <saikrishna1511@gmail.com> | 2011-04-28 17:13:27 +0530 |
|---|---|---|
| committer | Sai Krishna <saikrishna1511@gmail.com> | 2011-04-28 17:13:27 +0530 |
| commit | 112f06fd9377d4e6964f8e0ee447f60267920cc0 (patch) | |
| tree | f58b93c34d26c369108eb6d9484aaa3bd47a0f64 | |
| parent | efd7eb952a478ba968a60daed0dacd52ffe67b89 (diff) | |
Adding First kestone repo
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 "–"> + <!ENTITY mdash "—"> + <!ENTITY hellip "…"> + + <!-- 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&<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 — 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/…). 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 — 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'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&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&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&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> + <schema . . .> + . . . + <import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2001/xml.xsd"/> + </pre> + <p> + or + </p> + <pre> + <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> + <type . . .> + . . . + <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 <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"> + <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(.),'
','')"/> + <variable name="noquote" select="translate($noeol,'"',"'")"/> + <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 Binary files differnew file mode 100644 index 00000000..3c37c310 --- /dev/null +++ b/keystone/content/idmdevguide.pdf 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', + ], + }, + ) @@ -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"><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="<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><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"> + <include schemaLocation="token.xsd"/> + <include schemaLocation="tenant.xsd"/> + <include schemaLocation="fault.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/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><schema . . .> + . . . + <import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2001/xml.xsd"/></pre> + <p>or</p> + <pre><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><type . . .> + . . . + <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 <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"><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"><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"><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"> + <v1:description>New Description</v1:description> +</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"><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant +enabled="true" id="my_new_tenant" +xmlns:v1="http://docs.openstack.org/idm/api/v1.0"><v1:description>This +is a description of my tenant. Thank you very +much.</v1:description></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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><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"><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"><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"><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"><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"><v1:description>This is a description of my tenant. Thank you very much.</v1:description></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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"><v1:description>This is a description of my tenant. Thank you very much.</v1:description></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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="false" id="mt2" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"><v1:description>New Disabled Tenant</v1:description></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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant id="mt3" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"><v1:description>New Tenant 3</v1:description></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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"><v1:description>New Tenant No ID</v1:description></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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"></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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="to_delete" + xmlns:v1="http://docs.openstack.org/idm/api/v1.0"> + <v1:description>To Be Deleted</v1:description> +</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"><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"><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"><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"><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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="to_update" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"> + <v1:description>ToUpdate</v1:description> +</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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"> + <v1:description>ToUpdate2</v1:description> +</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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="false" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"> + <v1:description>ToUpdate2</v1:description> +</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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant id="boogabooga" enabled="false" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"> + <v1:description>ToUpdate2</v1:description> +</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"><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"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/idm/api/v1.0"> + <v1:description>ToUpdate3</v1:description> +</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"><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"><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"><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"><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"><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"><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"><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"><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()
|
