32
32
from saml2 .s_utils import UnknownSystemEntity
33
33
from saml2 .sigver import split_len
34
34
from saml2 .validate import valid_instance
35
- from saml2 .time_util import valid , instant , add_duration , before , str_to_time
35
+ from saml2 .time_util import valid
36
+ from saml2 .time_util import instant
37
+ from saml2 .time_util import add_duration
38
+ from saml2 .time_util import before
39
+ from saml2 .time_util import str_to_time
36
40
from saml2 .validate import NotValid
37
41
from saml2 .sigver import security_context
38
42
from saml2 .extension .mdattr import NAMESPACE as NS_MDATTR
72
76
ENTITY_CATEGORY_SUPPORT = "http://macedir.org/entity-category-support"
73
77
ASSURANCE_CERTIFICATION = "urn:oasis:names:tc:SAML:attribute:assurance-certification"
74
78
79
+ SAML_METADATA_CONTENT_TYPE = "application/samlmetadata+xml"
80
+ DEFAULT_FRESHNESS_PERIOD = "P0Y0M0DT12H0M0S"
81
+
82
+
83
+
75
84
REQ2SRV = {
76
85
# IDP
77
86
"authn_request" : "single_sign_on_service" ,
@@ -664,22 +673,21 @@ def signed(self):
664
673
def parse_and_check_signature (self , txt ):
665
674
self .parse (txt )
666
675
667
- if self .cert :
668
- if not self .signed ():
669
- return True
670
-
671
- node_name = self .node_name \
672
- or "%s:%s" % (md .EntitiesDescriptor .c_namespace ,
673
- md .EntitiesDescriptor .c_tag )
676
+ if not self .cert :
677
+ return True
674
678
675
- if self .security .verify_signature (
676
- txt , node_name = node_name , cert_file = self .cert ):
677
- return True
678
- else :
679
- return False
680
- else :
679
+ if not self .signed ():
681
680
return True
682
681
682
+ fallback_name = "{ns}:{tag}" .format (
683
+ ns = md .EntitiesDescriptor .c_namespace , tag = md .EntitiesDescriptor .c_tag
684
+ )
685
+ node_name = self .node_name or fallback_name
686
+
687
+ return self .security .verify_signature (
688
+ txt , node_name = node_name , cert_file = self .cert
689
+ )
690
+
683
691
684
692
class MetaDataFile (InMemoryMetaData ):
685
693
"""
@@ -805,9 +813,6 @@ def load(self, *args, **kwargs):
805
813
self .entity [key ] = item
806
814
807
815
808
- SAML_METADATA_CONTENT_TYPE = 'application/samlmetadata+xml'
809
-
810
-
811
816
class MetaDataMDX (InMemoryMetaData ):
812
817
"""
813
818
Uses the MDQ protocol to fetch entity information.
@@ -817,8 +822,9 @@ class MetaDataMDX(InMemoryMetaData):
817
822
818
823
@staticmethod
819
824
def sha1_entity_transform (entity_id ):
820
- return "{{sha1}}{}" .format (
821
- hashlib .sha1 (entity_id .encode ("utf-8" )).hexdigest ())
825
+ entity_id_sha1 = hashlib .sha1 (entity_id .encode ("utf-8" )).hexdigest ()
826
+ transform = "{{sha1}}{digest}" .format (digest = entity_id_sha1 )
827
+ return transform
822
828
823
829
def __init__ (self , url = None , security = None , cert = None ,
824
830
entity_transform = None , freshness_period = None , ** kwargs ):
@@ -847,53 +853,61 @@ def __init__(self, url=None, security=None, cert=None,
847
853
848
854
self .cert = cert
849
855
self .security = security
850
- self .freshness_period = freshness_period
851
- if freshness_period :
852
- self .expiration_date = {}
856
+ self .freshness_period = freshness_period or DEFAULT_FRESHNESS_PERIOD
857
+ self .expiration_date = {}
853
858
854
859
# We assume that the MDQ server will return a single entity
855
860
# described by a single <EntityDescriptor> element. The protocol
856
861
# does allow multiple entities to be returned in an
857
862
# <EntitiesDescriptor> element but we will not currently support
858
863
# that use case since it is unlikely to be leveraged for most
859
864
# flows.
860
- self .node_name = "%s:%s" % (md .EntityDescriptor .c_namespace ,
861
- md .EntityDescriptor .c_tag )
865
+ self .node_name = "{ns}:{tag}" .format (
866
+ ns = md .EntityDescriptor .c_namespace , tag = md .EntityDescriptor .c_tag
867
+ )
862
868
863
869
def load (self , * args , ** kwargs ):
864
870
# Do nothing
865
871
pass
866
872
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
873
+ def _fetch_metadata (self , item ):
874
+ mdx_url = "{url}/entities/{id}" .format (
875
+ url = self .url , id = self .entity_transform (item )
876
+ )
882
877
883
- def _is_fresh (self , item ):
884
- return self .freshness_period and before (self .expiration_date [item ])
878
+ response = requests .get (mdx_url , headers = {"Accept" : SAML_METADATA_CONTENT_TYPE })
879
+ if response .status_code != 200 :
880
+ error_msg = "Fething {item}: Got response status {status}" .format (
881
+ item = item , status = response .status_code
882
+ )
883
+ logger .info (error_msg )
884
+ raise KeyError (error_msg )
885
+
886
+ _txt = response .content
887
+ if not self .parse_and_check_signature (_txt ):
888
+ error_msg = "Fething {item}: invalid signature" .format (
889
+ item = item , status = response .status_code
890
+ )
891
+ logger .info (error_msg )
892
+ raise KeyError (error_msg )
893
+
894
+ curr_time = str_to_time (instant ())
895
+ self .expiration_date [item ] = add_duration (curr_time , self .freshness_period )
896
+ return self .entity [item ]
897
+
898
+ def _is_metadata_fresh (self , item ):
899
+ return before (self .expiration_date [item ])
885
900
886
901
def __getitem__ (self , item ):
887
- if item in self .entity :
888
- if self ._is_fresh (item ):
889
- entity = self .entity [item ]
890
- else :
891
- logger .info ("Metadata for {} have expired, refreshing "
892
- "metadata" .format (item ))
893
- self .entity .pop (item )
894
- entity = self .fetch_metadata (item )
902
+ if item not in self .entity :
903
+ entity = self ._fetch_metadata (item )
904
+ elif not self ._is_metadata_fresh (item ):
905
+ msg = "Metadata for {} have expired; refreshing metadata" .format (item )
906
+ logger .info (msg )
907
+ old_entity = self .entity .pop (item )
908
+ entity = self ._fetch_metadata (item )
895
909
else :
896
- entity = self .fetch_metadata ( item )
910
+ entity = self .entity [ item ]
897
911
return entity
898
912
899
913
def single_sign_on_service (self , entity_id , binding = None , typ = "idpsso" ):
0 commit comments