From 761d5af6eca2cfc3aa5cb76dc2f962ade21b1218 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Sun, 3 Apr 2011 22:18:05 -0500 Subject: [PATCH] SEC-965: Added integration tests for CAS Sample Application --- samples/cas/cas.gradle | 73 +++++++++-- .../security/samples/cas/BaseSpec.groovy | 45 +++++++ .../samples/cas/CasSampleProxySpec.groovy | 115 ++++++++++++++++ .../security/samples/cas/CasSampleSpec.groovy | 111 ++++++++++++++++ .../samples/cas/JettyCasService.groovy | 124 ++++++++++++++++++ .../samples/cas/modules/NavModule.groovy | 31 +++++ .../samples/cas/pages/AccessDeniedPage.groovy | 27 ++++ .../cas/pages/ExtremelySecurePage.groovy | 32 +++++ .../samples/cas/pages/HomePage.groovy | 32 +++++ .../samples/cas/pages/LocalLogoutPage.groovy | 35 +++++ .../samples/cas/pages/LoginPage.groovy | 46 +++++++ .../samples/cas/pages/SecurePage.groovy | 33 +++++ 12 files changed, 693 insertions(+), 11 deletions(-) create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/BaseSpec.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleProxySpec.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleSpec.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/modules/NavModule.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/AccessDeniedPage.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ExtremelySecurePage.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/HomePage.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LocalLogoutPage.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LoginPage.groovy create mode 100644 samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/SecurePage.groovy diff --git a/samples/cas/cas.gradle b/samples/cas/cas.gradle index b0f171689a..230f2221b5 100644 --- a/samples/cas/cas.gradle +++ b/samples/cas/cas.gradle @@ -2,22 +2,35 @@ apply plugin: 'war' apply plugin: 'jetty' +apply plugin: 'groovy' -def excludeModules = ['spring-security-acl', 'jsr250-api', 'ehcache', 'spring-jdbc', 'spring-tx'] +def excludeModules = ['spring-security-acl', 'jsr250-api', 'spring-jdbc', 'spring-tx'] +def jettyVersion = '7.1.6.v20100715' +def keystore = "$rootDir/samples/certificates/server.jks" +def password = 'password' configurations { casServer -} - -configurations { excludeModules.each {name -> runtime.exclude module: name } runtime.exclude group: 'org.aspectj' + + integrationTestCompile.extendsFrom groovy +} + +sourceSets.integrationTest { + groovy.srcDir file('src/integration-test/groovy') +} + +eclipseClasspath { + plusConfigurations += configurations.integrationTestRuntime } dependencies { + groovy 'org.codehaus.groovy:groovy:1.7.7' + casServer "org.jasig.cas:cas-server-webapp:3.4.3.1@war" runtime project(':spring-security-web'), @@ -25,11 +38,16 @@ dependencies { project(':spring-security-config'), "org.slf4j:jcl-over-slf4j:$slf4jVersion", "ch.qos.logback:logback-classic:$logbackVersion" + + integrationTestCompile project(':spring-security-cas'), + 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.0a7', + 'org.spockframework:spock-core:0.4-groovy-1.7', + 'org.codehaus.geb:geb-spock:0.5.1', + 'commons-httpclient:commons-httpclient:3.1', + "org.eclipse.jetty:jetty-server:$jettyVersion", + "org.eclipse.jetty:jetty-servlet:$jettyVersion" } - -def keystore = "$rootDir/samples/certificates/server.jks" - [jettyRun, jettyRunWar]*.configure { contextPath = "/cas-sample" def httpConnector = new org.mortbay.jetty.nio.SelectChannelConnector(); @@ -38,33 +56,66 @@ def keystore = "$rootDir/samples/certificates/server.jks" def httpsConnector = new org.mortbay.jetty.security.SslSocketConnector(); httpsConnector.port = 8443 httpsConnector.keystore = httpsConnector.truststore = keystore - httpsConnector.keyPassword = httpsConnector.trustPassword = 'password' + httpsConnector.keyPassword = httpsConnector.trustPassword = password connectors = [httpConnector, httpsConnector] + doFirst() { + System.setProperty('cas.server.host', casServer.httpsHost) + System.setProperty('cas.service.host', jettyRunWar.httpsHost) + } } - task casServer (type: org.gradle.api.plugins.jetty.JettyRunWar) { contextPath = "/cas" connectors = [new org.mortbay.jetty.security.SslSocketConnector()] connectors[0].port = 9443 connectors[0].keystore = connectors[0].truststore = keystore - connectors[0].keyPassword = connectors[0].trustPassword = 'password' + connectors[0].keyPassword = connectors[0].trustPassword = password connectors[0].wantClientAuth = true connectors[0].needClientAuth = false webApp = configurations.casServer.resolve().toArray()[0] doFirst() { System.setProperty('javax.net.ssl.trustStore', keystore) - System.setProperty('javax.net.ssl.trustStorePassword', 'password') + System.setProperty('javax.net.ssl.trustStorePassword', password) } } task cas (dependsOn: [jettyRunWar, casServer]) { } +integrationTest.dependsOn cas +integrationTest.doFirst { + systemProperties['cas.server.host'] = casServer.httpsHost + systemProperties['cas.service.host'] = jettyRunWar.httpsHost + systemProperties['jar.path'] = jar.archivePath + systemProperties['javax.net.ssl.trustStore'] = keystore + systemProperties['javax.net.ssl.trustStorePassword'] = password +} + gradle.taskGraph.whenReady {graph -> if (graph.hasTask(cas)) { jettyRunWar.dependsOn(casServer) casServer.daemon = true } + if(graph.hasTask(integrationTest)) { + jettyRunWar.daemon = true + jettyRunWar.httpConnector.port = availablePort() + jettyRunWar.httpsConnector.port = jettyRunWar.httpConnector.confidentialPort = availablePort() + casServer.httpsConnector.port = availablePort() + } } +[casServer,jettyRunWar]*.metaClass*.getHttpsConnector {-> + delegate.connectors.find { it instanceof org.mortbay.jetty.security.SslSocketConnector } +} +[casServer,jettyRunWar]*.metaClass*.getHttpsHost {-> + "localhost:"+delegate.httpsConnector.port +} +jettyRunWar.metaClass.getHttpConnector {-> + delegate.connectors.find { it instanceof org.mortbay.jetty.nio.SelectChannelConnector } +} +def availablePort() { + ServerSocket server = new ServerSocket(0) + int port = server.localPort + server.close() + port +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/BaseSpec.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/BaseSpec.groovy new file mode 100644 index 0000000000..e96229f3ec --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/BaseSpec.groovy @@ -0,0 +1,45 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.cas; + +import java.io.File; + +import geb.spock.* + + +/** + * Base test for Geb testing. + * + * @author Rob Winch + */ +class BaseSpec extends GebReportingSpec { + + /** + * All relative urls will be interpreted against this. The host can change based upon a system property. This + * allows for the port to be randomly selected from available ports for CI. + */ + String getBaseUrl() { + def host = System.getProperty('cas.service.host', 'localhost:8443') + "https://${host}/cas-sample/" + } + + /** + * Write out responses and screenshots here. + */ + File getReportDir() { + new File('build/geb-reports') + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleProxySpec.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleProxySpec.groovy new file mode 100644 index 0000000000..ec92ec3ac4 --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleProxySpec.groovy @@ -0,0 +1,115 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.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 CasSampleProxySpec extends BaseSpec { + 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('

Secure Page

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

VERY Secure Page

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

VERY Secure Page

') + } + + 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 + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleSpec.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleSpec.groovy new file mode 100644 index 0000000000..8c426b330b --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleSpec.groovy @@ -0,0 +1,111 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.cas + +import geb.spock.* + +import org.junit.runner.RunWith; +import org.spockframework.runtime.Sputnik; +import org.springframework.security.samples.cas.pages.* + +import spock.lang.Stepwise; + +/** + * Tests the CAS sample application using service tickets. + * + * @author Rob Winch + */ +@Stepwise +class CasSampleSpec extends BaseSpec { + + 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' + to ExtremelySecurePage + then: 'The login page is displayed' + at LoginPage + } + + def 'authenticate attempt with invaid ticket fails'() { + when: 'present invalid ticket' + go "j_spring_cas_security_check?ticket=invalid" + then: 'the login failed page is displayed' + println driver.pageSource + $("h2").text() == 'Login to CAS failed!' + } + + def 'access secure page with unauthenticated user requires login'() { + when: 'Unauthenticated user accesses the secure page' + to 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 extremely secure page with ROLE_USER is denied'() { + when: 'User with ROLE_USER accesses extremely secure page' + to 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() + to 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() + to ExtremelySecurePage + then: 'login page is displayed' + at LoginPage + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy new file mode 100644 index 0000000000..438ac0d2c0 --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy @@ -0,0 +1,124 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.cas; + +import java.io.IOException + +import javax.servlet.ServletException +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +import org.apache.commons.httpclient.HttpClient +import org.apache.commons.httpclient.methods.GetMethod; +import org.eclipse.jetty.server.Request +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.server.handler.AbstractHandler +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl; +import org.jasig.cas.client.validation.Assertion +import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; + +/** + * 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() + + /** + * The Proxy Granting Ticket. To initialize pgt, authenticate to the CAS Server with the service parameter + * equal to {@link #serviceUrl()}. + */ + String pgt + + /** + * Start the CAS Service which will be available at {@link #serviceUrl()}. + * + * @param casServerUrl + * @return + */ + def init(String 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') + SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector() + ssl_connector.setPort(port) + ssl_connector.setKeystore(getTrustStore()) + ssl_connector.setPassword(password) + ssl_connector.setKeyPassword(password) + addConnector(ssl_connector) + 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()) + } + def pgt = request.getParameter('pgtId') + if(pgt) { + JettyCasService.this.pgt = pgt + } + response.setStatus(HttpServletResponse.SC_OK); + baseRequest.setHandled(true); + } + }) + start() + this + } + + /** + * Returns the absolute URL that this CAS service is available at. + * @return + */ + String serviceUrl() { + absoluteUrl('service') + } + + /** + * Given a relative url, will provide an absolute url for this CAS Service. + * @param relativeUrl the relative url (i.e. service, callback, etc) + * @return + */ + private String absoluteUrl(String relativeUrl) { + "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.') + } + 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 + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/modules/NavModule.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/modules/NavModule.groovy new file mode 100644 index 0000000000..d6f8ecc5c9 --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/modules/NavModule.groovy @@ -0,0 +1,31 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.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") } + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/AccessDeniedPage.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/AccessDeniedPage.groovy new file mode 100644 index 0000000000..29d5d31be7 --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/AccessDeniedPage.groovy @@ -0,0 +1,27 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.cas.pages; + +import geb.* + +/** + * Represents the access denied page + * + * @author Rob Winch + */ +class AccessDeniedPage extends Page { + static at = { $("*",text: iContains(~/.*?403.*/)) } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ExtremelySecurePage.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ExtremelySecurePage.groovy new file mode 100644 index 0000000000..b4e6ff11a7 --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/ExtremelySecurePage.groovy @@ -0,0 +1,32 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.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 } + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/HomePage.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/HomePage.groovy new file mode 100644 index 0000000000..65020581a1 --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/HomePage.groovy @@ -0,0 +1,32 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.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') } + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LocalLogoutPage.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LocalLogoutPage.groovy new file mode 100644 index 0000000000..dacae6327f --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LocalLogoutPage.groovy @@ -0,0 +1,35 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.cas.pages; + +import geb.* + + +/** + * This represents the local logout page. This page is where the user is logged out of the CAS Sample application, but + * since the user is still logged into the CAS Server accessing a protected page within the CAS Sample application would result + * in SSO occurring again. To fully logout, the user should click the cas server logout url which logs out of the cas server and performs + * single logout on the other services. + * + * @author Rob Winch + */ +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') } + } +} diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LoginPage.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LoginPage.groovy new file mode 100644 index 0000000000..b20679db8a --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/LoginPage.groovy @@ -0,0 +1,46 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.cas.pages; + +import geb.* + +/** + * The CAS login page. + * + * @author Rob Winch + */ +class LoginPage extends Page { + static url = loginUrl() + static at = { driver.currentUrl == 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 an randomly available port for CI. + * @return + */ + private static String loginUrl() { + def host = System.getProperty('cas.server.host', 'localhost:9443') + "https://${host}/cas/login" + } +} \ No newline at end of file diff --git a/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/SecurePage.groovy b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/SecurePage.groovy new file mode 100644 index 0000000000..3231c01eab --- /dev/null +++ b/samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/pages/SecurePage.groovy @@ -0,0 +1,33 @@ +/* + * Copyright 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.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 } + } +} \ No newline at end of file