summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-07-13 05:10:59 +0000
committerGerrit Code Review <review@openstack.org>2013-07-13 05:10:59 +0000
commit46eaab3f249cb47f5c75cb44a10c1bc4cf1b7cd8 (patch)
treec3229f12ad233e21c808690936ad9bd40f28c4c0
parent85a502278143bba5309dbfc2268bd083472400ad (diff)
parentc5900d0f43e5c76566059895d515477d8b716df0 (diff)
Merge "Register Extensions"
-rw-r--r--keystone/common/extension.py47
-rw-r--r--keystone/contrib/admin_crud/core.py21
-rw-r--r--keystone/contrib/ec2/core.py20
-rw-r--r--keystone/contrib/s3/core.py18
-rw-r--r--keystone/contrib/stats/core.py18
-rw-r--r--keystone/contrib/user_crud/core.py20
-rw-r--r--keystone/controllers.py40
-rw-r--r--tests/test_content_types.py48
8 files changed, 182 insertions, 50 deletions
diff --git a/keystone/common/extension.py b/keystone/common/extension.py
new file mode 100644
index 00000000..f176a197
--- /dev/null
+++ b/keystone/common/extension.py
@@ -0,0 +1,47 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+
+ADMIN_EXTENSIONS = {}
+PUBLIC_EXTENSIONS = {}
+
+
+def register_admin_extension(url_prefix, extension_data):
+ """Register extension with collection of admin extensions.
+
+ Extensions register the information here that will show
+ up in the /extensions page as a way to indicate that the extension is
+ active.
+
+ url_prefix: unique key for the extension that will appear in the
+ urls generated by the extension.
+
+ extension_data is a dictionary. The expected fields are:
+ 'name': short, human readable name of the extnsion
+ 'namespace': xml namespace
+ 'alias': identifier for the extension
+ 'updated': date the extension was last updated
+ 'description': text description of the extension
+ 'links': hyperlinks to documents describing the extension
+
+ """
+ ADMIN_EXTENSIONS[url_prefix] = extension_data
+
+
+def register_public_extension(url_prefix, extension_data):
+ """Same as register_admin_extension but for public extensions."""
+
+ PUBLIC_EXTENSIONS[url_prefix] = extension_data
diff --git a/keystone/contrib/admin_crud/core.py b/keystone/contrib/admin_crud/core.py
index c06afcf7..f98397ae 100644
--- a/keystone/contrib/admin_crud/core.py
+++ b/keystone/contrib/admin_crud/core.py
@@ -14,10 +14,31 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystone import catalog
+from keystone.common import extension
from keystone.common import wsgi
from keystone import identity
+extension.register_admin_extension(
+ 'OS-KSADM', {
+ 'name': 'OpenStack Keystone Admin',
+ 'namespace': 'http://docs.openstack.org/identity/api/ext/'
+ 'OS-KSADM/v1.0',
+ 'alias': 'OS-KSADM',
+ 'updated': '2013-07-11T17:14:00-00:00',
+ 'description': 'OpenStack extensions to Keystone v2.0 API '
+ 'enabling Administrative Operations.',
+ 'links': [
+ {
+ 'rel': 'describedby',
+ # TODO(dolph): link needs to be revised after
+ # bug 928059 merges
+ 'type': 'text/html',
+ 'href': 'https://github.com/openstack/identity-api',
+ }
+ ]})
+
+
class CrudExtension(wsgi.ExtensionRouter):
"""Previously known as the OS-KSADM extension.
diff --git a/keystone/contrib/ec2/core.py b/keystone/contrib/ec2/core.py
index 5254b53f..7e6c04f5 100644
--- a/keystone/contrib/ec2/core.py
+++ b/keystone/contrib/ec2/core.py
@@ -40,6 +40,7 @@ from keystoneclient.contrib.ec2 import utils as ec2_utils
from keystone.common import controller
from keystone.common import dependency
+from keystone.common import extension
from keystone.common import manager
from keystone.common import utils
from keystone.common import wsgi
@@ -51,6 +52,25 @@ from keystone import token
CONF = config.CONF
+EXTENSION_DATA = {
+ 'name': 'OpenStack EC2 API',
+ 'namespace': 'http://docs.openstack.org/identity/api/ext/'
+ 'OS-EC2/v1.0',
+ 'alias': 'OS-EC2',
+ 'updated': '2013-07-07T12:00:0-00:00',
+ 'description': 'OpenStack EC2 Credentials backend.',
+ 'links': [
+ {
+ 'rel': 'describedby',
+ # TODO(ayoung): needs a description
+ 'type': 'text/html',
+ 'href': 'https://github.com/openstack/identity-api',
+ }
+ ]}
+extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
+extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
+
+
@dependency.provider('ec2_api')
class Manager(manager.Manager):
"""Default pivot point for the EC2 Credentials backend.
diff --git a/keystone/contrib/s3/core.py b/keystone/contrib/s3/core.py
index 44b038d4..29ea4fe2 100644
--- a/keystone/contrib/s3/core.py
+++ b/keystone/contrib/s3/core.py
@@ -27,6 +27,7 @@ import base64
import hashlib
import hmac
+from keystone.common import extension
from keystone.common import utils
from keystone.common import wsgi
from keystone import config
@@ -35,6 +36,23 @@ from keystone import exception
CONF = config.CONF
+EXTENSION_DATA = {
+ 'name': 'OpenStack S3 API',
+ 'namespace': 'http://docs.openstack.org/identity/api/ext/'
+ 's3tokens/v1.0',
+ 'alias': 's3tokens',
+ 'updated': '2013-07-07T12:00:0-00:00',
+ 'description': 'OpenStack S3 API.',
+ 'links': [
+ {
+ 'rel': 'describedby',
+ # TODO(ayoung): needs a description
+ 'type': 'text/html',
+ 'href': 'https://github.com/openstack/identity-api',
+ }
+ ]}
+extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
+
class S3Extension(wsgi.ExtensionRouter):
def add_routes(self, mapper):
diff --git a/keystone/contrib/stats/core.py b/keystone/contrib/stats/core.py
index 0325c4fa..1d7b2cdf 100644
--- a/keystone/contrib/stats/core.py
+++ b/keystone/contrib/stats/core.py
@@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from keystone.common import extension
from keystone.common import logging
from keystone.common import manager
from keystone.common import wsgi
@@ -27,6 +28,23 @@ from keystone import token
CONF = config.CONF
LOG = logging.getLogger(__name__)
+extension_data = {
+ 'name': 'Openstack Keystone Stats API',
+ 'namespace': 'http://docs.openstack.org/identity/api/ext/'
+ 'OS-STATS/v1.0',
+ 'alias': 'OS-STATS',
+ 'updated': '2013-07-07T12:00:0-00:00',
+ 'description': 'Openstack Keystone Stats API.',
+ 'links': [
+ {
+ 'rel': 'describedby',
+ # TODO(ayoung): needs a description
+ 'type': 'text/html',
+ 'href': 'https://github.com/openstack/identity-api',
+ }
+ ]}
+extension.register_admin_extension(extension_data['alias'], extension_data)
+
class Manager(manager.Manager):
"""Default pivot point for the Stats backend.
diff --git a/keystone/contrib/user_crud/core.py b/keystone/contrib/user_crud/core.py
index 7b3f459f..f9f09b89 100644
--- a/keystone/contrib/user_crud/core.py
+++ b/keystone/contrib/user_crud/core.py
@@ -17,6 +17,7 @@
import copy
import uuid
+from keystone.common import extension
from keystone.common import logging
from keystone.common import wsgi
from keystone import exception
@@ -26,6 +27,25 @@ from keystone import identity
LOG = logging.getLogger(__name__)
+extension.register_public_extension(
+ 'OS-KSCRUD', {
+ 'name': 'OpenStack Keystone User CRUD',
+ 'namespace': 'http://docs.openstack.org/identity/api/ext/'
+ 'OS-KSCRUD/v1.0',
+ 'alias': 'OS-KSCRUD',
+ 'updated': '2013-07-07T12:00:0-00:00',
+ 'description': 'OpenStack extensions to Keystone v2.0 API '
+ 'enabling User Operations.',
+ 'links': [
+ {
+ 'rel': 'describedby',
+ # TODO(ayoung): needs a description
+ 'type': 'text/html',
+ 'href': 'https://github.com/openstack/identity-api',
+ }
+ ]})
+
+
class UserController(identity.controllers.User):
def set_user_password(self, context, user_id, user):
token_id = context.get('token_id')
diff --git a/keystone/controllers.py b/keystone/controllers.py
index 6dd303e1..8ffa073a 100644
--- a/keystone/controllers.py
+++ b/keystone/controllers.py
@@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from keystone.common import extension
from keystone.common import logging
from keystone.common import wsgi
from keystone import config
@@ -32,10 +33,10 @@ _VERSIONS = []
class Extensions(wsgi.Application):
"""Base extensions controller to be extended by public and admin API's."""
- def __init__(self, extensions=None):
- super(Extensions, self).__init__()
-
- self.extensions = extensions or {}
+ #extend in subclass to specify the set of extensions
+ @property
+ def extensions(self):
+ return None
def get_extensions_info(self, context):
return {'extensions': {'values': self.extensions.values()}}
@@ -48,34 +49,15 @@ class Extensions(wsgi.Application):
class AdminExtensions(Extensions):
- def __init__(self, *args, **kwargs):
- super(AdminExtensions, self).__init__(*args, **kwargs)
-
- # TODO(dolph): Extensions should obviously provide this information
- # themselves, but hardcoding it here allows us to match
- # the API spec in the short term with minimal complexity.
- self.extensions['OS-KSADM'] = {
- 'name': 'Openstack Keystone Admin',
- 'namespace': 'http://docs.openstack.org/identity/api/ext/'
- 'OS-KSADM/v1.0',
- 'alias': 'OS-KSADM',
- 'updated': '2011-08-19T13:25:27-06:00',
- 'description': 'Openstack extensions to Keystone v2.0 API '
- 'enabling Admin Operations.',
- 'links': [
- {
- 'rel': 'describedby',
- # TODO(dolph): link needs to be revised after
- # bug 928059 merges
- 'type': 'text/html',
- 'href': 'https://github.com/openstack/identity-api',
- }
- ]
- }
+ @property
+ def extensions(self):
+ return extension.ADMIN_EXTENSIONS
class PublicExtensions(Extensions):
- pass
+ @property
+ def extensions(self):
+ return extension.PUBLIC_EXTENSIONS
def register_version(version):
diff --git a/tests/test_content_types.py b/tests/test_content_types.py
index 278a1098..3213656c 100644
--- a/tests/test_content_types.py
+++ b/tests/test_content_types.py
@@ -23,6 +23,7 @@ import webtest
from keystone import test
+from keystone.common import extension
from keystone.common import serializer
from keystone.openstack.common import jsonutils
@@ -334,14 +335,14 @@ class CoreApiTests(object):
self.assertValidVersionResponse(r)
def test_public_extensions(self):
- self.public_request(path='/v2.0/extensions',)
-
- # TODO(dolph): can't test this without any public extensions defined
- # self.assertValidExtensionListResponse(r)
+ r = self.public_request(path='/v2.0/extensions')
+ self.assertValidExtensionListResponse(r,
+ extension.PUBLIC_EXTENSIONS)
def test_admin_extensions(self):
- r = self.admin_request(path='/v2.0/extensions',)
- self.assertValidExtensionListResponse(r)
+ r = self.admin_request(path='/v2.0/extensions')
+ self.assertValidExtensionListResponse(r,
+ extension.ADMIN_EXTENSIONS)
def test_admin_extensions_404(self):
self.admin_request(path='/v2.0/extensions/invalid-extension',
@@ -353,7 +354,8 @@ class CoreApiTests(object):
def test_admin_osksadm_extension(self):
r = self.admin_request(path='/v2.0/extensions/OS-KSADM')
- self.assertValidExtensionResponse(r)
+ self.assertValidExtensionResponse(r,
+ extension.ADMIN_EXTENSIONS)
def test_authenticate(self):
r = self.public_request(
@@ -611,24 +613,26 @@ class JsonTestCase(RestfulTestCase, CoreApiTests):
self.assertValidError(r.result['error'])
self.assertEqual(r.result['error']['code'], r.status_code)
- def assertValidExtension(self, extension):
+ def assertValidExtension(self, extension, expected):
super(JsonTestCase, self).assertValidExtension(extension)
-
- self.assertIsNotNone(extension.get('description'))
+ descriptions = [ext['description'] for ext in expected.itervalues()]
+ description = extension.get('description')
+ self.assertIsNotNone(description)
+ self.assertIn(description, descriptions)
self.assertIsNotNone(extension.get('links'))
self.assertNotEmpty(extension.get('links'))
for link in extension.get('links'):
self.assertValidExtensionLink(link)
- def assertValidExtensionListResponse(self, r):
+ def assertValidExtensionListResponse(self, r, expected):
self.assertIsNotNone(r.result.get('extensions'))
self.assertIsNotNone(r.result['extensions'].get('values'))
self.assertNotEmpty(r.result['extensions'].get('values'))
for extension in r.result['extensions']['values']:
- self.assertValidExtension(extension)
+ self.assertValidExtension(extension, expected)
- def assertValidExtensionResponse(self, r):
- self.assertValidExtension(r.result.get('extension'))
+ def assertValidExtensionResponse(self, r, expected):
+ self.assertValidExtension(r.result.get('extension'), expected)
def assertValidAuthenticationResponse(self, r,
require_service_catalog=False):
@@ -850,29 +854,31 @@ class XmlTestCase(RestfulTestCase, CoreApiTests):
self.assertValidError(xml)
self.assertEqual(xml.get('code'), str(r.status_code))
- def assertValidExtension(self, extension):
+ def assertValidExtension(self, extension, expected):
super(XmlTestCase, self).assertValidExtension(extension)
self.assertIsNotNone(extension.find(self._tag('description')))
self.assertTrue(extension.find(self._tag('description')).text)
links = extension.find(self._tag('links'))
self.assertNotEmpty(links.findall(self._tag('link')))
+ descriptions = [ext['description'] for ext in expected.itervalues()]
+ description = extension.find(self._tag('description')).text
+ self.assertIn(description, descriptions)
for link in links.findall(self._tag('link')):
self.assertValidExtensionLink(link)
- def assertValidExtensionListResponse(self, r):
+ def assertValidExtensionListResponse(self, r, expected):
xml = r.result
self.assertEqual(xml.tag, self._tag('extensions'))
-
self.assertNotEmpty(xml.findall(self._tag('extension')))
- for extension in xml.findall(self._tag('extension')):
- self.assertValidExtension(extension)
+ for ext in xml.findall(self._tag('extension')):
+ self.assertValidExtension(ext, expected)
- def assertValidExtensionResponse(self, r):
+ def assertValidExtensionResponse(self, r, expected):
xml = r.result
self.assertEqual(xml.tag, self._tag('extension'))
- self.assertValidExtension(xml)
+ self.assertValidExtension(xml, expected)
def assertValidVersion(self, version):
super(XmlTestCase, self).assertValidVersion(version)