Merge "Change the default version discovery URLs"
This commit is contained in:
commit
e547af2286
|
@ -36,12 +36,24 @@
|
|||
|
||||
# The base public endpoint URL for keystone that are
|
||||
# advertised to clients (NOTE: this does NOT affect how
|
||||
# keystone listens for connections). (string value)
|
||||
# keystone listens for connections) (string value).
|
||||
# Defaults to the base host URL of the request. Eg a
|
||||
# request to http://server:5000/v2.0/users will
|
||||
# default to http://server:5000. You should only need
|
||||
# to set this value if the base URL contains a path
|
||||
# (eg /prefix/v2.0) or the endpoint should be found on
|
||||
# a different server.
|
||||
#public_endpoint=http://localhost:%(public_port)s/
|
||||
|
||||
# The base admin endpoint URL for keystone that are advertised
|
||||
# to clients (NOTE: this does NOT affect how keystone listens
|
||||
# for connections). (string value)
|
||||
# for connections) (string value).
|
||||
# Defaults to the base host URL of the request. Eg a
|
||||
# request to http://server:35357/v2.0/users will
|
||||
# default to http://server:35357. You should only need
|
||||
# to set this value if the base URL contains a path
|
||||
# (eg /prefix/v2.0) or the endpoint should be found on
|
||||
# a different server.
|
||||
#admin_endpoint=http://localhost:%(admin_port)s/
|
||||
|
||||
# onready allows you to send a notification when the process
|
||||
|
|
|
@ -574,7 +574,7 @@ class RoleAssignmentV3(controller.V3Controller):
|
|||
# the wrapper as have already included the links in the entities
|
||||
pass
|
||||
|
||||
def _format_entity(self, entity):
|
||||
def _format_entity(self, context, entity):
|
||||
"""Format an assignment entity for API response.
|
||||
|
||||
The driver layer returns entities as dicts containing the ids of the
|
||||
|
@ -641,16 +641,17 @@ class RoleAssignmentV3(controller.V3Controller):
|
|||
else:
|
||||
target_link = '/domains/%s' % entity['domain_id']
|
||||
formatted_entity.setdefault('links', {})
|
||||
formatted_entity['links']['assignment'] = (
|
||||
self.base_url('%(target)s/%(actor)s/roles/%(role)s%(suffix)s' % {
|
||||
'target': target_link,
|
||||
'actor': actor_link,
|
||||
'role': entity['role_id'],
|
||||
'suffix': suffix}))
|
||||
|
||||
path = '%(target)s/%(actor)s/roles/%(role)s%(suffix)s' % {
|
||||
'target': target_link,
|
||||
'actor': actor_link,
|
||||
'role': entity['role_id'],
|
||||
'suffix': suffix}
|
||||
formatted_entity['links']['assignment'] = self.base_url(context, path)
|
||||
|
||||
return formatted_entity
|
||||
|
||||
def _expand_indirect_assignments(self, refs):
|
||||
def _expand_indirect_assignments(self, context, refs):
|
||||
"""Processes entity list into all-direct assignments.
|
||||
|
||||
For any group role assignments in the list, create a role assignment
|
||||
|
@ -710,7 +711,7 @@ class RoleAssignmentV3(controller.V3Controller):
|
|||
user_entry = copy.deepcopy(template)
|
||||
user_entry['user'] = {'id': user['id']}
|
||||
user_entry['links']['membership'] = (
|
||||
self.base_url('/groups/%s/users/%s' %
|
||||
self.base_url(context, '/groups/%s/users/%s' %
|
||||
(group_id, user['id'])))
|
||||
return user_entry
|
||||
|
||||
|
@ -727,6 +728,7 @@ class RoleAssignmentV3(controller.V3Controller):
|
|||
project_entry['scope']['project'] = {'id': project_id}
|
||||
project_entry['links']['assignment'] = (
|
||||
self.base_url(
|
||||
context,
|
||||
'/OS-INHERIT/domains/%s/users/%s/roles/%s'
|
||||
'/inherited_to_projects' % (
|
||||
domain_id, project_entry['user']['id'],
|
||||
|
@ -746,12 +748,13 @@ class RoleAssignmentV3(controller.V3Controller):
|
|||
project_entry['user'] = {'id': user_id}
|
||||
project_entry['scope']['project'] = {'id': project_id}
|
||||
project_entry['links']['assignment'] = (
|
||||
self.base_url('/OS-INHERIT/domains/%s/groups/%s/roles/%s'
|
||||
self.base_url(context,
|
||||
'/OS-INHERIT/domains/%s/groups/%s/roles/%s'
|
||||
'/inherited_to_projects' % (
|
||||
domain_id, group_id,
|
||||
project_entry['role']['id'])))
|
||||
project_entry['links']['membership'] = (
|
||||
self.base_url('/groups/%s/users/%s' %
|
||||
self.base_url(context, '/groups/%s/users/%s' %
|
||||
(group_id, user_id)))
|
||||
return project_entry
|
||||
|
||||
|
@ -862,14 +865,15 @@ class RoleAssignmentV3(controller.V3Controller):
|
|||
hints = self.build_driver_hints(context, filters)
|
||||
refs = self.assignment_api.list_role_assignments()
|
||||
formatted_refs = (
|
||||
[self._format_entity(x) for x in refs
|
||||
[self._format_entity(context, x) for x in refs
|
||||
if self._filter_inherited(x)])
|
||||
|
||||
if ('effective' in context['query_string'] and
|
||||
self._query_filter_is_true(
|
||||
context['query_string']['effective'])):
|
||||
|
||||
formatted_refs = self._expand_indirect_assignments(formatted_refs)
|
||||
formatted_refs = self._expand_indirect_assignments(context,
|
||||
formatted_refs)
|
||||
|
||||
return self.wrap_collection(context, formatted_refs, hints=hints)
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# under the License.
|
||||
|
||||
from keystone import auth
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.contrib.oauth1 import core as oauth
|
||||
from keystone.contrib.oauth1 import validator
|
||||
|
@ -54,7 +55,7 @@ class OAuth(auth.AuthMethodHandler):
|
|||
if now > expires:
|
||||
raise exception.Unauthorized(_('Access token is expired'))
|
||||
|
||||
url = oauth.rebuild_url(context['path'])
|
||||
url = controller.V3Controller.base_url(context, context['path'])
|
||||
access_verifier = oauth.ResourceEndpoint(
|
||||
request_validator=validator.OAuthValidator(),
|
||||
token_generator=oauth.token_generator)
|
||||
|
|
|
@ -50,15 +50,25 @@ FILE_OPTIONS = {
|
|||
help='The port number which the public service listens '
|
||||
'on.'),
|
||||
cfg.StrOpt('public_endpoint',
|
||||
default='http://localhost:%(public_port)s/',
|
||||
help='The base public endpoint URL for keystone that are '
|
||||
'advertised to clients (NOTE: this does NOT affect '
|
||||
'how keystone listens for connections).'),
|
||||
'how keystone listens for connections). '
|
||||
'Defaults to the base host URL of the request. Eg a '
|
||||
'request to http://server:5000/v2.0/users will '
|
||||
'default to http://server:5000. You should only need '
|
||||
'to set this value if the base URL contains a path '
|
||||
'(eg /prefix/v2.0) or the endpoint should be found on '
|
||||
'a different server.'),
|
||||
cfg.StrOpt('admin_endpoint',
|
||||
default='http://localhost:%(admin_port)s/',
|
||||
help='The base admin endpoint URL for keystone that are '
|
||||
'advertised to clients (NOTE: this does NOT affect '
|
||||
'how keystone listens for connections).'),
|
||||
'how keystone listens for connections). '
|
||||
'Defaults to the base host URL of the request. Eg a '
|
||||
'request to http://server:35357/v2.0/users will '
|
||||
'default to http://server:35357. You should only need '
|
||||
'to set this value if the base URL contains a path '
|
||||
'(eg /prefix/v2.0) or the endpoint should be found on '
|
||||
'a different server.'),
|
||||
cfg.StrOpt('onready',
|
||||
help='onready allows you to send a notification when the '
|
||||
'process is ready to serve For example, to have it '
|
||||
|
|
|
@ -292,28 +292,21 @@ class V3Controller(wsgi.Application):
|
|||
get_member_from_driver = None
|
||||
|
||||
@classmethod
|
||||
def base_url(cls, path=None):
|
||||
endpoint = CONF.public_endpoint % CONF
|
||||
def base_url(cls, context, path=None):
|
||||
endpoint = super(V3Controller, cls).base_url(context, 'public')
|
||||
if not path:
|
||||
path = cls.collection_name
|
||||
|
||||
# allow a missing trailing slash in the config
|
||||
if endpoint[-1] != '/':
|
||||
endpoint += '/'
|
||||
|
||||
url = endpoint + 'v3'
|
||||
|
||||
if path:
|
||||
return url + path
|
||||
else:
|
||||
return url + '/' + cls.collection_name
|
||||
return '%s/%s/%s' % (endpoint, 'v3', path.lstrip('/'))
|
||||
|
||||
@classmethod
|
||||
def _add_self_referential_link(cls, ref):
|
||||
def _add_self_referential_link(cls, context, ref):
|
||||
ref.setdefault('links', {})
|
||||
ref['links']['self'] = cls.base_url() + '/' + ref['id']
|
||||
ref['links']['self'] = cls.base_url(context) + '/' + ref['id']
|
||||
|
||||
@classmethod
|
||||
def wrap_member(cls, context, ref):
|
||||
cls._add_self_referential_link(ref)
|
||||
cls._add_self_referential_link(context, ref)
|
||||
return {cls.member_name: ref}
|
||||
|
||||
@classmethod
|
||||
|
@ -349,7 +342,7 @@ class V3Controller(wsgi.Application):
|
|||
container = {cls.collection_name: refs}
|
||||
container['links'] = {
|
||||
'next': None,
|
||||
'self': cls.base_url(path=context['path']),
|
||||
'self': cls.base_url(context, path=context['path']),
|
||||
'previous': None}
|
||||
|
||||
if list_limited:
|
||||
|
|
|
@ -185,6 +185,7 @@ class Application(BaseApplication):
|
|||
context['query_string'] = dict(six.iteritems(req.params))
|
||||
context['headers'] = dict(six.iteritems(req.headers))
|
||||
context['path'] = req.environ['PATH_INFO']
|
||||
context['host_url'] = req.host_url
|
||||
params = req.environ.get(PARAMS_ENV, {})
|
||||
#authentication and authorization attributes are set as environment
|
||||
#values by the container and processed by the pipeline. the complete
|
||||
|
@ -208,17 +209,21 @@ class Application(BaseApplication):
|
|||
LOG.warning(
|
||||
_('Authorization failed. %(exception)s from %(remote_addr)s'),
|
||||
{'exception': e, 'remote_addr': req.environ['REMOTE_ADDR']})
|
||||
return render_exception(e, user_locale=best_match_language(req))
|
||||
return render_exception(e, context=context,
|
||||
user_locale=best_match_language(req))
|
||||
except exception.Error as e:
|
||||
LOG.warning(e)
|
||||
return render_exception(e, user_locale=best_match_language(req))
|
||||
return render_exception(e, context=context,
|
||||
user_locale=best_match_language(req))
|
||||
except TypeError as e:
|
||||
LOG.exception(e)
|
||||
return render_exception(exception.ValidationError(e),
|
||||
context=context,
|
||||
user_locale=best_match_language(req))
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
return render_exception(exception.UnexpectedError(exception=e),
|
||||
context=context,
|
||||
user_locale=best_match_language(req))
|
||||
|
||||
if result is None:
|
||||
|
@ -302,6 +307,22 @@ class Application(BaseApplication):
|
|||
|
||||
return token_ref.get('trust_id')
|
||||
|
||||
@classmethod
|
||||
def base_url(cls, context, endpoint_type):
|
||||
url = CONF['%s_endpoint' % endpoint_type]
|
||||
|
||||
if url:
|
||||
url = url % CONF
|
||||
else:
|
||||
# NOTE(jamielennox): if url is not set via the config file we
|
||||
# should set it relative to the url that the user used to get here
|
||||
# so as not to mess with version discovery. This is not perfect.
|
||||
# host_url omits the path prefix, but there isn't another good
|
||||
# solution that will work for all urls.
|
||||
url = context['host_url']
|
||||
|
||||
return url.rstrip('/')
|
||||
|
||||
|
||||
class Middleware(Application):
|
||||
"""Base WSGI middleware.
|
||||
|
@ -370,15 +391,17 @@ class Middleware(Application):
|
|||
return self.process_response(request, response)
|
||||
except exception.Error as e:
|
||||
LOG.warning(e)
|
||||
return render_exception(e,
|
||||
return render_exception(e, request=request,
|
||||
user_locale=best_match_language(request))
|
||||
except TypeError as e:
|
||||
LOG.exception(e)
|
||||
return render_exception(exception.ValidationError(e),
|
||||
request=request,
|
||||
user_locale=best_match_language(request))
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
return render_exception(exception.UnexpectedError(exception=e),
|
||||
request=request,
|
||||
user_locale=best_match_language(request))
|
||||
|
||||
|
||||
|
@ -475,9 +498,10 @@ class Router(object):
|
|||
"""
|
||||
match = req.environ['wsgiorg.routing_args'][1]
|
||||
if not match:
|
||||
return render_exception(
|
||||
exception.NotFound(_('The resource could not be found.')),
|
||||
user_locale=best_match_language(req))
|
||||
msg = _('The resource could not be found.')
|
||||
return render_exception(exception.NotFound(msg),
|
||||
request=req,
|
||||
user_locale=best_match_language(req))
|
||||
app = match['controller']
|
||||
return app
|
||||
|
||||
|
@ -571,7 +595,7 @@ def render_response(body=None, status=None, headers=None):
|
|||
headerlist=headers)
|
||||
|
||||
|
||||
def render_exception(error, user_locale=None):
|
||||
def render_exception(error, context=None, request=None, user_locale=None):
|
||||
"""Forms a WSGI response based on the current error."""
|
||||
|
||||
error_message = error.args[0]
|
||||
|
@ -590,9 +614,18 @@ def render_exception(error, user_locale=None):
|
|||
if isinstance(error, exception.AuthPluginException):
|
||||
body['error']['identity'] = error.authentication
|
||||
elif isinstance(error, exception.Unauthorized):
|
||||
headers.append(('WWW-Authenticate',
|
||||
'Keystone uri="%s"' % (
|
||||
CONF.public_endpoint % CONF)))
|
||||
url = CONF.public_endpoint
|
||||
if not url:
|
||||
if request:
|
||||
context = {'host_url': request.host_url}
|
||||
if context:
|
||||
url = Application.base_url(context, 'public')
|
||||
else:
|
||||
url = 'http://localhost:%d' % CONF.public_port
|
||||
else:
|
||||
url = url % CONF
|
||||
|
||||
headers.append(('WWW-Authenticate', 'Keystone uri="%s"' % url))
|
||||
return render_response(status=(error.code, error.title),
|
||||
body=body,
|
||||
headers=headers)
|
||||
|
|
|
@ -29,11 +29,11 @@ class _ControllerBase(controller.V3Controller):
|
|||
"""Base behaviors for federation controllers."""
|
||||
|
||||
@classmethod
|
||||
def base_url(cls, path=None):
|
||||
def base_url(cls, context, path=None):
|
||||
"""Construct a path and pass it to V3Controller.base_url method."""
|
||||
|
||||
path = '/OS-FEDERATION/' + cls.collection_name
|
||||
return controller.V3Controller.base_url(path=path)
|
||||
return super(_ControllerBase, cls).base_url(context, path=path)
|
||||
|
||||
|
||||
@dependency.requires('federation_api')
|
||||
|
@ -46,7 +46,7 @@ class IdentityProvider(_ControllerBase):
|
|||
_public_parameters = frozenset(['id', 'enabled', 'description', 'links'])
|
||||
|
||||
@classmethod
|
||||
def _add_related_links(cls, ref):
|
||||
def _add_related_links(cls, context, ref):
|
||||
"""Add URLs for entities related with Identity Provider.
|
||||
|
||||
Add URLs pointing to:
|
||||
|
@ -56,21 +56,22 @@ class IdentityProvider(_ControllerBase):
|
|||
ref.setdefault('links', {})
|
||||
base_path = ref['links'].get('self')
|
||||
if base_path is None:
|
||||
base_path = '/'.join([IdentityProvider.base_url(), ref['id']])
|
||||
base_path = '/'.join([IdentityProvider.base_url(context),
|
||||
ref['id']])
|
||||
for name in ['protocols']:
|
||||
ref['links'][name] = '/'.join([base_path, name])
|
||||
|
||||
@classmethod
|
||||
def _add_self_referential_link(cls, ref):
|
||||
def _add_self_referential_link(cls, context, ref):
|
||||
id = ref.get('id')
|
||||
self_path = '/'.join([cls.base_url(), id])
|
||||
self_path = '/'.join([cls.base_url(context), id])
|
||||
ref.setdefault('links', {})
|
||||
ref['links']['self'] = self_path
|
||||
|
||||
@classmethod
|
||||
def wrap_member(cls, context, ref):
|
||||
cls._add_self_referential_link(ref)
|
||||
cls._add_related_links(ref)
|
||||
cls._add_self_referential_link(context, ref)
|
||||
cls._add_related_links(context, ref)
|
||||
ref = cls.filter_params(ref)
|
||||
return {cls.member_name: ref}
|
||||
|
||||
|
@ -135,7 +136,7 @@ class FederationProtocol(_ControllerBase):
|
|||
_mutable_parameters = frozenset(['mapping_id'])
|
||||
|
||||
@classmethod
|
||||
def _add_self_referential_link(cls, ref):
|
||||
def _add_self_referential_link(cls, context, ref):
|
||||
"""Add 'links' entry to the response dictionary.
|
||||
|
||||
Calls IdentityProvider.base_url() class method, as it constructs
|
||||
|
@ -147,14 +148,14 @@ class FederationProtocol(_ControllerBase):
|
|||
ref.setdefault('links', {})
|
||||
base_path = ref['links'].get('identity_provider')
|
||||
if base_path is None:
|
||||
base_path = [IdentityProvider.base_url(), ref['idp_id']]
|
||||
base_path = [IdentityProvider.base_url(context), ref['idp_id']]
|
||||
base_path = '/'.join(base_path)
|
||||
self_path = [base_path, 'protocols', ref['id']]
|
||||
self_path = '/'.join(self_path)
|
||||
ref['links']['self'] = self_path
|
||||
|
||||
@classmethod
|
||||
def _add_related_links(cls, ref):
|
||||
def _add_related_links(cls, context, ref):
|
||||
"""Add new entries to the 'links' subdictionary in the response.
|
||||
|
||||
Adds 'identity_provider' key with URL pointing to related identity
|
||||
|
@ -164,13 +165,14 @@ class FederationProtocol(_ControllerBase):
|
|||
|
||||
"""
|
||||
ref.setdefault('links', {})
|
||||
base_path = '/'.join([IdentityProvider.base_url(), ref['idp_id']])
|
||||
base_path = '/'.join([IdentityProvider.base_url(context),
|
||||
ref['idp_id']])
|
||||
ref['links']['identity_provider'] = base_path
|
||||
|
||||
@classmethod
|
||||
def wrap_member(cls, context, ref):
|
||||
cls._add_related_links(ref)
|
||||
cls._add_self_referential_link(ref)
|
||||
cls._add_related_links(context, ref)
|
||||
cls._add_self_referential_link(context, ref)
|
||||
ref = cls.filter_params(ref)
|
||||
return {cls.member_name: ref}
|
||||
|
||||
|
|
|
@ -35,13 +35,13 @@ class ConsumerCrudV3(controller.V3Controller):
|
|||
member_name = 'consumer'
|
||||
|
||||
@classmethod
|
||||
def base_url(cls, path=None):
|
||||
def base_url(cls, context, path=None):
|
||||
"""Construct a path and pass it to V3Controller.base_url method."""
|
||||
|
||||
# NOTE(stevemar): Overriding path to /OS-OAUTH1/consumers so that
|
||||
# V3Controller.base_url handles setting the self link correctly.
|
||||
path = '/OS-OAUTH1/' + cls.collection_name
|
||||
return controller.V3Controller.base_url(path=path)
|
||||
return controller.V3Controller.base_url(context, path=path)
|
||||
|
||||
@controller.protected()
|
||||
def create_consumer(self, context, consumer):
|
||||
|
@ -90,13 +90,14 @@ class AccessTokenCrudV3(controller.V3Controller):
|
|||
access_token = self.oauth_api.get_access_token(access_token_id)
|
||||
if access_token['authorizing_user_id'] != user_id:
|
||||
raise exception.NotFound()
|
||||
access_token = self._format_token_entity(access_token)
|
||||
access_token = self._format_token_entity(context, access_token)
|
||||
return AccessTokenCrudV3.wrap_member(context, access_token)
|
||||
|
||||
@controller.protected()
|
||||
def list_access_tokens(self, context, user_id):
|
||||
refs = self.oauth_api.list_access_tokens(user_id)
|
||||
formatted_refs = ([self._format_token_entity(x) for x in refs])
|
||||
formatted_refs = ([self._format_token_entity(context, x)
|
||||
for x in refs])
|
||||
return AccessTokenCrudV3.wrap_collection(context, formatted_refs)
|
||||
|
||||
@controller.protected()
|
||||
|
@ -107,7 +108,7 @@ class AccessTokenCrudV3(controller.V3Controller):
|
|||
return self.oauth_api.delete_access_token(
|
||||
user_id, access_token_id)
|
||||
|
||||
def _format_token_entity(self, entity):
|
||||
def _format_token_entity(self, context, entity):
|
||||
|
||||
formatted_entity = entity.copy()
|
||||
access_token_id = formatted_entity['id']
|
||||
|
@ -124,7 +125,7 @@ class AccessTokenCrudV3(controller.V3Controller):
|
|||
'access_token_id': access_token_id})
|
||||
|
||||
formatted_entity.setdefault('links', {})
|
||||
formatted_entity['links']['roles'] = (self.base_url(url))
|
||||
formatted_entity['links']['roles'] = (self.base_url(context, url))
|
||||
|
||||
return formatted_entity
|
||||
|
||||
|
@ -185,7 +186,7 @@ class OAuthControllerV3(controller.V3Controller):
|
|||
raise exception.ValidationError(
|
||||
attribute='requested_project_id', target='request')
|
||||
|
||||
url = oauth1.rebuild_url(context['path'])
|
||||
url = self.base_url(context, context['path'])
|
||||
|
||||
req_headers = {'Requested-Project-Id': requested_project_id}
|
||||
req_headers.update(headers)
|
||||
|
@ -250,7 +251,7 @@ class OAuthControllerV3(controller.V3Controller):
|
|||
if now > expires:
|
||||
raise exception.Unauthorized(_('Request token is expired'))
|
||||
|
||||
url = oauth1.rebuild_url(context['path'])
|
||||
url = self.base_url(context, context['path'])
|
||||
|
||||
access_verifier = oauth1.AccessTokenEndpoint(
|
||||
request_validator=validator.OAuthValidator(),
|
||||
|
|
|
@ -106,17 +106,6 @@ def filter_token(access_token_ref):
|
|||
return access_token_ref
|
||||
|
||||
|
||||
def rebuild_url(path):
|
||||
endpoint = CONF.public_endpoint % CONF
|
||||
|
||||
# allow a missing trailing slash in the config
|
||||
if endpoint[-1] != '/':
|
||||
endpoint += '/'
|
||||
|
||||
url = endpoint + 'v3'
|
||||
return url + path
|
||||
|
||||
|
||||
def get_oauth_headers(headers):
|
||||
parameters = {}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class RevokeController(controller.V3Controller):
|
|||
'links': {
|
||||
'next': None,
|
||||
'self': RevokeController.base_url(
|
||||
context,
|
||||
path=context['path']) + '/events',
|
||||
'previous': None}
|
||||
}
|
||||
|
|
|
@ -69,12 +69,10 @@ class Version(wsgi.Application):
|
|||
|
||||
super(Version, self).__init__()
|
||||
|
||||
def _get_identity_url(self, version='v2.0'):
|
||||
def _get_identity_url(self, context, version):
|
||||
"""Returns a URL to keystone's own endpoint."""
|
||||
url = CONF['%s_endpoint' % self.endpoint_url_type] % CONF
|
||||
if url[-1] != '/':
|
||||
url += '/'
|
||||
return '%s%s/' % (url, version)
|
||||
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."""
|
||||
|
@ -87,7 +85,7 @@ class Version(wsgi.Application):
|
|||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': self._get_identity_url(version='v2.0'),
|
||||
'href': self._get_identity_url(context, 'v2.0'),
|
||||
}, {
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html',
|
||||
|
@ -120,7 +118,7 @@ class Version(wsgi.Application):
|
|||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': self._get_identity_url(version='v3'),
|
||||
'href': self._get_identity_url(context, 'v3'),
|
||||
}
|
||||
],
|
||||
'media-types': [
|
||||
|
|
|
@ -118,7 +118,7 @@ class JsonBodyMiddleware(wsgi.Middleware):
|
|||
if request.content_type not in ('application/json', ''):
|
||||
e = exception.ValidationError(attribute='application/json',
|
||||
target='Content-Type header')
|
||||
return wsgi.render_exception(e)
|
||||
return wsgi.render_exception(e, request=request)
|
||||
|
||||
params_parsed = {}
|
||||
try:
|
||||
|
@ -126,7 +126,7 @@ class JsonBodyMiddleware(wsgi.Middleware):
|
|||
except ValueError:
|
||||
e = exception.ValidationError(attribute='valid JSON',
|
||||
target='request body')
|
||||
return wsgi.render_exception(e)
|
||||
return wsgi.render_exception(e, request=request)
|
||||
finally:
|
||||
if not params_parsed:
|
||||
params_parsed = {}
|
||||
|
@ -166,7 +166,7 @@ class XmlBodyMiddleware(wsgi.Middleware):
|
|||
LOG.exception('Serializer failed')
|
||||
e = exception.ValidationError(attribute='valid XML',
|
||||
target='request body')
|
||||
return wsgi.render_exception(e)
|
||||
return wsgi.render_exception(e, request=request)
|
||||
|
||||
def process_response(self, request, response):
|
||||
"""Transform the response from JSON to XML."""
|
||||
|
|
|
@ -35,6 +35,8 @@ CONF = config.CONF
|
|||
TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
|
||||
DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
|
||||
|
||||
HOST_URL = 'http://keystone:5001'
|
||||
|
||||
|
||||
def _build_user_auth(token=None, user_id=None, username=None,
|
||||
password=None, tenant_id=None, tenant_name=None,
|
||||
|
@ -674,7 +676,8 @@ class AuthWithTrust(AuthTest):
|
|||
auth_context = authorization.token_to_auth_context(
|
||||
token_ref['token_data'])
|
||||
return {'environment': {authorization.AUTH_CONTEXT_ENV: auth_context},
|
||||
'token_id': token_id}
|
||||
'token_id': token_id,
|
||||
'host_url': HOST_URL}
|
||||
|
||||
def create_trust(self, expires_at=None, impersonation=True):
|
||||
username = self.trustor['name']
|
||||
|
@ -723,9 +726,9 @@ class AuthWithTrust(AuthTest):
|
|||
role_ids = [self.role_browser['id'], self.role_member['id']]
|
||||
self.assertTrue(timeutils.parse_strtime(self.new_trust['expires_at'],
|
||||
fmt=TIME_FORMAT))
|
||||
self.assertIn('http://localhost:5000/v3/OS-TRUST/',
|
||||
self.assertIn('%s/v3/OS-TRUST/' % HOST_URL,
|
||||
self.new_trust['links']['self'])
|
||||
self.assertIn('http://localhost:5000/v3/OS-TRUST/',
|
||||
self.assertIn('%s/v3/OS-TRUST/' % HOST_URL,
|
||||
self.new_trust['roles_links']['self'])
|
||||
|
||||
for role in self.new_trust['roles']:
|
||||
|
@ -743,7 +746,8 @@ class AuthWithTrust(AuthTest):
|
|||
expires_at="Z")
|
||||
|
||||
def test_get_trust(self):
|
||||
context = {'token_id': self.unscoped_token['access']['token']['id']}
|
||||
context = {'token_id': self.unscoped_token['access']['token']['id'],
|
||||
'host_url': HOST_URL}
|
||||
trust = self.trust_controller.get_trust(context,
|
||||
self.new_trust['id'])['trust']
|
||||
self.assertEqual(self.trustor['id'], trust['trustor_user_id'])
|
||||
|
|
|
@ -681,7 +681,16 @@ class CoreApiTests(object):
|
|||
r = self.public_request(
|
||||
path='/v2.0/tenants',
|
||||
expected_status=401)
|
||||
self.assertEqual('Keystone uri="%s"' % (CONF.public_endpoint % CONF),
|
||||
self.assertEqual('Keystone uri="http://localhost"',
|
||||
r.headers.get('WWW-Authenticate'))
|
||||
|
||||
def test_www_authenticate_header_host(self):
|
||||
test_url = 'http://%s:4187' % uuid.uuid4().hex
|
||||
self.config_fixture.config(public_endpoint=test_url)
|
||||
r = self.public_request(
|
||||
path='/v2.0/tenants',
|
||||
expected_status=401)
|
||||
self.assertEqual('Keystone uri="%s"' % test_url,
|
||||
r.headers.get('WWW-Authenticate'))
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import uuid
|
|||
|
||||
from lxml import etree
|
||||
import six
|
||||
from testtools import matchers
|
||||
|
||||
from keystone import auth
|
||||
from keystone.common import authorization
|
||||
|
@ -395,19 +396,17 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase):
|
|||
def assertValidListLinks(self, links):
|
||||
self.assertIsNotNone(links)
|
||||
self.assertIsNotNone(links.get('self'))
|
||||
self.assertIn(CONF.public_endpoint % CONF, links['self'])
|
||||
self.assertThat(links['self'], matchers.StartsWith('http://localhost'))
|
||||
|
||||
self.assertIn('next', links)
|
||||
if links['next'] is not None:
|
||||
self.assertIn(
|
||||
CONF.public_endpoint % CONF,
|
||||
links['next'])
|
||||
self.assertThat(links['next'],
|
||||
matchers.StartsWith('http://localhost'))
|
||||
|
||||
self.assertIn('previous', links)
|
||||
if links['previous'] is not None:
|
||||
self.assertIn(
|
||||
CONF.public_endpoint % CONF,
|
||||
links['previous'])
|
||||
self.assertThat(links['previous'],
|
||||
matchers.StartsWith('http://localhost'))
|
||||
|
||||
def assertValidListResponse(self, resp, key, entity_validator, ref=None,
|
||||
expected_length=None, keys_to_check=None):
|
||||
|
@ -467,7 +466,8 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase):
|
|||
|
||||
self.assertIsNotNone(entity.get('links'))
|
||||
self.assertIsNotNone(entity['links'].get('self'))
|
||||
self.assertIn(CONF.public_endpoint % CONF, entity['links']['self'])
|
||||
self.assertThat(entity['links']['self'],
|
||||
matchers.StartsWith('http://localhost'))
|
||||
self.assertIn(entity['id'], entity['links']['self'])
|
||||
|
||||
if ref:
|
||||
|
|
|
@ -51,7 +51,7 @@ class OAuth1Tests(test_v3.RestfulTestCase):
|
|||
super(OAuth1Tests, self).setUp()
|
||||
|
||||
# Now that the app has been served, we can query CONF values
|
||||
self.base_url = (CONF.public_endpoint % CONF) + "v3"
|
||||
self.base_url = 'http://localhost/v3'
|
||||
self.controller = controllers.OAuthControllerV3()
|
||||
|
||||
def _create_single_consumer(self):
|
||||
|
@ -153,7 +153,7 @@ class ConsumerCRUDTests(OAuth1Tests):
|
|||
consumer = self._create_single_consumer()
|
||||
consumer_id = consumer['id']
|
||||
resp = self.get(self.CONSUMER_URL + '/%s' % consumer_id)
|
||||
self_url = [CONF.public_endpoint % CONF, 'v3', self.CONSUMER_URL,
|
||||
self_url = ['http://localhost/v3', self.CONSUMER_URL,
|
||||
'/', consumer_id]
|
||||
self_url = ''.join(self_url)
|
||||
self.assertEqual(resp.result['consumer']['links']['self'], self_url)
|
||||
|
@ -164,7 +164,7 @@ class ConsumerCRUDTests(OAuth1Tests):
|
|||
resp = self.get(self.CONSUMER_URL)
|
||||
entities = resp.result['consumers']
|
||||
self.assertIsNotNone(entities)
|
||||
self_url = [CONF.public_endpoint % CONF, 'v3', self.CONSUMER_URL]
|
||||
self_url = ['http://localhost/v3', self.CONSUMER_URL]
|
||||
self_url = ''.join(self_url)
|
||||
self.assertEqual(resp.result['links']['self'], self_url)
|
||||
self.assertValidListLinks(resp.result['links'])
|
||||
|
|
|
@ -118,6 +118,10 @@ class VersionTestCase(tests.TestCase):
|
|||
self.public_app = self.loadapp('keystone', 'main')
|
||||
self.admin_app = self.loadapp('keystone', 'admin')
|
||||
|
||||
self.config_fixture.config(
|
||||
public_endpoint='http://localhost:%(public_port)d',
|
||||
admin_endpoint='http://localhost:%(admin_port)d')
|
||||
|
||||
fixture = self.useFixture(moxstubout.MoxStubout())
|
||||
self.stubs = fixture.stubs
|
||||
|
||||
|
@ -161,6 +165,25 @@ class VersionTestCase(tests.TestCase):
|
|||
version, 'http://localhost:%s/v2.0/' % CONF.admin_port)
|
||||
self.assertEqual(data, expected)
|
||||
|
||||
def test_use_site_url_if_endpoint_unset(self):
|
||||
self.config_fixture.config(public_endpoint=None, admin_endpoint=None)
|
||||
|
||||
for app in (self.public_app, self.admin_app):
|
||||
client = self.client(app)
|
||||
resp = client.get('/')
|
||||
self.assertEqual(resp.status_int, 300)
|
||||
data = jsonutils.loads(resp.body)
|
||||
expected = VERSIONS_RESPONSE
|
||||
for version in expected['versions']['values']:
|
||||
# localhost happens to be the site url for tests
|
||||
if version['id'] == 'v3.0':
|
||||
self._paste_in_port(
|
||||
version, 'http://localhost/v3/')
|
||||
elif version['id'] == 'v2.0':
|
||||
self._paste_in_port(
|
||||
version, 'http://localhost/v2.0/')
|
||||
self.assertEqual(data, expected)
|
||||
|
||||
def test_public_version_v2(self):
|
||||
client = self.client(self.public_app)
|
||||
resp = client.get('/v2.0/')
|
||||
|
@ -181,6 +204,17 @@ class VersionTestCase(tests.TestCase):
|
|||
'http://localhost:%s/v2.0/' % CONF.admin_port)
|
||||
self.assertEqual(data, expected)
|
||||
|
||||
def test_use_site_url_if_endpoint_unset_v2(self):
|
||||
self.config_fixture.config(public_endpoint=None, admin_endpoint=None)
|
||||
for app in (self.public_app, self.admin_app):
|
||||
client = self.client(app)
|
||||
resp = client.get('/v2.0/')
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
data = jsonutils.loads(resp.body)
|
||||
expected = v2_VERSION_RESPONSE
|
||||
self._paste_in_port(expected['version'], 'http://localhost/v2.0/')
|
||||
self.assertEqual(data, expected)
|
||||
|
||||
def test_public_version_v3(self):
|
||||
client = self.client(self.public_app)
|
||||
resp = client.get('/v3/')
|
||||
|
@ -201,6 +235,17 @@ class VersionTestCase(tests.TestCase):
|
|||
'http://localhost:%s/v3/' % CONF.admin_port)
|
||||
self.assertEqual(data, expected)
|
||||
|
||||
def test_use_site_url_if_endpoint_unset_v3(self):
|
||||
self.config_fixture.config(public_endpoint=None, admin_endpoint=None)
|
||||
for app in (self.public_app, self.admin_app):
|
||||
client = self.client(app)
|
||||
resp = client.get('/v3/')
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
data = jsonutils.loads(resp.body)
|
||||
expected = v3_VERSION_RESPONSE
|
||||
self._paste_in_port(expected['version'], 'http://localhost/v3/')
|
||||
self.assertEqual(data, expected)
|
||||
|
||||
def test_v2_disabled(self):
|
||||
self.stubs.Set(controllers, '_VERSIONS', ['v3'])
|
||||
client = self.client(self.public_app)
|
||||
|
@ -331,6 +376,10 @@ vnd.openstack.identity-v3+xml"/>
|
|||
self.public_app = self.loadapp('keystone', 'main')
|
||||
self.admin_app = self.loadapp('keystone', 'admin')
|
||||
|
||||
self.config_fixture.config(
|
||||
public_endpoint='http://localhost:%(public_port)d',
|
||||
admin_endpoint='http://localhost:%(admin_port)d')
|
||||
|
||||
fixture = self.useFixture(moxstubout.MoxStubout())
|
||||
self.stubs = fixture.stubs
|
||||
|
||||
|
@ -355,6 +404,14 @@ vnd.openstack.identity-v3+xml"/>
|
|||
expected = self.VERSIONS_RESPONSE % dict(port=CONF.admin_port)
|
||||
self.assertThat(data, matchers.XMLEquals(expected))
|
||||
|
||||
def test_use_site_url_if_endpoint_unset(self):
|
||||
client = self.client(self.public_app)
|
||||
resp = client.get('/', headers=self.REQUEST_HEADERS)
|
||||
self.assertEqual(resp.status_int, 300)
|
||||
data = resp.body
|
||||
expected = self.VERSIONS_RESPONSE % dict(port=CONF.public_port)
|
||||
self.assertThat(data, matchers.XMLEquals(expected))
|
||||
|
||||
def test_public_version_v2(self):
|
||||
client = self.client(self.public_app)
|
||||
resp = client.get('/v2.0/', headers=self.REQUEST_HEADERS)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import gettext
|
||||
import socket
|
||||
import uuid
|
||||
|
||||
from babel import localedata
|
||||
import mock
|
||||
|
@ -114,6 +115,13 @@ class ApplicationTest(BaseWSGITest):
|
|||
resp = wsgi.render_exception(e)
|
||||
self.assertEqual(resp.status_int, 401)
|
||||
|
||||
def test_render_exception_host(self):
|
||||
e = exception.Unauthorized(message=u'\u7f51\u7edc')
|
||||
context = {'host_url': 'http://%s:5000' % uuid.uuid4().hex}
|
||||
resp = wsgi.render_exception(e, context=context)
|
||||
|
||||
self.assertEqual(resp.status_int, 401)
|
||||
|
||||
|
||||
class ExtensionRouterTest(BaseWSGITest):
|
||||
def test_extensionrouter_local_config(self):
|
||||
|
|
|
@ -53,13 +53,13 @@ class TrustV3(controller.V3Controller):
|
|||
member_name = "trust"
|
||||
|
||||
@classmethod
|
||||
def base_url(cls, path=None):
|
||||
def base_url(cls, context, path=None):
|
||||
"""Construct a path and pass it to V3Controller.base_url method."""
|
||||
|
||||
# NOTE(stevemar): Overriding path to /OS-TRUST/trusts so that
|
||||
# V3Controller.base_url handles setting the self link correctly.
|
||||
path = '/OS-TRUST/' + cls.collection_name
|
||||
return controller.V3Controller.base_url(path=path)
|
||||
return super(TrustV3, cls).base_url(context, path=path)
|
||||
|
||||
def _get_user_id(self, context):
|
||||
if 'token_id' in context:
|
||||
|
@ -99,7 +99,7 @@ class TrustV3(controller.V3Controller):
|
|||
trust_full_roles.append(full_role)
|
||||
trust['roles'] = trust_full_roles
|
||||
trust['roles_links'] = {
|
||||
'self': (self.base_url() + "/%s/roles" % trust['id']),
|
||||
'self': (self.base_url(context) + "/%s/roles" % trust['id']),
|
||||
'next': None,
|
||||
'previous': None}
|
||||
|
||||
|
|
Loading…
Reference in New Issue