package net.unicon.idp.authn.provider;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.BDDMockito;
import org.mockito.Mockito;
import org.opensaml.util.storage.StorageService;
import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
import edu.internet2.middleware.shibboleth.idp.authn.LoginContextEntry;
import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
import edu.internet2.middleware.shibboleth.idp.authn.provider.ExternalAuthnSystemLoginHandler;
/**
* Validates the CasLoginHandler code
* @author chasegawa@unicon.net
*/
public class CasLoginHandlerTests {
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private LoginContext loginContext;
/**
* Mock out the request, response and session
*/
@Before
public void beforeTests() {
request = Mockito.mock(HttpServletRequest.class);
response = Mockito.mock(HttpServletResponse.class);
session = Mockito.mock(HttpSession.class);
BDDMockito.given(request.getSession()).willReturn(session);
ServletContext context = Mockito.mock(ServletContext.class);
BDDMockito.given(session.getServletContext()).willReturn(context);
@SuppressWarnings("unchecked")
StorageService<String, Object> storageService = Mockito.mock(StorageService.class);
BDDMockito.given(context.getAttribute(Mockito.anyString())).willReturn(storageService);
LoginContextEntry loginContextEntry = Mockito.mock(LoginContextEntry.class);
BDDMockito.given(loginContextEntry.isExpired()).willReturn(false);
loginContext = Mockito.mock(LoginContext.class);
BDDMockito.given(loginContext.getRelyingPartyId()).willReturn("dummyPartyId");
BDDMockito.given(loginContextEntry.getLoginContext()).willReturn(loginContext);
BDDMockito.given(storageService.get(Mockito.anyString(), Mockito.anyString())).willReturn(loginContextEntry);
Cookie cookie = Mockito.mock(Cookie.class);
BDDMockito.given(cookie.getName()).willReturn("_idp_authn_lc_key");
BDDMockito.given(cookie.getValue()).willReturn("anythingNotNull");
Cookie[] requestCookies = { cookie };
BDDMockito.given(request.getCookies()).willReturn(requestCookies);
}
private Reader getReader(final String file) {
return new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream(file));
}
@Test
public void testCustomConfigCallsCasCorrectly() throws IOException {
BDDMockito.given(loginContext.isForceAuthRequired()).willReturn(true);
LoginHandler handler = new CasLoginHandler(getReader("customProps.properties"), "customProps.properties", "");
handler.login(request, response);
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(session).setAttribute(Mockito.anyString(), argument.capture());
Assert.assertEquals("Value set in session was incorrect", "renew=true", argument.getValue());
argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(response).encodeRedirectURL(argument.capture());
Assert.assertEquals("Incorrect URL built",
"hhttttppss://casserv:8443/CAS/login?renew=true&entityId=dummyPartyId&service=sptth://idpserv:9443/pdi/my/Casback",
argument.getValue());
}
@Test(expected = IllegalArgumentException.class)
public void testMissingCasServerConfig() {
new CasLoginHandler(getReader("missingCasServerProp.properties"), "missingCasServerProp.properties", "");
}
@Test(expected = IllegalArgumentException.class)
public void testMissingIDPServerConfig() {
new CasLoginHandler(getReader("missingIDPServerProp.properties"), "missingIDPServerProp.properties", "");
}
@Test(expected = FileNotFoundException.class)
public void testNoPropertiesFile() throws FileNotFoundException {
new CasLoginHandler("notAValidPathToPropertiesFile.txt", "");
}
@Test
public void testSimpleConfigCallsCasCorrectly() throws IOException {
LoginHandler handler = new CasLoginHandler(getReader("simpleProps.properties"), "simpleProps.properties", "");
handler.login(request, response);
// Check nothing set in session that shouldn't be there
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(session).setAttribute(Mockito.anyString(), argument.capture());
Assert.assertTrue(StringUtils.isEmpty(argument.getValue()));
argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(response).encodeRedirectURL(argument.capture());
Assert.assertEquals("Incorrect URL built",
"https://localhost:443/cas/login?entityId=dummyPartyId&service=https://localhost:443/idp/Authn/Cas",
argument.getValue());
}
/**
* https://github.com/Unicon/shib-cas-authn2/issues/6
* EntityIdParameterBuilder is in the Login Handler by default. When it is explicitly added, it should not end up in the map
* twice (or more).
*/
@Test
public void testSimpleConfigWithHandlerAddedTwiceDoesNotAddEntityIdTwice() throws IOException {
// Add the same builder a couple of extra times.
LoginHandler handler = new CasLoginHandler(getReader("simpleProps.properties"), "simpleProps.properties",
"net.unicon.idp.authn.provider.extra.EntityIdParameterBuilder, net.unicon.idp.authn.provider.extra.EntityIdParameterBuilder");
handler.login(request, response);
// Check nothing set in session that shouldn't be there
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(session).setAttribute(Mockito.anyString(), argument.capture());
Assert.assertTrue(StringUtils.isEmpty(argument.getValue()));
argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(response).encodeRedirectURL(argument.capture());
Assert.assertEquals("Incorrect URL built",
"https://localhost:443/cas/login?entityId=dummyPartyId&service=https://localhost:443/idp/Authn/Cas",
argument.getValue());
}
@Test
public void testSimpleConfigWithGateway() {
BDDMockito.given(loginContext.isPassiveAuthRequired()).willReturn(true);
LoginHandler handler = new CasLoginHandler(getReader("simpleProps.properties"), "simpleProps.properties", "");
handler.login(request, response);
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(session).setAttribute(Mockito.anyString(), argument.capture());
Assert.assertEquals("Value set in session was incorrect", "gateway=true", argument.getValue());
}
@Test
public void testSimpleConfigWithRenew() {
BDDMockito.given(loginContext.isForceAuthRequired()).willReturn(true);
LoginHandler handler = new CasLoginHandler(getReader("simpleProps.properties"), "simpleProps.properties", "");
handler.login(request, response);
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(session).setAttribute(Mockito.anyString(), argument.capture());
Assert.assertEquals("Value set in session was incorrect", "renew=true", argument.getValue());
argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(response).encodeRedirectURL(argument.capture());
Assert.assertEquals("Incorrect URL built",
"https://localhost:443/cas/login?renew=true&entityId=dummyPartyId&service=https://localhost:443/idp/Authn/Cas",
argument.getValue());
}
@Test
public void testSimpleConfigWithRenewAndGateway() {
BDDMockito.given(loginContext.isPassiveAuthRequired()).willReturn(true);
BDDMockito.given(loginContext.isForceAuthRequired()).willReturn(true);
LoginHandler handler = new CasLoginHandler(getReader("simpleProps.properties"), "simpleProps.properties", "");
handler.login(request, response);
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(session).setAttribute(Mockito.anyString(), argument.capture());
Assert.assertEquals("Value set in session was incorrect", "renew=true&gateway=true", argument.getValue());
}
}