From f72e286fca31750c680f23b7ca0dc3eb8af31449 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 9 Jun 2021 22:18:53 +1200 Subject: fix: saml2/time_util: get before/after docstrings right Align the docstrings with what the functions actually implement. --- src/saml2/time_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/saml2/time_util.py b/src/saml2/time_util.py index 332d84bb..9eb4cec0 100644 --- a/src/saml2/time_util.py +++ b/src/saml2/time_util.py @@ -269,7 +269,7 @@ def utc_now(): def before(point): - """ True if point datetime specification is before now. + """ True if current time is before point datetime specification. NOTE: If point is specified it is supposed to be in local time. Not UTC/GMT !! This is because that is what gmtime() expects. @@ -286,7 +286,7 @@ def before(point): def after(point): - """ True if point datetime specification is equal or after now """ + """ True if current time is after or equal to point datetime specification.""" if not point: return True else: -- cgit v1.2.1 From a084c8f9f8922ec8292ee71c75a12c254be19758 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 9 Jun 2021 22:21:16 +1200 Subject: fix: mdstore: fix MetadataStore.dumps(format="md") MetadataStore.dumps(format="md") was failing with TypeError: Object of type dict_items is not JSON serializable ... because self.items() returns dictitems() - while only a dict would be serializable into JSON. Convert the dictitems back into a dict. --- src/saml2/mdstore.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py index f3f26546..999d1ecf 100644 --- a/src/saml2/mdstore.py +++ b/src/saml2/mdstore.py @@ -1693,4 +1693,5 @@ class MetadataStore(MetaData): return "%s" % res elif format == "md": - return json.dumps(self.items(), indent=2) + # self.items() returns dictitems(), convert that back into a dict + return json.dumps(dict(self.items()), indent=2) -- cgit v1.2.1 From 65674f8458c8a6f1c5050238313b2dd932bfa735 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 9 Jun 2021 22:24:11 +1200 Subject: fix: mdstore: fix exception handler in InMemoryMetaData.parse The exception handler in InMemoryMetaData.parse was failing for subclasses other then `MetaDataFile` with: AttributeError: 'MetaDataExtern' object has no attribute 'filename' - because `self.filename` is only defined for MetaDataFile but not MetaDataExtern The handler was essentially expecting it would only be invoked for MetaDataFile and not other subclasses of InMemoryMetaData. Provide useful descriptive messages for MetaDataFile and MetaDataExtern subclassses - and fall back to a generic (but safe) message otherwise. --- src/saml2/mdstore.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py index 999d1ecf..eb797944 100644 --- a/src/saml2/mdstore.py +++ b/src/saml2/mdstore.py @@ -618,7 +618,10 @@ class InMemoryMetaData(MetaData): try: self.entities_descr = md.entities_descriptor_from_string(xmlstr) except Exception as e: - raise SAMLError(f'Failed to parse metadata file: {self.filename}') from e + _md_desc = (f'metadata file: {self.filename}' if isinstance(self,MetaDataFile) else + f'remote metadata: {self.url}' if isinstance(self, MetaDataExtern) else + 'metadata') + raise SAMLError(f'Failed to parse {_md_desc}') from e if not self.entities_descr: self.entity_descr = md.entity_descriptor_from_string(xmlstr) -- cgit v1.2.1 From 59604b6980bc3cc2d7a1a2b5a3aed515e9b1df17 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Thu, 10 Jun 2021 14:53:25 +1200 Subject: fix: saml2.assertion: safeguard _filter_values against vals=None In certain circumstances, such as an Saml2IdP receiving a request from an SP where the SP metadata has a RequestedAttribute with specific values, `_filter_values` may be called with vals=None when processing the AuthnRequest. Safeguard against this by returning early, returning the None value unfiltered. (It will get later replaced with an [] in `_apply_attr_value_restrictions`). --- src/saml2/assertion.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/saml2/assertion.py b/src/saml2/assertion.py index cd01463b..4474bf42 100644 --- a/src/saml2/assertion.py +++ b/src/saml2/assertion.py @@ -35,6 +35,9 @@ def _filter_values(vals, vlist=None, must=False): if not vlist: # No value specified equals any value return vals + if vals is None: # cannot iterate over None, return early + return vals + if isinstance(vlist, six.string_types): vlist = [vlist] -- cgit v1.2.1 From 14506c065274cee44eb435338f291f6774cd635d Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Fri, 11 Jun 2021 10:53:30 +1200 Subject: new: saml2.Entity: support reloading metadata Support reloading metadata by adding a reload_metadata method to saml2.Entity. This method gets the metadata configuration in the same format as the 'metadata' entry in the configuration passed to saml2.Config. To keep metadata refreshed, this method needs to be periodically explicitly called. For a metadata refresh with the same configuration, the calling application should keep a copy of the original configuration to pass to this method. Resolves #808 --- src/saml2/entity.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/saml2/entity.py b/src/saml2/entity.py index 1a07807c..44596272 100644 --- a/src/saml2/entity.py +++ b/src/saml2/entity.py @@ -203,6 +203,40 @@ class Entity(HTTPBase): self.msg_cb = msg_cb + def reload_metadata(self, metadata_conf): + """ + Reload metadata configuration. + + Load a new metadata configuration as defined by metadata_conf (by + passing this to Config.load_metadata) and make this entity (as well as + subordinate objects with own metadata reference) use the new metadata. + + The structure of metadata_conf is the same as the 'metadata' entry in + the configuration passed to saml2.Config. + + param metadata_conf: Metadata configuration as passed to Config.load_metadata + return: True if successfully reloaded + """ + logger.debug("Loading new metadata") + try: + new_metadata = self.config.load_metadata(metadata_conf) + except Exception as ex: + logger.error("Loading metadata failed", exc_info=ex) + return False + + logger.debug("Applying new metadata to main config") + ( self.metadata, self.sec.metadata, self.config.metadata ) = [new_metadata]*3 + for typ in ["aa", "idp", "sp", "pdp", "aq"]: + policy = getattr(self.config, "_%s_policy" % typ, None) + if policy and policy.metadata_store: + logger.debug("Applying new metadata to %s policy", typ) + policy.metadata_store = self.metadata + + logger.debug("Applying new metadata source_id") + self.sourceid = self.metadata.construct_source_id() + + return True + def _issuer(self, entityid=None): """ Return an Issuer instance """ if entityid: -- cgit v1.2.1 From 4f324f53c254f44b268ab90f7a676ec0e8f0129c Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Tue, 20 Jul 2021 14:45:01 +1200 Subject: nfc: fix formatting in comment (tab vs spaces) in src/saml2/entity.py Co-authored-by: Ivan Kanakarakis --- src/saml2/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/saml2/entity.py b/src/saml2/entity.py index 44596272..ad9d2d08 100644 --- a/src/saml2/entity.py +++ b/src/saml2/entity.py @@ -207,7 +207,7 @@ class Entity(HTTPBase): """ Reload metadata configuration. - Load a new metadata configuration as defined by metadata_conf (by + Load a new metadata configuration as defined by metadata_conf (by passing this to Config.load_metadata) and make this entity (as well as subordinate objects with own metadata reference) use the new metadata. -- cgit v1.2.1 From d50235cbf82237d1baf247721f2939ae0eb22156 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Tue, 20 Jul 2021 14:47:36 +1200 Subject: nfc: reformat expression in src/saml2/mdstore.py as per review Co-authored-by: Ivan Kanakarakis --- src/saml2/mdstore.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py index eb797944..d001999d 100644 --- a/src/saml2/mdstore.py +++ b/src/saml2/mdstore.py @@ -618,9 +618,13 @@ class InMemoryMetaData(MetaData): try: self.entities_descr = md.entities_descriptor_from_string(xmlstr) except Exception as e: - _md_desc = (f'metadata file: {self.filename}' if isinstance(self,MetaDataFile) else - f'remote metadata: {self.url}' if isinstance(self, MetaDataExtern) else - 'metadata') + _md_desc = ( + f'metadata file: {self.filename}' + if isinstance(self,MetaDataFile) + else f'remote metadata: {self.url}' + if isinstance(self, MetaDataExtern) + else 'metadata' + ) raise SAMLError(f'Failed to parse {_md_desc}') from e if not self.entities_descr: -- cgit v1.2.1 From 6e90788b40edecb5d649679cc8395677d1ced6ed Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Tue, 20 Jul 2021 15:12:16 +1200 Subject: fix: saml2.Entity/reload_metadata: use self.entity_type instead of iterating over all types As per review suggestion in #809 --- src/saml2/entity.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/saml2/entity.py b/src/saml2/entity.py index ad9d2d08..f6ca396c 100644 --- a/src/saml2/entity.py +++ b/src/saml2/entity.py @@ -226,11 +226,10 @@ class Entity(HTTPBase): logger.debug("Applying new metadata to main config") ( self.metadata, self.sec.metadata, self.config.metadata ) = [new_metadata]*3 - for typ in ["aa", "idp", "sp", "pdp", "aq"]: - policy = getattr(self.config, "_%s_policy" % typ, None) - if policy and policy.metadata_store: - logger.debug("Applying new metadata to %s policy", typ) - policy.metadata_store = self.metadata + policy = getattr(self.config, "_%s_policy" % self.entity_type, None) + if policy and policy.metadata_store: + logger.debug("Applying new metadata to %s policy", self.entity_type) + policy.metadata_store = self.metadata logger.debug("Applying new metadata source_id") self.sourceid = self.metadata.construct_source_id() -- cgit v1.2.1