cassample groovy->java

Issue gh-4939
This commit is contained in:
Josh Cummings 2020-02-07 16:43:33 -07:00
parent cb695ae60f
commit 7c4d56319f
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
28 changed files with 822 additions and 586 deletions

View File

@ -3,12 +3,6 @@ apply plugin: 'io.spring.convention.spring-sample-war'
def keystore = "$rootDir/samples/certificates/server.jks"
def password = 'password'
sourceSets {
test.resources.exclude 'GebConfig.groovy'
integrationTest.groovy.srcDir file('src/integration-test/groovy')
}
dependencies {
compile project(':spring-security-cas')
compile project(':spring-security-core')
@ -24,10 +18,7 @@ dependencies {
runtime 'org.springframework:spring-context-support'
integrationTestCompile project(':spring-security-cas')
integrationTestCompile gebDependencies
integrationTestCompile seleniumDependencies
integrationTestCompile spockDependencies
integrationTestCompile 'org.codehaus.groovy:groovy'
integrationTestCompile 'org.eclipse.jetty:jetty-server'
integrationTestCompile 'org.eclipse.jetty:jetty-servlet'
integrationTestCompile 'org.slf4j:jcl-over-slf4j'
@ -40,8 +31,6 @@ integrationTest {
def casServerHost = 'localhost:' + casServer().gretty.httpsPort
systemProperties['cas.server.host'] = casServerHost
systemProperties['cas.service.host'] = casServiceHost
systemProperties['geb.build.baseUrl'] = 'https://'+casServiceHost+'/cas-sample/'
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
systemProperties['jar.path'] = jar.archivePath
systemProperties['javax.net.ssl.trustStore'] = keystore
systemProperties['javax.net.ssl.trustStorePassword'] = password

View File

@ -1,36 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas
import org.springframework.security.samples.cas.pages.LoginPage;
import java.io.File;
import geb.spock.*
/**
* Base test for Geb testing.
*
* @author Rob Winch
*/
class AbstractCasTests extends GebReportingSpec {
def cleanupSpec() {
to LoginPage
resetBrowser()
}
}

View File

@ -1,122 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas
import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.methods.GetMethod
import org.jasig.cas.client.jaas.CasLoginModule;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever
import org.springframework.security.samples.cas.pages.*
import spock.lang.*
/**
* Tests authenticating to the CAS Sample application using Proxy Tickets. Geb is used to authenticate the {@link JettyCasService}
* to the CAS Server in order to obtain the Ticket Granting Ticket. Afterwards HttpClient is used for accessing the CAS Sample application
* using Proxy Tickets obtained using the Proxy Granting Ticket.
*
* @author Rob Winch
*/
@Stepwise
class CasSampleProxyTests extends AbstractCasTests {
HttpClient client = new HttpClient()
@Shared String casServerUrl = LoginPage.url.replaceFirst('/login','')
@Shared JettyCasService service = new JettyCasService().init(casServerUrl)
@Shared Cas20ProxyRetriever retriever = new Cas20ProxyRetriever(casServerUrl,'UTF-8')
@Shared String pt
def cleanupSpec() {
service.stop()
}
def 'access secure page succeeds with ROLE_USER'() {
setup: 'Obtain a pgt for a user with ROLE_USER'
driver.get LoginPage.url+"?service="+service.serviceUrl()
at LoginPage
login 'scott'
when: 'User with ROLE_USER accesses the secure page'
def content = getSecured(getBaseUrl()+SecurePage.url).responseBodyAsString
then: 'The secure page is returned'
content.contains('<h1>Secure Page</h1>')
}
def 'access proxy ticket sample succeeds with ROLE_USER'() {
when: 'a proxy ticket is used to create another proxy ticket'
def content = getSecured(getBaseUrl()+ProxyTicketSamplePage.url).responseBodyAsString
then: 'The proxy ticket sample page is returned'
content.contains('<h1>Secure Page using a Proxy Ticket</h1>')
}
def 'access extremely secure page with ROLE_USER is denied'() {
when: 'User with ROLE_USER accesses the extremely secure page'
GetMethod method = getSecured(getBaseUrl()+ExtremelySecurePage.url)
then: 'access is denied'
assert method.responseBodyAsString =~ /(?i)403.*?Denied/
assert 403 == method.statusCode
}
def 'access secure page with ROLE_SUPERVISOR succeeds'() {
setup: 'Obtain pgt for user with ROLE_SUPERVISOR'
to LocalLogoutPage
casServerLogout.click()
driver.get(LoginPage.url+"?service="+service.serviceUrl())
at LoginPage
login 'rod'
when: 'User with ROLE_SUPERVISOR accesses the secure page'
def content = getSecured(getBaseUrl()+ExtremelySecurePage.url).responseBodyAsString
then: 'The secure page is returned'
content.contains('<h1>VERY Secure Page</h1>')
}
def 'access extremely secure page with ROLE_SUPERVISOR reusing pt succeeds (stateless mode works)'() {
when: 'User with ROLE_SUPERVISOR accesses extremely secure page with used pt'
def content = getSecured(getBaseUrl()+ExtremelySecurePage.url,pt).responseBodyAsString
then: 'The extremely secure page is returned'
content.contains('<h1>VERY Secure Page</h1>')
}
def 'access secure page with invalid proxy ticket fails'() {
when: 'Invalid ticket is used to access secure page'
GetMethod method = getSecured(getBaseUrl()+SecurePage.url,'invalidticket')
then: 'Authentication fails'
method.statusCode == 401
}
/**
* Gets the result of calling a url with a proxy ticket
* @param targetUrl the absolute url to attempt to access
* @param pt the proxy ticket to use. Defaults to {@link #getPt(String)} with targetUrl specified for the targetUrl.
* @return the GetMethod after calling a url with a specified proxy ticket
*/
GetMethod getSecured(String targetUrl,String pt=getPt(targetUrl)) {
assert pt != null
GetMethod method = new GetMethod(targetUrl+"?ticket="+pt)
int status = client.executeMethod(method)
method
}
/**
* Obtains a proxy ticket using the pgt from the {@link #service}.
* @param targetService the targetService that the proxy ticket will be valid for
* @return a proxy ticket for targetService
*/
String getPt(String targetService) {
assert service.pgt != null
pt = retriever.getProxyTicketIdFor(service.pgt, targetService)
pt
}
}

View File

@ -1,135 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas
import geb.spock.*
import org.apache.http.impl.conn.DefaultClientConnectionOperator;
import org.junit.runner.RunWith;
import org.spockframework.runtime.Sputnik;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.core.context.ThreadLocalSecurityContextHolderStrategy;
import org.springframework.security.samples.cas.pages.*
import spock.lang.Shared;
import spock.lang.Stepwise;
/**
* Tests the CAS sample application using service tickets.
*
* @author Rob Winch
*/
@Stepwise
class CasSampleTests extends AbstractCasTests {
@Shared String casServerLogoutUrl = LoginPage.url.replaceFirst('/login','/logout')
def 'access home page with unauthenticated user succeeds'() {
when: 'Unauthenticated user accesses the Home Page'
to HomePage
then: 'The home page succeeds'
at HomePage
}
def 'access extremely secure page with unauthenitcated user requires login'() {
when: 'Unauthenticated user accesses the extremely secure page'
via ExtremelySecurePage
then: 'The login page is displayed'
at LoginPage
}
def 'authenticate attempt with invaid ticket fails'() {
when: 'present invalid ticket'
go "login/cas?ticket=invalid"
then: 'the login failed page is displayed'
$("h2").text() == 'Login to CAS failed!'
}
def 'access secure page with unauthenticated user requires login'() {
when: 'Unauthenticated user accesses the secure page'
via SecurePage
then: 'The login page is displayed'
at LoginPage
}
def 'saved request is used for secure page'() {
when: 'login with ROLE_USER after requesting the secure page'
login 'scott'
then: 'the secure page is displayed'
at SecurePage
}
def 'access proxy ticket sample with ROLE_USER is allowed'() {
when: 'user with ROLE_USER requests the proxy ticket sample page'
to ProxyTicketSamplePage
then: 'the proxy ticket sample page is displayed'
at ProxyTicketSamplePage
}
def 'access extremely secure page with ROLE_USER is denied'() {
when: 'User with ROLE_USER accesses extremely secure page'
via ExtremelySecurePage
then: 'the access denied page is displayed'
at AccessDeniedPage
}
def 'clicking local logout link displays local logout page'() {
setup: 'Navigate to page with logout link'
to SecurePage
when: 'Local logout link is clicked'
navModule.logout.click()
then: 'the local logout page is displayed'
at LocalLogoutPage
}
def 'clicking cas server logout link successfully performs logout'() {
when: 'the cas server logout link is clicked and the secure page is requested'
casServerLogout.click()
via SecurePage
then: 'the login page is displayed'
at LoginPage
}
def 'access extremely secure page with ROLE_SUPERVISOR succeeds'() {
setup: 'login with ROLE_SUPERVISOR'
login 'rod'
when: 'access extremely secure page'
to ExtremelySecurePage
then: 'extremely secure page is displayed'
at ExtremelySecurePage
}
def 'after logout extremely secure page requires login'() {
when: 'logout and request extremely secure page'
navModule.logout.click()
casServerLogout.click()
via ExtremelySecurePage
then: 'login page is displayed'
at LoginPage
}
def 'logging out of the cas server successfully logs out of the cas sample application'() {
setup: 'login with ROLE_USER'
via SecurePage
at LoginPage
login 'rod'
at SecurePage
when: 'logout of the CAS Server'
go casServerLogoutUrl
via SecurePage
then: 'user is logged out of the CAS Service'
at LoginPage
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.modules;
import geb.*
import org.springframework.security.samples.cas.pages.*
/**
* Represents the navigation for the CAS Sample application
*
* @author Rob Winch
*/
class NavModule extends Module {
static content = {
home(to: HomePage) { $("a", text: "Home") }
logout(to: LocalLogoutPage) { $("a", text: "Logout") }
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import geb.*
import org.springframework.security.samples.cas.modules.*
/**
* Represents the extremely secure page of the CAS Sample application.
*
* @author Rob Winch
*/
class ExtremelySecurePage extends Page {
static url = "secure/extreme/"
static at = { assert $('h1').text() == 'VERY Secure Page'; true; }
static content = {
navModule { module NavModule }
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages
import geb.*
/**
* Represents the Home page of the CAS sample application
*
* @author Rob Winch
*/
class HomePage extends Page {
static at = { assert $('h1').text() == 'Home Page'; true}
static url = ''
static content = {
securePage { $('a',text: 'Secure page') }
extremelySecurePage { $('a',text: 'Extremely secure page') }
}
}

View File

@ -1,46 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import geb.*
/**
* The CAS login page.
*
* @author Rob Winch
*/
class LoginPage extends Page {
static url = loginUrl()
static at = { assert driver.currentUrl.startsWith(loginUrl()); true}
static content = {
login(required:false) { user, password=user ->
loginForm.username = user
loginForm.password = password
submit.click()
}
loginForm { $('#login') }
submit { $('input', type: 'submit') }
}
/**
* Gets the login page url which might change based upon the system properties. This is to support using a randomly available port for CI.
* @return
*/
private static String loginUrl() {
def host = System.getProperty('cas.server.host', 'localhost:9443')
"https://${host}/cas/login"
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import geb.*
import org.springframework.security.samples.cas.modules.*
/**
* Represents the proxy ticket sample page within the CAS Sample application.
*
* @author Rob Winch
*/
class ProxyTicketSamplePage extends Page {
static url = "secure/ptSample"
static at = { assert $('h1').text() == 'Secure Page using a Proxy Ticket'; true}
static content = {
navModule { module NavModule }
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import geb.*
import org.springframework.security.samples.cas.modules.*
/**
* Represents the secure page within the CAS Sample application.
*
* @author Rob Winch
*/
class SecurePage extends Page {
static url = "secure/"
static at = { assert $('h1').text() == 'Secure Page'; true}
static content = {
navModule { module NavModule }
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas;
import java.util.HashMap;
import java.util.Map;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.springframework.security.samples.cas.pages.AccessDeniedPage;
import org.springframework.security.samples.cas.pages.ExtremelySecurePage;
import org.springframework.security.samples.cas.pages.LoginPage;
import org.springframework.security.samples.cas.pages.ProxyTicketSamplePage;
import org.springframework.security.samples.cas.pages.SecurePage;
import org.springframework.security.samples.cas.pages.UnauthorizedPage;
/**
* Tests authenticating to the CAS Sample application using Proxy Tickets. Geb is used to authenticate the {@link JettyCasService}
* to the CAS Server in order to obtain the Ticket Granting Ticket. Afterwards HttpClient is used for accessing the CAS Sample application
* using Proxy Tickets obtained using the Proxy Granting Ticket.
*
* @author Rob Winch
* @author Josh Cummings
*/
public class CasSampleProxyTests {
private static String serverUrl;
private static String serviceUrl;
private static JettyCasService service;
private static Cas20ProxyRetriever retriever;
private WebDriver driver = new HtmlUnitDriver();
private LoginPage login;
private SecurePage secure;
private ExtremelySecurePage extremelySecure;
private ProxyTicketSamplePage proxyTicketSample;
private AccessDeniedPage accessDenied;
private UnauthorizedPage unauthorized;
@BeforeClass
public static void setupClass() {
String serverHost = System.getProperty("cas.server.host", "localhost:8443");
serverUrl = "https://" + serverHost + "/cas";
String serviceHost = System.getProperty("cas.service.host", "localhost:8443");
serviceUrl = "https://" + serviceHost + "/cas-sample";
service = new JettyCasService().init(serverUrl);
retriever = new Cas20ProxyRetriever(serverUrl, "UTF-8");
}
@AfterClass
public static void teardownClass() throws Exception {
service.stop();
}
@Before
public void setup() {
this.login = new LoginPage(this.driver, serverUrl);
this.secure = new SecurePage(this.driver, serviceUrl);
this.extremelySecure = new ExtremelySecurePage(this.driver, serviceUrl);
this.proxyTicketSample = new ProxyTicketSamplePage(this.driver, serviceUrl);
this.accessDenied = new AccessDeniedPage(this.driver);
this.unauthorized = new UnauthorizedPage(this.driver);
}
@After
public void teardown() {
this.driver.close();
}
@Test
public void securePageWhenRoleUserThenDisplays() {
this.login.to(this::serviceParam).assertAt().login("scott");
this.secure.to(this::ticketParam).assertAt();
}
@Test
public void proxyTicketSamplePageWhenRoleUserThenDisplays() {
this.login.to(this::serviceParam).assertAt().login("scott");
this.proxyTicketSample.to(this::ticketParam).assertAt();
}
@Test
public void extremelySecurePageWhenRoleUserThenDenies() {
this.login.to(this::serviceParam).assertAt().login("scott");
this.extremelySecure.to(this::ticketParam);
this.accessDenied.assertAt();
}
@Test
public void extremelySecurePageWhenRoleSupervisorThenDisplays() {
this.login.to(this::serviceParam).assertAt().login("rod");
this.extremelySecure.to(this::ticketParam).assertAt();
}
@Test
public void extremelySecurePageWhenReusingTicketThenDisplays() {
this.login.to(this::serviceParam).assertAt().login("rod");
Map<String, String> ptCache = new HashMap<>();
this.extremelySecure.to(url -> url + "?ticket=" + ptCache.computeIfAbsent(url, this::getPt)).assertAt();
this.extremelySecure.to(url -> url + "?ticket=" + ptCache.get(url)).assertAt();
}
@Test
public void securePageWhenInvalidTicketThenFails() {
this.login.to(this::serviceParam).assertAt().login("scott");
this.secure.to(url -> url + "?ticket=invalid");
this.unauthorized.assertAt();
}
private String serviceParam(String url) {
return url + "?service=" + service.serviceUrl();
}
private String ticketParam(String url) {
return url + "?ticket=" + getPt(url);
}
/**
* Obtains a proxy ticket using the pgt from the {@link #service}.
* @param targetService the targetService that the proxy ticket will be valid for
* @return a proxy ticket for targetService
*/
String getPt(String targetService) {
assert service.pgt != null;
return retriever.getProxyTicketIdFor(service.pgt, targetService);
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.springframework.security.samples.cas.pages.AccessDeniedPage;
import org.springframework.security.samples.cas.pages.ExtremelySecurePage;
import org.springframework.security.samples.cas.pages.HomePage;
import org.springframework.security.samples.cas.pages.LocalLogoutPage;
import org.springframework.security.samples.cas.pages.LoginPage;
import org.springframework.security.samples.cas.pages.ProxyTicketSamplePage;
import org.springframework.security.samples.cas.pages.SecurePage;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests the CAS sample application using service tickets.
*
* @author Rob Winch
* @author Josh Cummings
*/
public class CasSampleTests {
private WebDriver driver = new HtmlUnitDriver();
private String serviceUrl;
private String serverUrl;
private LoginPage login;
private HomePage home;
private SecurePage secure;
private ExtremelySecurePage extremelySecure;
private ProxyTicketSamplePage proxyTicketSample;
private LocalLogoutPage localLogout;
private AccessDeniedPage accessDenied;
@Before
public void setup() {
String serverHost = System.getProperty("cas.server.host", "localhost:8443");
this.serverUrl = "https://" + serverHost + "/cas";
String serviceHost = System.getProperty("cas.service.host", "localhost:8443");
this.serviceUrl = "https://" + serviceHost + "/cas-sample";
this.login = new LoginPage(this.driver, this.serverUrl);
this.home = new HomePage(this.driver, this.serviceUrl);
this.secure = new SecurePage(this.driver, this.serviceUrl);
this.extremelySecure = new ExtremelySecurePage(this.driver, this.serviceUrl);
this.proxyTicketSample = new ProxyTicketSamplePage(this.driver, this.serviceUrl);
this.localLogout = new LocalLogoutPage(this.driver, this.serviceUrl);
this.accessDenied = new AccessDeniedPage(this.driver);
}
@After
public void tearDown() {
this.driver.close();
}
@Test
public void homePageWhenUnauthenticatedUserThenSucceeds() {
this.home.to().assertAt();
}
@Test
public void extremelySecurePageWhenUnauthenticatedThenRequiresLogin() {
this.extremelySecure.to();
this.login.assertAt();
}
@Test
public void authenticateWhenInvalidTicketThenFails() {
this.driver.get(this.serviceUrl + "/login/cas?ticket=invalid");
assertThat(this.driver.findElement(By.tagName("h2")).getText())
.isEqualTo("Login to CAS failed!");
}
@Test
public void securePageWhenUnauthenticatedThenRequiresLogin() {
this.secure.to();
this.login.assertAt();
}
@Test
public void securePageWhenRoleUserThenDisplays() {
this.login.to().login("scott");
this.secure.to().assertAt();
}
@Test
public void proxyTicketSamplePageWhenRoleUserThenDisplays() {
this.login.to().login("scott");
this.proxyTicketSample.to().assertAt();
}
@Test
public void extremelySecurePageWhenRoleUserThenDenies() {
this.login.to().login("scott");
this.extremelySecure.to();
this.accessDenied.assertAt();
}
@Test
public void localLogoutLinkWhenClickedThenRedirectsToLocalLogoutPage() {
this.login.to().login("scott");
this.secure.to().logout();
this.localLogout.assertAt();
}
@Test
public void casLogoutWhenClickedThenPerformsCompleteLogout() {
this.login.to().login("scott");
this.driver.get(this.serverUrl + "/logout");
this.secure.to();
this.login.assertAt();
}
@Test
public void extremelySecureWhenRoleSupervisorThenDisplays() {
this.login.to().login("rod");
this.extremelySecure.to().assertAt();
}
@Test
public void casLogoutWhenClickedThenExtremelySecurePageRequiresLogin() {
this.login.to().login("scott");
this.driver.get(this.serverUrl + "/logout");
this.extremelySecure.to();
this.login.assertAt();
}
@Test
public void casLogoutWhenVisitedThenLogsOutSample() {
this.secure.to();
this.login.assertAt().login("rod");
this.secure.assertAt();
this.driver.get(this.serverUrl + "/logout");
this.secure.to();
this.login.assertAt();
}
}

View File

@ -13,41 +13,46 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.samples.cas
package org.springframework.security.samples.cas;
import org.eclipse.jetty.http.HttpVersion
import org.eclipse.jetty.server.HttpConfiguration
import org.eclipse.jetty.server.HttpConnectionFactory
import org.eclipse.jetty.server.SecureRequestCustomizer
import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.server.SslConnectionFactory
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.handler.AbstractHandler
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.springframework.util.StringUtils;
/**
* A CAS Service that allows a PGT to be obtained. This is useful for testing use of proxy tickets.
*
* @author Rob Winch
*/
class JettyCasService extends Server {
private Cas20ProxyTicketValidator validator
private int port = availablePort()
public class JettyCasService extends Server {
private Cas20ProxyTicketValidator validator;
private int port = availablePort();
/**
* The Proxy Granting Ticket. To initialize pgt, authenticate to the CAS Server with the service parameter
* equal to {@link #serviceUrl()}.
*/
String pgt
String pgt;
/**
* Start the CAS Service which will be available at {@link #serviceUrl()}.
@ -55,16 +60,15 @@ class JettyCasService extends Server {
* @param casServerUrl
* @return
*/
def init(String casServerUrl) {
println "Initializing to " + casServerUrl
ProxyGrantingTicketStorage storage = new ProxyGrantingTicketStorageImpl()
validator = new Cas20ProxyTicketValidator(casServerUrl)
validator.setAcceptAnyProxy(true)
validator.setProxyGrantingTicketStorage(storage)
validator.setProxyCallbackUrl(absoluteUrl('callback'))
String password = System.getProperty('javax.net.ssl.trustStorePassword','password')
JettyCasService init(String casServerUrl) {
System.out.println("Initializing to " + casServerUrl);
ProxyGrantingTicketStorage storage = new ProxyGrantingTicketStorageImpl();
this.validator = new Cas20ProxyTicketValidator(casServerUrl);
this.validator.setAcceptAnyProxy(true);
this.validator.setProxyGrantingTicketStorage(storage);
this.validator.setProxyCallbackUrl(absoluteUrl("callback"));
String password = System.getProperty("javax.net.ssl.trustStorePassword", "password");
SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(getTrustStore());
@ -83,30 +87,39 @@ class JettyCasService extends Server {
https_config.addCustomizer(src);
ServerConnector https = new ServerConnector(this,
new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(https_config));
https.setPort(port);
https.setIdleTimeout(500000);
addConnector(https)
addConnector(https);
setHandler(new AbstractHandler() {
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
def st = request.getParameter('ticket')
if(st) {
JettyCasService.this.validator.validate(st, JettyCasService.this.serviceUrl())
String st = request.getParameter("ticket");
if (StringUtils.hasText(st)) {
try {
JettyCasService.this.validator.validate(st, JettyCasService.this.serviceUrl());
} catch (TicketValidationException e) {
throw new IllegalArgumentException(e);
}
}
def pgt = request.getParameter('pgtId')
if(pgt) {
JettyCasService.this.pgt = pgt
String pgt = request.getParameter("pgtId");
if (StringUtils.hasText(pgt)) {
JettyCasService.this.pgt = pgt;
}
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
}
})
start()
this
});
try {
start();
} catch (Exception e) {
throw new IllegalStateException(e);
}
return this;
}
/**
@ -114,7 +127,7 @@ class JettyCasService extends Server {
* @return
*/
String serviceUrl() {
absoluteUrl('service')
return absoluteUrl("service");
}
/**
@ -123,24 +136,27 @@ class JettyCasService extends Server {
* @return
*/
private String absoluteUrl(String relativeUrl) {
"https://localhost:${port}/${relativeUrl}"
return "https://localhost:" + port + "/" + relativeUrl;
}
private static String getTrustStore() {
String trustStoreLocation = System.getProperty('javax.net.ssl.trustStore')
if(trustStoreLocation == null || !new File(trustStoreLocation).isFile()) {
throw new IllegalStateException('Could not find the trust store at path "'+trustStoreLocation+'". Specify the location using the javax.net.ssl.trustStore system property.')
String trustStoreLocation = System.getProperty("javax.net.ssl.trustStore");
if (trustStoreLocation == null || !new File(trustStoreLocation).isFile()) {
throw new IllegalStateException("Could not find the trust store at path \"" + trustStoreLocation +
"\". Specify the location using the javax.net.ssl.trustStore system property.");
}
trustStoreLocation
return trustStoreLocation;
}
/**
* Obtains a random available port (i.e. one that is not in use)
* @return
*/
private static int availablePort() {
ServerSocket server = new ServerSocket(0)
int port = server.localPort
server.close()
port
try (ServerSocket server = new ServerSocket(0)) {
return server.getLocalPort();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}

View File

@ -15,13 +15,38 @@
*/
package org.springframework.security.samples.cas.pages;
import geb.*
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Represents the access denied page
*
* @author Rob Winch
* @author Josh Cummings
*/
class AccessDeniedPage extends Page {
static at = { $("*",text: iContains(~/.*?403.*/)) }
public class AccessDeniedPage {
private final Content content;
public AccessDeniedPage(WebDriver driver) {
this.content = PageFactory.initElements(driver, Content.class);
}
public AccessDeniedPage assertAt() {
assertThat(this.content.header()).contains("403 - Access Denied");
return this;
}
public static class Content {
@FindBy(tagName="h1")
WebElement header;
public String header() {
return this.header.getText();
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Represents the extremely secure page of the CAS Sample application.
*
* @author Rob Winch
* @author Josh Cummings
*/
public class ExtremelySecurePage extends Page<ExtremelySecurePage> {
private final Content content;
public ExtremelySecurePage(WebDriver driver, String baseUrl) {
super(driver, baseUrl + "/secure/extreme");
this.content = PageFactory.initElements(driver, Content.class);
}
@Override
public ExtremelySecurePage assertAt() {
assertThat(this.content.getText()).isEqualTo("VERY Secure Page");
return this;
}
public static class Content {
@FindBy(tagName="h1")
WebElement header;
public String getText() {
return this.header.getText();
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Represents the Home page of the CAS sample application
*
* @author Rob Winch
* @author Josh Cummings
*/
public class HomePage extends Page<HomePage> {
private final Content content;
public HomePage(WebDriver driver, String baseUrl) {
super(driver, baseUrl);
this.content = PageFactory.initElements(driver, Content.class);
}
@Override
public HomePage assertAt() {
assertThat(this.content.header()).isEqualTo("Home Page");
return this;
}
public static class Content {
@FindBy(tagName="h1")
WebElement header;
public String header() {
return this.header.getText();
}
}
}

View File

@ -15,8 +15,7 @@
*/
package org.springframework.security.samples.cas.pages;
import geb.*
import org.openqa.selenium.WebDriver;
/**
* This represents the local logout page. This page is where the user is logged out of the CAS Sample application, but
@ -25,11 +24,10 @@ import geb.*
* single logout on the other services.
*
* @author Rob Winch
* @author Josh Cummings
*/
class LocalLogoutPage extends Page {
static url = 'cas-logout.jsp'
static at = { assert driver.currentUrl.endsWith(url); true }
static content = {
casServerLogout { $('a',text: 'Logout of CAS') }
public class LocalLogoutPage extends Page<LocalLogoutPage> {
public LocalLogoutPage(WebDriver driver, String baseUrl) {
super(driver, baseUrl + "/cas-logout.jsp");
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
/**
* The CAS login page.
*
* @author Rob Winch
* @author Josh Cummings
*/
public class LoginPage extends Page<LoginPage> {
private final Content content;
public LoginPage(WebDriver driver, String baseUrl) {
super(driver, baseUrl + "/login");
this.content = PageFactory.initElements(driver, Content.class);
}
public void login(String user) {
login(user, user);
}
public void login(String user, String password) {
this.content.username(user).password(password).submit();
}
public static class Content {
private WebElement username;
private WebElement password;
@FindBy(css = "input[type=submit]")
private WebElement submit;
public Content username(String username) {
this.username.sendKeys(username);
return this;
}
public Content password(String password) {
this.password.sendKeys(password);
return this;
}
public void submit() {
this.submit.click();
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import java.util.function.Function;
import org.openqa.selenium.WebDriver;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Josh Cummings
*/
public abstract class Page<T extends Page<T>> {
private final WebDriver driver;
private final String url;
protected Page(WebDriver driver, String url) {
this.driver = driver;
this.url = url;
}
public T assertAt() {
assertThat(this.driver.getCurrentUrl()).startsWith(this.url);
return (T) this;
}
public T to() {
this.driver.get(this.url);
return (T) this;
}
public T to(Function<String, String> urlPostProcessor) {
this.driver.get(urlPostProcessor.apply(this.url));
return (T) this;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Represents the proxy ticket sample page within the CAS Sample application.
*
* @author Rob Winch
* @author Josh Cummings
*/
public class ProxyTicketSamplePage extends Page<ProxyTicketSamplePage> {
private final Content content;
public ProxyTicketSamplePage(WebDriver driver, String baseUrl) {
super(driver, baseUrl + "/secure/ptSample");
this.content = PageFactory.initElements(driver, Content.class);
}
@Override
public ProxyTicketSamplePage assertAt() {
assertThat(this.content.getText()).isEqualTo("Secure Page using a Proxy Ticket");
return this;
}
public static class Content {
@FindBy(tagName="h1")
WebElement header;
public String getText() {
return this.header.getText();
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Represents the secure page within the CAS Sample application.
*
* @author Rob Winch
* @author Josh Cummings
*/
public class SecurePage extends Page<SecurePage> {
private final Content content;
public SecurePage(WebDriver driver, String baseUrl) {
super(driver, baseUrl + "/secure");
this.content = PageFactory.initElements(driver, Content.class);
}
@Override
public SecurePage assertAt() {
assertThat(this.content.header()).isEqualTo("Secure Page");
return this;
}
public SecurePage logout() {
this.content.logout();
return this;
}
public static class Content {
@FindBy(tagName="h1")
WebElement header;
WebElement logout;
public String header() {
return this.header.getText();
}
public void logout() {
this.logout.click();
}
}
// had nav
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.security.samples.cas.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Represents the unauthorized page
*
* @author Josh Cummings
*/
public class UnauthorizedPage {
private final Content content;
public UnauthorizedPage(WebDriver driver) {
this.content = PageFactory.initElements(driver, Content.class);
}
public UnauthorizedPage assertAt() {
assertThat(this.content.header()).contains("401 - Unauthorized");
return this;
}
public static class Content {
@FindBy(tagName="h1")
WebElement header;
public String header() {
return this.header.getText();
}
}
}

View File

@ -0,0 +1,8 @@
<html>
<head>
<title>401 - Unauthorized</title>
</head>
<body>
<h1>401 - Unauthorized</h1>
</body>
</html>

View File

@ -78,6 +78,10 @@
<servlet-name>ptSampleServlet</servlet-name>
<url-pattern>/secure/ptSample</url-pattern>
</servlet-mapping>
<error-page>
<error-code>401</error-code>
<location>/401.jsp</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/403.jsp</location>

View File

@ -9,7 +9,7 @@
<p>You have logged out of this application, but may still have an active single-sign on session with CAS.</p>
<p><a href="logout/cas">Logout of CAS</a></p>
<p><a id="casLogout" href="logout/cas">Logout of CAS</a></p>
</body>
</html>
</html>

View File

@ -5,8 +5,8 @@
<p>Your principal object is....: <%= request.getUserPrincipal() %></p>
<p><a href="secure/index.jsp">Secure page</a></p>
<p><a href="secure/ptSample">Proxy Ticket Sample page</a></p>
<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
<p><a id="secure" href="secure/index.jsp">Secure page</a></p>
<p><a id="proxy" href="secure/ptSample">Proxy Ticket Sample page</a></p>
<p><a id="extremelySecure" href="secure/extreme/index.jsp">Extremely secure page</a></p>
</body>
</html>
</html>

View File

@ -3,10 +3,10 @@
<h1>VERY Secure Page</h1>
This is a protected page. You can only see me if you are a supervisor.
<p><a href="../../">Home</a>
<p><a href="../../secure/index.jsp">Secure page</a></p>
<p><a href="../../secure/ptSample">Proxy Ticket Sample page</a></p>
<p><a href="../../logout">Logout</a>
<
<p><a id="home" href="../../">Home</a>
<p><a id="secure" href="../../secure/index.jsp">Secure page</a></p>
<p><a id="ptSample" href="../../secure/ptSample">Proxy Ticket Sample page</a></p>
<p><a id="logout" href="../../logout">Logout</a>
</body>
</html>
</html>

View File

@ -8,8 +8,8 @@ or if you've authenticated this session.</p>
<p>You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.</p>
<% } %>
<p><a href="../">Home</a>
<p><a href="ptSample">Proxy Ticket Sample page</a></p>
<p><a href="../logout">Logout</a>
<p><a id="home" href="../">Home</a>
<p><a id="proxy" href="ptSample">Proxy Ticket Sample page</a></p>
<p><a id="logout" href="../logout">Logout</a>
</body>
</html>
</html>