package org.pac4j.cas.client;
import org.junit.Test;
import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.core.context.MockWebContext;
import org.pac4j.core.credentials.TokenCredentials;
import org.pac4j.core.exception.HttpAction;
import org.pac4j.core.util.TestsConstants;
import org.pac4j.core.util.TestsHelper;
import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.util.zip.Deflater;
import static org.junit.Assert.*;
import static org.pac4j.core.context.HttpConstants.*;
/**
* This class tests the {@link CasClient} class.
*
* @author Jerome Leleu
* @since 1.4.0
*/
public final class CasClientTests implements TestsConstants {
private static final String CAS = "/cas";
private static final String CASBACK = "/casback";
private static final String HOST = "protocol://myHost";
private static final String LOGIN = "/login";
private static final String PREFIX_URL = "http://myserver/";
private static final String PREFIX_URL_WITHOUT_SLASH = "http://myserver";
private static final String LOGOUT_MESSAGE = "\"<samlp:LogoutRequest xmlns:samlp=\\\"urn:oasis:names:tc:SAML:2.0:protocol\\\" ID=\\\"LR-1-B2b0CVRW5eSvPBZPsAVXdNPj7jee4SWjr9y\\\" Version=\\\"2.0\\\" IssueInstant=\\\"2012-12-19T15:30:55Z\\\"><saml:NameID xmlns:saml=\\\"urn:oasis:names:tc:SAML:2.0:assertion\\\">@NOT_USED@</saml:NameID><samlp:SessionIndex>\" + TICKET + \"</samlp:SessionIndex></samlp:LogoutRequest>\";";
@Test
public void testMissingCasUrls() {
final CasClient casClient = new CasClient();
casClient.setCallbackUrl(CALLBACK_URL);
TestsHelper.initShouldFail(casClient, "loginUrl, prefixUrl and restUrl cannot be all blank");
}
@Test
public void testMissingSlashOnPrefixUrl() {
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(LOGIN_URL);
configuration.setPrefixUrl(PREFIX_URL_WITHOUT_SLASH);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
casClient.init(null);
assertEquals(PREFIX_URL, configuration.getPrefixUrl());
}
@Test
public void testInitPrefixUrl() {
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(LOGIN_URL);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
assertEquals(null, configuration.getPrefixUrl());
casClient.init(null);
assertEquals(PREFIX_URL, configuration.getPrefixUrl());
}
@Test
public void testInitLoginUrl() {
final CasConfiguration configuration = new CasConfiguration();
configuration.setPrefixUrl(PREFIX_URL);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
assertEquals(null, configuration.getLoginUrl());
casClient.init(null);
assertEquals(LOGIN_URL, configuration.getLoginUrl());
}
@Test
public void testCallbackUrlResolver() {
final CasConfiguration configuration = new CasConfiguration();
configuration.setPrefixUrl(CAS);
configuration.setLoginUrl(CAS + LOGIN);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CASBACK);
casClient.setCallbackUrlResolver((callbackUrl, context) -> HOST + callbackUrl);
casClient.init(null);
assertEquals(HOST + CAS + LOGIN, configuration.computeFinalLoginUrl(null));
assertEquals(HOST + CAS + "/", configuration.computeFinalPrefixUrl(null));
}
@Test
public void testRenew() throws HttpAction {
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(LOGIN_URL);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
MockWebContext context = MockWebContext.create();
casClient.redirect(context);
assertFalse(context.getResponseLocation().indexOf("renew=true") >= 0);
configuration.setRenew(true);
casClient.reinit(null);
context = MockWebContext.create();
casClient.redirect(context);
assertTrue(context.getResponseLocation().indexOf("renew=true") >= 0);
}
@Test
public void testGateway() throws HttpAction {
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(LOGIN_URL);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
final MockWebContext context = MockWebContext.create();
casClient.redirect(context);
assertFalse(context.getResponseLocation().indexOf("gateway=true") >= 0);
configuration.setGateway(true);
casClient.reinit(null);
casClient.redirect(context);
assertTrue(context.getResponseLocation().indexOf("gateway=true") >= 0);
final TokenCredentials credentials = casClient.getCredentials(context);
assertNull(credentials);
}
@Test
public void testBackLogout() {
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(LOGIN_URL);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
casClient.init(null);
final MockWebContext context = MockWebContext.create().addRequestParameter(CasConfiguration.LOGOUT_REQUEST_PARAMETER, LOGOUT_MESSAGE)
.setRequestMethod(HTTP_METHOD.POST.name());
TestsHelper.expectException(() -> casClient.getCredentials(context), HttpAction.class, "back logout request: no credential returned");
assertEquals(200, context.getResponseStatus());
}
private String deflateAndBase64(final String data) {
try {
final Deflater deflater = new Deflater();
deflater.setInput(data.getBytes(UTF8_ENCODING));
deflater.finish();
final byte[] buffer = new byte[data.length()];
final int resultSize = deflater.deflate(buffer);
final byte[] output = new byte[resultSize];
System.arraycopy(buffer, 0, output, 0, resultSize);
return DatatypeConverter.printBase64Binary(output);
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException("Cannot find encoding:" + UTF8_ENCODING, e);
}
}
@Test
public void testFrontLogout() throws HttpAction {
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(LOGIN_URL);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
casClient.init(null);
final MockWebContext context = MockWebContext.create().addRequestParameter(CasConfiguration.LOGOUT_REQUEST_PARAMETER, deflateAndBase64(LOGOUT_MESSAGE))
.setRequestMethod(HTTP_METHOD.GET.name());
assertNull(casClient.getCredentials(context));
}
@Test
public void testFrontLogoutWithRelayState() {
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(LOGIN_URL);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
casClient.init(null);
final MockWebContext context = MockWebContext.create().addRequestParameter(CasConfiguration.LOGOUT_REQUEST_PARAMETER, deflateAndBase64(LOGOUT_MESSAGE))
.addRequestParameter(CasConfiguration.RELAY_STATE_PARAMETER, VALUE).setRequestMethod(HTTP_METHOD.GET.name());
final HttpAction action = (HttpAction) TestsHelper.expectException(() -> casClient.getCredentials(context));
assertEquals(TEMP_REDIRECT, action.getCode());
assertEquals("Force redirect to CAS server for front channel logout", action.getMessage());
}
@Test
public void testInitUrlWithLoginString() {
final String testCasLoginUrl = "https://login.foo.bar/login/login";
final String testCasPrefixUrl = "https://login.foo.bar/login/";
final CasConfiguration configuration = new CasConfiguration();
configuration.setLoginUrl(testCasLoginUrl);
final CasClient casClient = new CasClient(configuration);
casClient.setCallbackUrl(CALLBACK_URL);
assertEquals(null, configuration.getPrefixUrl());
casClient.init(null);
assertEquals(testCasPrefixUrl, configuration.getPrefixUrl());
}
}