1212from saml2 .config import SPConfig
1313from saml2 .extension .ui import NAMESPACE as UI_NAMESPACE
1414from saml2 .metadata import create_metadata_string
15+ from saml2 .authn_context import requested_authn_context
1516
17+ import satosa .util as util
1618from satosa .base import SAMLBaseModule
1719from .base import BackendModule
1820from ..exception import SATOSAAuthenticationError
2325 ContactPersonDesc , UIInfoDesc )
2426from ..response import SeeOther , Response
2527from ..saml_util import make_saml_response
26- from ..util import rndstr
2728
2829logger = logging .getLogger (__name__ )
2930
@@ -32,6 +33,8 @@ class SAMLBackend(BackendModule, SAMLBaseModule):
3233 """
3334 A saml2 backend module (acting as a SP).
3435 """
36+ VALUE_ACR_CLASS_REF_DEFAULT = 'http://eidas.europa.eu/LoA/low'
37+ VALUE_ACR_COMPARISON_DEFAULT = 'minimum'
3538
3639 def __init__ (self , outgoing , internal_attributes , config , base_url , name ):
3740 """
@@ -56,6 +59,7 @@ def __init__(self, outgoing, internal_attributes, config, base_url, name):
5659 self .config = config
5760 self .attribute_profile = config .get ("attribute_profile" , "saml" )
5861 self .discosrv = config .get ("disco_srv" )
62+ self .acr_mapping = config .get ("acr_mapping" )
5963 self .encryption_keys = []
6064 self .outstanding_queries = {}
6165
@@ -109,6 +113,27 @@ def disco_query(self):
109113 loc = self .sp .create_discovery_service_request (self .discosrv , self .sp .config .entityid , ** {"return" : return_url })
110114 return SeeOther (loc )
111115
116+ def construct_requested_authn_context (self , entity_id ):
117+ if not self .acr_mapping :
118+ return None
119+
120+ acr_entry = util .get_dict_defaults (self .acr_mapping , entity_id )
121+ if not acr_entry :
122+ return None
123+
124+ if type (acr_entry ) is not dict :
125+ acr_entry = {
126+ "class_ref" : acr_entry ,
127+ "comparison" : self .VALUE_ACR_COMPARISON_DEFAULT ,
128+ }
129+
130+ authn_context = requested_authn_context (
131+ acr_entry .get ('class_ref' , self .VALUE_ACR_CLASS_REF_DEFAULT ),
132+ comparison = acr_entry .get (
133+ 'comparison' , self .VALUE_ACR_COMPARISON_DEFAULT ))
134+
135+ return authn_context
136+
112137 def authn_request (self , context , entity_id ):
113138 """
114139 Do an authorization request on idp with given entity id.
@@ -122,14 +147,20 @@ def authn_request(self, context, entity_id):
122147 :param entity_id: Target IDP entity id
123148 :return: response to the user agent
124149 """
150+ kwargs = {}
151+ authn_context = self .construct_requested_authn_context (entity_id )
152+ if authn_context :
153+ kwargs ['requested_authn_context' ] = authn_context
154+
125155 try :
126156 binding , destination = self .sp .pick_binding (
127157 "single_sign_on_service" , None , "idpsso" , entity_id = entity_id )
128158 satosa_logging (logger , logging .DEBUG , "binding: %s, destination: %s" % (binding , destination ),
129159 context .state )
130160 acs_endp , response_binding = self .sp .config .getattr ("endpoints" , "sp" )["assertion_consumer_service" ][0 ]
131- req_id , req = self .sp .create_authn_request (destination , binding = response_binding )
132- relay_state = rndstr ()
161+ req_id , req = self .sp .create_authn_request (
162+ destination , binding = response_binding , ** kwargs )
163+ relay_state = util .rndstr ()
133164 ht_args = self .sp .apply_binding (binding , "%s" % req , destination , relay_state = relay_state )
134165 satosa_logging (logger , logging .DEBUG , "ht_args: %s" % ht_args , context .state )
135166 except Exception as exc :
0 commit comments