@@ -240,4 +240,123 @@ public static function showUserAttributeScenarioProvider(): array
240240 [['uid ' => 'stu3@example.edu ' , 'mail ' => 'user@example.edu ' ], false , true , 'user@example.edu ' ],
241241 ];
242242 }
243+
244+ /**
245+ * Test SP restriction functionality
246+ *
247+ * @param array $userAttributes The attributes to test
248+ * @param string|null $spEntityId The SP Entity ID in the state
249+ * @param bool $isAuthorized Should the user be authorized
250+ */
251+ #[DataProvider('spRestrictionScenarioProvider ' )]
252+ public function testSpRestriction (array $ userAttributes , ?string $ spEntityId , bool $ isAuthorized ): void
253+ {
254+ $ attributeUtils = new Utils \Attributes ();
255+ $ userAttributes = $ attributeUtils ->normalizeAttributesArray ($ userAttributes );
256+ $ config = [
257+ 'uid ' => [
258+ '/.*@example.com$/ ' ,
259+ 'spEntityIDs ' => [
260+ 'https://sp1.example.com ' ,
261+ 'https://sp2.example.com ' ,
262+ ],
263+ ],
264+ 'group ' => [
265+ '/^admins$/ ' ,
266+ 'spEntityIDs ' => [
267+ 'https://admin.example.com ' ,
268+ ],
269+ ],
270+ ];
271+
272+ $ state = ['Attributes ' => $ userAttributes ];
273+ if ($ spEntityId !== null ) {
274+ $ state ['saml:sp:State ' ]['core:SP ' ] = $ spEntityId ;
275+ }
276+
277+ $ resultState = $ this ->processFilter ($ config , $ state );
278+ $ resultAuthorized = isset ($ resultState ['NOT_AUTHORIZED ' ]) ? false : true ;
279+ $ this ->assertEquals ($ isAuthorized , $ resultAuthorized );
280+ }
281+
282+ /**
283+ * @return array
284+ */
285+ public static function spRestrictionScenarioProvider (): array
286+ {
287+ return [
288+ // Should be allowed - matching attribute and SP
289+ [['uid ' => 'user@example.com ' ], 'https://sp1.example.com ' , true ],
290+ [['uid ' => 'user@example.com ' ], 'https://sp2.example.com ' , true ],
291+ [['group ' => 'admins ' ], 'https://admin.example.com ' , true ],
292+
293+ // Should be denied - matching attribute but wrong SP
294+ [['uid ' => 'user@example.com ' ], 'https://wrong.example.com ' , false ],
295+ [['group ' => 'admins ' ], 'https://sp1.example.com ' , false ],
296+
297+ // Should be denied - no SP specified but attribute would match
298+ [['uid ' => 'user@example.com ' ], null , false ],
299+ [['group ' => 'admins ' ], null , false ],
300+
301+ // Should be denied - wrong attribute regardless of SP
302+ [['uid ' => 'user@wrong.com ' ], 'https://sp1.example.com ' , false ],
303+ [['group ' => 'users ' ], 'https://admin.example.com ' , false ],
304+ ];
305+ }
306+
307+ /**
308+ * Test mixed SP and non-SP rules
309+ *
310+ * @param array $userAttributes The attributes to test
311+ * @param string|null $spEntityId The SP Entity ID in the state
312+ * @param bool $isAuthorized Should the user be authorized
313+ */
314+ #[DataProvider('mixedRulesScenarioProvider ' )]
315+ public function testMixedSpAndNonSpRules (array $ userAttributes , ?string $ spEntityId , bool $ isAuthorized ): void
316+ {
317+ $ attributeUtils = new Utils \Attributes ();
318+ $ userAttributes = $ attributeUtils ->normalizeAttributesArray ($ userAttributes );
319+ $ config = [
320+ // Rule with SP restriction
321+ 'uid ' => [
322+ '/.*@restricted.com$/ ' ,
323+ 'spEntityIDs ' => ['https://restricted.example.com ' ],
324+ ],
325+ // Rule without SP restriction (should work for all SPs)
326+ 'role ' => [
327+ '/^admin$/ ' ,
328+ '/^superuser$/ ' ,
329+ ],
330+ ];
331+
332+ $ state = ['Attributes ' => $ userAttributes ];
333+ if ($ spEntityId !== null ) {
334+ $ state ['saml:sp:State ' ]['core:SP ' ] = $ spEntityId ;
335+ }
336+
337+ $ resultState = $ this ->processFilter ($ config , $ state );
338+ $ resultAuthorized = isset ($ resultState ['NOT_AUTHORIZED ' ]) ? false : true ;
339+ $ this ->assertEquals ($ isAuthorized , $ resultAuthorized );
340+ }
341+
342+ /**
343+ * @return array
344+ */
345+ public static function mixedRulesScenarioProvider (): array
346+ {
347+ return [
348+ // Should be allowed - role rule matches (no SP restriction)
349+ [['role ' => 'admin ' ], 'https://any.example.com ' , true ],
350+ [['role ' => 'superuser ' ], null , true ],
351+
352+ // Should be allowed - uid rule matches and SP is correct
353+ [['uid ' => 'user@restricted.com ' ], 'https://restricted.example.com ' , true ],
354+
355+ // Should be denied - uid rule matches but SP is wrong
356+ [['uid ' => 'user@restricted.com ' ], 'https://other.example.com ' , false ],
357+
358+ // Should be denied - no matching rules
359+ [['uid ' => 'user@other.com ' , 'role ' => 'user ' ], 'https://any.example.com ' , false ],
360+ ];
361+ }
243362}
0 commit comments