Register Extensions
Extensions register themselves with keystone/common/extension.py as either public, admin, or both, and they show up in the extensions collection on http://<hostname>:<port>/v2.0/extensions/ Bug 1177531 Change-Id: Ic0b5c84e28342e96c3197c1b46f8b1656e2d7050
This commit is contained in:
parent
9a5b0c3a95
commit
c5900d0f43
47
keystone/common/extension.py
Normal file
47
keystone/common/extension.py
Normal file
@ -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
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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):
|
||||
|
@ -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.
|
||||
|
@ -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')
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user