diff options
author | Zuul <zuul@review.opendev.org> | 2020-03-23 19:24:38 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2020-03-23 19:24:38 +0000 |
commit | 9f9040257f876ff32d71483ccf4a47e135f0bb4d (patch) | |
tree | 9df32ab3d1727ec5aa02b54bc5d096585adb6225 /keystone/federation | |
parent | d9af77e1d854b7a49c749a2f86002fbe0336ae39 (diff) | |
parent | dda426b61a18590a81c5b3af281eb0c410756692 (diff) | |
download | keystone-9f9040257f876ff32d71483ccf4a47e135f0bb4d.tar.gz |
Merge "Add openstack_groups to assertion"
Diffstat (limited to 'keystone/federation')
-rw-r--r-- | keystone/federation/idp.py | 26 | ||||
-rw-r--r-- | keystone/federation/utils.py | 62 |
2 files changed, 67 insertions, 21 deletions
diff --git a/keystone/federation/idp.py b/keystone/federation/idp.py index e0c983e7b..fd464f5c2 100644 --- a/keystone/federation/idp.py +++ b/keystone/federation/idp.py @@ -48,7 +48,8 @@ class SAMLGenerator(object): self.assertion_id = uuid.uuid4().hex def samlize_token(self, issuer, recipient, user, user_domain_name, roles, - project, project_domain_name, expires_in=None): + project, project_domain_name, groups, + expires_in=None): """Convert Keystone attributes to a SAML assertion. :param issuer: URL of the issuing party @@ -65,6 +66,9 @@ class SAMLGenerator(object): :type project: string :param project_domain_name: Project Domain name :type project_domain_name: string + :param groups: List of strings of user groups and domain name, where + strings are serialized dictionaries. + :type groups: list :param expires_in: Sets how long the assertion is valid for, in seconds :type expires_in: int @@ -76,7 +80,8 @@ class SAMLGenerator(object): saml_issuer = self._create_issuer(issuer) subject = self._create_subject(user, expiration_time, recipient) attribute_statement = self._create_attribute_statement( - user, user_domain_name, roles, project, project_domain_name) + user, user_domain_name, roles, project, project_domain_name, + groups) authn_statement = self._create_authn_statement(issuer, expiration_time) signature = self._create_signature() @@ -162,7 +167,8 @@ class SAMLGenerator(object): return subject def _create_attribute_statement(self, user, user_domain_name, roles, - project, project_domain_name): + project, project_domain_name, + groups): """Create an object that represents a SAML AttributeStatement. <ns0:AttributeStatement> @@ -188,6 +194,15 @@ class SAMLGenerator(object): <ns0:AttributeValue xsi:type="xs:string">Default</ns0:AttributeValue> </ns0:Attribute> + <ns0:Attribute Name="openstack_groups"> + <ns0:AttributeValue + xsi:type="xs:string">JSON:{"name":"group1","domain":{"name":"Default"}} + </ns0:AttributeValue> + <ns0:AttributeValue + xsi:type="xs:string">JSON:{"name":"group2","domain":{"name":"Default"}} + </ns0:AttributeValue> + </ns0:Attribute> + </ns0:AttributeStatement> :returns: XML <AttributeStatement> object @@ -218,6 +233,11 @@ class SAMLGenerator(object): attribute_statement.attribute.append(project_attribute) attribute_statement.attribute.append(project_domain_attribute) attribute_statement.attribute.append(user_domain_attribute) + + if groups: + groups_attribute = _build_attribute( + 'openstack_groups', groups) + attribute_statement.attribute.append(groups_attribute) return attribute_statement def _create_authn_statement(self, issuer, expiration_time): diff --git a/keystone/federation/utils.py b/keystone/federation/utils.py index 9bfaea8c0..95a8e8305 100644 --- a/keystone/federation/utils.py +++ b/keystone/federation/utils.py @@ -19,6 +19,7 @@ import flask import jsonschema from oslo_config import cfg from oslo_log import log +from oslo_serialization import jsonutils from oslo_utils import timeutils from keystone.common import provider_api @@ -554,6 +555,48 @@ class RuleProcessor(object): LOG.debug('mapped_properties: %s', 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): """Transform local mappings, to an easier to understand format. @@ -641,24 +684,7 @@ class RuleProcessor(object): groups_by_domain.setdefault(domain, list()).append(group) group_names.extend(extract_groups(groups_by_domain)) if 'groups' in identity_value: - 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) - # 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_dicts = self._normalize_groups(identity_value) group_names.extend(group_dicts) if 'group_ids' in identity_value: # If identity_values['group_ids'] is a string representation |