summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kanakarakis <ivan.kanak@gmail.com>2020-07-11 18:26:38 +0300
committerIvan Kanakarakis <ivan.kanak@gmail.com>2020-07-11 19:11:19 +0300
commite5d0b4f0760144430d885165d41d777b59ef5d6a (patch)
treeb86960a5b7e3b871d96d7cbc448e302e964a5a90
parent21eb11fa2333b85309257a7627b794242ebe6b8d (diff)
downloadpysaml2-e5d0b4f0760144430d885165d41d777b59ef5d6a.tar.gz
Support arbitrary entity attributes
Introduce new configuration option `entity_attributes` that defines a list of dictionaries each of which represents an <Attribute> element. Each dicrionary has fields for the NameFormat, the Name, the FriendName and a list of strings that are used to create <AttributeValue> elements, each with the string as the text node. "entity_attributes": [ { "name_format": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "name": "urn:oasis:names:tc:SAML:profiles:subject-id:req", # "friendly_name" is not set "values": ["any"], }, ] Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
-rw-r--r--docs/howto/config.rst23
-rw-r--r--src/saml2/config.py2
-rw-r--r--src/saml2/metadata.py18
-rw-r--r--tests/sp_mdext_conf.py14
-rw-r--r--tests/test_83_md_extensions.py82
5 files changed, 116 insertions, 23 deletions
diff --git a/docs/howto/config.rst b/docs/howto/config.rst
index 9f20eba6..a46927a4 100644
--- a/docs/howto/config.rst
+++ b/docs/howto/config.rst
@@ -76,6 +76,29 @@ if you need to include a certificate chain.
Each entry in *additional_cert_files* must be a PEM formatted file with a single certificate.
+entity_attributes
+^^^^^^^^^^^^^^^^^
+
+Generates an ``Attribute`` element with the given NameFormat, Name, FriendlyName and
+values, each as an ``AttributeValue`` element.
+
+The element is added under the generated metadata ``EntityDescriptor`` as an
+``Extension`` element under the ``EntityAttributes`` element.
+
+And omit
+
+Example::
+
+ "entity_attributes": [
+ {
+ "name_format": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
+ "name": "urn:oasis:names:tc:SAML:profiles:subject-id:req",
+ # "friendly_name" is not set
+ "values": ["any"],
+ },
+ ]
+
+
assurance_certification
^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/saml2/config.py b/src/saml2/config.py
index 278aba16..dbf57aa3 100644
--- a/src/saml2/config.py
+++ b/src/saml2/config.py
@@ -54,6 +54,7 @@ COMMON_ARGS = [
"preferred_binding",
"session_storage",
"assurance_certification",
+ "entity_attributes",
"entity_category",
"entity_category_support",
"xmlsec_path",
@@ -226,6 +227,7 @@ class Config(object):
self.domain = ""
self.name_qualifier = ""
self.assurance_certification = []
+ self.entity_attributes = []
self.entity_category = []
self.entity_category_support = []
self.crypto_backend = 'xmlsec1'
diff --git a/src/saml2/metadata.py b/src/saml2/metadata.py
index d80b41ac..50b4ff71 100644
--- a/src/saml2/metadata.py
+++ b/src/saml2/metadata.py
@@ -708,6 +708,24 @@ def entity_descriptor(confd):
if confd.contact_person is not None:
entd.contact_person = do_contact_persons_info(confd.contact_person)
+ if confd.entity_attributes:
+ if not entd.extensions:
+ entd.extensions = md.Extensions()
+ attributes = [
+ Attribute(
+ name_format=attr.get("format"),
+ name=attr.get("name"),
+ friendly_name=attr.get("friendly_name"),
+ attribute_value=[
+ AttributeValue(text=value)
+ for value in attr.get("values", [])
+ ],
+ )
+ for attr in confd.entity_attributes
+ ]
+ for attribute in attributes:
+ _add_attr_to_entity_attributes(entd.extensions, attribute)
+
if confd.assurance_certification:
if not entd.extensions:
entd.extensions = md.Extensions()
diff --git a/tests/sp_mdext_conf.py b/tests/sp_mdext_conf.py
index b1f0cf42..2fe43390 100644
--- a/tests/sp_mdext_conf.py
+++ b/tests/sp_mdext_conf.py
@@ -3,6 +3,20 @@ from pathutils import full_path, xmlsec_path
CONFIG = {
"entityid": "urn:mace:example.com:saml:roland:sp",
"name": "urn:mace:example.com:saml:roland:sp",
+ "entity_attributes": [
+ {
+ "name_format": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
+ "name": "urn:oasis:names:tc:SAML:profiles:subject-id:req",
+ # "friendly_name" is not set
+ "values": ["any"],
+ },
+ {
+ "name": "somename",
+ "friendly_name": "somefriendlyname",
+ "name_format": "format",
+ "values": ["x", "y", "z"],
+ },
+ ],
"description": "My own SP",
"service": {
"sp": {
diff --git a/tests/test_83_md_extensions.py b/tests/test_83_md_extensions.py
index dace10a5..35098595 100644
--- a/tests/test_83_md_extensions.py
+++ b/tests/test_83_md_extensions.py
@@ -1,27 +1,63 @@
+from saml2 import create_class_from_xml_string as parse_str_as
+from saml2 import create_class_from_element_tree as parse_element_as
from saml2.config import Config
-from saml2.metadata import entity_descriptor
from saml2.extension.sp_type import SPType
+from saml2.metadata import Attribute
+from saml2.metadata import entity_descriptor
-__author__ = 'roland'
-
-fil = "sp_mdext_conf.py"
-
-cnf = Config().load_file(fil, metadata_construction=True)
-ed = entity_descriptor(cnf)
-
-print(ed)
-
-assert ed.spsso_descriptor.extensions
-assert len(ed.spsso_descriptor.extensions.extension_elements) == 3
-
-assert ed.extensions
-assert len(ed.extensions.extension_elements) > 1
-
-assert any(e.tag is SPType.c_tag for e in ed.extensions.extension_elements)
-
-cnf.setattr('sp', 'sp_type_in_metadata', False)
-ed = entity_descriptor(cnf)
-
-print(ed)
-assert all(e.tag is not SPType.c_tag for e in ed.extensions.extension_elements)
+class TestMDExt():
+ def test_sp_type_true(self):
+ fil = "sp_mdext_conf.py"
+ cnf = Config().load_file(fil, metadata_construction=True)
+ ed = entity_descriptor(cnf)
+
+ assert ed.spsso_descriptor.extensions
+ assert len(ed.spsso_descriptor.extensions.extension_elements) == 3
+ assert ed.extensions
+ assert len(ed.extensions.extension_elements) > 1
+ assert any(e.tag is SPType.c_tag for e in ed.extensions.extension_elements)
+
+ def test_sp_type_false(self):
+ fil = "sp_mdext_conf.py"
+ cnf = Config().load_file(fil, metadata_construction=True)
+ cnf.setattr('sp', 'sp_type_in_metadata', False)
+ ed = entity_descriptor(cnf)
+
+ assert all(e.tag is not SPType.c_tag for e in ed.extensions.extension_elements)
+
+ def test_entity_attributes(self):
+ fil = "sp_mdext_conf.py"
+ cnf = Config().load_file(fil, metadata_construction=True)
+ ed = entity_descriptor(cnf)
+
+ entity_attributes = next(
+ e
+ for e in ed.extensions.extension_elements
+ if e.tag == 'EntityAttributes'
+ )
+ attributes = [
+ parse_str_as(Attribute, e.to_string())
+ for e in entity_attributes.children
+ ]
+ assert all(
+ a.name in [
+ "urn:oasis:names:tc:SAML:profiles:subject-id:req",
+ "somename",
+ ]
+ for a in attributes
+ )
+
+ import saml2.attribute_converter
+ attrc = saml2.attribute_converter.ac_factory()
+
+ import saml2.mdstore
+ mds = saml2.mdstore.MetadataStore(attrc, cnf)
+
+ mds.load("inline", ed.to_string())
+ entityid = ed.entity_id
+ entity_attributes = mds.entity_attributes(entityid)
+ assert entity_attributes == {
+ 'urn:oasis:names:tc:SAML:profiles:subject-id:req': ['any'],
+ 'somename': ['x', 'y', 'z'],
+ }