You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by GitBox <gi...@apache.org> on 2018/10/27 19:25:07 UTC

[GitHub] rhtyd closed pull request #2185: CLOUDSTACK-9976: Redirect saml2 failed login message to a configurable URL

rhtyd closed pull request #2185: CLOUDSTACK-9976: Redirect saml2 failed login message to a configurable URL
URL: https://github.com/apache/cloudstack/pull/2185
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
index 41005ab8f9c..4b486466f8e 100644
--- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
+++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
@@ -16,14 +16,18 @@
 // under the License.
 package org.apache.cloudstack.api.command;
 
-import com.cloud.api.response.ApiResponseSerializer;
-import com.cloud.exception.CloudAuthenticationException;
-import com.cloud.user.Account;
-import com.cloud.user.DomainManager;
-import com.cloud.user.UserAccount;
-import com.cloud.user.UserAccountVO;
-import com.cloud.user.dao.UserAccountDao;
-import com.cloud.utils.db.EntityManager;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.FactoryConfigurationError;
+
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiErrorCode;
@@ -35,11 +39,14 @@
 import org.apache.cloudstack.api.auth.APIAuthenticator;
 import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
 import org.apache.cloudstack.api.response.LoginCmdResponse;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.saml.SAML2AuthManager;
 import org.apache.cloudstack.saml.SAMLPluginConstants;
 import org.apache.cloudstack.saml.SAMLProviderMetadata;
 import org.apache.cloudstack.saml.SAMLTokenVO;
 import org.apache.cloudstack.saml.SAMLUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.opensaml.DefaultBootstrap;
 import org.opensaml.saml2.core.Assertion;
@@ -62,19 +69,17 @@
 import org.opensaml.xml.validation.ValidationException;
 import org.xml.sax.SAXException;
 
-import javax.inject.Inject;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.stream.FactoryConfigurationError;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.util.List;
-import java.util.Map;
+import com.cloud.api.response.ApiResponseSerializer;
+import com.cloud.exception.CloudAuthenticationException;
+import com.cloud.user.Account;
+import com.cloud.user.DomainManager;
+import com.cloud.user.UserAccount;
+import com.cloud.user.UserAccountVO;
+import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.db.EntityManager;
 
 @APICommand(name = "samlSso", description = "SP initiated SAML Single Sign On", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, entityType = {})
-public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthenticator {
+public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthenticator, Configurable {
     public static final Logger s_logger = Logger.getLogger(SAML2LoginAPIAuthenticatorCmd.class.getName());
     private static final String s_name = "loginresponse";
 
@@ -85,15 +90,18 @@
     private String idpId;
 
     @Inject
-    ApiServerService _apiServer;
+    ApiServerService apiServer;
     @Inject
-    EntityManager _entityMgr;
+    EntityManager entityMgr;
     @Inject
-    DomainManager _domainMgr;
+    DomainManager domainMgr;
     @Inject
-    private UserAccountDao _userAccountDao;
+    private UserAccountDao userAccountDao;
+
+    protected static ConfigKey<String> saml2FailedLoginRedirectUrl = new ConfigKey<String>("Advanced", String.class, "saml2.failed.login.redirect.url", "",
+            "The URL to redirect the SAML2 login failed message (the default vaulue is empty).", true);
 
-    SAML2AuthManager _samlAuthManager;
+    SAML2AuthManager samlAuthManager;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
@@ -159,27 +167,27 @@ public String authenticate(final String command, final Map<String, Object[]> par
                     }
                 }
 
-                SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
-                SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(idpId);
+                SAMLProviderMetadata spMetadata = samlAuthManager.getSPMetadata();
+                SAMLProviderMetadata idpMetadata = samlAuthManager.getIdPMetadata(idpId);
                 if (idpMetadata == null) {
-                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
+                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
                             "IdP ID (" + idpId + ") is not found in our list of supported IdPs, cannot proceed.",
                             params, responseType));
                 }
                 if (idpMetadata.getSsoUrl() == null || idpMetadata.getSsoUrl().isEmpty()) {
-                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
+                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
                             "IdP ID (" + idpId + ") has no Single Sign On URL defined please contact "
                                     + idpMetadata.getContactPersonName() + " <" + idpMetadata.getContactPersonEmail() + ">, cannot proceed.",
                             params, responseType));
                 }
                 String authnId = SAMLUtils.generateSecureRandomId();
-                _samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId());
+                samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId());
                 s_logger.debug("Sending SAMLRequest id=" + authnId);
                 String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value());
                 resp.sendRedirect(redirectUrl);
                 return "";
             } if (params.containsKey("SAMLart")) {
-                throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.UNSUPPORTED_ACTION_ERROR.getHttpCode(),
+                throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, apiServer.getSerializedApiError(ApiErrorCode.UNSUPPORTED_ACTION_ERROR.getHttpCode(),
                         "SAML2 HTTP Artifact Binding is not supported",
                         params, responseType));
             } else {
@@ -187,27 +195,27 @@ public String authenticate(final String command, final Map<String, Object[]> par
                 Response processedSAMLResponse = this.processSAMLResponse(samlResponse);
                 String statusCode = processedSAMLResponse.getStatus().getStatusCode().getValue();
                 if (!statusCode.equals(StatusCode.SUCCESS_URI)) {
-                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                             "Identity Provider send a non-successful authentication status code",
                             params, responseType));
                 }
 
                 String username = null;
                 Issuer issuer = processedSAMLResponse.getIssuer();
-                SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
-                SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(issuer.getValue());
+                SAMLProviderMetadata spMetadata = samlAuthManager.getSPMetadata();
+                SAMLProviderMetadata idpMetadata = samlAuthManager.getIdPMetadata(issuer.getValue());
 
                 String responseToId = processedSAMLResponse.getInResponseTo();
                 s_logger.debug("Received SAMLResponse in response to id=" + responseToId);
-                SAMLTokenVO token = _samlAuthManager.getToken(responseToId);
+                SAMLTokenVO token = samlAuthManager.getToken(responseToId);
                 if (token != null) {
                     if (!(token.getEntity().equalsIgnoreCase(issuer.getValue()))) {
-                        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                                 "The SAML response contains Issuer Entity ID that is different from the original SAML request",
                                 params, responseType));
                     }
                 } else {
-                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                             "Received SAML response for a SSO request that we may not have made or has expired, please try logging in again",
                             params, responseType));
                 }
@@ -224,7 +232,7 @@ public String authenticate(final String command, final Map<String, Object[]> par
                         validator.validate(sig);
                     } catch (ValidationException e) {
                         s_logger.error("SAML Response's signature failed to be validated by IDP signing key:" + e.getMessage());
-                        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                                 "SAML Response's signature failed to be validated by IDP signing key",
                                 params, responseType));
                     }
@@ -269,7 +277,7 @@ public String authenticate(final String command, final Map<String, Object[]> par
                                     validator.validate(encSig);
                                 } catch (ValidationException e) {
                                     s_logger.error("SAML Response's signature failed to be validated by IDP signing key:" + e.getMessage());
-                                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                                             "SAML Response's signature failed to be validated by IDP signing key",
                                             params, responseType));
                                 }
@@ -285,16 +293,16 @@ public String authenticate(final String command, final Map<String, Object[]> par
                 }
 
                 if (username == null) {
-                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                             "Failed to find admin configured username attribute in the SAML Response. Please ask your administrator to check SAML user attribute name.", params, responseType));
                 }
 
                 UserAccount userAccount = null;
-                List<UserAccountVO> possibleUserAccounts = _userAccountDao.getAllUsersByNameAndEntity(username, issuer.getValue());
+                List<UserAccountVO> possibleUserAccounts = userAccountDao.getAllUsersByNameAndEntity(username, issuer.getValue());
                 if (possibleUserAccounts != null && possibleUserAccounts.size() > 0) {
                     // Log into the first enabled user account
                     // Users can switch to other allowed accounts later
-                    for (UserAccountVO possibleUserAccount: possibleUserAccounts) {
+                    for (UserAccountVO possibleUserAccount : possibleUserAccounts) {
                         if (possibleUserAccount.getAccountState().equals(Account.State.enabled.toString())) {
                             userAccount = possibleUserAccount;
                             break;
@@ -302,15 +310,11 @@ public String authenticate(final String command, final Map<String, Object[]> par
                     }
                 }
 
-                if (userAccount == null || userAccount.getExternalEntity() == null || !_samlAuthManager.isUserAuthorized(userAccount.getId(), issuer.getValue())) {
-                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
-                            "Your authenticated user is not authorized for SAML Single Sign-On, please contact your administrator",
-                            params, responseType));
-                }
+                whenFailToAuthenticateThrowExceptionOrRedirectToUrl(params, responseType, resp, issuer, userAccount);
 
                 try {
-                    if (_apiServer.verifyUser(userAccount.getId())) {
-                        LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, userAccount.getUsername(), userAccount.getUsername() + userAccount.getSource().toString(),
+                    if (apiServer.verifyUser(userAccount.getId())) {
+                        LoginCmdResponse loginResponse = (LoginCmdResponse) apiServer.loginUser(session, userAccount.getUsername(), userAccount.getUsername() + userAccount.getSource().toString(),
                                 userAccount.getDomainId(), null, remoteAddress, params);
                         SAMLUtils.setupSamlUserCookies(loginResponse, resp);
                         resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
@@ -324,11 +328,29 @@ public String authenticate(final String command, final Map<String, Object[]> par
             auditTrailSb.append("SP initiated SAML authentication using HTTP redirection failed:");
             auditTrailSb.append(e.getMessage());
         }
-        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                 "Unable to authenticate user while performing SAML based SSO. Please make sure your user/account has been added, enable and authorized by the admin before you can authenticate. Please contact your administrator.",
                 params, responseType));
     }
 
+    /**
+     * If it fails to authenticate the user, the method gets the value from configuration
+     * Saml2FailedLoginRedirectUrl; if the user configured an error URL then it redirects to that
+     * URL, otherwise it throws the ServerApiException
+     */
+    protected void whenFailToAuthenticateThrowExceptionOrRedirectToUrl(final Map<String, Object[]> params, final String responseType, final HttpServletResponse resp, Issuer issuer,
+            UserAccount userAccount) throws IOException {
+        if (userAccount == null || userAccount.getExternalEntity() == null || !samlAuthManager.isUserAuthorized(userAccount.getId(), issuer.getValue())) {
+            String saml2RedirectUrl = saml2FailedLoginRedirectUrl.value();
+            if (StringUtils.isBlank(saml2RedirectUrl)) {
+                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                        "Your authenticated user is not authorized for SAML Single Sign-On, please contact your administrator", params, responseType));
+            } else {
+                resp.sendRedirect(saml2RedirectUrl);
+            }
+        }
+    }
+
     @Override
     public APIAuthenticationType getAPIType() {
         return APIAuthenticationType.LOGIN_API;
@@ -338,11 +360,22 @@ public APIAuthenticationType getAPIType() {
     public void setAuthenticators(List<PluggableAPIAuthenticator> authenticators) {
         for (PluggableAPIAuthenticator authManager: authenticators) {
             if (authManager != null && authManager instanceof SAML2AuthManager) {
-                _samlAuthManager = (SAML2AuthManager) authManager;
+                samlAuthManager = (SAML2AuthManager) authManager;
             }
         }
-        if (_samlAuthManager == null) {
+        if (samlAuthManager == null) {
             s_logger.error("No suitable Pluggable Authentication Manager found for SAML2 Login Cmd");
         }
     }
+
+    @Override
+    public String getConfigComponentName() {
+        return SAML2LoginAPIAuthenticatorCmd.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {saml2FailedLoginRedirectUrl};
+    }
+
 }
diff --git a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java
index 2ce88414b40..cc45cbb987d 100644
--- a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java
+++ b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java
@@ -21,6 +21,7 @@
 
 import static org.junit.Assert.assertFalse;
 
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.net.InetAddress;
 import java.security.KeyPair;
@@ -36,6 +37,7 @@
 import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.auth.APIAuthenticationType;
+import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.saml.SAML2AuthManager;
 import org.apache.cloudstack.saml.SAMLPluginConstants;
 import org.apache.cloudstack.saml.SAMLProviderMetadata;
@@ -45,8 +47,10 @@
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.Spy;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.opensaml.common.SAMLVersion;
 import org.opensaml.saml2.core.Assertion;
@@ -106,6 +110,10 @@
     @Mock
     HttpServletRequest req;
 
+    @Spy
+    @InjectMocks
+    private SAML2LoginAPIAuthenticatorCmd cmdSpy;
+
     private Response buildMockResponse() throws Exception {
         Response samlMessage = new ResponseBuilder().buildObject();
         samlMessage.setID("foo");
@@ -139,11 +147,11 @@ private Response buildMockResponse() throws Exception {
     public void testAuthenticate() throws Exception {
         SAML2LoginAPIAuthenticatorCmd cmd = Mockito.spy(new SAML2LoginAPIAuthenticatorCmd());
 
-        Field apiServerField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_apiServer");
+        Field apiServerField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("apiServer");
         apiServerField.setAccessible(true);
         apiServerField.set(cmd, apiServer);
 
-        Field managerField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_samlAuthManager");
+        Field managerField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("samlAuthManager");
         managerField.setAccessible(true);
         managerField.set(cmd, samlAuthManager);
 
@@ -151,11 +159,11 @@ public void testAuthenticate() throws Exception {
         accountServiceField.setAccessible(true);
         accountServiceField.set(cmd, accountService);
 
-        Field domainMgrField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_domainMgr");
+        Field domainMgrField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("domainMgr");
         domainMgrField.setAccessible(true);
         domainMgrField.set(cmd, domainMgr);
 
-        Field userAccountDaoField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_userAccountDao");
+        Field userAccountDaoField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("userAccountDao");
         userAccountDaoField.setAccessible(true);
         userAccountDaoField.set(cmd, userAccountDao);
 
@@ -205,4 +213,87 @@ public void testAuthenticate() throws Exception {
     public void testGetAPIType() {
         Assert.assertTrue(new SAML2LoginAPIAuthenticatorCmd().getAPIType() == APIAuthenticationType.LOGIN_API);
     }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectUrlBlank() throws IOException {
+        UserAccountVO userAccount = configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl("entity", " ", false);
+        boolean expectServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(userAccount);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(true, expectServerApiException, 0, 1);
+    }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectUrlNull() throws IOException {
+        UserAccountVO userAccount = configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl("entity", null, false);
+        boolean expectServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(userAccount);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(true, expectServerApiException, 0, 1);
+    }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectUrlEmpty() throws IOException {
+        UserAccountVO userAccount = configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl("entity", "", false);
+        boolean hasThrownServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(userAccount);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(true, hasThrownServerApiException, 0, 1);
+    }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectExternalEntityNullAndUrlNotConfigured() throws IOException {
+        UserAccountVO userAccount = configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(null, " ", false);
+        boolean hasThrownServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(userAccount);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(true, hasThrownServerApiException, 0, 1);
+    }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectExternalEntityNullAndUrlConfigured() throws IOException {
+        UserAccountVO userAccount = configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(null, "some.url", true);
+        boolean hasThrownServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(userAccount);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(false, hasThrownServerApiException, 1, 1);
+    }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectUrlConfigured() throws IOException {
+        UserAccountVO userAccount = configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl("entity", "some.url", false);
+        boolean hasThrownServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(userAccount);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(false, hasThrownServerApiException, 1, 1);
+    }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectUrlUserAccountNull() throws IOException {
+        configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl("entity", "some.url", true);
+        boolean hasThrownServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(null);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(false, hasThrownServerApiException, 1, 1);
+    }
+
+    @Test
+    public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLoginRedirectUrlIsUserAuthorized() throws IOException {
+        UserAccountVO userAccount = configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl("entity", "some.url", true);
+        boolean hasThrownServerApiException = runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(userAccount);
+        verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(false, hasThrownServerApiException, 0, 0);
+    }
+
+    private UserAccountVO configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(String entity, String configurationValue, Boolean isUserAuthorized)
+            throws IOException {
+        Mockito.when(samlAuthManager.isUserAuthorized(Mockito.anyLong(), Mockito.anyString())).thenReturn(isUserAuthorized);
+        SAML2LoginAPIAuthenticatorCmd.saml2FailedLoginRedirectUrl = new ConfigKey<String>("Advanced", String.class, "saml2.failed.login.redirect.url", configurationValue,
+                "The URL to redirect the SAML2 login failed message (the default vaulue is empty).", true);
+        UserAccountVO userAccount = new UserAccountVO();
+        userAccount.setExternalEntity(entity);
+        userAccount.setId(0l);
+        return userAccount;
+    }
+
+    private void verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(boolean expectServerApiException, boolean hasThrownServerApiException, int timesOfSendRedirect,
+            int timesOfConfigDao) throws IOException {
+        Mockito.verify(resp, Mockito.times(timesOfSendRedirect)).sendRedirect(Mockito.anyString());
+        Assert.assertEquals(expectServerApiException, hasThrownServerApiException);
+    }
+
+    private boolean runTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(UserAccountVO userAccount) throws IOException {
+        try {
+            cmdSpy.whenFailToAuthenticateThrowExceptionOrRedirectToUrl(null, "responseType", resp, new IssuerBuilder().buildObject(), userAccount);
+        } catch (ServerApiException e) {
+            return true;
+        }
+        return false;
+    }
+
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services