parent
							
								
									cb695ae60f
								
							
						
					
					
						commit
						7c4d56319f
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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() | ||||
| 	} | ||||
| } | ||||
|  | @ -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 | ||||
| 	} | ||||
| } | ||||
|  | @ -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 | ||||
| 	} | ||||
| } | ||||
|  | @ -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") } | ||||
| 	} | ||||
| } | ||||
|  | @ -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 } | ||||
| 	} | ||||
| } | ||||
|  | @ -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') } | ||||
| 	} | ||||
| } | ||||
|  | @ -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" | ||||
| 	} | ||||
| } | ||||
|  | @ -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 } | ||||
| 	} | ||||
| } | ||||
|  | @ -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 } | ||||
| 	} | ||||
| } | ||||
|  | @ -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); | ||||
| 	} | ||||
| } | ||||
|  | @ -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(); | ||||
| 	} | ||||
| } | ||||
|  | @ -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()); | ||||
|  | @ -88,25 +92,34 @@ class JettyCasService extends Server { | |||
| 		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') | ||||
| 		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.') | ||||
| 			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); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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"); | ||||
| 	} | ||||
| } | ||||
|  | @ -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(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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; | ||||
| 	} | ||||
| } | ||||
|  | @ -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(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,8 @@ | |||
| <html> | ||||
| <head> | ||||
| <title>401 - Unauthorized</title> | ||||
| </head> | ||||
| <body> | ||||
| <h1>401 - Unauthorized</h1> | ||||
| </body> | ||||
| </html> | ||||
|  | @ -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> | ||||
|  |  | |||
|  | @ -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> | ||||
|  | @ -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> | ||||
|  | @ -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> | ||||
|  | @ -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> | ||||
		Loading…
	
		Reference in New Issue