diff options
Diffstat (limited to 'keystone-moon/keystone/version')
-rw-r--r-- | keystone-moon/keystone/version/__init__.py | 0 | ||||
-rw-r--r-- | keystone-moon/keystone/version/controllers.py | 215 | ||||
-rw-r--r-- | keystone-moon/keystone/version/routers.py | 80 | ||||
-rw-r--r-- | keystone-moon/keystone/version/service.py | 161 |
4 files changed, 456 insertions, 0 deletions
diff --git a/keystone-moon/keystone/version/__init__.py b/keystone-moon/keystone/version/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/keystone-moon/keystone/version/__init__.py diff --git a/keystone-moon/keystone/version/controllers.py b/keystone-moon/keystone/version/controllers.py new file mode 100644 index 00000000..2a7bacdf --- /dev/null +++ b/keystone-moon/keystone/version/controllers.py @@ -0,0 +1,215 @@ +# Copyright 2012 OpenStack Foundation +# +# 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 oslo_serialization import jsonutils +import webob + +from keystone.common import extension +from keystone.common import json_home +from keystone.common import wsgi +from keystone import exception + + +MEDIA_TYPE_JSON = 'application/vnd.openstack.identity-%s+json' + +_VERSIONS = [] + +# NOTE(blk-u): latest_app will be set by keystone.version.service.loadapp(). It +# gets set to the application that was just loaded. In the case of keystone-all +# loadapp() gets called twice, once for the public app and once for the admin +# app. In the case of httpd/keystone, loadapp() gets called once for the public +# app if this is the public instance or loadapp() gets called for the admin app +# if it's the admin instance. +# This is used to fetch the /v3 JSON Home response. The /v3 JSON Home response +# is the same whether it's the admin or public service so either admin or +# public works. +latest_app = None + + +def request_v3_json_home(new_prefix): + if 'v3' not in _VERSIONS: + # No V3 support, so return an empty JSON Home document. + return {'resources': {}} + + req = webob.Request.blank( + '/v3', headers={'Accept': 'application/json-home'}) + v3_json_home_str = req.get_response(latest_app).body + v3_json_home = jsonutils.loads(v3_json_home_str) + json_home.translate_urls(v3_json_home, new_prefix) + + return v3_json_home + + +class Extensions(wsgi.Application): + """Base extensions controller to be extended by public and admin API's.""" + + # extend in subclass to specify the set of extensions + @property + def extensions(self): + return None + + def get_extensions_info(self, context): + return {'extensions': {'values': list(self.extensions.values())}} + + def get_extension_info(self, context, extension_alias): + try: + return {'extension': self.extensions[extension_alias]} + except KeyError: + raise exception.NotFound(target=extension_alias) + + +class AdminExtensions(Extensions): + @property + def extensions(self): + return extension.ADMIN_EXTENSIONS + + +class PublicExtensions(Extensions): + @property + def extensions(self): + return extension.PUBLIC_EXTENSIONS + + +def register_version(version): + _VERSIONS.append(version) + + +class MimeTypes(object): + JSON = 'application/json' + JSON_HOME = 'application/json-home' + + +def v3_mime_type_best_match(context): + + # accept_header is a WebOb MIMEAccept object so supports best_match. + accept_header = context['accept_header'] + + if not accept_header: + return MimeTypes.JSON + + SUPPORTED_TYPES = [MimeTypes.JSON, MimeTypes.JSON_HOME] + return accept_header.best_match(SUPPORTED_TYPES) + + +class Version(wsgi.Application): + + def __init__(self, version_type, routers=None): + self.endpoint_url_type = version_type + self._routers = routers + + super(Version, self).__init__() + + def _get_identity_url(self, context, version): + """Returns a URL to keystone's own endpoint.""" + url = self.base_url(context, self.endpoint_url_type) + return '%s/%s/' % (url, version) + + def _get_versions_list(self, context): + """The list of versions is dependent on the context.""" + versions = {} + if 'v2.0' in _VERSIONS: + versions['v2.0'] = { + 'id': 'v2.0', + 'status': 'stable', + 'updated': '2014-04-17T00:00:00Z', + 'links': [ + { + 'rel': 'self', + 'href': self._get_identity_url(context, 'v2.0'), + }, { + 'rel': 'describedby', + 'type': 'text/html', + 'href': 'http://docs.openstack.org/' + } + ], + 'media-types': [ + { + 'base': 'application/json', + 'type': MEDIA_TYPE_JSON % 'v2.0' + } + ] + } + + if 'v3' in _VERSIONS: + versions['v3'] = { + 'id': 'v3.6', + 'status': 'stable', + 'updated': '2016-04-04T00:00:00Z', + 'links': [ + { + 'rel': 'self', + 'href': self._get_identity_url(context, 'v3'), + } + ], + 'media-types': [ + { + 'base': 'application/json', + 'type': MEDIA_TYPE_JSON % 'v3' + } + ] + } + + return versions + + def get_versions(self, context): + + req_mime_type = v3_mime_type_best_match(context) + if req_mime_type == MimeTypes.JSON_HOME: + v3_json_home = request_v3_json_home('/v3') + return wsgi.render_response( + body=v3_json_home, + headers=(('Content-Type', MimeTypes.JSON_HOME),)) + + versions = self._get_versions_list(context) + return wsgi.render_response(status=(300, 'Multiple Choices'), body={ + 'versions': { + 'values': list(versions.values()) + } + }) + + def get_version_v2(self, context): + versions = self._get_versions_list(context) + if 'v2.0' in _VERSIONS: + return wsgi.render_response(body={ + 'version': versions['v2.0'] + }) + else: + raise exception.VersionNotFound(version='v2.0') + + def _get_json_home_v3(self): + + def all_resources(): + for router in self._routers: + for resource in router.v3_resources: + yield resource + + return { + 'resources': dict(all_resources()) + } + + def get_version_v3(self, context): + versions = self._get_versions_list(context) + if 'v3' in _VERSIONS: + req_mime_type = v3_mime_type_best_match(context) + + if req_mime_type == MimeTypes.JSON_HOME: + return wsgi.render_response( + body=self._get_json_home_v3(), + headers=(('Content-Type', MimeTypes.JSON_HOME),)) + + return wsgi.render_response(body={ + 'version': versions['v3'] + }) + else: + raise exception.VersionNotFound(version='v3') diff --git a/keystone-moon/keystone/version/routers.py b/keystone-moon/keystone/version/routers.py new file mode 100644 index 00000000..5da4951c --- /dev/null +++ b/keystone-moon/keystone/version/routers.py @@ -0,0 +1,80 @@ +# Copyright 2012 OpenStack Foundation +# +# 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. +""" +The only types of routers in this file should be ``ComposingRouters``. + +The routers for the backends should be in the backend-specific router modules. +For example, the ``ComposableRouter`` for ``identity`` belongs in:: + + keystone.identity.routers + +""" + + +from keystone.common import wsgi +from keystone.version import controllers + + +class Extension(wsgi.ComposableRouter): + def __init__(self, is_admin=True): + if is_admin: + self.controller = controllers.AdminExtensions() + else: + self.controller = controllers.PublicExtensions() + + def add_routes(self, mapper): + extensions_controller = self.controller + mapper.connect('/extensions', + controller=extensions_controller, + action='get_extensions_info', + conditions=dict(method=['GET'])) + mapper.connect('/extensions/{extension_alias}', + controller=extensions_controller, + action='get_extension_info', + conditions=dict(method=['GET'])) + + +class VersionV2(wsgi.ComposableRouter): + def __init__(self, description): + self.description = description + + def add_routes(self, mapper): + version_controller = controllers.Version(self.description) + mapper.connect('/', + controller=version_controller, + action='get_version_v2') + + +class VersionV3(wsgi.ComposableRouter): + def __init__(self, description, routers): + self.description = description + self._routers = routers + + def add_routes(self, mapper): + version_controller = controllers.Version(self.description, + routers=self._routers) + mapper.connect('/', + controller=version_controller, + action='get_version_v3') + + +class Versions(wsgi.ComposableRouter): + def __init__(self, description): + self.description = description + + def add_routes(self, mapper): + version_controller = controllers.Version(self.description) + mapper.connect('/', + controller=version_controller, + action='get_versions') diff --git a/keystone-moon/keystone/version/service.py b/keystone-moon/keystone/version/service.py new file mode 100644 index 00000000..b0ed3b76 --- /dev/null +++ b/keystone-moon/keystone/version/service.py @@ -0,0 +1,161 @@ +# Copyright 2012 OpenStack Foundation +# +# 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 functools +import sys + +from oslo_config import cfg +from oslo_log import log +from paste import deploy +import routes + +from keystone.assignment import routers as assignment_routers +from keystone.auth import routers as auth_routers +from keystone.catalog import routers as catalog_routers +from keystone.common import wsgi +from keystone.credential import routers as credential_routers +from keystone.endpoint_policy import routers as endpoint_policy_routers +from keystone.federation import routers as federation_routers +from keystone.i18n import _LW +from keystone.identity import routers as identity_routers +from keystone.oauth1 import routers as oauth1_routers +from keystone.policy import routers as policy_routers +from keystone.resource import routers as resource_routers +from keystone.revoke import routers as revoke_routers +from keystone.token import _simple_cert as simple_cert_ext +from keystone.token import routers as token_routers +from keystone.trust import routers as trust_routers +from keystone.v2_crud import admin_crud +from keystone.v2_crud import user_crud +from keystone.version import controllers +from keystone.version import routers + + +CONF = cfg.CONF +LOG = log.getLogger(__name__) + + +def loadapp(conf, name): + # NOTE(blk-u): Save the application being loaded in the controllers module. + # This is similar to how public_app_factory() and v3_app_factory() + # register the version with the controllers module. + controllers.latest_app = deploy.loadapp(conf, name=name) + return controllers.latest_app + + +def fail_gracefully(f): + """Logs exceptions and aborts.""" + @functools.wraps(f) + def wrapper(*args, **kw): + try: + return f(*args, **kw) + except Exception as e: + LOG.debug(e, exc_info=True) + + # exception message is printed to all logs + LOG.critical(e) + sys.exit(1) + + return wrapper + + +def warn_local_conf(f): + @functools.wraps(f) + def wrapper(*args, **local_conf): + if local_conf: + LOG.warning(_LW('\'local conf\' from PasteDeploy INI is being ' + 'ignored.')) + return f(*args, **local_conf) + return wrapper + + +@fail_gracefully +@warn_local_conf +def public_app_factory(global_conf, **local_conf): + controllers.register_version('v2.0') + return wsgi.ComposingRouter(routes.Mapper(), + [assignment_routers.Public(), + token_routers.Router(), + user_crud.Router(), + routers.VersionV2('public'), + routers.Extension(False)]) + + +@fail_gracefully +@warn_local_conf +def admin_app_factory(global_conf, **local_conf): + controllers.register_version('v2.0') + return wsgi.ComposingRouter(routes.Mapper(), + [identity_routers.Admin(), + assignment_routers.Admin(), + token_routers.Router(), + resource_routers.Admin(), + admin_crud.Router(), + routers.VersionV2('admin'), + routers.Extension()]) + + +@fail_gracefully +@warn_local_conf +def public_version_app_factory(global_conf, **local_conf): + return wsgi.ComposingRouter(routes.Mapper(), + [routers.Versions('public')]) + + +@fail_gracefully +@warn_local_conf +def admin_version_app_factory(global_conf, **local_conf): + return wsgi.ComposingRouter(routes.Mapper(), + [routers.Versions('admin')]) + + +@fail_gracefully +@warn_local_conf +def v3_app_factory(global_conf, **local_conf): + controllers.register_version('v3') + mapper = routes.Mapper() + sub_routers = [] + _routers = [] + + # NOTE(dstanek): Routers should be ordered by their frequency of use in + # a live system. This is due to the routes implementation. The most + # frequently used routers should appear first. + all_api_routers = [auth_routers, + assignment_routers, + catalog_routers, + credential_routers, + identity_routers, + policy_routers, + resource_routers, + revoke_routers, + federation_routers, + oauth1_routers, + # TODO(morganfainberg): Remove the simple_cert router + # when PKI and PKIZ tokens are removed. + simple_cert_ext] + + if CONF.trust.enabled: + all_api_routers.append(trust_routers) + + if CONF.endpoint_policy.enabled: + all_api_routers.append(endpoint_policy_routers) + + for api_routers in all_api_routers: + routers_instance = api_routers.Routers() + _routers.append(routers_instance) + routers_instance.append_v3_routers(mapper, sub_routers) + + # Add in the v3 version api + sub_routers.append(routers.VersionV3('public', _routers)) + return wsgi.ComposingRouter(mapper, sub_routers) |