mirror of https://github.com/apache/cloudstack.git
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:
parent
b8ed159f47
commit
bfc326384d
|
|
@ -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};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue