summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Sklikas <nsklikas@admin.grnet.gr>2020-05-05 14:12:02 +0300
committerNikos Sklikas <nsklikas@admin.grnet.gr>2020-05-28 13:00:06 +0300
commit531f6bd1eac3488987be0e153d847ff59f5bbfbf (patch)
tree0e5f6fc6248d07efbf13aaccb0a0c998693c40a8
parent32ab8e68b48cb0f32b7299a1c72a36f25af3cc17 (diff)
downloadpysaml2-531f6bd1eac3488987be0e153d847ff59f5bbfbf.tar.gz
Add requested_attributes param
Add requested_attributes param to create_authn_request
-rw-r--r--src/saml2/client_base.py42
-rw-r--r--src/saml2/config.py44
-rw-r--r--tests/server_conf.py6
-rw-r--r--tests/test_51_client.py36
4 files changed, 106 insertions, 22 deletions
diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py
index ea0c86f9..a6a2c871 100644
--- a/src/saml2/client_base.py
+++ b/src/saml2/client_base.py
@@ -12,8 +12,6 @@ import logging
from saml2.entity import Entity
-import saml2.attributemaps as attributemaps
-
from saml2.mdstore import destinations
from saml2.profile import paos, ecp
from saml2.saml import NAMEID_FORMAT_TRANSIENT
@@ -24,7 +22,8 @@ from saml2.samlp import AuthzDecisionQuery
from saml2.samlp import AuthnRequest
from saml2.samlp import Extensions
from saml2.extension import sp_type
-from saml2.extension import requested_attributes
+from saml2.extension.requested_attributes import RequestedAttribute
+from saml2.extension.requested_attributes import RequestedAttributes
import saml2
from saml2.soap import make_soap_enveloped_saml_thingy
@@ -235,7 +234,7 @@ class Base(Entity):
service_url_binding=None, message_id=0,
consent=None, extensions=None, sign=None,
allow_create=None, sign_prepare=False, sign_alg=None,
- digest_alg=None, **kwargs):
+ digest_alg=None, requested_attributes=None, **kwargs):
""" Creates an authentication request.
:param destination: Where the request should be sent.
@@ -253,6 +252,9 @@ class Base(Entity):
:param allow_create: If the identity provider is allowed, in the course
of fulfilling the request, to create a new identifier to represent
the principal.
+ :param requested_attributes: A list of dicts which contain attributes
+ to be appended to the requested_attributes config option. The
+ dicts format is similar to the requested_attributes config option.
:param kwargs: Extra key word arguments
:return: either a tuple of request ID and <samlp:AuthnRequest> instance
or a tuple of request ID and str when sign is set to True
@@ -379,17 +381,19 @@ class Base(Entity):
item = sp_type.SPType(text=conf_sp_type)
extensions.add_extension_element(item)
- requested_attrs = self.config.getattr('requested_attributes', 'sp')
- if requested_attrs:
+ if requested_attributes:
+ requested_attributes += \
+ self.config.getattr('requested_attributes', 'sp')
+ else:
+ requested_attributes = \
+ self.config.getattr('requested_attributes', 'sp')
+
+ if requested_attributes:
if not extensions:
extensions = Extensions()
- attributemapsmods = []
- for modname in attributemaps.__all__:
- attributemapsmods.append(getattr(attributemaps, modname))
-
items = []
- for attr in requested_attrs:
+ for attr in requested_attributes:
friendly_name = attr.get('friendly_name')
name = attr.get('name')
name_format = attr.get('name_format')
@@ -401,34 +405,34 @@ class Base(Entity):
'name', 'friendly_name'))
if not name:
- for mod in attributemapsmods:
+ for converter in self.config.attribute_converters:
try:
- name = mod.MAP['to'][friendly_name]
+ name = converter._to[friendly_name.lower()]
except KeyError:
continue
else:
if not name_format:
- name_format = mod.MAP['identifier']
+ name_format = converter.name_format
break
if not friendly_name:
- for mod in attributemapsmods:
+ for converter in self.config.attribute_converters:
try:
- friendly_name = mod.MAP['fro'][name]
+ friendly_name = converter._fro[name.lower()]
except KeyError:
continue
else:
if not name_format:
- name_format = mod.MAP['identifier']
+ name_format = converter.name_format
break
- items.append(requested_attributes.RequestedAttribute(
+ items.append(RequestedAttribute(
is_required=is_required,
name_format=name_format,
friendly_name=friendly_name,
name=name))
- item = requested_attributes.RequestedAttributes(
+ item = RequestedAttributes(
extension_elements=items)
extensions.add_extension_element(item)
diff --git a/src/saml2/config.py b/src/saml2/config.py
index 147d1bdf..011ab43d 100644
--- a/src/saml2/config.py
+++ b/src/saml2/config.py
@@ -509,6 +509,50 @@ class SPConfig(Config):
return None
+ def load(self, cnf, metadata_construction=False):
+ super().load(cnf, metadata_construction=False)
+ self.fix_requested_attributes()
+ return self
+
+ def fix_requested_attributes(self):
+ """Add friendly_name or name if missing to the requested attributes"""
+ requested_attrs = self.getattr('requested_attributes', 'sp')
+
+ if not requested_attrs:
+ return
+
+ for attr in requested_attrs:
+ friendly_name = attr.get('friendly_name')
+ name = attr.get('name')
+ name_format = attr.get('name_format')
+
+ if not name and not friendly_name:
+ raise ValueError(
+ "Missing required attribute: '{}' or '{}'".format(
+ 'name', 'friendly_name'))
+
+ if not name:
+ for converter in self.attribute_converters:
+ try:
+ attr['name'] = converter._to[friendly_name.lower()]
+ except KeyError:
+ continue
+ else:
+ if not name_format:
+ attr['name_format'] = converter.name_format
+ break
+
+ if not friendly_name:
+ for converter in self.attribute_converters:
+ try:
+ attr['friendly_name'] = converter._fro[name.lower()]
+ except KeyError:
+ continue
+ else:
+ if not name_format:
+ attr['name_format'] = converter.name_format
+ break
+
class IdPConfig(Config):
def_context = "idp"
diff --git a/tests/server_conf.py b/tests/server_conf.py
index 4b528119..2b87b942 100644
--- a/tests/server_conf.py
+++ b/tests/server_conf.py
@@ -16,15 +16,15 @@ CONFIG = {
"idp": ["urn:mace:example.com:saml:roland:idp"],
"requested_attributes": [
{
- "name": "http://eidas.europa.eu/attributes/naturalperson/DateOfBirth",
+ "name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.2",
"required": False,
},
{
- "friendly_name": "PersonIdentifier",
+ "friendly_name": "eduPersonNickname",
"required": True,
},
{
- "friendly_name": "PlaceOfBirth",
+ "friendly_name": "eduPersonScopedAffiliation",
},
],
}
diff --git a/tests/test_51_client.py b/tests/test_51_client.py
index f6fc2759..cd1f0669 100644
--- a/tests/test_51_client.py
+++ b/tests/test_51_client.py
@@ -286,6 +286,42 @@ class TestClient:
assert c.attributes['FriendlyName']
assert c.attributes['NameFormat']
+ def test_create_auth_request_requested_attributes(self):
+ req_attr = [{"friendly_name": "eduPersonOrgUnitDN", "required": True}]
+ ar_str = "%s" % self.client.create_authn_request(
+ "http://www.example.com/sso",
+ message_id="id1",
+ requested_attributes=req_attr
+ )[1]
+
+ ar = samlp.authn_request_from_string(ar_str)
+
+ node_requested_attributes = None
+ for e in ar.extensions.extension_elements:
+ if e.tag == RequestedAttributes.c_tag:
+ node_requested_attributes = e
+ break
+ assert node_requested_attributes is not None
+
+ attr = None
+ for c in node_requested_attributes.children:
+ if c.attributes['FriendlyName'] == "eduPersonOrgUnitDN":
+ attr = c
+ break
+
+ assert attr
+ assert attr.tag == RequestedAttribute.c_tag
+ assert attr.attributes['isRequired'] == 'true'
+ assert (
+ attr.attributes['Name']
+ == 'urn:mace:dir:attribute-def:eduPersonOrgUnitDN'
+ )
+ assert attr.attributes['FriendlyName'] == 'eduPersonOrgUnitDN'
+ assert (
+ attr.attributes['NameFormat']
+ == 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic'
+ )
+
def test_create_auth_request_unset_force_authn_by_default(self):
req_id, req = self.client.create_authn_request(
"http://www.example.com/sso", sign=False, message_id="id1"