14
14
from saml2 import BINDING_HTTP_POST
15
15
from saml2 import BINDING_SOAP
16
16
17
- import saml2 .xmldsig as ds
17
+ from saml2 .xmldsig import DefaultSignature
18
18
19
19
from saml2 .ident import decode , code
20
20
from saml2 .httpbase import HTTPError
@@ -40,10 +40,19 @@ class Saml2Client(Base):
40
40
""" The basic pySAML2 service provider class """
41
41
42
42
def prepare_for_authenticate (
43
- self , entityid = None , relay_state = "" ,
44
- binding = saml2 .BINDING_HTTP_REDIRECT , vorg = "" , nameid_format = None ,
45
- scoping = None , consent = None , extensions = None , sign = None ,
46
- response_binding = saml2 .BINDING_HTTP_POST , ** kwargs ):
43
+ self ,
44
+ entityid = None ,
45
+ relay_state = "" ,
46
+ binding = saml2 .BINDING_HTTP_REDIRECT ,
47
+ vorg = "" ,
48
+ nameid_format = None ,
49
+ scoping = None ,
50
+ consent = None , extensions = None ,
51
+ sign = None ,
52
+ sigalg = None ,
53
+ response_binding = saml2 .BINDING_HTTP_POST ,
54
+ ** kwargs ,
55
+ ):
47
56
""" Makes all necessary preparations for an authentication request.
48
57
49
58
:param entityid: The entity ID of the IdP to send the request to
@@ -61,19 +70,20 @@ def prepare_for_authenticate(
61
70
:return: session id and AuthnRequest info
62
71
"""
63
72
64
- reqid , negotiated_binding , info = \
65
- self .prepare_for_negotiated_authenticate (
66
- entityid = entityid ,
67
- relay_state = relay_state ,
68
- binding = binding ,
69
- vorg = vorg ,
70
- nameid_format = nameid_format ,
71
- scoping = scoping ,
72
- consent = consent ,
73
- extensions = extensions ,
74
- sign = sign ,
75
- response_binding = response_binding ,
76
- ** kwargs )
73
+ reqid , negotiated_binding , info = self .prepare_for_negotiated_authenticate (
74
+ entityid = entityid ,
75
+ relay_state = relay_state ,
76
+ binding = binding ,
77
+ vorg = vorg ,
78
+ nameid_format = nameid_format ,
79
+ scoping = scoping ,
80
+ consent = consent ,
81
+ extensions = extensions ,
82
+ sign = sign ,
83
+ sigalg = sigalg ,
84
+ response_binding = response_binding ,
85
+ ** kwargs ,
86
+ )
77
87
78
88
if negotiated_binding != binding :
79
89
raise ValueError (
@@ -85,9 +95,20 @@ def prepare_for_authenticate(
85
95
return reqid , info
86
96
87
97
def prepare_for_negotiated_authenticate (
88
- self , entityid = None , relay_state = "" , binding = None , vorg = "" ,
89
- nameid_format = None , scoping = None , consent = None , extensions = None ,
90
- sign = None , response_binding = saml2 .BINDING_HTTP_POST , ** kwargs ):
98
+ self ,
99
+ entityid = None ,
100
+ relay_state = "" ,
101
+ binding = None ,
102
+ vorg = "" ,
103
+ nameid_format = None ,
104
+ scoping = None ,
105
+ consent = None ,
106
+ extensions = None ,
107
+ sign = None ,
108
+ response_binding = saml2 .BINDING_HTTP_POST ,
109
+ sigalg = None ,
110
+ ** kwargs ,
111
+ ):
91
112
""" Makes all necessary preparations for an authentication request
92
113
that negotiates which binding to use for authentication.
93
114
@@ -115,27 +136,41 @@ def prepare_for_negotiated_authenticate(
115
136
destination = self ._sso_location (entityid , binding )
116
137
logger .info ("destination to provider: %s" , destination )
117
138
139
+ # XXX - sign_post will embed the signature to the xml doc
140
+ # XXX ^through self.create_authn_request(...)
141
+ # XXX - sign_redirect will add the signature to the query params
142
+ # XXX ^through self.apply_binding(...)
143
+ sign_post = (binding == BINDING_HTTP_POST and sign )
144
+ sign_redirect = (binding == BINDING_HTTP_REDIRECT and sign )
145
+
118
146
reqid , request = self .create_authn_request (
119
- destination , vorg , scoping , response_binding , nameid_format ,
120
- consent = consent , extensions = extensions , sign = sign ,
121
- ** kwargs )
147
+ destination ,
148
+ vorg ,
149
+ scoping ,
150
+ response_binding ,
151
+ nameid_format ,
152
+ consent = consent ,
153
+ extensions = extensions ,
154
+ sign = sign_post ,
155
+ sign_alg = sigalg ,
156
+ ** kwargs ,
157
+ )
122
158
123
159
_req_str = str (request )
124
-
125
160
logger .info ("AuthNReq: %s" , _req_str )
126
161
127
- try :
128
- args = {'sigalg' : kwargs ["sigalg" ]}
129
- except KeyError :
130
- args = {}
131
-
132
- http_info = self .apply_binding (binding , _req_str , destination ,
133
- relay_state , sign = sign , ** args )
162
+ http_info = self .apply_binding (
163
+ binding ,
164
+ _req_str ,
165
+ destination ,
166
+ relay_state ,
167
+ sign = sign_redirect ,
168
+ sigalg = sigalg ,
169
+ )
134
170
135
171
return reqid , binding , http_info
136
172
else :
137
- raise SignOnError (
138
- "No supported bindings available for authentication" )
173
+ raise SignOnError ("No supported bindings available for authentication" )
139
174
140
175
def global_logout (self , name_id , reason = "" , expire = None , sign = None ,
141
176
sign_alg = None , digest_alg = None ):
@@ -194,14 +229,13 @@ def do_logout(self, name_id, entity_ids, reason, expire, sign=None,
194
229
for entity_id in entity_ids :
195
230
logger .debug ("Logout from '%s'" , entity_id )
196
231
# for all where I can use the SOAP binding, do those first
197
- for binding in [BINDING_SOAP , BINDING_HTTP_POST ,
198
- BINDING_HTTP_REDIRECT ]:
232
+ for binding in [BINDING_SOAP , BINDING_HTTP_POST , BINDING_HTTP_REDIRECT ]:
199
233
if expected_binding and binding != expected_binding :
200
234
continue
201
235
try :
202
- srvs = self .metadata .single_logout_service (entity_id ,
203
- binding ,
204
- "idpsso" )
236
+ srvs = self .metadata .single_logout_service (
237
+ entity_id , binding , "idpsso"
238
+ )
205
239
except :
206
240
srvs = None
207
241
@@ -212,63 +246,66 @@ def do_logout(self, name_id, entity_ids, reason, expire, sign=None,
212
246
destination = next (locations (srvs ), None )
213
247
logger .info ("destination to provider: %s" , destination )
214
248
try :
215
- session_info = self .users .get_info_from (name_id ,
216
- entity_id ,
217
- False )
249
+ session_info = self .users .get_info_from (
250
+ name_id , entity_id , False
251
+ )
218
252
session_indexes = [session_info ['session_index' ]]
219
253
except KeyError :
220
254
session_indexes = None
221
255
req_id , request = self .create_logout_request (
222
256
destination , entity_id , name_id = name_id , reason = reason ,
223
257
expire = expire , session_indexes = session_indexes )
224
258
225
- # to_sign = []
226
- if binding .startswith ("http://" ):
227
- sign = True
228
-
229
- if sign is None :
230
- sign = self .logout_requests_signed
259
+ sign = sign if sign is not None else self .logout_requests_signed
260
+ def_sig = DefaultSignature ()
261
+ sign_alg = def_sig .get_sign_alg () if sign_alg is None else sign_alg
262
+ digest_alg = (
263
+ def_sig .get_digest_alg ()
264
+ if digest_alg is None
265
+ else digest_alg
266
+ )
231
267
232
- sigalg = None
233
268
if sign :
234
269
if binding == BINDING_HTTP_REDIRECT :
235
- sigalg = kwargs .get (
236
- "sigalg" , ds .DefaultSignature ().get_sign_alg ())
237
- # key = kwargs.get("key", self.signkey)
238
270
srequest = str (request )
239
271
else :
240
- srequest = self .sign (request , sign_alg = sign_alg ,
241
- digest_alg = digest_alg )
272
+ srequest = self .sign (
273
+ request , sign_alg = sign_alg , digest_alg = digest_alg
274
+ )
242
275
else :
243
276
srequest = str (request )
244
277
245
278
relay_state = self ._relay_state (req_id )
246
279
247
- http_info = self .apply_binding (binding , srequest , destination ,
248
- relay_state , sign = sign , sigalg = sigalg )
280
+ http_info = self .apply_binding (
281
+ binding ,
282
+ srequest ,
283
+ destination ,
284
+ relay_state ,
285
+ sign = sign ,
286
+ sigalg = sign_alg ,
287
+ )
249
288
250
289
if binding == BINDING_SOAP :
251
290
response = self .send (** http_info )
252
-
253
291
if response and response .status_code == 200 :
254
292
not_done .remove (entity_id )
255
293
response = response .text
256
294
logger .info ("Response: %s" , response )
257
- res = self .parse_logout_request_response (response ,
258
- binding )
295
+ res = self .parse_logout_request_response (response , binding )
259
296
responses [entity_id ] = res
260
297
else :
261
298
logger .info ("NOT OK response from %s" , destination )
262
-
263
299
else :
264
- self .state [req_id ] = {"entity_id" : entity_id ,
265
- "operation" : "SLO" ,
266
- "entity_ids" : entity_ids ,
267
- "name_id" : code (name_id ),
268
- "reason" : reason ,
269
- "not_on_or_after" : expire ,
270
- "sign" : sign }
271
-
300
+ self .state [req_id ] = {
301
+ "entity_id" : entity_id ,
302
+ "operation" : "SLO" ,
303
+ "entity_ids" : entity_ids ,
304
+ "name_id" : code (name_id ),
305
+ "reason" : reason ,
306
+ "not_on_or_after" : expire ,
307
+ "sign" : sign ,
308
+ }
272
309
responses [entity_id ] = (binding , http_info )
273
310
not_done .remove (entity_id )
274
311
@@ -419,11 +456,22 @@ def do_authn_query(self, entity_id,
419
456
420
457
return None
421
458
422
- def do_attribute_query (self , entityid , subject_id ,
423
- attribute = None , sp_name_qualifier = None ,
424
- name_qualifier = None , nameid_format = None ,
425
- real_id = None , consent = None , extensions = None ,
426
- sign = False , binding = BINDING_SOAP , nsprefix = None ):
459
+ def do_attribute_query (
460
+ self ,
461
+ entityid ,
462
+ subject_id ,
463
+ attribute = None ,
464
+ sp_name_qualifier = None ,
465
+ name_qualifier = None ,
466
+ nameid_format = None ,
467
+ real_id = None ,
468
+ consent = None ,
469
+ extensions = None ,
470
+ sign = False ,
471
+ binding = BINDING_SOAP ,
472
+ nsprefix = None ,
473
+ sign_alg = None ,
474
+ ):
427
475
""" Does a attribute request to an attribute authority, this is
428
476
by default done over SOAP.
429
477
@@ -482,13 +530,20 @@ def do_attribute_query(self, entityid, subject_id,
482
530
"subject_id" : subject_id ,
483
531
"sign" : sign }
484
532
relay_state = self ._relay_state (query .id )
485
- return self .apply_binding (binding , "%s" % query , destination ,
486
- relay_state , sign = sign )
533
+ return self .apply_binding (
534
+ binding ,
535
+ str (query ),
536
+ destination ,
537
+ relay_state ,
538
+ sign = sign ,
539
+ sigalg = sign_alg ,
540
+ )
487
541
else :
488
542
raise SAMLError ("Unsupported binding" )
489
543
490
- def handle_logout_request (self , request , name_id , binding , sign = None ,
491
- sign_alg = None , relay_state = "" ):
544
+ def handle_logout_request (
545
+ self , request , name_id , binding , sign = None , sign_alg = None , relay_state = ""
546
+ ):
492
547
"""
493
548
Deal with a LogoutRequest
494
549
@@ -531,16 +586,22 @@ def handle_logout_request(self, request, name_id, binding, sign=None,
531
586
elif binding in [BINDING_HTTP_POST , BINDING_HTTP_REDIRECT ]:
532
587
response_bindings = [BINDING_HTTP_POST , BINDING_HTTP_REDIRECT ]
533
588
else :
534
- response_bindings = self .config .preferred_binding [
535
- "single_logout_service" ]
589
+ response_bindings = self .config .preferred_binding ["single_logout_service" ]
536
590
537
591
if sign is None :
538
592
sign = self .logout_responses_signed
539
593
540
- response = self .create_logout_response (_req .message , response_bindings ,
541
- status , sign , sign_alg = sign_alg )
594
+ response = self .create_logout_response (
595
+ _req .message , response_bindings , status , sign , sign_alg = sign_alg
596
+ )
542
597
rinfo = self .response_args (_req .message , response_bindings )
543
598
544
- return self .apply_binding (rinfo ["binding" ], response ,
545
- rinfo ["destination" ], relay_state ,
546
- response = True , sign = sign )
599
+ return self .apply_binding (
600
+ rinfo ["binding" ],
601
+ response ,
602
+ rinfo ["destination" ],
603
+ relay_state ,
604
+ response = True ,
605
+ sign = sign ,
606
+ sigalg = sign_alg ,
607
+ )
0 commit comments