summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/keystone3
-rwxr-xr-xbin/keystone-admin3
-rwxr-xr-xbin/keystone-auth2
-rwxr-xr-xbin/keystone-manage2
-rw-r--r--docs/guide/src/docbkx/identity.wadl376
-rw-r--r--docs/guide/src/docbkx/identitydevguide.xml4
-rw-r--r--docs/guide/src/docbkx/samples/auth.json52
-rw-r--r--docs/guide/src/docbkx/samples/auth.xml29
-rw-r--r--docs/guide/src/docbkx/samples/baseURL.json7
-rw-r--r--docs/guide/src/docbkx/samples/baseURL.xml7
-rw-r--r--docs/guide/src/docbkx/samples/baseURLRef.json3
-rw-r--r--docs/guide/src/docbkx/samples/baseURLRef.xml2
-rw-r--r--docs/guide/src/docbkx/samples/baseURLRefs.json15
-rw-r--r--docs/guide/src/docbkx/samples/baseURLRefs.xml16
-rw-r--r--docs/guide/src/docbkx/samples/baseURLs.json29
-rw-r--r--docs/guide/src/docbkx/samples/baseURLs.xml29
-rw-r--r--docs/guide/src/docbkx/samples/role.json2
-rw-r--r--docs/guide/src/docbkx/samples/role.xml7
-rw-r--r--docs/guide/src/docbkx/samples/roleRefs.json14
-rw-r--r--docs/guide/src/docbkx/samples/roleRefs.xml4
-rw-r--r--docs/guide/src/docbkx/samples/roles.json4
-rw-r--r--docs/guide/src/docbkx/samples/roles.xml10
-rw-r--r--docs/guide/src/docbkx/samples/validatetoken.json8
-rw-r--r--docs/guide/src/docbkx/samples/validatetoken.xml4
-rw-r--r--docs/guide/src/docbkx/xsd/api.xsd2
-rw-r--r--docs/guide/src/docbkx/xsd/roles.xsd14
-rw-r--r--docs/guide/src/docbkx/xsd/token.xsd102
-rw-r--r--keystone/db/sqlalchemy/api.py11
-rw-r--r--keystone/logic/service.py46
-rw-r--r--keystone/logic/types/fault.py7
-rw-r--r--keystone/logic/types/role.py107
-rw-r--r--keystone/server.py50
-rw-r--r--test/unit/test_common.py28
-rw-r--r--test/unit/test_roles.py166
34 files changed, 1041 insertions, 124 deletions
diff --git a/bin/keystone b/bin/keystone
index 1afebe8b..e2087a8c 100755
--- a/bin/keystone
+++ b/bin/keystone
@@ -25,7 +25,6 @@ Keystone Identity Server - Admin and Service API
import optparse
import os
import sys
-import tools.tracer #load this first
# 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...
@@ -34,7 +33,7 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir))
if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')):
sys.path.insert(0, possible_topdir)
-
+import tools.tracer #load this first
import keystone
from keystone.common import config
from keystone.common import wsgi
diff --git a/bin/keystone-admin b/bin/keystone-admin
index 11442673..2a99b8a3 100755
--- a/bin/keystone-admin
+++ b/bin/keystone-admin
@@ -25,7 +25,6 @@ Keystone Identity Server - Admin API
import optparse
import os
import sys
-import tools.tracer #load this first
# 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...
@@ -35,6 +34,8 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')):
sys.path.insert(0, possible_topdir)
+import tools.tracer #load this first
+
import keystone
from keystone.common import config
from keystone.common import wsgi
diff --git a/bin/keystone-auth b/bin/keystone-auth
index 3e59d3e8..67fb8a5a 100755
--- a/bin/keystone-auth
+++ b/bin/keystone-auth
@@ -25,7 +25,6 @@ Keystone Identity Server - Service API
import optparse
import os
import sys
-import tools.tracer #load this first
# 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...
@@ -35,6 +34,7 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')):
sys.path.insert(0, possible_topdir)
+import tools.tracer #load this first
import keystone
from keystone.common import config
from keystone.common import wsgi
diff --git a/bin/keystone-manage b/bin/keystone-manage
index f2de1deb..0b9e67bc 100755
--- a/bin/keystone-manage
+++ b/bin/keystone-manage
@@ -34,7 +34,7 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir))
if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')):
sys.path.insert(0, possible_topdir)
-
+import tools.tracer
import keystone
from keystone.common import config
import keystone.db.sqlalchemy.api as db_api
diff --git a/docs/guide/src/docbkx/identity.wadl b/docs/guide/src/docbkx/identity.wadl
index 2cad5a48..cade9c07 100644
--- a/docs/guide/src/docbkx/identity.wadl
+++ b/docs/guide/src/docbkx/identity.wadl
@@ -45,6 +45,38 @@
<method href="#getTenant" />
<method href="#updateTenant" />
<method href="#deleteTenant" />
+ <resource id="baseURLRefs" path="baseURLRefs">
+ <method href="#getBaseURLRefs"/>
+ <method href="#addBaseURLRef"/>
+ <resource id="baseURLRef" path="{baseURLId}">
+ <param name="baseURLId" style="template" type="xsd:int"/>
+ <method href="#getBaseURLRef"/>
+ <method href="#deleteBaseURLRef"/>
+ </resource>
+ </resource>
+ <resource id="roleRefs" path="roleRefs">
+ <method href="#getRoleRefs"/>
+ <method href="#addRoleRef"/>
+ <resource id="roleRef" path="{roleId}">
+ <param name="roleId" style="template" type="xsd:int"/>
+ <method href="#getRoleRef"/>
+ <method href="#deleteRoleRef"/>
+ </resource>
+ </resource>
+ </resource>
+ </resource>
+ <resource id="baseURLs" path="baseURLs">
+ <method href="#getBaseURLs"/>
+ <resource id="baseURLId" path="{baseURLId}">
+ <param name="baseURLId" style="template" type="xsd:int"/>
+ <method href="#getBaseURL"/>
+ </resource>
+ </resource>
+ <resource id="roles" path="roles">
+ <method href="#getRoles"/>
+ <resource id="roleId" path="{roleId}">
+ <param name="roleId" style="template" type="xsd:int"/>
+ <method href="#getRole"/>
</resource>
</resource>
</resource>
@@ -351,5 +383,349 @@
<representation mediaType="application/json"/>
</response>
</method>
+
+ <!-- Base URLs -->
+
+ <method name="GET" id="getBaseURLs">
+ <request>
+ <param name="serviceName" style="query"
+ required="false" type="xsd:string"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:baseURLs"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:identityFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getBaseURL">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:baseURL"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:authFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getBaseURLRefs">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:baseURLRefs"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:authFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="POST" id="addBaseURLRef">
+ <request>
+ <representation mediaType="application/xml" element="identity:baseURLRef"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="201">
+ <representation mediaType="application/xml" element="identity:baseURLRef">
+ <param name="Location" type="xsd:anyURI" style="header"/>
+ </representation>
+ <representation mediaType="application/json">
+ <param name="Location" type="xsd:anyURI" style="header"/>
+ </representation>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:identityFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getBaseURLRef">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:baseURLRef"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:identityFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="DELETE" id="deleteBaseURLRef">
+ <response status="204"/>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:authFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <!--Roles-->
+ <method name="GET" id="getRoles">
+ <request>
+ <param name="serviceName" style="query"
+ required="false" type="xsd:string"/>
+ </request>
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:roles"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:identityFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getRole">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:role"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:authFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getRoleRefs">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:roleRefs"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:authFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="POST" id="addRoleRef">
+ <request>
+ <representation mediaType="application/xml" element="identity:roleRef"/>
+ <representation mediaType="application/json"/>
+ </request>
+ <response status="201">
+ <representation mediaType="application/xml" element="identity:roleRef">
+ <param name="Location" type="xsd:anyURI" style="header"/>
+ </representation>
+ <representation mediaType="application/json">
+ <param name="Location" type="xsd:anyURI" style="header"/>
+ </representation>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:identityFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+
+ <method name="GET" id="getRoleRef">
+ <response status="200 203">
+ <representation mediaType="application/xml" element="identity:roleRef"/>
+ <representation mediaType="application/json"/>
+ </response>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:identityFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
+ <method name="DELETE" id="deleteRoleRef">
+ <response status="204"/>
+ <response status="400">
+ <representation mediaType="application/xml" element="identity:badRequest"/>
+ </response>
+ <response status="401">
+ <representation mediaType="application/xml" element="identity:unauthorized"/>
+ </response>
+ <response status="403">
+ <representation mediaType="application/xml" element="identity:forbidden"/>
+ </response>
+ <response status="404">
+ <representation mediaType="application/xml" element="identity:itemNotFound"/>
+ </response>
+ <response status="500">
+ <representation mediaType="application/xml" element="identity:authFault"/>
+ </response>
+ <response status="503">
+ <representation mediaType="application/xml" element="identity:serviceUnavailable"/>
+ </response>
+ <response status="400 401 403 404 500 503">
+ <representation mediaType="application/json"/>
+ </response>
+ </method>
</application>
diff --git a/docs/guide/src/docbkx/identitydevguide.xml b/docs/guide/src/docbkx/identitydevguide.xml
index 5ea766ab..4418ce42 100644
--- a/docs/guide/src/docbkx/identitydevguide.xml
+++ b/docs/guide/src/docbkx/identitydevguide.xml
@@ -1404,14 +1404,14 @@ Host: identity.api.openstack.org/v1.1/
<title>Add Role Request: XML</title>
<?dbfo keep-together="always"?>
<programlisting language="xml">
-<xi:include href="samples/role.xml" parse="text"/>
+<xi:include href="samples/roleRef.xml" parse="text"/>
</programlisting>
</example>
<example>
<title>Add Role Request: JSON</title>
<?dbfo keep-together="always"?>
<programlisting language="javascript">
-<xi:include href="samples/role.json" parse="text"/>
+<xi:include href="samples/roleRef.json" parse="text"/>
</programlisting>
</example>
</section>
diff --git a/docs/guide/src/docbkx/samples/auth.json b/docs/guide/src/docbkx/samples/auth.json
index fa56c131..b5fbada2 100644
--- a/docs/guide/src/docbkx/samples/auth.json
+++ b/docs/guide/src/docbkx/samples/auth.json
@@ -1,19 +1,37 @@
{
- "auth" : {
- "token": {
- "id": "ab48a9efdfedb23ty3494",
- "expires": "2010-11-01T03:32:15-05:00"
- },
- "user" : {
- "groups": {
- "group": [
- {
- "tenantId" : "1234",
- "id": "Admin"
- }
- ]},
- "username": "jqsmith",
- "tenantId": "1234"
- }
- }
+ "auth" : {
+ "token" : {
+ "id" : "asdasdasd-adsasdads-asdasdasd-adsadsasd",
+ "expires" : "2010-11-01T03:32:15-05:00"
+ },
+ "serviceCatalog" : {
+ "service1" : [
+ {
+ "region" : "DFW",
+ "publicURL" : "https://service1-public/v1/blah-blah",
+ "internalURL" : "https://service1-internal/v1/blah-blah"
+ },
+ {
+ "region" : "ORD",
+ "publicURL" : "https://service1-public-ord/v1/blah-blah",
+ "internalURL" : "https://service1-internal-ord/v1/blah-blah"
+ }
+ ],
+ "service2" : [
+ {
+ "region" : "DFW",
+ "publicURL" : "https://service2-public-dfw/v1/blah-blah",
+ },
+ {
+ "region" : "ORD",
+ "publicURL" : "https://service2-public-orf/v1/blah-blah",
+ }
+ ],
+ "service3" : [
+ {
+ "publicURL" : "https://service3-public/v1/blah-blah",
+ }
+ ]
+ }
+ }
}
diff --git a/docs/guide/src/docbkx/samples/auth.xml b/docs/guide/src/docbkx/samples/auth.xml
index 0e114a64..6d90c064 100644
--- a/docs/guide/src/docbkx/samples/auth.xml
+++ b/docs/guide/src/docbkx/samples/auth.xml
@@ -2,9 +2,28 @@
<auth xmlns="http://docs.openstack.org/identity/api/v2.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>
+ <serviceCatalog>
+ <service name="service1">
+ <endpoint
+ region="DFW"
+ publicURL="https://service1.public.com/v2.0/blah-blah"
+ internalURL="https://service1.internal.com/v2.0/blah-blah"/>
+ <endpoint
+ region="ORD"
+ publicURL="https://service1.public.com/v2.0/blah-blah"
+ internalURL="https://service1.internal.com/v2.0/blah-blah"/>
+ </service>
+ <service name="service2">
+ <endpoint
+ region="DFW"
+ publicURL="https://service2.public.com/v2.0/blah-blah"/>
+ <endpoint
+ region="ORD"
+ publicURL="https://service2.public.com/v2.0/blah-blah"/>
+ </service>
+ <service name="service3">
+ <endpoint
+ publicURL="https://service3.public.com/v2.0/blah-blah"/>
+ </service>
+ </serviceCatalog>
</auth>
diff --git a/docs/guide/src/docbkx/samples/baseURL.json b/docs/guide/src/docbkx/samples/baseURL.json
index c07d943c..534484aa 100644
--- a/docs/guide/src/docbkx/samples/baseURL.json
+++ b/docs/guide/src/docbkx/samples/baseURL.json
@@ -2,12 +2,11 @@
"baseURL" :
{
"id" : 1,
- "userType" : "NAST",
"region" : "DFW",
"default" : true,
- "serviceName" : "cloudFiles",
- "publicURL" : "https://storage.clouddrive.com/v1",
- "internalURL" : "https://storage-snet.clouddrive.com/v1",
+ "serviceName" : "service1",
+ "publicURL" : "https://service-public.com/v1",
+ "internalURL" : "https://service-internal.com/v1",
"enabled" : true
}
}
diff --git a/docs/guide/src/docbkx/samples/baseURL.xml b/docs/guide/src/docbkx/samples/baseURL.xml
index 415fbf10..a66d5516 100644
--- a/docs/guide/src/docbkx/samples/baseURL.xml
+++ b/docs/guide/src/docbkx/samples/baseURL.xml
@@ -3,11 +3,10 @@
<baseURL
xmlns="http://docs.openstack.org/identity/api/v2.0"
id="1"
- userType="NAST"
region="DFW"
default="true"
- serviceName="cloudFiles"
- publicURL="https://storage.clouddrive.com/v1"
- internalURL="https://storage-snet.clouddrive.com/v1"
+ serviceName="service1"
+ publicURL="https://service-public.com/v1"
+ internalURL="https://service-internal.com/v1"
enabled="true"
/>
diff --git a/docs/guide/src/docbkx/samples/baseURLRef.json b/docs/guide/src/docbkx/samples/baseURLRef.json
index 4e0cd4b2..aed01500 100644
--- a/docs/guide/src/docbkx/samples/baseURLRef.json
+++ b/docs/guide/src/docbkx/samples/baseURLRef.json
@@ -1,6 +1,5 @@
{
"baseURLRef" : {
- "id" : 3,
- "v1Default" : true
+ "id" : 3
}
}
diff --git a/docs/guide/src/docbkx/samples/baseURLRef.xml b/docs/guide/src/docbkx/samples/baseURLRef.xml
index b9dc8c46..16c71870 100644
--- a/docs/guide/src/docbkx/samples/baseURLRef.xml
+++ b/docs/guide/src/docbkx/samples/baseURLRef.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<baseURLRef xmlns="http://docs.openstack.org/identity/api/v2.0"
- id="3" v1Default="true"/>
+ id="3" />
diff --git a/docs/guide/src/docbkx/samples/baseURLRefs.json b/docs/guide/src/docbkx/samples/baseURLRefs.json
index 30e56d45..a815f2b9 100644
--- a/docs/guide/src/docbkx/samples/baseURLRefs.json
+++ b/docs/guide/src/docbkx/samples/baseURLRefs.json
@@ -2,26 +2,23 @@
"baseURLRefs" : [
{
"id" : 1,
- "href" : "https://auth.api.rackspacecloud.com/v1.1/baseURLs/1",
- "v1Default" : true
+ "href" : "https://auth.keystone.com/v2.0/baseURLs/1"
},
{
- "id" : 2,
- "href" : "https://auth.api.rackspacecloud.com/v1.1/baseURLs/2"
+ "id" : 2
+ "href" : "https://auth.keystone.com/v2.0/baseURLs/2"
},
{
"id" : 3,
- "href" : "https://auth.api.rackspacecloud.com/v1.1/baseURLs/3",
- "v1Default" : true
+ "href" : "https://auth.keystone.com/v2.0/baseURLs/3"
},
{
"id" : 4,
- "href" : "https://auth.api.rackspacecloud.com/v1.1/baseURLs/4"
+ "href" : "https://auth.keystone.com/v2.0/baseURLs/4"
},
{
"id" : 5,
- "href" : "https://auth.api.rackspacecloud.com/v1.1/baseURLs/5",
- "v1Default" : true
+ "href" : "https://auth.keystone.com/v2.0/baseURLs/5"
}
]
}
diff --git a/docs/guide/src/docbkx/samples/baseURLRefs.xml b/docs/guide/src/docbkx/samples/baseURLRefs.xml
index 51029ff5..0c095626 100644
--- a/docs/guide/src/docbkx/samples/baseURLRefs.xml
+++ b/docs/guide/src/docbkx/samples/baseURLRefs.xml
@@ -3,18 +3,18 @@
<baseURLRefs
xmlns="http://docs.openstack.org/identity/api/v2.0">
<baseURLRef
- href="https://auth.api.rackspacecloud.com/v1.1/baseURLs/1"
- id="1" v1Default="true" />
+ href="https://auth.keystone.com/v2.0/baseURLs/1"
+ id="1" />
<baseURLRef
- href="https://auth.api.rackspacecloud.com/v1.1/baseURLs/2"
+ href="https://auth.keystone.com/v2.0/baseURLs/2"
id="2" />
<baseURLRef
- href="https://auth.api.rackspacecloud.com/v1.1/baseURLs/3"
- id="3" v1Default="true" />
+ href="https://auth.keystone.com/v2.0/baseURLs/3"
+ id="3" />
<baseURLRef
- href="https://auth.api.rackspacecloud.com/v1.1/baseURLs/4"
+ href="https://auth.keystone.com/v2.0/baseURLs/4"
id="4" />
<baseURLRef
- href="https://auth.api.rackspacecloud.com/v1.1/baseURLs/5"
- id="5" v1Default="true" />
+ href="https://auth.keystone.com/v2.0/baseURLs/5"
+ id="5" />
</baseURLRefs>
diff --git a/docs/guide/src/docbkx/samples/baseURLs.json b/docs/guide/src/docbkx/samples/baseURLs.json
index 58b5bd24..da3d1d14 100644
--- a/docs/guide/src/docbkx/samples/baseURLs.json
+++ b/docs/guide/src/docbkx/samples/baseURLs.json
@@ -2,46 +2,41 @@
"baseURLs" : [
{
"id" : 1,
- "userType" : "NAST",
"region" : "DFW",
"default" : true,
- "serviceName" : "cloudFiles",
- "publicURL" : "https://storage.clouddrive.com/v1",
- "internalURL" : "https://storage-snet.clouddrive.com/v1",
+ "serviceName" : "service1",
+ "publicURL" : "https://service1.public.com/v1",
+ "internalURL" : "https://service1.internal.com/v1",
"enabled" : true
},
{
"id" : 2,
- "userType" : "NAST",
"region" : "ORD",
- "serviceName" : "cloudFiles",
- "publicURL" : "https://otherstorage.clouddrive.com/v1",
- "internalURL" : "https://otherstorage-snet.clouddrive.com/v1",
+ "serviceName" : "service2",
+ "publicURL" : "https://service2.public.com/v1",
+ "internalURL" : "https://service2.internal.com/v1",
"enabled" : false
},
{
"id" : 3,
- "userType" : "NAST",
"region" : "DFW",
"default" : true,
- "serviceName" : "cloudFilesCDN",
- "publicURL" : "https://cdn.clouddrive.com/v1",
+ "serviceName" : "service1",
+ "publicURL" : "https://service.public.com/v1",
"enabled" : true
},
{
"id" : 4,
- "userType" : "NAST",
"region" : "ORD",
- "serviceName" : "cloudFilesCDN",
- "publicURL" : "https://othercdn.clouddrive.com/v1",
+ "serviceName" : "service2",
+ "publicURL" : "https://service2.public.com/v1",
"enabled" : true
},
{
"id" : 5,
- "userType" : "MOSSO",
"default" : true,
- "serviceName" : "cloudServers",
- "publicURL" : "https://servers.api.rackspacecloud.com/v1.0",
+ "serviceName" : "service3",
+ "publicURL" : "https://service3.public.com/v1.0",
"enabled" : true
}
]
diff --git a/docs/guide/src/docbkx/samples/baseURLs.xml b/docs/guide/src/docbkx/samples/baseURLs.xml
index 0faa3a5b..9f829e9d 100644
--- a/docs/guide/src/docbkx/samples/baseURLs.xml
+++ b/docs/guide/src/docbkx/samples/baseURLs.xml
@@ -3,45 +3,40 @@
<baseURLs xmlns="http://docs.openstack.org/identity/api/v2.0">
<baseURL
id="1"
- userType="NAST"
region="DFW"
default="true"
- serviceName="cloudFiles"
- publicURL="https://storage.clouddrive.com/v1"
- internalURL="https://storage-snet.clouddrive.com/v1"
+ serviceName="service1"
+ publicURL="https://service1.public.com/v1"
+ internalURL="https://service1.internal.clouddrive.com/v1"
enabled="true"
/>
<baseURL
id="2"
- userType="NAST"
region="ORD"
- serviceName="cloudFiles"
- publicURL="https://otherstorage.clouddrive.com/v1"
- internalURL="https://otherstorage-snet.clouddrive.com/v1"
+ serviceName="service2"
+ publicURL="https://service2.public.com/v1"
+ internalURL="https://service2.internal.public.com/v1"
enabled="false"
/>
<baseURL
id="3"
- userType="NAST"
region="DFW"
default="true"
- serviceName="cloudFilesCDN"
- publicURL="https://cdn.clouddrive.com/v1"
+ serviceName="service1"
+ publicURL="https://service1.public.com/v1"
enabled="true"
/>
<baseURL
id="4"
- userType="NAST"
region="ORD"
- serviceName="cloudFilesCDN"
- publicURL="https://othercdn.clouddrive.com/v1"
+ serviceName="service2"
+ publicURL="https://service2.public.com/v1"
enabled="true"
/>
<baseURL
id="5"
- userType="MOSSO"
default="true"
- serviceName="cloudServers"
- publicURL="https://servers.api.rackspacecloud.com/v1.0"
+ serviceName="service3"
+ publicURL="https://service3.public.com/v1"
/>
</baseURLs>
diff --git a/docs/guide/src/docbkx/samples/role.json b/docs/guide/src/docbkx/samples/role.json
index 08c360e1..d52e2c76 100644
--- a/docs/guide/src/docbkx/samples/role.json
+++ b/docs/guide/src/docbkx/samples/role.json
@@ -2,6 +2,6 @@
"role" :
{
"id" : "Admin",
- "serviceName" : "cloudFiles"
+ "description" : "cloudFiles"
}
} \ No newline at end of file
diff --git a/docs/guide/src/docbkx/samples/role.xml b/docs/guide/src/docbkx/samples/role.xml
index 50353631..5a4ecf19 100644
--- a/docs/guide/src/docbkx/samples/role.xml
+++ b/docs/guide/src/docbkx/samples/role.xml
@@ -1,7 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
-<role
- xmlns="http://docs.openstack.org/identity/api/v2.0"
- id="Admin"
- serviceName="cloudFiles"
- />
+<role xmlns="http://docs.openstack.org/identity/api/v2.0" id="Admin"
+ description="cloudFiles" />
diff --git a/docs/guide/src/docbkx/samples/roleRefs.json b/docs/guide/src/docbkx/samples/roleRefs.json
index a4300231..77819d44 100644
--- a/docs/guide/src/docbkx/samples/roleRefs.json
+++ b/docs/guide/src/docbkx/samples/roleRefs.json
@@ -1,14 +1,14 @@
{
"roleRefs" : [
{
- "id" : 1,
- "href" : "https://.openstack.org/identity/v1.1/roles/admin",
- "tenantId" : "one"
+ "id" : 1,
+ "href" : "https://.openstack.org/identity/v2.0/roles/admin",
+ "tenantId" : "one"
},
{
- "id" : 2,
- "href" : "https://.openstack.org/identity/v1.1/roles/test",
- "tenantId" : "two"
- },
+ "id" : 2,
+ "href" : "https://.openstack.org/identity/v2.0/roles/test",
+ "tenantId" : "two"
+ }
]
}
diff --git a/docs/guide/src/docbkx/samples/roleRefs.xml b/docs/guide/src/docbkx/samples/roleRefs.xml
index 36906df4..284154ce 100644
--- a/docs/guide/src/docbkx/samples/roleRefs.xml
+++ b/docs/guide/src/docbkx/samples/roleRefs.xml
@@ -3,7 +3,7 @@
<roleRefs
xmlns="http://docs.openstack.org/identity/api/v2.0">
<roleRef xmlns="http://docs.openstack.org/identity/api/v2.0"
- href="https://.openstack.org/identity/v1.1/roles/admin" id="3" tenantId="tenantId"/>
+ href="https://.openstack.org/identity/v2.0/roles/admin" id="3" tenantId="tenantId"/>
<roleRef xmlns="http://docs.openstack.org/identity/api/v2.0"
- href="https://.openstack.org/identity/v1.1/roles/test" id="4" tenantId="tenantId"/>
+ href="https://.openstack.org/identity/v2.0/roles/test" id="4" tenantId="tenantId"/>
</roleRefs>
diff --git a/docs/guide/src/docbkx/samples/roles.json b/docs/guide/src/docbkx/samples/roles.json
index 091ecbe8..5d5636ea 100644
--- a/docs/guide/src/docbkx/samples/roles.json
+++ b/docs/guide/src/docbkx/samples/roles.json
@@ -2,11 +2,11 @@
"roles" : [
{
"id" : 1,
- "serviceName" : "cloudFiles"
+ "description" : "cloudFiles"
},
{
"id" : 2,
- "serviceName" : "cloudFiles"
+ "description" : "cloudFiles"
},
]
}
diff --git a/docs/guide/src/docbkx/samples/roles.xml b/docs/guide/src/docbkx/samples/roles.xml
index 32073f63..53fbdf73 100644
--- a/docs/guide/src/docbkx/samples/roles.xml
+++ b/docs/guide/src/docbkx/samples/roles.xml
@@ -1,12 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<roles xmlns="http://docs.openstack.org/identity/api/v2.0">
- <role
- id="4"
- serviceName="cloudFilesCDN"
- />
- <role
- id="5"
- serviceName="cloudServers"
- />
+ <role id="4" description="cloudFilesCDN" />
+ <role id="5" description="cloudServers" />
</roles> \ No newline at end of file
diff --git a/docs/guide/src/docbkx/samples/validatetoken.json b/docs/guide/src/docbkx/samples/validatetoken.json
index c4721873..6c9f16f9 100644
--- a/docs/guide/src/docbkx/samples/validatetoken.json
+++ b/docs/guide/src/docbkx/samples/validatetoken.json
@@ -12,6 +12,14 @@
"name": "Admin"
}
]},
+ "roleRefs": {
+ "roleRef" : [
+ {
+ "id" : 1,
+ "href" : "https://.openstack.org/identity/v2.0/roles/admin",
+ "tenantId" : "one"
+ }
+ ]},
"username": "jqsmith",
"tenantId": "1234",
}
diff --git a/docs/guide/src/docbkx/samples/validatetoken.xml b/docs/guide/src/docbkx/samples/validatetoken.xml
index e04742ae..af40a8b6 100644
--- a/docs/guide/src/docbkx/samples/validatetoken.xml
+++ b/docs/guide/src/docbkx/samples/validatetoken.xml
@@ -6,5 +6,9 @@
<groups>
<group tenantId="1245" name="Admin"/>
</groups>
+ <roleRefs>
+ <roleRef href="https://.openstack.org/identity/v2.0/roles/admin" id="3" tenantId="tenantId"/>
+ <roleRef href="https://.openstack.org/identity/v2.0/roles/test" id="4" tenantId="tenantId"/>
+ </roleRefs>
</user>
</auth>
diff --git a/docs/guide/src/docbkx/xsd/api.xsd b/docs/guide/src/docbkx/xsd/api.xsd
index d4603c2e..a23abd99 100644
--- a/docs/guide/src/docbkx/xsd/api.xsd
+++ b/docs/guide/src/docbkx/xsd/api.xsd
@@ -11,4 +11,6 @@
<include schemaLocation="token.xsd"/>
<include schemaLocation="tenant.xsd"/>
<include schemaLocation="fault.xsd"/>
+ <include schemaLocation="baseURLs.xsd"/>
+ <include schemaLocation="roles.xsd"/>
</schema>
diff --git a/docs/guide/src/docbkx/xsd/roles.xsd b/docs/guide/src/docbkx/xsd/roles.xsd
index 4ae8449b..6f8cc207 100644
--- a/docs/guide/src/docbkx/xsd/roles.xsd
+++ b/docs/guide/src/docbkx/xsd/roles.xsd
@@ -62,9 +62,16 @@
<!-- Complex Types -->
<complexType name="Role">
<attribute name="id" type="xsd:string" use="required"/>
- <attribute name="service" type="xsd:string" use="optional"/>
+ <attribute name="description" type="xsd:string" use="optional"/>
</complexType>
+ <complexType name="RoleList">
+ <sequence>
+ <element name="role" type="idm:Role" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ </complexType>
+
+
<complexType name="RoleRef">
<attribute name="id" type="xsd:int" use="required"/>
<attribute name="tenantId" type="xsd:string" use="required" />
@@ -81,11 +88,6 @@
</attribute>
</complexType>
- <complexType name="RoleList">
- <sequence>
- <element name="role" type="idm:Role" minOccurs="0" maxOccurs="unbounded"/>
- </sequence>
- </complexType>
<complexType name="RoleRefList">
<sequence>
diff --git a/docs/guide/src/docbkx/xsd/token.xsd b/docs/guide/src/docbkx/xsd/token.xsd
index 888bbebf..5e9aae46 100644
--- a/docs/guide/src/docbkx/xsd/token.xsd
+++ b/docs/guide/src/docbkx/xsd/token.xsd
@@ -8,6 +8,7 @@
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://docs.openstack.org/identity/api/v2.0"
>
+ <include schemaLocation="roles.xsd"/>
<!-- Elements -->
<element name="passwordCredentials" type="identity:PasswordCredentials"/>
<element name="auth" type="identity:AuthData"/>
@@ -32,11 +33,12 @@
<sequence>
<element name="token" type="identity:Token"/>
<element name="user" type="identity:User"/>
+ <element name="serviceCatalog" type="identity:ServiceCatalog"/>
<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" />
@@ -49,6 +51,7 @@
<complexType name="User">
<sequence>
<element name="groups" type="identity:Groups" />
+ <element name="roleRefs" type="identity:RoleRefList" />
<any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="tenantId" type="xsd:string"/>
@@ -63,10 +66,105 @@
</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>
+
+ <complexType name="ServiceCatalog">
+ <sequence>
+ <element name="service" type="identity:Service" minOccurs="1" maxOccurs="unbounded">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ A list of services.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </element>
+ </sequence>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="Service">
+ <sequence>
+ <element name="endpoint" type="identity:Endpoint" minOccurs="1" maxOccurs="unbounded">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ A list of endpoints.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </element>
+ </sequence>
+ <attribute name="name" type="xsd:ID" use="required">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ The service name.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ <complexType name="Endpoint">
+ <attribute name="region" type="xsd:string" use="optional">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ The airport code of the region where the endpoint
+ lives.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ <attribute name="publicURL" type="xsd:anyURI" use="required">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ The publically accessible service URL.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ <attribute name="internalURL" type="xsd:anyURI" use="optional">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ A service URL, accessible only within the
+ Rackspace Cloud.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ <attribute name="adminURL" type="xsd:anyURI" use="optional">
+ <annotation>
+ <xsd:documentation
+ xml:lang="EN"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ A service URL used for administration. This may expose
+ additional functionality not found in the public and
+ internal URL.
+ </p>
+ </xsd:documentation>
+ </annotation>
+ </attribute>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
</schema>
diff --git a/keystone/db/sqlalchemy/api.py b/keystone/db/sqlalchemy/api.py
index 95fbb9ee..d6943693 100644
--- a/keystone/db/sqlalchemy/api.py
+++ b/keystone/db/sqlalchemy/api.py
@@ -101,6 +101,17 @@ def role_get_all(session=None):
session = get_session()
return session.query(models.Role).all()
+def role_get_page(marker, limit, session=None):
+ if not session:
+ session = get_session()
+
+ if marker:
+ return session.query(models.Role).filter("id>:marker").params(\
+ marker='%s' % marker).order_by(\
+ models.Tenant.id.desc()).limit(limit).all()
+ else:
+ return session.query(models.Tenant).order_by(\
+ models.Tenant.id.desc()).limit(limit).all()
#
# Tenant API operations
diff --git a/keystone/logic/service.py b/keystone/logic/service.py
index c9fc0e2d..90040a97 100644
--- a/keystone/logic/service.py
+++ b/keystone/logic/service.py
@@ -23,6 +23,7 @@ import keystone.db.sqlalchemy.api as db_api
import keystone.db.sqlalchemy.models as db_models
import keystone.logic.types.fault as fault
import keystone.logic.types.tenant as tenants
+import keystone.logic.types.role as roles
import keystone.logic.types.user as users
@@ -864,3 +865,48 @@ class IdentityService(object):
"to make this call")
'''
return (token, user)
+
+ def create_role(self, admin_token, role):
+ self.__validate_token(admin_token)
+
+ if not isinstance(role, roles.Role):
+ raise fault.BadRequestFault("Expecting a Role")
+
+ if role.role_id == None:
+ raise fault.BadRequestFault("Expecting a Role Id")
+
+ if db_api.role_get(role.role_id) != None:
+ raise fault.RoleConflictFault(
+ "A role with that id already exists")
+ drole = db_models.Role()
+ drole.id = role.role_id
+ drole.desc = role.desc
+ db_api.role_create(drole)
+ return role
+
+ def get_roles(self, admin_token, marker, limit, url):
+ self.__validate_token(admin_token)
+
+ ts = []
+ droles = db_api.role_get_page(marker, limit)
+ for drole in droles:
+ ts.append(roles.Role(drole.id,
+ drole.desc))
+ prev, next = db_api.tenant_get_page_markers(marker, limit)
+ links = []
+ if prev:
+ links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \
+ % (url, prev, limit)))
+ if next:
+ links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \
+ % (url, next, limit)))
+ return roles.Roles(ts, links)
+
+ def get_role(self, admin_token, role_id):
+ self.__validate_token(admin_token)
+
+ drole = db_api.role_get(role_id)
+ if not drole:
+ raise fault.ItemNotFoundFault("The role could not be found")
+ return roles.Role(drole.id, drole.desc)
+
diff --git a/keystone/logic/types/fault.py b/keystone/logic/types/fault.py
index cbded19f..cfcd15d2 100644
--- a/keystone/logic/types/fault.py
+++ b/keystone/logic/types/fault.py
@@ -159,3 +159,10 @@ class UserGroupConflictFault(IdentityFault):
def __init__(self, msg, details=None, code=409):
super(UserGroupConflictFault, self).__init__(msg, details, code)
self.key = "userGroupConflict"
+
+class RoleConflictFault(IdentityFault):
+ "The User already exists?"
+
+ def __init__(self, msg, details=None, code=409):
+ super(RoleConflictFault, self).__init__(msg, details, code)
+ self.key = "roleConflict"
diff --git a/keystone/logic/types/role.py b/keystone/logic/types/role.py
new file mode 100644
index 00000000..f0d7ab41
--- /dev/null
+++ b/keystone/logic/types/role.py
@@ -0,0 +1,107 @@
+# 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
+import string
+
+import keystone.logic.types.fault as fault
+
+class Role(object):
+ def __init__(self, role_id, desc):
+ self.role_id = role_id
+ self.desc = desc
+ @staticmethod
+ def from_xml(xml_str):
+ try:
+ dom = etree.Element("root")
+ dom.append(etree.fromstring(xml_str))
+ root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \
+ "role")
+ if root == None:
+ raise fault.BadRequestFault("Expecting Role")
+ role_id = root.get("id")
+ desc = root.get("description")
+ if role_id == None:
+ raise fault.BadRequestFault("Expecting Role")
+ return Role(role_id, desc)
+ except etree.LxmlError as e:
+ raise fault.BadRequestFault("Cannot parse Role", str(e))
+
+ @staticmethod
+ def from_json(json_str):
+ try:
+ obj = json.loads(json_str)
+ if not "role" in obj:
+ raise fault.BadRequestFault("Expecting Role")
+ role = obj["role"]
+ if not "id" in role:
+ role_id = None
+ else:
+ role_id = role["id"]
+ if role_id == None:
+ raise fault.BadRequestFault("Expecting Role")
+ desc = role["description"]
+ return Role(role_id, desc)
+ except (ValueError, TypeError) as e:
+ raise fault.BadRequestFault("Cannot parse Role", str(e))
+
+ def to_dom(self):
+ dom = etree.Element("role",
+ xmlns="http://docs.openstack.org/identity/api/v2.0")
+ if self.role_id:
+ dom.set("id", self.role_id)
+ if self.desc:
+ dom.set("description", string.lower(str(self.desc)))
+ return dom
+
+ def to_xml(self):
+ return etree.tostring(self.to_dom())
+
+ def to_dict(self):
+ role = {}
+ if self.role_id:
+ role["id"] = self.role_id
+ if self.desc:
+ role["description"] = self.desc
+ return {'role': role}
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+class Roles(object):
+ "A collection of roles."
+
+ def __init__(self, values, links):
+ self.values = values
+ self.links = links
+
+ def to_xml(self):
+ dom = etree.Element("roles")
+ dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0")
+
+ for t in self.values:
+ dom.append(t.to_dom())
+
+ for t in self.links:
+ dom.append(t.to_dom())
+
+ return etree.tostring(dom)
+
+ def to_json(self):
+ values = [t.to_dict()["role"] for t in self.values]
+ links = [t.to_dict()["links"] for t in self.links]
+ return json.dumps({"roles": {"values": values, "links": links}})
+ \ No newline at end of file
diff --git a/keystone/server.py b/keystone/server.py
index 59a3f8e3..b128ddf6 100644
--- a/keystone/server.py
+++ b/keystone/server.py
@@ -55,6 +55,7 @@ from keystone.common import wsgi
from keystone.db.sqlalchemy import api as db_api
import keystone.logic.service as serv
import keystone.logic.types.tenant as tenants
+import keystone.logic.types.role as roles
import keystone.logic.types.auth as auth
import keystone.logic.types.user as users
import keystone.common.template as template
@@ -512,8 +513,49 @@ class GroupsController(wsgi.Controller):
return utils.send_result(204, req, service.delete_user_global_group(\
utils.get_auth_token(req), group_id, user_id))
+
+class RolesController(wsgi.Controller):
+ """
+ Roles Controller -
+ Controller for Role related operations
+ """
+ def __init__(self, options):
+ self.options = options
+
+ # Not exposed yet.
+ @utils.wrap_error
+ def create_role(self, req):
+ role = utils.get_normalized_request_content(roles.Role, req)
+ return utils.send_result(201, req,
+ service.create_role(utils.get_auth_token(req),
+ role))
+ @utils.wrap_error
+ def get_roles(self, req):
+ marker = None
+ if "marker" in req.GET:
+ marker = req.GET["marker"]
+
+ if "limit" in req.GET:
+ limit = req.GET["limit"]
+ else:
+ limit = 10
+
+ url = '%s://%s:%s%s' % (req.environ['wsgi.url_scheme'],
+ req.environ.get("SERVER_NAME"),
+ req.environ.get("SERVER_PORT"),
+ req.environ['PATH_INFO'])
+ roles = service.get_roles(utils.get_auth_token(req),
+ marker, limit, url)
+
+ return utils.send_result(200, req, roles)
+
+ @utils.wrap_error
+ def get_role(self, req, role_id):
+ role = service.get_role(utils.get_auth_token(req), role_id)
+ return utils.send_result(200, req, role)
+
class KeystoneAPI(wsgi.Router):
"""WSGI entry point for public Keystone API requests."""
@@ -733,6 +775,14 @@ class KeystoneAdminAPI(wsgi.Router):
action="delete_user_global_group",
conditions=dict(method=["DELETE"]))
+ #Roles
+ roles_controller = RolesController(options)
+ mapper.connect("/v2.0/roles", controller=roles_controller,
+ action="get_roles", conditions=dict(method=["GET"]))
+ mapper.connect("/v2.0/roles/{role_id}", controller=roles_controller,
+ action="get_role", conditions=dict(method=["GET"]))
+
+
# Miscellaneous Operations
version_controller = VersionController(options)
mapper.connect("/v2.0/", controller=version_controller,
diff --git a/test/unit/test_common.py b/test/unit/test_common.py
index 800f1a7c..2dc6e193 100644
--- a/test/unit/test_common.py
+++ b/test/unit/test_common.py
@@ -704,3 +704,31 @@ def handle_user_resp(self, content, respvalue, resptype):
self.fail('Identity Fault')
elif respvalue == 503:
self.fail('Service Not Available')
+
+def create_role(roleid, auth_token):
+ header = httplib2.Http(".cache")
+
+ url = '%sroles' % (URL)
+ body = {"role": {"id": roleid,
+ "description": "A description ..."}}
+ resp, content = header.request(url, "POST", body=json.dumps(body),
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": auth_token})
+ return (resp, content)
+
+def create_role_xml(role_id, auth_token):
+ header = httplib2.Http(".cache")
+ url = '%sroles' % (URL)
+ body = '<?xml version="1.0" encoding="UTF-8"?>\
+ <role xmlns="http://docs.openstack.org/identity/api/v2.0" \
+ id="%s" description="A Description of the group"/>\
+ ' % role_id
+ print "Role XML Body is :" ,body
+ resp, content = header.request(url, "POST", body=body,
+ headers={"Content-Type": "application/xml",
+ "X-Auth-Token": auth_token,
+ "ACCEPT": "application/xml"})
+ return (resp, content)
+
+if __name__ == '__main__':
+ unittest.main() \ No newline at end of file
diff --git a/test/unit/test_roles.py b/test/unit/test_roles.py
new file mode 100644
index 00000000..37021440
--- /dev/null
+++ b/test/unit/test_roles.py
@@ -0,0 +1,166 @@
+# 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 httplib2
+import json
+from lxml import etree
+import os
+import sys
+sys.path.append(os.path.abspath(os.path.join(os.path.abspath(__file__),
+ '..', '..', '..', '..', 'keystone')))
+import unittest
+
+import test_common as utils
+from test_common import URL
+
+class RolesTest(unittest.TestCase):
+ def setUp(self):
+ self.tenant = utils.get_tenant()
+ self.password = utils.get_password()
+ self.email = utils.get_email()
+ self.user = utils.get_user()
+ self.userdisabled = utils.get_userdisabled()
+ self.auth_token = utils.get_auth_token()
+ self.exp_auth_token = utils.get_exp_auth_token()
+ self.disabled_token = utils.get_disabled_token()
+ self.missing_token = utils.get_none_token()
+ self.invalid_token = utils.get_non_existing_token()
+ utils.create_tenant(self.tenant, str(self.auth_token))
+ utils.add_user_json(self.tenant, self.user, self.auth_token)
+ self.token = utils.get_token(self.user, 'secrete', self.tenant,
+ 'token')
+
+ def tearDown(self):
+ utils.delete_user(self.tenant, self.user, self.auth_token)
+ utils.delete_tenant(self.tenant, self.auth_token)
+
+class GetRolesTest(RolesTest):
+ def test_get_roles(self):
+ header = httplib2.Http(".cache")
+ url = '%sroles' % (utils.URL)
+ #test for Content-Type = application/json
+ resp, content = header.request(url, "GET", body='{}',
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_roles_xml(self):
+ header = httplib2.Http(".cache")
+ url = '%sroles' % (utils.URL)
+ #test for Content-Type = application/json
+ resp, content = header.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('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_roles_exp_token(self):
+ header = httplib2.Http(".cache")
+ url = '%sroles' % (utils.URL)
+ #test for Content-Type = application/json
+ resp, content = header.request(url, "GET", body='{}',
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": self.exp_auth_token})
+ if int(resp['status']) == 500:
+ self.fail('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+ def test_get_roles_exp_token_xml(self):
+ header = httplib2.Http(".cache")
+ url = '%stenants' % (utils.URL)
+ #test for Content-Type = application/json
+ resp, content = header.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('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(403, int(resp['status']))
+
+class GetRoleTest(RolesTest):
+
+ def test_get_role(self):
+ self.role = 'Admin'
+ header = httplib2.Http(".cache")
+ url = '%sroles/%s' % (utils.URL, self.role)
+ #test for Content-Type = application/json
+ resp, content = header.request(url, "GET", body='{}',
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_role_xml(self):
+ self.role = 'Admin'
+ header = httplib2.Http(".cache")
+ url = '%sroles/%s' % (utils.URL, self.role)
+ #test for Content-Type = application/json
+ resp, content = header.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('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(200, int(resp['status']))
+
+ def test_get_role_bad(self):
+ header = httplib2.Http(".cache")
+ url = '%sroles/%s' % (utils.URL, 'tenant_bad')
+ #test for Content-Type = application/json
+ resp, content = header.request(url, "GET", body='',
+ headers={"Content-Type": "application/json",
+ "X-Auth-Token": self.auth_token})
+ if int(resp['status']) == 500:
+ self.fail('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+ def test_get_role_bad_xml(self):
+ header = httplib2.Http(".cache")
+ resp, content = utils.create_tenant(self.tenant, str(self.auth_token))
+ url = '%sroles/%s' % (utils.URL, 'role_bad')
+ #test for Content-Type = application/json
+ resp, content = header.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('Identity Fault')
+ elif int(resp['status']) == 503:
+ self.fail('Service Not Available')
+ self.assertEqual(404, int(resp['status']))
+
+if __name__ == '__main__':
+ unittest.main()