143 lines
4.9 KiB
Python
143 lines
4.9 KiB
Python
# 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 flask
|
|
from flask import request
|
|
from oslo_serialization import jsonutils
|
|
from six.moves import http_client
|
|
|
|
from keystone.common import json_home
|
|
from keystone.common import wsgi
|
|
import keystone.conf
|
|
from keystone import exception
|
|
|
|
|
|
CONF = keystone.conf.CONF
|
|
MEDIA_TYPE_JSON = 'application/vnd.openstack.identity-%s+json'
|
|
_VERSIONS = []
|
|
_DISCOVERY_BLUEPRINT = flask.Blueprint('Discovery', __name__)
|
|
|
|
|
|
def register_version(version):
|
|
_VERSIONS.append(version)
|
|
|
|
|
|
def _get_versions_list(identity_url):
|
|
versions = {}
|
|
if 'v3' in _VERSIONS:
|
|
versions['v3'] = {
|
|
'id': 'v3.10',
|
|
'status': 'stable',
|
|
'updated': '2018-02-28T00:00:00Z',
|
|
'links': [
|
|
{
|
|
'rel': 'self',
|
|
'href': identity_url,
|
|
}
|
|
],
|
|
'media-types': [
|
|
{
|
|
'base': 'application/json',
|
|
'type': MEDIA_TYPE_JSON % 'v3'
|
|
}
|
|
]
|
|
}
|
|
|
|
return versions
|
|
|
|
|
|
class MimeTypes(object):
|
|
JSON = 'application/json'
|
|
JSON_HOME = 'application/json-home'
|
|
|
|
|
|
def _v3_json_home_content():
|
|
# TODO(morgan): Eliminate this, we should never be disabling an API version
|
|
# now, JSON Home should never be empty.
|
|
if 'v3' not in _VERSIONS:
|
|
# No V3 Support, so return an empty JSON Home document.
|
|
return {'resources': {}}
|
|
return json_home.JsonHomeResources.resources()
|
|
|
|
|
|
def v3_mime_type_best_match():
|
|
if not request.accept_mimetypes:
|
|
return MimeTypes.JSON
|
|
|
|
return request.accept_mimetypes.best_match(
|
|
[MimeTypes.JSON, MimeTypes.JSON_HOME])
|
|
|
|
|
|
@_DISCOVERY_BLUEPRINT.route('/')
|
|
def get_versions():
|
|
if v3_mime_type_best_match() == MimeTypes.JSON_HOME:
|
|
# RENDER JSON-Home form, we have a clever client who will
|
|
# understand the JSON-Home document.
|
|
v3_json_home = _v3_json_home_content()
|
|
json_home.translate_urls(v3_json_home, '/v3')
|
|
return flask.Response(response=jsonutils.dumps(v3_json_home),
|
|
mimetype=MimeTypes.JSON_HOME)
|
|
else:
|
|
# NOTE(morgan): wsgi.Application.base_url will eventually need to
|
|
# be moved to a better "common" location. For now, we'll just lean
|
|
# on it for the sake of leaning on common code where possible.
|
|
identity_url = '%s/v3/' % wsgi.Application.base_url(
|
|
context={'environment': request.environ})
|
|
versions = _get_versions_list(identity_url)
|
|
return flask.Response(
|
|
response=jsonutils.dumps(
|
|
{'versions': {
|
|
'values': list(versions.values())}}),
|
|
mimetype=MimeTypes.JSON,
|
|
status=http_client.MULTIPLE_CHOICES)
|
|
|
|
|
|
@_DISCOVERY_BLUEPRINT.route('/v3')
|
|
def get_version_v3():
|
|
if 'v3' not in _VERSIONS:
|
|
raise exception.VersionNotFound(version='v3')
|
|
|
|
if v3_mime_type_best_match() == MimeTypes.JSON_HOME:
|
|
# RENDER JSON-Home form, we have a clever client who will
|
|
# understand the JSON-Home document.
|
|
content = _v3_json_home_content()
|
|
return flask.Response(response=jsonutils.dumps(content),
|
|
mimetype=MimeTypes.JSON_HOME)
|
|
else:
|
|
# NOTE(morgan): wsgi.Application.base_url will eventually need to
|
|
# be moved to a better "common" location. For now, we'll just lean
|
|
# on it for the sake of leaning on common code where possible.
|
|
identity_url = '%s/v3/' % wsgi.Application.base_url(
|
|
context={'environment': request.environ})
|
|
versions = _get_versions_list(identity_url)
|
|
return flask.Response(
|
|
response=jsonutils.dumps({'version': versions['v3']}),
|
|
mimetype=MimeTypes.JSON)
|
|
|
|
|
|
class DiscoveryAPI(object):
|
|
# NOTE(morgan): The Discovery Bits are so special they cannot conform to
|
|
# Flask-RESTful-isms. We are using straight flask Blueprint(s) here so that
|
|
# we have a lot more control over what the heck is going on. This is just
|
|
# a stub object to ensure we can load discovery in the same manner we
|
|
# handle the rest of keystone.api
|
|
|
|
@staticmethod
|
|
def instantiate_and_register_to_app(flask_app):
|
|
# This is a lot more magical than the normal setup as the discovery
|
|
# API does not lean on flask-restful. We're statically building a
|
|
# single blueprint here.
|
|
flask_app.register_blueprint(_DISCOVERY_BLUEPRINT)
|
|
|
|
|
|
APIs = (DiscoveryAPI,)
|