3232from saml2 .s_utils import UnknownSystemEntity
3333from saml2 .sigver import split_len
3434from saml2 .validate import valid_instance
35- from saml2 .time_util import valid
35+ from saml2 .time_util import valid , instant , add_duration , before , str_to_time
3636from saml2 .validate import NotValid
3737from saml2 .sigver import security_context
3838from saml2 .extension .mdattr import NAMESPACE as NS_MDATTR
@@ -821,7 +821,7 @@ def sha1_entity_transform(entity_id):
821821 hashlib .sha1 (entity_id .encode ("utf-8" )).hexdigest ())
822822
823823 def __init__ (self , url = None , security = None , cert = None ,
824- entity_transform = None , ** kwargs ):
824+ entity_transform = None , freshness_period = None , ** kwargs ):
825825 """
826826 :params url: mdx service url
827827 :params security: SecurityContext()
@@ -831,6 +831,8 @@ def __init__(self, url=None, security=None, cert=None,
831831 hash) the entity id. It is applied to the entity id before it is
832832 concatenated with the request URL sent to the MDX server. Defaults to
833833 sha1 transformation.
834+ :params freshness_period: a duration in the format described at
835+ https://www.w3.org/TR/xmlschema-2/#duration
834836 """
835837 super (MetaDataMDX , self ).__init__ (None , ** kwargs )
836838 if not url :
@@ -845,6 +847,9 @@ def __init__(self, url=None, security=None, cert=None,
845847
846848 self .cert = cert
847849 self .security = security
850+ self .freshness_period = freshness_period
851+ if freshness_period :
852+ self .expiration_date = {}
848853
849854 # We assume that the MDQ server will return a single entity
850855 # described by a single <EntityDescriptor> element. The protocol
@@ -859,21 +864,37 @@ def load(self, *args, **kwargs):
859864 # Do nothing
860865 pass
861866
867+ def fetch_metadata (self , item ):
868+ mdx_url = "%s/entities/%s" % (self .url , self .entity_transform (item ))
869+ response = requests .get (mdx_url , headers = {
870+ 'Accept' : SAML_METADATA_CONTENT_TYPE })
871+ if response .status_code == 200 :
872+ _txt = response .content
873+ if self .parse_and_check_signature (_txt ):
874+ if self .freshness_period :
875+ curr_time = str_to_time (instant ())
876+ self .expiration_date [item ] = add_duration (
877+ curr_time , self .freshness_period )
878+ return self .entity [item ]
879+ else :
880+ logger .info ("Response status: %s" , response .status_code )
881+ raise KeyError
882+
883+ def _is_fresh (self , item ):
884+ return self .freshness_period and before (self .expiration_date [item ])
885+
862886 def __getitem__ (self , item ):
863- try :
864- return self .entity [item ]
865- except KeyError :
866- mdx_url = "%s/entities/%s" % (self .url , self .entity_transform (item ))
867- response = requests .get (mdx_url , headers = {
868- 'Accept' : SAML_METADATA_CONTENT_TYPE })
869- if response .status_code == 200 :
870- _txt = response .content
871-
872- if self .parse_and_check_signature (_txt ):
873- return self .entity [item ]
887+ if item in self .entity :
888+ if self ._is_fresh (item ):
889+ entity = self .entity [item ]
874890 else :
875- logger .info ("Response status: %s" , response .status_code )
876- raise KeyError
891+ logger .info ("Metadata for {} have expired, refreshing "
892+ "metadata" .format (item ))
893+ self .entity .pop (item , None )
894+ entity = self .fetch_metadata (item )
895+ else :
896+ entity = self .fetch_metadata (item )
897+ return entity
877898
878899 def single_sign_on_service (self , entity_id , binding = None , typ = "idpsso" ):
879900 if binding is None :
@@ -960,9 +981,11 @@ def load(self, *args, **kwargs):
960981 key = kwargs ['url' ]
961982 url = kwargs ['url' ]
962983 cert = kwargs .get ('cert' )
984+ freshness_period = kwargs .get ('freshness_period' , None )
963985 security = self .security
964986 entity_transform = kwargs .get ('entity_transform' , None )
965- _md = MetaDataMDX (url , security , cert , entity_transform )
987+ _md = MetaDataMDX (url , security , cert , entity_transform ,
988+ freshness_period = freshness_period )
966989 else :
967990 key = args [1 ]
968991 url = args [1 ]
0 commit comments