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 <[email protected]>
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;
+ }
+
}