@@ -179,6 +179,193 @@ def test_authn_request(self, context, idp_conf):
179179 req_params = dict (parse_qsl (urlparse (resp .message ).query ))
180180 assert context .state [self .samlbackend .name ]["relay_state" ] == req_params ["RelayState" ]
181181
182+ def test_authn_request_requested_attributes (
183+ self , context , idp_conf , sp_conf
184+ ):
185+ requested_attributes = [
186+ {"friendly_name" : "cn" , "required" : True },
187+ {"friendly_name" : "sn" , "required" : False }
188+ ]
189+
190+ backend = SAMLBackend (
191+ None ,
192+ INTERNAL_ATTRIBUTES ,
193+ {
194+ SAMLBackend .KEY_DYNAMIC_REQUESTED_ATTRIBUTES : requested_attributes ,
195+ "sp_config" : sp_conf
196+ },
197+ "base_url" ,
198+ "saml_backend"
199+ )
200+
201+ with patch .object (
202+ backend .sp ,
203+ "create_authn_request" ,
204+ wraps = backend .sp .create_authn_request
205+ ) as mock :
206+ backend .authn_request (
207+ context ,
208+ idp_conf ["entityid" ],
209+ requested_attributes = ["name" , "surname" ]
210+ )
211+
212+ kwargs = mock .call_args [1 ]
213+ assert "requested_attributes" in kwargs
214+ assert kwargs ["requested_attributes" ] == [
215+ {"friendly_name" : "cn" , "required" : True },
216+ {"friendly_name" : "sn" , "required" : False }
217+ ]
218+
219+ def test_authn_request_requested_attributes_ignore_extra (
220+ self , context , idp_conf , sp_conf
221+ ):
222+ """
223+ Extra internal attributes should be ignored
224+ """
225+ requested_attributes = [
226+ {"friendly_name" : "cn" , "required" : True },
227+ {"friendly_name" : "sn" , "required" : False }
228+ ]
229+
230+ backend = SAMLBackend (
231+ None ,
232+ INTERNAL_ATTRIBUTES ,
233+ {
234+ SAMLBackend .KEY_DYNAMIC_REQUESTED_ATTRIBUTES : requested_attributes ,
235+ "sp_config" : sp_conf
236+ },
237+ "base_url" ,
238+ "saml_backend"
239+ )
240+
241+ with patch .object (
242+ backend .sp ,
243+ "create_authn_request" ,
244+ wraps = backend .sp .create_authn_request
245+ ) as mock :
246+ backend .authn_request (
247+ context ,
248+ idp_conf ["entityid" ],
249+ requested_attributes = ["name" , "surname" , "email" ]
250+ )
251+
252+ kwargs = mock .call_args [1 ]
253+ assert "requested_attributes" in kwargs
254+ assert kwargs ["requested_attributes" ] == [
255+ {"friendly_name" : "cn" , "required" : True },
256+ {"friendly_name" : "sn" , "required" : False }
257+ ]
258+
259+ def test_authn_request_requested_attributes_not_present (
260+ self , context , idp_conf , sp_conf
261+ ):
262+ """
263+ If some requested attributes are not in the requested don't add them to
264+ the request
265+ """
266+ requested_attributes = [
267+ {"friendly_name" : "cn" , "required" : True },
268+ {"friendly_name" : "sn" , "required" : False }
269+ ]
270+
271+ backend = SAMLBackend (
272+ None ,
273+ INTERNAL_ATTRIBUTES ,
274+ {
275+ SAMLBackend .KEY_DYNAMIC_REQUESTED_ATTRIBUTES : requested_attributes ,
276+ "sp_config" : sp_conf
277+ },
278+ "base_url" ,
279+ "saml_backend"
280+ )
281+
282+ with patch .object (
283+ backend .sp ,
284+ "create_authn_request" ,
285+ wraps = backend .sp .create_authn_request
286+ ) as mock :
287+ backend .authn_request (
288+ context ,
289+ idp_conf ["entityid" ],
290+ requested_attributes = ["name" ]
291+ )
292+
293+ kwargs = mock .call_args [1 ]
294+ assert "requested_attributes" in kwargs
295+ assert kwargs ["requested_attributes" ] == [
296+ {"friendly_name" : "cn" , "required" : True },
297+ ]
298+
299+ @pytest .mark .parametrize ("req_attributes" , [[], ["email" ]])
300+ def test_authn_request_no_requested_attributes (
301+ self , context , idp_conf , sp_conf , req_attributes
302+ ):
303+ """
304+ If no attributes are requested or if they are not in the ,
305+ configuration don't add the extention
306+ """
307+ requested_attributes = [
308+ {"friendly_name" : "cn" , "required" : True },
309+ {"friendly_name" : "sn" , "required" : False }
310+ ]
311+
312+ backend = SAMLBackend (
313+ None ,
314+ INTERNAL_ATTRIBUTES ,
315+ {
316+ SAMLBackend .KEY_DYNAMIC_REQUESTED_ATTRIBUTES : requested_attributes ,
317+ "sp_config" : sp_conf
318+ },
319+ "base_url" ,
320+ "saml_backend"
321+ )
322+
323+ with patch .object (
324+ backend .sp ,
325+ "create_authn_request" ,
326+ wraps = backend .sp .create_authn_request
327+ ) as mock :
328+ backend .authn_request (
329+ context ,
330+ idp_conf ["entityid" ],
331+ requested_attributes = req_attributes
332+ )
333+
334+ kwargs = mock .call_args [1 ]
335+ assert "requested_attributes" not in kwargs
336+
337+ @pytest .mark .parametrize ("req_attributes" , [False , None , []])
338+ def test_authn_request_no_requested_attributes_configured (
339+ self , context , idp_conf , sp_conf , req_attributes
340+ ):
341+ """
342+ If requested attributes is not configured, don't add the extention
343+ """
344+ backend = SAMLBackend (
345+ None ,
346+ INTERNAL_ATTRIBUTES ,
347+ {
348+ SAMLBackend .KEY_DYNAMIC_REQUESTED_ATTRIBUTES : req_attributes ,
349+ "sp_config" : sp_conf
350+ },
351+ "base_url" ,
352+ "saml_backend"
353+ )
354+
355+ with patch .object (
356+ backend .sp ,
357+ "create_authn_request" ,
358+ wraps = backend .sp .create_authn_request
359+ ) as mock :
360+ backend .authn_request (
361+ context ,
362+ idp_conf ["entityid" ],
363+ requested_attributes = ["email" ]
364+ )
365+
366+ kwargs = mock .call_args [1 ]
367+ assert "requested_attributes" not in kwargs
368+
182369 def test_authn_response (self , context , idp_conf , sp_conf ):
183370 response_binding = BINDING_HTTP_REDIRECT
184371 fakesp = FakeSP (SPConfig ().load (sp_conf , metadata_construction = False ))
0 commit comments