You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2018/10/27 19:25:14 UTC
[cloudstack] branch master updated: saml: redirect saml2 failed
login message to a configurable URL (#2185)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push:
new bfc3263 saml: redirect saml2 failed login message to a configurable URL (#2185)
bfc3263 is described below
commit bfc326384d9c453490a2e0b14d20989e7dcb76ad
Author: Gabriel Beims Bräscher <ga...@gmail.com>
AuthorDate: Sat Oct 27 16:25:06 2018 -0300
saml: redirect saml2 failed login message to a configurable URL (#2185)
When a user fails to authenticate with SAML2, it returns an error page
showing the content of the attached image.
To make it more user-friendly and customizable, one could configure a
desirable URL to redirect when such authentication failure happens.
This ticket proposes a global settings variable
(saml2.failed.login.redirect.url). If null, the SAML2 authentication
flow does not change from the current; however, if the user configures
an URL then ACS redirects to that URL.
---
.../api/command/SAML2LoginAPIAuthenticatorCmd.java | 135 +++++++++++++--------
.../command/SAML2LoginAPIAuthenticatorCmdTest.java | 99 ++++++++++++++-
2 files changed, 179 insertions(+), 55 deletions(-)
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 41005ab..4b48646 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.APIAuthenticationType;
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.signature.SignatureValidator;
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 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
}
}
- 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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
}
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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
}
}
- 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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
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 2ce8841..cc45cbb 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 @@ package org.apache.cloudstack.api.command;
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.ApiServerService;
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.joda.time.DateTime;
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 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
@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 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
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 class SAML2LoginAPIAuthenticatorCmdTest {
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 class SAML2LoginAPIAuthenticatorCmdTest {
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;
+ }
+
}