"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "keystone/federation/utils.py" between
keystone-16.0.1.tar.gz and keystone-17.0.0.tar.gz

About: OpenStack Keystone (Core Service: Identity) provides an authentication and authorization service for other OpenStack services. Provides a catalog of endpoints for all OpenStack services.
The "Ussuri" series (latest release).

utils.py  (keystone-16.0.1):utils.py  (keystone-17.0.0)
skipping to change at line 22 skipping to change at line 22
"""Utilities for Federation Extension.""" """Utilities for Federation Extension."""
import ast import ast
import re import re
import flask import flask
import jsonschema import jsonschema
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log from oslo_log import log
from oslo_serialization import jsonutils
from oslo_utils import timeutils from oslo_utils import timeutils
import six
from keystone.common import provider_api from keystone.common import provider_api
import keystone.conf import keystone.conf
from keystone import exception from keystone import exception
from keystone.i18n import _ from keystone.i18n import _
CONF = keystone.conf.CONF CONF = keystone.conf.CONF
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
PROVIDERS = provider_api.ProviderAPIs PROVIDERS = provider_api.ProviderAPIs
skipping to change at line 281 skipping to change at line 281
def validate_expiration(token): def validate_expiration(token):
token_expiration_datetime = timeutils.normalize_time( token_expiration_datetime = timeutils.normalize_time(
timeutils.parse_isotime(token.expires_at) timeutils.parse_isotime(token.expires_at)
) )
if timeutils.utcnow() > token_expiration_datetime: if timeutils.utcnow() > token_expiration_datetime:
raise exception.Unauthorized(_('Federation token is expired')) raise exception.Unauthorized(_('Federation token is expired'))
def get_remote_id_parameter(idp, protocol): def get_remote_id_parameter(idp, protocol):
# NOTE(marco-fargetta): Since we support any protocol ID, we attempt to # NOTE(marco-fargetta): Since we support any protocol ID, we attempt to
# retrieve the remote_id_attribute of the protocol ID. It will look up first # retrieve the remote_id_attribute of the protocol ID. It will look up
# if the remote_id_attribute exists. # first if the remote_id_attribute exists.
protocol_ref = PROVIDERS.federation_api.get_protocol(idp['id'], protocol) protocol_ref = PROVIDERS.federation_api.get_protocol(idp['id'], protocol)
remote_id_parameter = protocol_ref.get('remote_id_attribute') remote_id_parameter = protocol_ref.get('remote_id_attribute')
if remote_id_parameter: if remote_id_parameter:
return remote_id_parameter return remote_id_parameter
else: else:
# If it's not registered in the config, then register the option and try # If it's not registered in the config, then register the option and
again. # try again. This allows the user to register protocols other than
# This allows the user to register protocols other than oidc and saml2. # oidc and saml2.
try: try:
remote_id_parameter = CONF[protocol]['remote_id_attribute'] remote_id_parameter = CONF[protocol]['remote_id_attribute']
except AttributeError: except AttributeError:
# TODO(dolph): Move configuration registration to keystone.conf # TODO(dolph): Move configuration registration to keystone.conf
CONF.register_opt(cfg.StrOpt('remote_id_attribute'), CONF.register_opt(cfg.StrOpt('remote_id_attribute'),
group=protocol) group=protocol)
try: try:
remote_id_parameter = CONF[protocol]['remote_id_attribute'] remote_id_parameter = CONF[protocol]['remote_id_attribute']
except AttributeError: # nosec except AttributeError: # nosec
# No remote ID attr, will be logged and use the default instead. # No remote ID attr, will be logged and use the default
# instead.
pass pass
if not remote_id_parameter: if not remote_id_parameter:
LOG.debug('Cannot find "remote_id_attribute" in configuration ' LOG.debug('Cannot find "remote_id_attribute" in configuration '
'group %s. Trying default location in ' 'group %s. Trying default location in '
'group federation.', protocol) 'group federation.', protocol)
remote_id_parameter = CONF.federation.remote_id_attribute remote_id_parameter = CONF.federation.remote_id_attribute
return remote_id_parameter return remote_id_parameter
def validate_idp(idp, protocol, assertion): def validate_idp(idp, protocol, assertion):
skipping to change at line 427 skipping to change at line 429
def get_assertion_params_from_env(): def get_assertion_params_from_env():
LOG.debug('Environment variables: %s', flask.request.environ) LOG.debug('Environment variables: %s', flask.request.environ)
prefix = CONF.federation.assertion_prefix prefix = CONF.federation.assertion_prefix
for k, v in list(flask.request.environ.items()): for k, v in list(flask.request.environ.items()):
if not k.startswith(prefix): if not k.startswith(prefix):
continue continue
# These bytes may be decodable as ISO-8859-1 according to Section # These bytes may be decodable as ISO-8859-1 according to Section
# 3.2.4 of RFC 7230. Let's assume that our web server plugins are # 3.2.4 of RFC 7230. Let's assume that our web server plugins are
# correctly encoding the data. # correctly encoding the data.
if not isinstance(v, six.text_type) and getattr(v, 'decode', False): if not isinstance(v, str) and getattr(v, 'decode', False):
v = v.decode('ISO-8859-1') v = v.decode('ISO-8859-1')
yield (k, v) yield (k, v)
class RuleProcessor(object): class RuleProcessor(object):
"""A class to process assertions and mapping rules.""" """A class to process assertions and mapping rules."""
class _EvalType(object): class _EvalType(object):
"""Mapping rule evaluation types.""" """Mapping rule evaluation types."""
ANY_ONE_OF = 'any_one_of' ANY_ONE_OF = 'any_one_of'
skipping to change at line 515 skipping to change at line 517
] ]
} }
""" """
# Assertions will come in as string key-value pairs, and will use a # Assertions will come in as string key-value pairs, and will use a
# semi-colon to indicate multiple values, i.e. groups. # semi-colon to indicate multiple values, i.e. groups.
# This will create a new dictionary where the values are arrays, and # This will create a new dictionary where the values are arrays, and
# any multiple values are stored in the arrays. # any multiple values are stored in the arrays.
LOG.debug('assertion data: %s', assertion_data) LOG.debug('assertion data: %s', assertion_data)
assertion = {n: v.split(';') for n, v in assertion_data.items() assertion = {n: v.split(';') for n, v in assertion_data.items()
if isinstance(v, six.string_types)} if isinstance(v, str)}
LOG.debug('assertion: %s', assertion) LOG.debug('assertion: %s', assertion)
identity_values = [] identity_values = []
LOG.debug('rules: %s', self.rules) LOG.debug('rules: %s', self.rules)
for rule in self.rules: for rule in self.rules:
direct_maps = self._verify_all_requirements(rule['remote'], direct_maps = self._verify_all_requirements(rule['remote'],
assertion) assertion)
# If the compare comes back as None, then the rule did not apply # If the compare comes back as None, then the rule did not apply
# to the assertion data, go on to the next rule # to the assertion data, go on to the next rule
skipping to change at line 544 skipping to change at line 546
else: else:
for local in rule['local']: for local in rule['local']:
new_local = self._update_local_mapping(local, direct_maps) new_local = self._update_local_mapping(local, direct_maps)
identity_values.append(new_local) identity_values.append(new_local)
LOG.debug('identity_values: %s', identity_values) LOG.debug('identity_values: %s', identity_values)
mapped_properties = self._transform(identity_values) mapped_properties = self._transform(identity_values)
LOG.debug('mapped_properties: %s', mapped_properties) LOG.debug('mapped_properties: %s', mapped_properties)
return mapped_properties return mapped_properties
def _normalize_groups(self, identity_value):
# In this case, identity_value['groups'] is a string
# representation of a list, and we want a real list. This is
# due to the way we do direct mapping substitutions today (see
# function _update_local_mapping() )
if 'name' in identity_value['groups']:
try:
group_names_list = ast.literal_eval(
identity_value['groups'])
except (ValueError, SyntaxError):
group_names_list = [identity_value['groups']]
def convert_json(group):
if group.startswith('JSON:'):
return jsonutils.loads(group.lstrip('JSON:'))
return group
group_dicts = [convert_json(g) for g in group_names_list]
for g in group_dicts:
if 'domain' not in g:
msg = _("Invalid rule: %(identity_value)s. Both "
"'groups' and 'domain' keywords must be "
"specified.")
msg = msg % {'identity_value': identity_value}
raise exception.ValidationError(msg)
else:
if 'domain' not in identity_value:
msg = _("Invalid rule: %(identity_value)s. Both "
"'groups' and 'domain' keywords must be "
"specified.")
msg = msg % {'identity_value': identity_value}
raise exception.ValidationError(msg)
try:
group_names_list = ast.literal_eval(
identity_value['groups'])
except (ValueError, SyntaxError):
group_names_list = [identity_value['groups']]
domain = identity_value['domain']
group_dicts = [{'name': name, 'domain': domain} for name in
group_names_list]
return group_dicts
def _transform(self, identity_values): def _transform(self, identity_values):
"""Transform local mappings, to an easier to understand format. """Transform local mappings, to an easier to understand format.
Transform the incoming array to generate the return value for Transform the incoming array to generate the return value for
the process function. Generating content for Keystone tokens will the process function. Generating content for Keystone tokens will
be easier if some pre-processing is done at this level. be easier if some pre-processing is done at this level.
:param identity_values: local mapping from valid evaluations :param identity_values: local mapping from valid evaluations
:type identity_values: array of dict :type identity_values: array of dict
skipping to change at line 631 skipping to change at line 675
if 'group' in identity_value: if 'group' in identity_value:
group = identity_value['group'] group = identity_value['group']
if 'id' in group: if 'id' in group:
group_ids.add(group['id']) group_ids.add(group['id'])
elif 'name' in group: elif 'name' in group:
domain = (group['domain'].get('name') or domain = (group['domain'].get('name') or
group['domain'].get('id')) group['domain'].get('id'))
groups_by_domain.setdefault(domain, list()).append(group) groups_by_domain.setdefault(domain, list()).append(group)
group_names.extend(extract_groups(groups_by_domain)) group_names.extend(extract_groups(groups_by_domain))
if 'groups' in identity_value: if 'groups' in identity_value:
if 'domain' not in identity_value: group_dicts = self._normalize_groups(identity_value)
msg = _("Invalid rule: %(identity_value)s. Both 'groups' "
"and 'domain' keywords must be specified.")
msg = msg % {'identity_value': identity_value}
raise exception.ValidationError(msg)
# In this case, identity_value['groups'] is a string
# representation of a list, and we want a real list. This is
# due to the way we do direct mapping substitutions today (see
# function _update_local_mapping() )
try:
group_names_list = ast.literal_eval(
identity_value['groups'])
except (ValueError, SyntaxError):
group_names_list = [identity_value['groups']]
domain = identity_value['domain']
group_dicts = [{'name': name, 'domain': domain} for name in
group_names_list]
group_names.extend(group_dicts) group_names.extend(group_dicts)
if 'group_ids' in identity_value: if 'group_ids' in identity_value:
# If identity_values['group_ids'] is a string representation # If identity_values['group_ids'] is a string representation
# of a list, parse it to a real list. Also, if the provided # of a list, parse it to a real list. Also, if the provided
# group_ids parameter contains only one element, it will be # group_ids parameter contains only one element, it will be
# parsed as a simple string, and not a list or the # parsed as a simple string, and not a list or the
# representation of a list. # representation of a list.
try: try:
group_ids.update( group_ids.update(
ast.literal_eval(identity_value['group_ids'])) ast.literal_eval(identity_value['group_ids']))
 End of changes. 9 change blocks. 
27 lines changed or deleted 53 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)