saml: Add unit tests for saml plugin

- Fixes signatures on plugin manager for ease of testing
- Fixes authenticator
- Adds unit testing for getType and authenticate methods for all cmd classes
- Adds SAMLAuthenticator test

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2014-08-25 17:32:13 +02:00
parent 1ed532fb20
commit de4e74b2b4
7 changed files with 419 additions and 14 deletions

View File

@ -89,7 +89,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
@Inject
ConfigurationDao _configDao;
@Inject
private DomainManager _domainMgr;
DomainManager _domainMgr;
SAML2AuthManager _samlAuthManager;
@ -141,7 +141,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
return redirectUrl;
}
private Response processSAMLResponse(String responseMessage) {
public Response processSAMLResponse(String responseMessage) {
Response responseObject = null;
try {
DefaultBootstrap.bootstrap();
@ -162,12 +162,12 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
if (idps != null && idps.length > 0) {
idpUrl = idps[0];
}
String redirectUrl = buildAuthnRequestUrl(idpUrl);
String redirectUrl = this.buildAuthnRequestUrl(idpUrl);
resp.sendRedirect(redirectUrl);
return "";
} else {
final String samlResponse = ((String[])params.get(SAMLUtils.SAML_RESPONSE))[0];
Response processedSAMLResponse = processSAMLResponse(samlResponse);
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(),
@ -209,7 +209,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
}
String username = null;
String password = "";
String password = SAMLUtils.generateSecureRandomId(); // Random password
String firstName = "";
String lastName = "";
String timeZone = "";
@ -229,8 +229,6 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
}
}
String issuer = assertion.getIssuer().getValue();
String audience = assertion.getConditions().getAudienceRestrictions().get(0).getAudiences().get(0).getAudienceURI();
AttributeStatement attributeStatement = assertion.getAttributeStatements().get(0);
List<Attribute> attributes = attributeStatement.getAttributes();

View File

@ -99,7 +99,7 @@ public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuthen
params, responseType));
}
if (params.containsKey("SAMLResponse")) {
if (params != null && params.containsKey("SAMLResponse")) {
try {
final String samlResponse = ((String[])params.get(SAMLUtils.SAML_RESPONSE))[0];
Response processedSAMLResponse = SAMLUtils.decodeSAMLResponse(samlResponse);

View File

@ -49,7 +49,7 @@ public class SAML2UserAuthenticator extends DefaultUserAuthenticator {
} else {
User user = _userDao.getUser(userAccount.getId());
if (user != null && SAMLUtils.checkSAMLUserId(user.getUuid()) &&
requestParameters.containsKey(SAMLUtils.SAML_RESPONSE)) {
requestParameters != null && requestParameters.containsKey(SAMLUtils.SAML_RESPONSE)) {
return new Pair<Boolean, ActionOnFailedAuthentication>(true, null);
}
}
@ -59,8 +59,6 @@ public class SAML2UserAuthenticator extends DefaultUserAuthenticator {
@Override
public String encode(final String password) {
// TODO: Complete method
StringBuilder sb = new StringBuilder(32);
return sb.toString();
return SAMLUtils.generateSecureRandomId();
}
}

View File

@ -19,21 +19,68 @@
package org.apache.cloudstack;
import com.cloud.server.auth.UserAuthenticator.ActionOnFailedAuthentication;
import com.cloud.user.UserAccountVO;
import com.cloud.user.UserVO;
import com.cloud.user.dao.UserAccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.Pair;
import org.apache.cloudstack.saml.SAML2UserAuthenticator;
import org.apache.cloudstack.utils.auth.SAMLUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@RunWith(MockitoJUnitRunner.class)
public class SAML2UserAuthenticatorTest {
@Mock
UserAccountDao userAccountDao;
@Mock
UserDao userDao;
@Test
public void encode() {
Assert.assertTrue(new SAML2UserAuthenticator().encode("random String").length() == 32);
}
@Test
public void authenticate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
SAML2UserAuthenticator authenticator = new SAML2UserAuthenticator();
Field daoField = SAML2UserAuthenticator.class.getDeclaredField("_userAccountDao");
daoField.setAccessible(true);
daoField.set(authenticator, userAccountDao);
Field userDaoField = SAML2UserAuthenticator.class.getDeclaredField("_userDao");
userDaoField.setAccessible(true);
userDaoField.set(authenticator, userDao);
UserAccountVO account = new UserAccountVO();
account.setPassword("5f4dcc3b5aa765d61d8327deb882cf99");
account.setId(1L);
UserVO user = new UserVO();
user.setUuid(SAMLUtils.createSAMLId("someUID"));
Mockito.when(userAccountDao.getUserAccount(Mockito.anyString(), Mockito.anyLong())).thenReturn(account);
Mockito.when(userDao.getUser(Mockito.anyLong())).thenReturn(user);
// When there is no SAMLRequest in params
Pair<Boolean, ActionOnFailedAuthentication> pair1 = authenticator.authenticate(SAMLUtils.createSAMLId("user1234"), "random", 1l, null);
Assert.assertFalse(pair1.first());
// When there is SAMLRequest in params
Map<String, Object[]> params = new HashMap<String, Object[]>();
params.put(SAMLUtils.SAML_RESPONSE, new Object[]{});
Pair<Boolean, ActionOnFailedAuthentication> pair2 = authenticator.authenticate(SAMLUtils.createSAMLId("user1234"), "random", 1l, params);
Assert.assertTrue(pair2.first());
}
}

View File

@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cloudstack.api.command;
import com.cloud.utils.HttpUtils;
import org.apache.cloudstack.api.ApiServerService;
import org.apache.cloudstack.api.auth.APIAuthenticationType;
import org.apache.cloudstack.saml.SAML2AuthManager;
import org.apache.cloudstack.utils.auth.SAMLUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
@RunWith(MockitoJUnitRunner.class)
public class GetServiceProviderMetaDataCmdTest {
@Mock
ApiServerService apiServer;
@Mock
SAML2AuthManager samlAuthManager;
@Mock
HttpSession session;
@Mock
HttpServletResponse resp;
@Test
public void testAuthenticate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, CertificateParsingException, CertificateEncodingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {
GetServiceProviderMetaDataCmd cmd = new GetServiceProviderMetaDataCmd();
Field apiServerField = GetServiceProviderMetaDataCmd.class.getDeclaredField("_apiServer");
apiServerField.setAccessible(true);
apiServerField.set(cmd, apiServer);
Field managerField = GetServiceProviderMetaDataCmd.class.getDeclaredField("_samlAuthManager");
managerField.setAccessible(true);
managerField.set(cmd, samlAuthManager);
String spId = "someSPID";
String url = "someUrl";
X509Certificate cert = SAMLUtils.generateRandomX509Certification();
Mockito.when(samlAuthManager.getServiceProviderId()).thenReturn(spId);
Mockito.when(samlAuthManager.getIdpSigningKey()).thenReturn(cert);
Mockito.when(samlAuthManager.getIdpSingleLogOutUrl()).thenReturn(url);
Mockito.when(samlAuthManager.getSpSingleLogOutUrl()).thenReturn(url);
String result = cmd.authenticate("command", null, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp);
Assert.assertTrue(result.contains("md:EntityDescriptor"));
Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getServiceProviderId();
Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getSpSingleSignOnUrl();
Mockito.verify(samlAuthManager, Mockito.atLeast(1)).getSpSingleLogOutUrl();
Mockito.verify(samlAuthManager, Mockito.never()).getIdpSingleSignOnUrl();
Mockito.verify(samlAuthManager, Mockito.never()).getIdpSingleLogOutUrl();
}
@Test
public void testGetAPIType() {
Assert.assertTrue(new GetServiceProviderMetaDataCmd().getAPIType() == APIAuthenticationType.LOGIN_API);
}
}

View File

@ -0,0 +1,175 @@
package org.apache.cloudstack.api.command;
import com.cloud.domain.Domain;
import com.cloud.user.AccountService;
import com.cloud.user.DomainManager;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import com.cloud.utils.HttpUtils;
import com.cloud.utils.db.EntityManager;
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.dao.ConfigurationDao;
import org.apache.cloudstack.saml.SAML2AuthManager;
import org.apache.cloudstack.utils.auth.SAMLUtils;
import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.opensaml.common.SAMLVersion;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.AuthnStatement;
import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.NameIDType;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.core.Status;
import org.opensaml.saml2.core.StatusCode;
import org.opensaml.saml2.core.Subject;
import org.opensaml.saml2.core.impl.AssertionBuilder;
import org.opensaml.saml2.core.impl.AttributeStatementBuilder;
import org.opensaml.saml2.core.impl.AuthnStatementBuilder;
import org.opensaml.saml2.core.impl.NameIDBuilder;
import org.opensaml.saml2.core.impl.ResponseBuilder;
import org.opensaml.saml2.core.impl.StatusBuilder;
import org.opensaml.saml2.core.impl.StatusCodeBuilder;
import org.opensaml.saml2.core.impl.SubjectBuilder;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
@RunWith(MockitoJUnitRunner.class)
public class SAML2LoginAPIAuthenticatorCmdTest {
@Mock
ApiServerService apiServer;
@Mock
SAML2AuthManager samlAuthManager;
@Mock
ConfigurationDao configDao;
@Mock
EntityManager entityMgr;
@Mock
DomainManager domainMgr;
@Mock
AccountService accountService;
@Mock
Domain domain;
@Mock
HttpSession session;
@Mock
HttpServletResponse resp;
private Response buildMockResponse() throws Exception {
Response samlMessage = new ResponseBuilder().buildObject();
samlMessage.setID("foo");
samlMessage.setVersion(SAMLVersion.VERSION_20);
samlMessage.setIssueInstant(new DateTime(0));
Status status = new StatusBuilder().buildObject();
StatusCode statusCode = new StatusCodeBuilder().buildObject();
statusCode.setValue(StatusCode.SUCCESS_URI);
status.setStatusCode(statusCode);
samlMessage.setStatus(status);
Assertion assertion = new AssertionBuilder().buildObject();
Subject subject = new SubjectBuilder().buildObject();
NameID nameID = new NameIDBuilder().buildObject();
nameID.setValue("SOME-UNIQUE-ID");
nameID.setFormat(NameIDType.PERSISTENT);
subject.setNameID(nameID);
assertion.setSubject(subject);
AuthnStatement authnStatement = new AuthnStatementBuilder().buildObject();
authnStatement.setSessionIndex("Some Session String");
assertion.getAuthnStatements().add(authnStatement);
AttributeStatement attributeStatement = new AttributeStatementBuilder().buildObject();
assertion.getAttributeStatements().add(attributeStatement);
samlMessage.getAssertions().add(assertion);
return samlMessage;
}
@Test
public void testAuthenticate() throws Exception {
SAML2LoginAPIAuthenticatorCmd cmd = Mockito.spy(new SAML2LoginAPIAuthenticatorCmd());
Field apiServerField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_apiServer");
apiServerField.setAccessible(true);
apiServerField.set(cmd, apiServer);
Field managerField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_samlAuthManager");
managerField.setAccessible(true);
managerField.set(cmd, samlAuthManager);
Field accountServiceField = BaseCmd.class.getDeclaredField("_accountService");
accountServiceField.setAccessible(true);
accountServiceField.set(cmd, accountService);
Field entityMgrField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_entityMgr");
entityMgrField.setAccessible(true);
entityMgrField.set(cmd, entityMgr);
Field domainMgrField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_domainMgr");
domainMgrField.setAccessible(true);
domainMgrField.set(cmd, domainMgr);
Field configDaoField = SAML2LoginAPIAuthenticatorCmd.class.getDeclaredField("_configDao");
configDaoField.setAccessible(true);
configDaoField.set(cmd, configDao);
String spId = "someSPID";
String url = "someUrl";
X509Certificate cert = SAMLUtils.generateRandomX509Certification();
Mockito.when(samlAuthManager.getServiceProviderId()).thenReturn(spId);
Mockito.when(samlAuthManager.getIdpSigningKey()).thenReturn(null);
Mockito.when(samlAuthManager.getIdpSingleSignOnUrl()).thenReturn(url);
Mockito.when(samlAuthManager.getSpSingleSignOnUrl()).thenReturn(url);
Mockito.when(session.getAttribute(Mockito.anyString())).thenReturn(null);
Mockito.when(configDao.getValue(Mockito.anyString())).thenReturn("someString");
Mockito.when(domain.getId()).thenReturn(1L);
Mockito.when(domainMgr.getDomain(Mockito.anyString())).thenReturn(domain);
UserVO user = new UserVO();
user.setUuid(SAMLUtils.createSAMLId("someUID"));
Mockito.when(entityMgr.findByUuid(Mockito.eq(User.class), Mockito.anyString())).thenReturn((User) user);
Mockito.when(apiServer.verifyUser(Mockito.anyLong())).thenReturn(false);
Map<String, Object[]> params = new HashMap<String, Object[]>();
// SSO redirection test
cmd.authenticate("command", params, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp);
Mockito.verify(resp, Mockito.times(1)).sendRedirect(Mockito.anyString());
// SSO SAMLResponse verification test, this should throw ServerApiException for auth failure
params.put(SAMLUtils.SAML_RESPONSE, new String[]{"Some String"});
Mockito.stub(cmd.processSAMLResponse(Mockito.anyString())).toReturn(buildMockResponse());
try {
cmd.authenticate("command", params, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp);
} catch (ServerApiException ignored) {
}
Mockito.verify(configDao, Mockito.atLeastOnce()).getValue(Mockito.anyString());
Mockito.verify(domainMgr, Mockito.times(1)).getDomain(Mockito.anyString());
Mockito.verify(entityMgr, Mockito.times(1)).findByUuid(Mockito.eq(User.class), Mockito.anyString());
Mockito.verify(apiServer, Mockito.times(1)).verifyUser(Mockito.anyLong());
}
@Test
public void testGetAPIType() {
Assert.assertTrue(new GetServiceProviderMetaDataCmd().getAPIType() == APIAuthenticationType.LOGIN_API);
}
}

View File

@ -0,0 +1,93 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cloudstack.api.command;
import com.cloud.utils.HttpUtils;
import org.apache.cloudstack.api.ApiServerService;
import org.apache.cloudstack.api.auth.APIAuthenticationType;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.saml.SAML2AuthManager;
import org.apache.cloudstack.utils.auth.SAMLUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.security.cert.X509Certificate;
@RunWith(MockitoJUnitRunner.class)
public class SAML2LogoutAPIAuthenticatorCmdTest {
@Mock
ApiServerService apiServer;
@Mock
SAML2AuthManager samlAuthManager;
@Mock
ConfigurationDao configDao;
@Mock
HttpSession session;
@Mock
HttpServletResponse resp;
@Test
public void testAuthenticate() throws Exception {
SAML2LogoutAPIAuthenticatorCmd cmd = new SAML2LogoutAPIAuthenticatorCmd();
Field apiServerField = SAML2LogoutAPIAuthenticatorCmd.class.getDeclaredField("_apiServer");
apiServerField.setAccessible(true);
apiServerField.set(cmd, apiServer);
Field managerField = SAML2LogoutAPIAuthenticatorCmd.class.getDeclaredField("_samlAuthManager");
managerField.setAccessible(true);
managerField.set(cmd, samlAuthManager);
Field configDaoField = SAML2LogoutAPIAuthenticatorCmd.class.getDeclaredField("_configDao");
configDaoField.setAccessible(true);
configDaoField.set(cmd, configDao);
String spId = "someSPID";
String url = "someUrl";
X509Certificate cert = SAMLUtils.generateRandomX509Certification();
Mockito.when(samlAuthManager.getServiceProviderId()).thenReturn(spId);
Mockito.when(samlAuthManager.getIdpSigningKey()).thenReturn(cert);
Mockito.when(samlAuthManager.getIdpSingleLogOutUrl()).thenReturn(url);
Mockito.when(samlAuthManager.getSpSingleLogOutUrl()).thenReturn(url);
Mockito.when(session.getAttribute(Mockito.anyString())).thenReturn(null);
Mockito.when(configDao.getValue(Mockito.anyString())).thenReturn("someString");
cmd.authenticate("command", null, session, "random", HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), resp);
Mockito.verify(resp, Mockito.times(1)).sendRedirect(Mockito.anyString());
Mockito.verify(session, Mockito.atLeastOnce()).getAttribute(Mockito.anyString());
}
@Test
public void testGetAPIType() throws Exception {
Assert.assertTrue(new SAML2LogoutAPIAuthenticatorCmd().getAPIType() == APIAuthenticationType.LOGOUT_API);
}
}