3939import org .opensaml .saml .saml2 .core .SubjectConfirmation ;
4040import org .opensaml .saml .saml2 .core .SubjectConfirmationData ;
4141import org .opensaml .saml .saml2 .core .impl .AuthnStatementBuilder ;
42+ import org .opensaml .saml .saml2 .core .impl .IssuerBuilder ;
4243import org .opensaml .saml .saml2 .encryption .Encrypter ;
4344import org .opensaml .security .credential .BasicCredential ;
4445import org .opensaml .security .credential .Credential ;
8384import static org .hamcrest .Matchers .contains ;
8485import static org .hamcrest .Matchers .containsInAnyOrder ;
8586import static org .hamcrest .Matchers .containsString ;
87+ import static org .hamcrest .Matchers .endsWith ;
8688import static org .hamcrest .Matchers .equalTo ;
89+ import static org .hamcrest .Matchers .hasLength ;
8790import static org .hamcrest .Matchers .instanceOf ;
8891import static org .hamcrest .Matchers .is ;
8992import static org .hamcrest .Matchers .iterableWithSize ;
@@ -106,6 +109,9 @@ public class SamlAuthenticatorTests extends SamlResponseHandlerTests {
106109 + "Attributes with a name clash may prevent authentication or interfere will role mapping. "
107110 + "Change your IdP configuration to use a different attribute *"
108111 + " that will not clash with any of [*]" ;
112+ private static final String SIGNATURE_VALIDATION_FAILED_LOG_MESSAGE = "The XML Signature of this SAML message cannot be validated. "
113+ + "Please verify that the saml realm uses the correct SAML metadata file/URL for this Identity Provider. "
114+ + "The issuer included in the SAML message was [https://idp.saml.elastic.test/]" ;
109115
110116 private SamlAuthenticator authenticator ;
111117
@@ -741,16 +747,29 @@ public void testIncorrectSigningKeyIsRejected() throws Exception {
741747 // check that the content is valid when signed by the correct key-pair
742748 assertThat (authenticator .authenticate (token (signer .transform (xml , idpSigningCertificatePair ))), notNullValue ());
743749
744- // check is rejected when signed by a different key-pair
745- final Tuple <X509Certificate , PrivateKey > wrongKey = readKeyPair ("RSA_4096_updated" );
746- final ElasticsearchSecurityException exception = expectThrows (
747- ElasticsearchSecurityException .class ,
748- () -> authenticator .authenticate (token (signer .transform (xml , wrongKey )))
749- );
750- assertThat (exception .getMessage (), containsString ("SAML Signature" ));
751- assertThat (exception .getMessage (), containsString ("could not be validated" ));
752- assertThat (exception .getCause (), nullValue ());
753- assertThat (SamlUtils .isSamlException (exception ), is (true ));
750+ try (var mockLog = MockLog .capture (authenticator .getClass ())) {
751+ mockLog .addExpectation (
752+ new MockLog .SeenEventExpectation (
753+ "Invalid Signature" ,
754+ authenticator .getClass ().getName (),
755+ Level .WARN ,
756+ SIGNATURE_VALIDATION_FAILED_LOG_MESSAGE
757+ )
758+ );
759+
760+ // check is rejected when signed by a different key-pair
761+ final Tuple <X509Certificate , PrivateKey > wrongKey = readKeyPair ("RSA_4096_updated" );
762+ final ElasticsearchSecurityException exception = expectThrows (
763+ ElasticsearchSecurityException .class ,
764+ () -> authenticator .authenticate (token (signer .transform (xml , wrongKey )))
765+ );
766+ assertThat (exception .getMessage (), containsString ("SAML Signature" ));
767+ assertThat (exception .getMessage (), containsString ("could not be validated" ));
768+ assertThat (exception .getCause (), nullValue ());
769+ assertThat (SamlUtils .isSamlException (exception ), is (true ));
770+
771+ mockLog .assertAllExpectationsMatched ();
772+ }
754773 }
755774
756775 public void testSigningKeyIsReloadedForEachRequest () throws Exception {
@@ -1301,24 +1320,80 @@ public void testFailureWhenIdPCredentialsAreEmpty() throws Exception {
13011320 authenticator = buildAuthenticator (() -> emptyList (), emptyList ());
13021321 final String xml = getSimpleResponseAsString (clock .instant ());
13031322 final SamlToken token = token (signResponse (xml ));
1304- final ElasticsearchSecurityException exception = expectSamlException (() -> authenticator .authenticate (token ));
1305- assertThat (exception .getCause (), nullValue ());
1306- assertThat (exception .getMessage (), containsString ("SAML Signature" ));
1307- assertThat (exception .getMessage (), containsString ("could not be validated" ));
1308- // Restore the authenticator with credentials for the rest of the test cases
1309- authenticator = buildAuthenticator (() -> buildOpenSamlCredential (idpSigningCertificatePair ), emptyList ());
1323+
1324+ try (var mockLog = MockLog .capture (authenticator .getClass ())) {
1325+ mockLog .addExpectation (
1326+ new MockLog .SeenEventExpectation (
1327+ "Invalid signature" ,
1328+ authenticator .getClass ().getName (),
1329+ Level .WARN ,
1330+ SIGNATURE_VALIDATION_FAILED_LOG_MESSAGE
1331+ )
1332+ );
1333+
1334+ final ElasticsearchSecurityException exception = expectSamlException (() -> authenticator .authenticate (token ));
1335+ assertThat (exception .getCause (), nullValue ());
1336+ assertThat (exception .getMessage (), containsString ("SAML Signature" ));
1337+ assertThat (exception .getMessage (), containsString ("could not be validated" ));
1338+
1339+ mockLog .awaitAllExpectationsMatched ();
1340+ }
13101341 }
13111342
13121343 public void testFailureWhenIdPCredentialsAreNull () throws Exception {
13131344 authenticator = buildAuthenticator (() -> singletonList (null ), emptyList ());
13141345 final String xml = getSimpleResponseAsString (clock .instant ());
13151346 final SamlToken token = token (signResponse (xml ));
1316- final ElasticsearchSecurityException exception = expectSamlException (() -> authenticator .authenticate (token ));
1317- assertThat (exception .getCause (), nullValue ());
1318- assertThat (exception .getMessage (), containsString ("SAML Signature" ));
1319- assertThat (exception .getMessage (), containsString ("could not be validated" ));
1320- // Restore the authenticator with credentials for the rest of the test cases
1321- authenticator = buildAuthenticator (() -> buildOpenSamlCredential (idpSigningCertificatePair ), emptyList ());
1347+
1348+ try (var mockLog = MockLog .capture (authenticator .getClass ())) {
1349+ mockLog .addExpectation (
1350+ new MockLog .SeenEventExpectation (
1351+ "Invalid signature" ,
1352+ authenticator .getClass ().getName (),
1353+ Level .WARN ,
1354+ SIGNATURE_VALIDATION_FAILED_LOG_MESSAGE
1355+ )
1356+ );
1357+ mockLog .addExpectation (
1358+ new MockLog .SeenEventExpectation (
1359+ "Null credentials" ,
1360+ authenticator .getClass ().getName (),
1361+ Level .WARN ,
1362+ "Exception while attempting to validate SAML Signature. "
1363+ + "The issuer included in the SAML message was [https://idp.saml.elastic.test/]"
1364+ )
1365+ );
1366+
1367+ final ElasticsearchSecurityException exception = expectSamlException (() -> authenticator .authenticate (token ));
1368+ assertThat (exception .getCause (), nullValue ());
1369+ assertThat (exception .getMessage (), containsString ("SAML Signature" ));
1370+ assertThat (exception .getMessage (), containsString ("could not be validated" ));
1371+
1372+ mockLog .awaitAllExpectationsMatched ();
1373+ }
1374+ }
1375+
1376+ public void testDescribeNullIssuer () {
1377+ final Issuer issuer = randomFrom (new IssuerBuilder ().buildObject (), null );
1378+ assertThat (SamlAuthenticator .describeIssuer (issuer ), equalTo ("" ));
1379+ }
1380+
1381+ public void testDescribeIssuer () {
1382+ final Issuer issuer = new IssuerBuilder ().buildObject ();
1383+ issuer .setValue ("https://idp.saml.elastic.test/" );
1384+ assertThat (
1385+ SamlAuthenticator .describeIssuer (issuer ),
1386+ equalTo (" The issuer included in the SAML message was [https://idp.saml.elastic.test/]" )
1387+ );
1388+ }
1389+
1390+ public void testDescribeVeryLongIssuer () {
1391+ final Issuer issuer = new IssuerBuilder ().buildObject ();
1392+ issuer .setValue ("https://idp.saml.elastic.test/" + randomAlphaOfLength (512 ));
1393+
1394+ final String description = SamlAuthenticator .describeIssuer (issuer );
1395+ assertThat (description , hasLength (562 ));
1396+ assertThat (description , endsWith ("..." ));
13221397 }
13231398
13241399 private interface CryptoTransform {
0 commit comments