/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig 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 the following location:
*
* 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.jasig.cas.login;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* Common test cases for /serviceValidate and /proxyValidate.
* @since 3.0
*/
public abstract class AbstractCas2ValidateCompatibilityTests extends AbstractCompatibilityTests {
/**
* The name of the compatibility test configuration property the value of which
* will be the URL of a proxy ticket receptor to which the CAS server under test
* can send proxy granting tickets. E.g., an instance of the Java CAS Client
* ProxyTicketReceptor servlet deployed behind SSL with an SSL cert trusted by the
* CAS server under test. We use this to test specifying a proxy callback URL
* and thereby obtaining a pgtiou.
*/
public static final String PROXY_RECEPTOR_URL_PROPERTY = "pgtreceptor.url";
public AbstractCas2ValidateCompatibilityTests() throws IOException {
super();
}
public AbstractCas2ValidateCompatibilityTests(final String name) throws IOException {
super(name);
}
/**
* Returns /serviceValidate in the case of /serviceValidate,
* and /proxyValidate in the case of /proxyValidate.
* Concrete subclasses implement this method to configure the common
* tests defined here.
* @return
*/
protected abstract String getValidationPath();
protected final String getProxyCallbackUrl() {
return getProperties().getProperty(PROXY_RECEPTOR_URL_PROPERTY);
}
public void testNoParameters() {
beginAt(getValidationPath());
assertTextPresent("cas:authenticationFailure");
// TODO actually test the validation response XML.
}
public void testBadServiceTicket() throws UnsupportedEncodingException {
final String service = getServiceUrl();
beginAt(getValidationPath() + "?service=" + URLEncoder.encode(service, "UTF-8") + "&ticket=test");
assertTextPresent("cas:authenticationFailure");
// TODO do more to test that the response is actually XML, etc. etc.
}
/**
* Test validation of a valid service ticket and that service tickets are
* not multiply validatable.
* @throws IOException
*/
public void testProperCredentialsAndServiceTicket() throws IOException {
final String service = getServiceUrl();
String encodedService = URLEncoder.encode(service, "UTF-8");
beginAt("/login?service=" + encodedService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
String serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it
beginAt(getValidationPath() + "?service=" + encodedService + "&" + "ticket=" + serviceTicket);
assertTextPresent("cas:authenticationSuccess");
// this assertion may be too strict. How does whitespace work here?
assertTextPresent("<cas:user>" + getUsername() + "</cas:user>");
// TODO do more to test that the response is actually XML, etc. etc.
// let's validate it again and ensure that we cannot again validate
// the ticket
beginAt(getValidationPath() + "?service=" + encodedService + "&" + "ticket=" + serviceTicket);
assertTextPresent("cas:authenticationFailure");
// TODO do more to test that the response is actually XML, etc. etc.
}
/**
* Test that renew=true, when specified both at login and ticket validation,
* validation succeeds.
* @throws IOException
*/
public void testRenew() throws IOException {
final String service = getServiceUrl();
String encodedService = URLEncoder.encode(service, "UTF-8");
beginAt("/login?renew=true&service=" + encodedService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
String serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it
beginAt(getValidationPath() + "?renew=true&service=" + encodedService + "&" + "ticket=" + serviceTicket);
assertTextPresent("cas:authenticationSuccess");
}
/**
* Test a couple renew=true logins...
* @throws IOException
*/
public void testMultipleRenew() throws IOException {
final String service = getServiceUrl();
String encodedService = URLEncoder.encode(service, "UTF-8");
beginAt("/login?renew=true&service=" + encodedService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
String serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it
beginAt(getValidationPath() + "?service=" + encodedService + "&" + "ticket=" + serviceTicket + "&renew=true");
assertTextPresent("cas:authenticationSuccess");
// now let's do it again
beginAt("/login?renew=true&service=" + encodedService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it
beginAt(getValidationPath() + "?service=" + encodedService + "&" + "ticket=" + serviceTicket + "&renew=true");
assertTextPresent("<cas:authenticationSuccess>");
}
/**
* Test that renew=true, when specified only at ticket validation,
* validation succeeds if username, password were presented at login even
* though renew wasn't set then.
* @throws IOException
*/
public void testAccidentalRenew() throws IOException {
final String service = getServiceUrl();
String encodedService = URLEncoder.encode(service, "UTF-8");
beginAt("/login?service=" + encodedService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
String serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it
beginAt(getValidationPath() + "?renew=true&service=" + encodedService + "&" + "ticket=" + serviceTicket);
assertTextPresent("<cas:authenticationSuccess>");
assertTextPresent("<cas:user>" + getUsername() + "</cas:user>");
}
/**
* Test that renew at ticket validation blocks validation of a ticket
* vended via SSO.
* @throws IOException
*/
public void testRenewBlocksSsoValidation() throws IOException {
// initial authentication
final String firstService = getServiceUrl();
final String encodedFirstService = URLEncoder.encode(firstService, "UTF-8");
beginAt("/login?service=" + encodedFirstService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
String firstServiceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// that established SSO. Now let's get another ticket via SSO
final String secondService= "http://www.uportal.org/";
final String encodedSecondService = URLEncoder.encode(secondService, "UTF-8");
beginAt("/login?service=" + encodedSecondService);
// read the service ticket
String secondServiceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// let's validate the second (non-renew) ticket.
beginAt(getValidationPath() + "?renew=true&service=" + encodedSecondService + "&ticket=" + secondServiceTicket);
assertTextPresent("cas:authenticationFailure");
//TODO test the authentication failure response in more detail
assertTextNotPresent("<cas:user>");
// however, we can validate the first ticket with renew=true.
beginAt(getValidationPath() + "?renew=true&service=" + encodedFirstService + "&ticket=" + firstServiceTicket);
assertTextPresent("cas:authenticationSuccess");
assertTextPresent("<cas:user>" + getUsername() + "</cas:user>");
//TODO assert more about the response
}
/**
* Test best-effort ticket validation when a specified proxy callback handler
* doesn't really exist.
* @throws IOException
*/
public void testBrokenProxyCallbackUrl() throws IOException {
final String service = getServiceUrl();
String encodedService = URLEncoder.encode(service, "UTF-8");
beginAt("/login?service=" + encodedService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
String serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it, specifying a bogus pgt callback
String encodedProxyCallbackUrl = URLEncoder.encode("https://secure.its.yale.edu/cas/noexist", "UTF-8");
beginAt(getValidationPath() + "?renew=true&service=" + encodedService + "&" + "ticket="
+ serviceTicket + "&pgtUrl=" + encodedProxyCallbackUrl);
assertTextPresent("<cas:authenticationSuccess>");
assertTextPresent("<cas:user>" + getUsername() + "</cas:user>");
// no pgtiou because failure in sending pgt to specified receptor URL.
assertTextNotPresent("<cas:pgtiou>");
}
public void testPgtAcquisition() throws IOException {
final String service = getServiceUrl();
String encodedService = URLEncoder.encode(service, "UTF-8");
beginAt("/login?service=" + encodedService);
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
String serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it, specifying a bogus pgt callback
String encodedProxyCallbackUrl = URLEncoder.encode(getProxyCallbackUrl(), "UTF-8");
beginAt(getValidationPath() + "?renew=true&service=" + encodedService + "&" + "ticket=" + serviceTicket
+ "&pgtUrl=" + encodedProxyCallbackUrl);
assertTextPresent("<cas:authenticationSuccess>");
assertTextPresent("<cas:user>" + getUsername() + "</cas:user>");
// pgtiou because success in sending pgt
assertTextPresent("<cas:proxyGrantingTicket>");
}
/**
* Test for JIRA issue CAS-224.
* 1. /cas/login?service=foo&renew=true
* 2. log in
* 3. /serviceValidate?ticket=[ticket]&service=foo&renew=true
*
* Issue was that validation fails whereas it should succeed.
*
* This testcase is almost certainly redundant, but it's explicitly here
* to cover this issue.
* @throws IOException
*/
public void test224() throws IOException {
final String service = getServiceUrl();
String encodedService = URLEncoder.encode(service, "UTF-8");
beginAt("/login?service=" + encodedService + "&renew=true");
setFormElement("username", getUsername());
setFormElement("password", getGoodPassword());
submit();
// read the service ticket
String serviceTicket = LoginHelper.serviceTicketFromResponse(getDialog().getResponse());
// great, now we have a ticket
// let's validate it
assertNotNull(serviceTicket);
beginAt(getValidationPath() + "?ticket=" + serviceTicket + "&service=" + encodedService + "&renew=true");
assertTextPresent("cas:authenticationSuccess");
}
}