Explorar o código

cassample groovy->java

Issue gh-4939
Josh Cummings %!s(int64=5) %!d(string=hai) anos
pai
achega
7c4d56319f
Modificáronse 28 ficheiros con 823 adicións e 587 borrados
  1. 0 11
      samples/xml/cas/cassample/spring-security-samples-xml-cassample.gradle
  2. 0 36
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/AbstractCasTests.groovy
  3. 0 122
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleProxyTests.groovy
  4. 0 135
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleTests.groovy
  5. 0 31
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/modules/NavModule.groovy
  6. 0 32
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ExtremelySecurePage.groovy
  7. 0 32
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/HomePage.groovy
  8. 0 46
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LoginPage.groovy
  9. 0 33
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ProxyTicketSamplePage.groovy
  10. 0 33
      samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/SecurePage.groovy
  11. 147 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleProxyTests.java
  12. 157 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleTests.java
  13. 66 50
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/JettyCasService.java
  14. 28 3
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/AccessDeniedPage.java
  15. 53 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ExtremelySecurePage.java
  16. 53 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/HomePage.java
  17. 5 7
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LocalLogoutPage.java
  18. 65 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LoginPage.java
  19. 51 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/Page.java
  20. 53 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ProxyTicketSamplePage.java
  21. 66 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/SecurePage.java
  22. 51 0
      samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/UnauthorizedPage.java
  23. 8 0
      samples/xml/cas/cassample/src/main/webapp/401.jsp
  24. 4 0
      samples/xml/cas/cassample/src/main/webapp/WEB-INF/web.xml
  25. 2 2
      samples/xml/cas/cassample/src/main/webapp/cas-logout.jsp
  26. 4 4
      samples/xml/cas/cassample/src/main/webapp/index.jsp
  27. 6 6
      samples/xml/cas/cassample/src/main/webapp/secure/extreme/index.jsp
  28. 4 4
      samples/xml/cas/cassample/src/main/webapp/secure/index.jsp

+ 0 - 11
samples/xml/cas/cassample/spring-security-samples-xml-cassample.gradle

@@ -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

+ 0 - 36
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/AbstractCasTests.groovy

@@ -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()
-	}
-}

+ 0 - 122
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleProxyTests.groovy

@@ -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
-	}
-}

+ 0 - 135
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleTests.groovy

@@ -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
-	}
-}

+ 0 - 31
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/modules/NavModule.groovy

@@ -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") }
-	}
-}

+ 0 - 32
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ExtremelySecurePage.groovy

@@ -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 }
-	}
-}

+ 0 - 32
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/HomePage.groovy

@@ -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') }
-	}
-}

+ 0 - 46
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LoginPage.groovy

@@ -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"
-	}
-}

+ 0 - 33
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ProxyTicketSamplePage.groovy

@@ -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 }
-	}
-}

+ 0 - 33
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/SecurePage.groovy

@@ -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 }
-	}
-}

+ 147 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleProxyTests.java

@@ -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);
+	}
+}

+ 157 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleTests.java

@@ -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();
+	}
+}

+ 66 - 50
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy → samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/JettyCasService.java

@@ -13,41 +13,46 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-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
+package org.springframework.security.samples.cas;
+
+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);
+		}
 	}
 }

+ 28 - 3
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/AccessDeniedPage.groovy → samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/AccessDeniedPage.java

@@ -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();
+		}
+	}
 }

+ 53 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ExtremelySecurePage.java

@@ -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();
+		}
+	}
+}

+ 53 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/HomePage.java

@@ -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();
+		}
+	}
+}

+ 5 - 7
samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LocalLogoutPage.groovy → samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LocalLogoutPage.java

@@ -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");
 	}
 }

+ 65 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LoginPage.java

@@ -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();
+		}
+	}
+}

+ 51 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/Page.java

@@ -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;
+	}
+}

+ 53 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ProxyTicketSamplePage.java

@@ -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();
+		}
+	}
+}

+ 66 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/SecurePage.java

@@ -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
+}

+ 51 - 0
samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/UnauthorizedPage.java

@@ -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();
+		}
+	}
+}

+ 8 - 0
samples/xml/cas/cassample/src/main/webapp/401.jsp

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

+ 4 - 0
samples/xml/cas/cassample/src/main/webapp/WEB-INF/web.xml

@@ -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>

+ 2 - 2
samples/xml/cas/cassample/src/main/webapp/cas-logout.jsp

@@ -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>

+ 4 - 4
samples/xml/cas/cassample/src/main/webapp/index.jsp

@@ -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>

+ 6 - 6
samples/xml/cas/cassample/src/main/webapp/secure/extreme/index.jsp

@@ -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>

+ 4 - 4
samples/xml/cas/cassample/src/main/webapp/secure/index.jsp

@@ -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>