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.
This commit is contained in:
Gabriel Beims Bräscher 2018-10-27 16:25:06 -03:00 committed by Rohit Yadav
parent b8ed159f47
commit bfc326384d
2 changed files with 179 additions and 55 deletions

View File

@ -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;
SAML2AuthManager _samlAuthManager;
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;
/////////////////////////////////////////////////////
/////////////////// 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};
}
}

View File

@ -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;
}
}