1010import org .elasticsearch .action .ActionListener ;
1111import org .elasticsearch .action .support .PlainActionFuture ;
1212import org .elasticsearch .common .settings .MockSecureSettings ;
13+ import org .elasticsearch .common .settings .SecureString ;
1314import org .elasticsearch .common .settings .Settings ;
1415import org .elasticsearch .common .settings .SettingsException ;
1516import org .elasticsearch .common .ssl .PemUtils ;
7273import java .util .Collections ;
7374import java .util .List ;
7475import java .util .Locale ;
76+ import java .util .Map ;
7577import java .util .Set ;
7678import java .util .concurrent .CountDownLatch ;
7779import java .util .concurrent .TimeUnit ;
8385import static org .elasticsearch .core .Strings .format ;
8486import static org .elasticsearch .test .ActionListenerUtils .anyActionListener ;
8587import static org .elasticsearch .test .TestMatchers .throwableWithMessage ;
88+ import static org .elasticsearch .xpack .security .authc .saml .SamlRealm .SECURE_ATTRIBUTES_METADATA ;
8689import static org .hamcrest .Matchers .arrayContainingInAnyOrder ;
8790import static org .hamcrest .Matchers .contains ;
8891import static org .hamcrest .Matchers .containsInAnyOrder ;
@@ -392,7 +395,8 @@ public void testAuthenticateWithEmptyRoleMapping() throws Exception {
392395 randomBoolean () ? REALM_NAME : null ,
393396 testWithDelimiter ? List .of ("STRIKE Team: Delta$shield" ) : Arrays .asList ("avengers" , "shield" ),
394397 testWithDelimiter ? "$" : null ,
395- randomBoolean () ? List .of ("superuser" , "kibana_admin" ) : randomFrom (List .of (), null )
398+ randomBoolean () ? List .of ("superuser" , "kibana_admin" ) : randomFrom (List .of (), null ),
399+ null
396400 );
397401 assertThat (result , notNullValue ());
398402 assertThat (result .getStatus (), equalTo (AuthenticationResult .Status .SUCCESS ));
@@ -419,7 +423,8 @@ public void testAuthenticateWithRoleMapping() throws Exception {
419423 authenticatingRealm ,
420424 testWithDelimiter ? List .of ("STRIKE Team: Delta$shield" ) : Arrays .asList ("avengers" , "shield" ),
421425 testWithDelimiter ? "$" : null ,
422- rolesToExclude
426+ rolesToExclude ,
427+ Map .of ("top_secret" , List .of ("Batman's secret identity is Bruce Wayne!" ))
423428 );
424429
425430 assertThat (result , notNullValue ());
@@ -440,8 +445,24 @@ public void testAuthenticateWithRoleMapping() throws Exception {
440445 assertThat (result .getValue ().metadata ().get ("saml_nameid" ), equalTo (nameIdValue ));
441446 assertThat (result .getValue ().metadata ().get ("saml_uid" ), instanceOf (Iterable .class ));
442447 assertThat ((Iterable <?>) result .getValue ().metadata ().get ("saml_uid" ), contains (uidValue ));
448+ assertThat (result .getValue ().metadata ().get ("saml_top_secret" ), nullValue ());
443449 }
444450
451+ assertThat (result .getMetadata (), notNullValue ());
452+ assertThat (result .getMetadata ().containsKey (SECURE_ATTRIBUTES_METADATA ), is (true ));
453+ @ SuppressWarnings ("unchecked" )
454+ Map <String , List <SecureString >> secureAttributesMetadata = (Map <String , List <SecureString >>) result .getMetadata ()
455+ .get (SECURE_ATTRIBUTES_METADATA );
456+ assertThat (secureAttributesMetadata , notNullValue ());
457+ assertThat (secureAttributesMetadata .keySet (), containsInAnyOrder ("top_secret" ));
458+ List <SecureString > secretAttribute = secureAttributesMetadata .get ("top_secret" );
459+ assertThat (secretAttribute , notNullValue ());
460+ assertThat (secretAttribute .size (), equalTo (1 ));
461+ assertEquals (
462+ "SecureString has already been closed" ,
463+ expectThrows (IllegalStateException .class , () -> secretAttribute .getFirst ().getChars ()).getMessage ()
464+ );
465+
445466 assertThat (userData .get ().getUsername (), equalTo (useNameId ? "clint.barton" : "cbarton" ));
446467 if (testWithDelimiter ) {
447468 assertThat (userData .get ().getGroups (), containsInAnyOrder ("STRIKE Team: Delta" , "shield" ));
@@ -533,6 +554,7 @@ private AuthenticationResult<User> performAuthentication(
533554 authenticatingRealm ,
534555 groups ,
535556 groupsDelimiter ,
557+ null ,
536558 null
537559 );
538560 }
@@ -546,7 +568,8 @@ private AuthenticationResult<User> performAuthentication(
546568 String authenticatingRealm ,
547569 List <String > groups ,
548570 String groupsDelimiter ,
549- List <String > rolesToExclude
571+ List <String > rolesToExclude ,
572+ Map <String , List <String >> secureAttributes
550573 ) throws Exception {
551574 final EntityDescriptor idp = mockIdp ();
552575 final SpConfiguration sp = new SingleSamlSpConfiguration ("<sp>" , "https://saml/" , null , null , null , Collections .emptyList ());
@@ -610,6 +633,12 @@ private AuthenticationResult<User> performAuthentication(
610633 String .join ("," , rolesToExclude )
611634 );
612635 }
636+ if (secureAttributes != null ) {
637+ settingsBuilder .put (
638+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .SECURE_ATTRIBUTES ),
639+ String .join ("," , secureAttributes .keySet ())
640+ );
641+ }
613642 if (useAuthorizingRealm ) {
614643 settingsBuilder .putList (
615644 RealmSettings .getFullSettingKey (
@@ -640,15 +669,35 @@ private AuthenticationResult<User> performAuthentication(
640669 final SamlAttributes attributes = new SamlAttributes (
641670 new SamlNameId (NameIDType .PERSISTENT , nameIdValue , idp .getEntityID (), sp .getEntityId (), null ),
642671 randomAlphaOfLength (16 ),
643- Arrays . asList (
672+ List . of (
644673 new SamlAttributes .SamlAttribute ("urn:oid:0.9.2342.19200300.100.1.1" , "uid" , Collections .singletonList (uidValue )),
645674 new SamlAttributes .SamlAttribute ("urn:oid:1.3.6.1.4.1.5923.1.5.1.1" , "groups" , groups ),
646675 new SamlAttributes .
SamlAttribute (
"urn:oid:0.9.2342.19200300.100.1.3" ,
"mail" ,
Arrays .
asList (
"[email protected] " ))
647- )
676+ ),
677+ secureAttributes == null
678+ ? List .of ()
679+ : secureAttributes .entrySet ()
680+ .stream ()
681+ .map (
682+ a -> new SamlAttributes .SamlSecureAttribute (a .getKey (), null , a .getValue ().stream ().map (SecureString ::new ).toList ())
683+ )
684+ .toList ()
648685 );
649686 when (authenticator .authenticate (token )).thenReturn (attributes );
650687
651- final PlainActionFuture <AuthenticationResult <User >> future = new PlainActionFuture <>();
688+ final PlainActionFuture <AuthenticationResult <User >> future = new PlainActionFuture <>() {
689+ @ Override
690+ public void onResponse (AuthenticationResult <User > result ) {
691+ if (secureAttributes != null && result .isAuthenticated ()) {
692+ assertThat (result .getMetadata (), notNullValue ());
693+ assertThat (result .getMetadata ().containsKey (SECURE_ATTRIBUTES_METADATA ), is (true ));
694+ @ SuppressWarnings ("unchecked" )
695+ var metadata = (Map <String , List <SecureString >>) result .getMetadata ().get (SECURE_ATTRIBUTES_METADATA );
696+ secureAttributes .forEach ((name , value ) -> assertThat (metadata .get (name ), equalTo (value )));
697+ }
698+ super .onResponse (result );
699+ }
700+ };
652701 realm .authenticate (token , future );
653702 return future .get ();
654703 }
@@ -725,13 +774,14 @@ private List<String> performAttributeSelectionWithSplit(String delimiter, String
725774 final SamlAttributes attributes = new SamlAttributes (
726775 new SamlNameId (NameIDType .TRANSIENT , randomAlphaOfLength (24 ), null , null , null ),
727776 randomAlphaOfLength (16 ),
728- Collections . singletonList (
777+ List . of (
729778 new SamlAttributes .SamlAttribute (
730779 "departments" ,
731780 "departments" ,
732781 Collections .singletonList (String .join (delimiter , returnedGroups ))
733782 )
734- )
783+ ),
784+ List .of ()
735785 );
736786 return parser .getAttribute (attributes );
737787 }
@@ -796,13 +846,14 @@ public void testAttributeSelectionWithSplitAndListThrowsSecurityException() {
796846 final SamlAttributes attributes = new SamlAttributes (
797847 new SamlNameId (NameIDType .TRANSIENT , randomAlphaOfLength (24 ), null , null , null ),
798848 randomAlphaOfLength (16 ),
799- Collections . singletonList (
849+ List . of (
800850 new SamlAttributes .SamlAttribute (
801851 "departments" ,
802852 "departments" ,
803853 List .of ("engineering" , String .join (delimiter , "elasticsearch-admins" , "employees" ))
804854 )
805- )
855+ ),
856+ List .of ()
806857 );
807858
808859 ElasticsearchSecurityException securityException = expectThrows (
@@ -828,13 +879,14 @@ public void testAttributeSelectionWithRegex() {
828879 final SamlAttributes attributes = new SamlAttributes (
829880 new SamlNameId (NameIDType .TRANSIENT , randomAlphaOfLength (24 ), null , null , null ),
830881 randomAlphaOfLength (16 ),
831- Collections . singletonList (
882+ List . of (
832883 new SamlAttributes .SamlAttribute (
833884 "urn:oid:0.9.2342.19200300.100.1.3" ,
834885 "mail" ,
835886836887 )
837- )
888+ ),
889+ List .of ()
838890 );
839891
840892 final List <String > strings = parser .getAttribute (attributes );
@@ -952,9 +1004,8 @@ public void testNonMatchingPrincipalPatternThrowsSamlException() throws Exceptio
9521004 final SamlAttributes attributes = new SamlAttributes (
9531005 new SamlNameId (NameIDType .TRANSIENT , randomAlphaOfLength (12 ), null , null , null ),
9541006 randomAlphaOfLength (16 ),
955- Collections .singletonList (
956- new SamlAttributes .SamlAttribute ("urn:oid:0.9.2342.19200300.100.1.3" , "mail" , Collections .singletonList (mail ))
957- )
1007+ List .of (new SamlAttributes .SamlAttribute ("urn:oid:0.9.2342.19200300.100.1.3" , "mail" , Collections .singletonList (mail ))),
1008+ List .of ()
9581009 );
9591010 when (authenticator .authenticate (token )).thenReturn (attributes );
9601011
0 commit comments