Use xml / javaconfig folders for samples
Fixes gh-3752
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-security-core')
|
||||
|
||||
aspectpath project(':spring-security-aspects')
|
||||
|
||||
runtime project(':spring-security-config'),
|
||||
project(':spring-security-aspects')
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-samples-xml-aspectj</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<name>spring-security-samples-xml-aspectj</name>
|
||||
<description>spring-security-samples-xml-aspectj</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
<organization>
|
||||
<name>spring.io</name>
|
||||
<url>http://spring.io/</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>rwinch</id>
|
||||
<name>Rob Winch</name>
|
||||
<email>rwinch@gopivotal.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
|
||||
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-security</url>
|
||||
</scm>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.2.5.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.aspectj;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
|
||||
/**
|
||||
* Service which is secured on the class level
|
||||
*
|
||||
* @author Mike Wiesner
|
||||
* @since 3.0
|
||||
*/
|
||||
@Secured("ROLE_USER")
|
||||
public class SecuredService {
|
||||
|
||||
public void secureMethod() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.aspectj;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
|
||||
/**
|
||||
* Service which is secured on method level
|
||||
*
|
||||
* @author Mike Wiesner
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Service {
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
public void secureMethod() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
public void publicMethod() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:sec="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
|
||||
|
||||
<sec:global-method-security secured-annotations="enabled" mode="aspectj" />
|
||||
<!--
|
||||
<bean id="aspectJSecurityInterceptor"
|
||||
class="org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor">
|
||||
<property name="authenticationManager" ref="authenticationManager" />
|
||||
<property name="accessDecisionManager" ref="accessDecisionManager" />
|
||||
<property name="securityMetadataSource">
|
||||
<bean
|
||||
class="org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="authenticationManager"
|
||||
class="org.springframework.security.authentication.ProviderManager">
|
||||
<property name="providers">
|
||||
<bean
|
||||
class="org.springframework.security.authentication.TestingAuthenticationProvider" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="accessDecisionManager"
|
||||
class="org.springframework.security.access.vote.AffirmativeBased">
|
||||
<property name="decisionVoters">
|
||||
<list>
|
||||
<bean
|
||||
class="org.springframework.security.access.vote.RoleVoter" />
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean
|
||||
class="org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect"
|
||||
factory-method="aspectOf">
|
||||
<property name="securityInterceptor" ref="aspectJSecurityInterceptor" />
|
||||
</bean>
|
||||
-->
|
||||
<bean class="sample.aspectj.Service" />
|
||||
|
||||
<bean class="sample.aspectj.SecuredService" />
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.aspectj;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = "classpath:aspectj-context.xml")
|
||||
public class AspectJInterceptorTests {
|
||||
private Authentication admin = new UsernamePasswordAuthenticationToken("test", "xxx",
|
||||
AuthorityUtils.createAuthorityList("ROLE_ADMIN"));
|
||||
private Authentication user = new UsernamePasswordAuthenticationToken("test", "xxx",
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Autowired
|
||||
private SecuredService securedService;
|
||||
|
||||
@Test
|
||||
public void testPublicMethod() throws Exception {
|
||||
service.publicMethod();
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationCredentialsNotFoundException.class)
|
||||
public void testSecuredMethodNotAuthenticated() throws Exception {
|
||||
service.secureMethod();
|
||||
}
|
||||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
public void testSecuredMethodWrongRole() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(admin);
|
||||
service.secureMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecuredMethodEverythingOk() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(user);
|
||||
service.secureMethod();
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationCredentialsNotFoundException.class)
|
||||
public void testSecuredClassNotAuthenticated() throws Exception {
|
||||
securedService.secureMethod();
|
||||
}
|
||||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
public void testSecuredClassWrongRole() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(admin);
|
||||
securedService.secureMethod();
|
||||
}
|
||||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
public void testSecuredClassWrongRoleOnNewedInstance() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(admin);
|
||||
new SecuredService().secureMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecuredClassEverythingOk() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(user);
|
||||
securedService.secureMethod();
|
||||
new SecuredService().secureMethod();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.security" level="${sec.log.level}:-WARN"/>
|
||||
|
||||
<root level="${root.level}:-WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,12 @@
|
||||
To run a CAS server and client application, just execute the command
|
||||
|
||||
./gradlew cas
|
||||
|
||||
from the project root directory. You should then be able to point your browser at
|
||||
|
||||
https://localhost:8443/cas-sample/
|
||||
|
||||
to view the sample application. On attempting to access a secure page,
|
||||
you'll be redirected to the CAS server where you can log in with one of
|
||||
the usernames from the sample application context (enter the username in the
|
||||
password field too, to authenticate to CAS in testing mode).
|
||||
@@ -0,0 +1,129 @@
|
||||
// CAS sample build file
|
||||
|
||||
apply plugin: 'war'
|
||||
apply plugin: 'jetty'
|
||||
apply plugin: 'groovy'
|
||||
|
||||
def excludeModules = ['spring-security-acl', 'jsr250-api', 'spring-jdbc', 'spring-tx']
|
||||
def jettyVersion = '8.1.9.v20130131'
|
||||
def keystore = "$rootDir/samples/certificates/server.jks"
|
||||
def password = 'password'
|
||||
|
||||
configurations {
|
||||
casServer
|
||||
excludeModules.each {name ->
|
||||
runtime.exclude module: name
|
||||
}
|
||||
|
||||
runtime.exclude group: 'org.aspectj'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
test.resources.exclude 'GebConfig.groovy'
|
||||
integrationTest.groovy.srcDir file('src/integration-test/groovy')
|
||||
}
|
||||
|
||||
eclipse.classpath.plusConfigurations += [configurations.integrationTestRuntime]
|
||||
|
||||
dependencies {
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
compile project(':spring-security-core'),
|
||||
project(':spring-security-cas'),
|
||||
"org.jasig.cas.client:cas-client-core:$casClientVersion"
|
||||
|
||||
runtime project(':spring-security-web'),
|
||||
project(':spring-security-config'),
|
||||
"org.springframework:spring-context-support:$springVersion",
|
||||
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
|
||||
"ch.qos.logback:logback-classic:$logbackVersion",
|
||||
"net.sf.ehcache:ehcache:$ehcacheVersion"
|
||||
|
||||
integrationTestCompile project(':spring-security-cas'),
|
||||
"org.seleniumhq.selenium:selenium-htmlunit-driver:$seleniumVersion",
|
||||
"org.gebish:geb-spock:$gebVersion",
|
||||
'commons-httpclient:commons-httpclient:3.1',
|
||||
"org.eclipse.jetty:jetty-server:$jettyVersion",
|
||||
"org.eclipse.jetty:jetty-servlet:$jettyVersion",
|
||||
"org.codehaus.groovy:groovy:$groovyVersion",
|
||||
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
|
||||
spockDependencies
|
||||
}
|
||||
|
||||
[jettyRun, jettyRunWar]*.configure {
|
||||
contextPath = "/cas-sample"
|
||||
def httpConnector = jettyRunWar.class.classLoader.loadClass('org.mortbay.jetty.nio.SelectChannelConnector').newInstance()
|
||||
httpConnector.port = 8080
|
||||
httpConnector.confidentialPort = 8443
|
||||
def httpsConnector = jettyRunWar.class.classLoader.loadClass('org.mortbay.jetty.security.SslSocketConnector').newInstance()
|
||||
httpsConnector.port = 8443
|
||||
httpsConnector.keystore = httpsConnector.truststore = keystore
|
||||
httpsConnector.keyPassword = httpsConnector.trustPassword = password
|
||||
|
||||
connectors = [httpConnector, httpsConnector]
|
||||
doFirst() {
|
||||
System.setProperty('cas.server.host', casServer().httpsHost)
|
||||
System.setProperty('cas.service.host', jettyRunWar.httpsHost)
|
||||
}
|
||||
}
|
||||
|
||||
task cas (dependsOn: [jettyRunWar]) {
|
||||
jettyRunWar.dependsOn(':spring-security-samples-xml-casserver:casServer')
|
||||
}
|
||||
|
||||
task casServer(dependsOn: ':spring-security-samples-xml-casserver:casServer') {
|
||||
}
|
||||
|
||||
integrationTest.dependsOn cas
|
||||
integrationTest.doFirst {
|
||||
def casServiceHost = jettyRunWar.httpsHost
|
||||
systemProperties['cas.server.host'] = casServer().httpsHost
|
||||
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
|
||||
}
|
||||
|
||||
gradle.taskGraph.whenReady {graph ->
|
||||
def casServer = casServer()
|
||||
[casServer,jettyRunWar]*.metaClass*.getHttpsConnector {->
|
||||
def sslSocketConnClass = jettyRunWar.class.classLoader.loadClass('org.mortbay.jetty.security.SslSocketConnector')
|
||||
delegate.connectors.find { it in sslSocketConnClass }
|
||||
}
|
||||
[casServer,jettyRunWar]*.metaClass*.getHttpsHost {->
|
||||
"localhost:"+delegate.httpsConnector.port
|
||||
}
|
||||
jettyRunWar.metaClass.getHttpConnector {->
|
||||
def channelConnClass = jettyRunWar.class.classLoader.loadClass('org.mortbay.jetty.nio.SelectChannelConnector')
|
||||
delegate.connectors.find { it in channelConnClass }
|
||||
}
|
||||
if (graph.hasTask(cas)) {
|
||||
casServer.daemon = true
|
||||
}
|
||||
if(graph.hasTask(integrationTest)) {
|
||||
tasks.getByPath(':spring-security-samples-xml-casserver:casServerOverlay').logLevel = 'ERROR'
|
||||
jettyRunWar {
|
||||
additionalRuntimeJars += file("src/integration-test/resources")
|
||||
daemon = true
|
||||
}
|
||||
|
||||
[jettyRunWar.httpConnector,jettyRunWar.httpsConnector,casServer.httpsConnector]*.metaClass*.reservePort { taskToCloseSocket ->
|
||||
def serverSocket = new ServerSocket(0)
|
||||
delegate.metaClass.serverSocket = serverSocket
|
||||
delegate.port = serverSocket.localPort
|
||||
taskToCloseSocket.doFirst {
|
||||
serverSocket.close()
|
||||
}
|
||||
}
|
||||
|
||||
[jettyRunWar.httpConnector,jettyRunWar.httpsConnector]*.reservePort(jettyRunWar)
|
||||
jettyRunWar.httpConnector.confidentialPort = jettyRunWar.httpsConnector.port
|
||||
casServer.httpsConnector.reservePort(casServer)
|
||||
}
|
||||
}
|
||||
|
||||
def casServer() {
|
||||
tasks.getByPath(':spring-security-samples-xml-casserver:casServer')
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-samples-xml-cassample</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<name>spring-security-samples-xml-cassample</name>
|
||||
<description>spring-security-samples-xml-cassample</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
<organization>
|
||||
<name>spring.io</name>
|
||||
<url>http://spring.io/</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>rwinch</id>
|
||||
<name>Rob Winch</name>
|
||||
<email>rwinch@gopivotal.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
|
||||
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-security</url>
|
||||
</scm>
|
||||
<properties>
|
||||
<m2eclipse.wtp.contextRoot>/spring-security-samples-xml-cassample</m2eclipse.wtp.contextRoot>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.2.5.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jasig.cas.client</groupId>
|
||||
<artifactId>cas-client-core</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-cas</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-httpclient</groupId>
|
||||
<artifactId>commons-httpclient</artifactId>
|
||||
<version>3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
<version>2.4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>8.1.9.v20130131</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>8.1.9.v20130131</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.gebish</groupId>
|
||||
<artifactId>geb-spock</artifactId>
|
||||
<version>0.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-htmlunit-driver</artifactId>
|
||||
<version>2.44.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
<version>0.7-groovy-2.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<version>0.7-groovy-2.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 AbstractCasTests extends GebReportingSpec {
|
||||
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 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
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.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
|
||||
}
|
||||
}
|
||||
+124
@@ -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
|
||||
}
|
||||
}
|
||||
+31
@@ -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") }
|
||||
}
|
||||
}
|
||||
+27
@@ -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.*/)) }
|
||||
}
|
||||
+32
@@ -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 }
|
||||
}
|
||||
}
|
||||
+32
@@ -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') }
|
||||
}
|
||||
}
|
||||
+35
@@ -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') }
|
||||
}
|
||||
}
|
||||
+46
@@ -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 = { 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"
|
||||
}
|
||||
}
|
||||
+33
@@ -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 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 }
|
||||
}
|
||||
}
|
||||
+33
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="error">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2011-2016 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.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationToken;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@link ProxyTicketSampleServlet} demonstrates how to obtain a proxy ticket and then use
|
||||
* it to make a remote call. To learn how proxy tickets work, see the <a
|
||||
* href="https://wiki.jasig.org/display/CAS/Proxy+CAS+Walkthrough">Proxy CAS
|
||||
* Walkthrough</a>
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class ProxyTicketSampleServlet extends HttpServlet {
|
||||
/**
|
||||
* This is the URL that will be called and authenticate a proxy ticket.
|
||||
*/
|
||||
private String targetUrl;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
// NOTE: The CasAuthenticationToken can also be obtained using
|
||||
// SecurityContextHolder.getContext().getAuthentication()
|
||||
final CasAuthenticationToken token = (CasAuthenticationToken) request
|
||||
.getUserPrincipal();
|
||||
// proxyTicket could be reused to make calls to to the CAS service even if the
|
||||
// target url differs
|
||||
final String proxyTicket = token.getAssertion().getPrincipal()
|
||||
.getProxyTicketFor(targetUrl);
|
||||
|
||||
// Make a remote call to ourself. This is a bit silly, but it works well to
|
||||
// demonstrate how to use proxy tickets.
|
||||
final String serviceUrl = targetUrl + "?ticket="
|
||||
+ URLEncoder.encode(proxyTicket, "UTF-8");
|
||||
String proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, "UTF-8");
|
||||
|
||||
// modify the response and write it out to inform the user that it was obtained
|
||||
// using a proxy ticket.
|
||||
proxyResponse = proxyResponse.replaceFirst("Secure Page",
|
||||
"Secure Page using a Proxy Ticket");
|
||||
proxyResponse = proxyResponse.replaceFirst("<p>", "<p>This page is rendered by "
|
||||
+ getClass().getSimpleName()
|
||||
+ " by making a remote call to the Secure Page using a proxy ticket ("
|
||||
+ proxyTicket + ") and inserts this message. ");
|
||||
final PrintWriter writer = response.getWriter();
|
||||
writer.write(proxyResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the target URL. It allows for the host to change based upon the
|
||||
* "cas.service.host" system property. If the property is not set, the default is
|
||||
* "localhost:8443".
|
||||
*/
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
super.init();
|
||||
String casServiceHost = System.getProperty("cas.service.host", "localhost:8443");
|
||||
targetUrl = "https://" + casServiceHost + "/cas-sample/secure/";
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -7720161771819727775L;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 - Access Denied</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>403 - Access Denied</h1>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
|
||||
<http entry-point-ref="casEntryPoint">
|
||||
<intercept-url pattern="/" access="permitAll"/>
|
||||
<intercept-url pattern="/index.jsp" access="permitAll"/>
|
||||
<intercept-url pattern="/cas-logout.jsp" access="permitAll"/>
|
||||
<intercept-url pattern="/casfailed.jsp" access="permitAll"/>
|
||||
|
||||
<intercept-url pattern="/secure/extreme/**"
|
||||
access="hasRole('ROLE_SUPERVISOR')" />
|
||||
<intercept-url pattern="/secure/**" access="hasRole('ROLE_USER')" />
|
||||
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
|
||||
<custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
|
||||
<custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
|
||||
<custom-filter ref="casFilter" position="CAS_FILTER" />
|
||||
<logout logout-success-url="/cas-logout.jsp"/>
|
||||
<csrf disabled="true"/>
|
||||
</http>
|
||||
|
||||
<authentication-manager alias="authManager">
|
||||
<authentication-provider ref="casAuthProvider" />
|
||||
</authentication-manager>
|
||||
|
||||
<user-service id="userService">
|
||||
<user name="rod" password="rod" authorities="ROLE_SUPERVISOR,ROLE_USER" />
|
||||
<user name="dianne" password="dianne" authorities="ROLE_USER" />
|
||||
<user name="scott" password="scott" authorities="ROLE_USER" />
|
||||
</user-service>
|
||||
|
||||
<!-- This filter handles a Single Logout Request from the CAS Server -->
|
||||
<b:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
|
||||
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
|
||||
<b:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter"
|
||||
p:filterProcessesUrl="/logout/cas">
|
||||
<b:constructor-arg value="https://${cas.server.host}/cas/logout"/>
|
||||
<b:constructor-arg>
|
||||
<b:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
|
||||
</b:constructor-arg>
|
||||
</b:bean>
|
||||
|
||||
<b:bean id="serviceProperties"
|
||||
class="org.springframework.security.cas.ServiceProperties"
|
||||
p:service="https://${cas.service.host}/cas-sample/login/cas"
|
||||
p:authenticateAllArtifacts="true"/>
|
||||
<b:bean id="casEntryPoint"
|
||||
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
|
||||
p:serviceProperties-ref="serviceProperties" p:loginUrl="https://${cas.server.host}/cas/login" />
|
||||
<b:bean id="casFilter"
|
||||
class="org.springframework.security.cas.web.CasAuthenticationFilter"
|
||||
p:authenticationManager-ref="authManager"
|
||||
p:serviceProperties-ref="serviceProperties"
|
||||
p:proxyGrantingTicketStorage-ref="pgtStorage"
|
||||
p:proxyReceptorUrl="/login/cas/proxyreceptor">
|
||||
<b:property name="authenticationDetailsSource">
|
||||
<b:bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource">
|
||||
<b:constructor-arg ref="serviceProperties"/>
|
||||
</b:bean>
|
||||
</b:property>
|
||||
<b:property name="authenticationFailureHandler">
|
||||
<b:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
|
||||
p:defaultFailureUrl="/casfailed.jsp"/>
|
||||
</b:property>
|
||||
</b:bean>
|
||||
<!--
|
||||
NOTE: In a real application you should not use an in memory implementation. You will also want
|
||||
to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup()
|
||||
-->
|
||||
<b:bean id="pgtStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>
|
||||
<b:bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider"
|
||||
p:serviceProperties-ref="serviceProperties"
|
||||
p:key="casAuthProviderKey">
|
||||
<b:property name="authenticationUserDetailsService">
|
||||
<b:bean
|
||||
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
|
||||
<b:constructor-arg ref="userService" />
|
||||
</b:bean>
|
||||
</b:property>
|
||||
<b:property name="ticketValidator">
|
||||
<b:bean
|
||||
class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator"
|
||||
p:acceptAnyProxy="true"
|
||||
p:proxyCallbackUrl="https://${cas.service.host}/cas-sample/login/cas/proxyreceptor"
|
||||
p:proxyGrantingTicketStorage-ref="pgtStorage">
|
||||
<b:constructor-arg value="https://${cas.server.host}/cas" />
|
||||
</b:bean>
|
||||
</b:property>
|
||||
<b:property name="statelessTicketCache">
|
||||
<b:bean class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
|
||||
<b:property name="cache">
|
||||
<b:bean id="ehcache" class="net.sf.ehcache.Cache"
|
||||
init-method="initialise"
|
||||
destroy-method="dispose">
|
||||
<b:constructor-arg value="casTickets"/>
|
||||
<b:constructor-arg value="50"/>
|
||||
<b:constructor-arg value="true"/>
|
||||
<b:constructor-arg value="false"/>
|
||||
<b:constructor-arg value="3600"/>
|
||||
<b:constructor-arg value="900"/>
|
||||
<b:property name="cacheManager">
|
||||
<b:bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
|
||||
</b:property>
|
||||
</b:bean>
|
||||
</b:property>
|
||||
</b:bean>
|
||||
</b:property>
|
||||
</b:bean>
|
||||
|
||||
<!-- Configuration for the environment can be overriden by system properties -->
|
||||
<context:property-placeholder system-properties-mode="OVERRIDE" properties-ref="environment"/>
|
||||
<util:properties id="environment">
|
||||
<b:prop key="cas.service.host">localhost:8443</b:prop>
|
||||
<b:prop key="cas.server.host">localhost:9443</b:prop>
|
||||
</util:properties>
|
||||
</b:beans>
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
- Tutorial web application
|
||||
-
|
||||
-->
|
||||
|
||||
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
|
||||
<display-name>Spring Security CAS Demo Application</display-name>
|
||||
|
||||
<!--
|
||||
- Location of the XML file that defines the root application context
|
||||
- Applied by ContextLoaderListener.
|
||||
-->
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>
|
||||
/WEB-INF/applicationContext-security.xml
|
||||
</param-value>
|
||||
</context-param>
|
||||
|
||||
<context-param>
|
||||
<param-name>webAppRootKey</param-name>
|
||||
<param-value>cas.root</param-value>
|
||||
</context-param>
|
||||
|
||||
<!--
|
||||
Include the character encoding Filter as per JASIG recommenation when doing Single Sign Out
|
||||
https://wiki.jasig.org/display/CASC/Configuring+Single+Sign+Out
|
||||
-->
|
||||
<filter>
|
||||
<filter-name>characterEncodingFilter</filter-name>
|
||||
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>encoding</param-name>
|
||||
<param-value>UTF-8</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
<filter>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>characterEncodingFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
<filter-mapping>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!--
|
||||
Included to support Single Logout. Note that the SingleSignOutFilter is included in the
|
||||
springSecurityFilterChain. However, it could also be placed as the first filter-mapping
|
||||
in the web.xml
|
||||
-->
|
||||
<listener>
|
||||
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<!--
|
||||
- Loads the root application context of this web app at startup.
|
||||
- The application context is then available via
|
||||
- WebApplicationContextUtils.getWebApplicationContext(servletContext).
|
||||
-->
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>ptSampleServlet</servlet-name>
|
||||
<servlet-class>org.springframework.security.samples.cas.web.ProxyTicketSampleServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>ptSampleServlet</servlet-name>
|
||||
<url-pattern>/secure/ptSample</url-pattern>
|
||||
</servlet-mapping>
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/403.jsp</location>
|
||||
</error-page>
|
||||
</web-app>
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Single-sign out?</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Do you want to log out of CAS?</h2>
|
||||
|
||||
<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>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
<%@ page import="org.springframework.security.core.AuthenticationException" %>
|
||||
<%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Login to CAS failed!</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Login to CAS failed!</h2>
|
||||
|
||||
<font color="red">
|
||||
Your CAS credentials were rejected.<br/>
|
||||
</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Home Page</h1>
|
||||
<p>Anyone can view this page.</p>
|
||||
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<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>
|
||||
<
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Secure Page</h1>
|
||||
<p>This is a protected page. You can get to me if you've been remembered,
|
||||
or if you've authenticated this session.</p>
|
||||
|
||||
<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %>
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,63 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
apply plugin: 'jetty'
|
||||
|
||||
def keystore = "$rootDir/samples/certificates/server.jks"
|
||||
def password = 'password'
|
||||
|
||||
configurations {
|
||||
casServer
|
||||
}
|
||||
dependencies {
|
||||
casServer "org.jasig.cas:cas-server-webapp:4.0.0@war"
|
||||
}
|
||||
|
||||
task casServerOverlay(type: Sync) {
|
||||
def war = configurations.casServer.resolve().toArray()[0]
|
||||
def warName = war.name.replace('.war','-custom')
|
||||
def overlayDir = file('src/main/webapp')
|
||||
def explodedWar = file("$buildDir/tmp/${warName}")
|
||||
ext.customWar = file("$buildDir/tmp/${warName}.war")
|
||||
ext.tokens = [logLevel: 'INFO']
|
||||
|
||||
inputs.files(war, overlayDir)
|
||||
inputs.property('tokens',{tokens})
|
||||
outputs.files (customWar,explodedWar,file("$buildDir/tmp/expandedArchives"))
|
||||
|
||||
from zipTree(war)
|
||||
from (overlayDir) {
|
||||
filter(ReplaceTokens,tokens: tokens)
|
||||
}
|
||||
into explodedWar
|
||||
|
||||
doLast {
|
||||
if(customWar.exists()) {
|
||||
customWar.delete()
|
||||
}
|
||||
ant.zip(destfile: customWar, baseDir: explodedWar)
|
||||
}
|
||||
}
|
||||
|
||||
casServerOverlay.metaClass.setLogLevel { level ->
|
||||
tokens['logLevel'] = level
|
||||
}
|
||||
|
||||
task casServer (type: org.gradle.api.plugins.jetty.JettyRunWar, dependsOn: 'casServerOverlay') {
|
||||
contextPath = "/cas"
|
||||
connectors = [casServer.class.classLoader.loadClass('org.mortbay.jetty.security.SslSocketConnector').newInstance()]
|
||||
connectors[0].port = 9443
|
||||
connectors[0].keystore = connectors[0].truststore = keystore
|
||||
connectors[0].keyPassword = connectors[0].trustPassword = password
|
||||
connectors[0].wantClientAuth = true
|
||||
connectors[0].needClientAuth = false
|
||||
webApp = casServerOverlay.customWar
|
||||
|
||||
inputs.file casServerOverlay.customWar
|
||||
|
||||
doFirst() {
|
||||
System.setProperty('javax.net.ssl.trustStore', keystore)
|
||||
System.setProperty('javax.net.ssl.trustStorePassword', password)
|
||||
System.setProperty('java.naming.factory.url.pkgs','org.mortbay.naming')
|
||||
System.setProperty('java.naming.factory.initial','org.mortbay.naming.InitialContextFactory')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-samples-xml-casserver</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<name>spring-security-samples-xml-casserver</name>
|
||||
<description>spring-security-samples-xml-casserver</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
<organization>
|
||||
<name>spring.io</name>
|
||||
<url>http://spring.io/</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>rwinch</id>
|
||||
<name>Rob Winch</name>
|
||||
<email>rwinch@gopivotal.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
|
||||
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-security</url>
|
||||
</scm>
|
||||
<properties>
|
||||
<m2eclipse.wtp.contextRoot>/spring-security-samples-xml-casserver</m2eclipse.wtp.contextRoot>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.2.5.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
|
||||
<log4j:configuration debug="false" xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="%d %p [%c] - <%m>%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
<logger name="org.jasig" additivity="true">
|
||||
<level value="@logLevel@" />
|
||||
</logger>
|
||||
<root>
|
||||
<level value="ERROR"/>
|
||||
<appender-ref ref="console"/>
|
||||
</root>
|
||||
</log4j:configuration>
|
||||
@@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to Jasig under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Jasig licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<!--
|
||||
| deployerConfigContext.xml centralizes into one file some of the declarative configuration that
|
||||
| all CAS deployers will need to modify.
|
||||
|
|
||||
| This file declares some of the Spring-managed JavaBeans that make up a CAS deployment.
|
||||
| The beans declared in this file are instantiated at context initialization time by the Spring
|
||||
| ContextLoaderListener declared in web.xml. It finds this file because this
|
||||
| file is among those declared in the context parameter "contextConfigLocation".
|
||||
|
|
||||
| By far the most common change you will need to make in this file is to change the last bean
|
||||
| declaration to replace the default authentication handler with
|
||||
| one implementing your approach for authenticating usernames and passwords.
|
||||
+-->
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:c="http://www.springframework.org/schema/c"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xmlns:sec="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<!--
|
||||
| The authentication manager defines security policy for authentication by specifying at a minimum
|
||||
| the authentication handlers that will be used to authenticate credential. While the AuthenticationManager
|
||||
| interface supports plugging in another implementation, the default PolicyBasedAuthenticationManager should
|
||||
| be sufficient in most cases.
|
||||
+-->
|
||||
<bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
|
||||
<constructor-arg>
|
||||
<map>
|
||||
<!--
|
||||
| IMPORTANT
|
||||
| Every handler requires a unique name.
|
||||
| If more than one instance of the same handler class is configured, you must explicitly
|
||||
| set its name to something other than its default name (typically the simple class name).
|
||||
-->
|
||||
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
|
||||
<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
|
||||
</map>
|
||||
</constructor-arg>
|
||||
|
||||
<!-- Uncomment the metadata populator to allow clearpass to capture and cache the password
|
||||
This switch effectively will turn on clearpass.
|
||||
<property name="authenticationMetaDataPopulators">
|
||||
<util:list>
|
||||
<bean class="org.jasig.cas.extension.clearpass.CacheCredentialsMetaDataPopulator"
|
||||
c:credentialCache-ref="encryptedMap" />
|
||||
</util:list>
|
||||
</property>
|
||||
-->
|
||||
|
||||
<!--
|
||||
| Defines the security policy around authentication. Some alternative policies that ship with CAS:
|
||||
|
|
||||
| * NotPreventedAuthenticationPolicy - all credential must either pass or fail authentication
|
||||
| * AllAuthenticationPolicy - all presented credential must be authenticated successfully
|
||||
| * RequiredHandlerAuthenticationPolicy - specifies a handler that must authenticate its credential to pass
|
||||
-->
|
||||
<property name="authenticationPolicy">
|
||||
<bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Required for proxy ticket mechanism. -->
|
||||
<bean id="proxyAuthenticationHandler"
|
||||
class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
|
||||
p:httpClient-ref="httpClient" />
|
||||
|
||||
<!--
|
||||
| TODO: Replace this component with one suitable for your enviroment.
|
||||
|
|
||||
| This component provides authentication for the kind of credential used in your environment. In most cases
|
||||
| credential is a username/password pair that lives in a system of record like an LDAP directory.
|
||||
| The most common authentication handler beans:
|
||||
|
|
||||
| * org.jasig.cas.authentication.LdapAuthenticationHandler
|
||||
| * org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler
|
||||
| * org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler
|
||||
| * org.jasig.cas.support.spnego.authentication.handler.support.JCIFSSpnegoAuthenticationHandler
|
||||
-->
|
||||
<bean id="primaryAuthenticationHandler"
|
||||
class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
|
||||
<property name="users">
|
||||
<map>
|
||||
<entry key="scott" value="scott"/>
|
||||
<entry key="rod" value="rod"/>
|
||||
<entry key="dianne" value="dianne"/>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Required for proxy ticket mechanism -->
|
||||
<bean id="proxyPrincipalResolver"
|
||||
class="org.jasig.cas.authentication.principal.BasicPrincipalResolver" />
|
||||
|
||||
<!--
|
||||
| Resolves a principal from a credential using an attribute repository that is configured to resolve
|
||||
| against a deployer-specific store (e.g. LDAP).
|
||||
-->
|
||||
<bean id="primaryPrincipalResolver"
|
||||
class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver" >
|
||||
<property name="attributeRepository" ref="attributeRepository" />
|
||||
</bean>
|
||||
|
||||
<!--
|
||||
Bean that defines the attributes that a service may return. This example uses the Stub/Mock version. A real implementation
|
||||
may go against a database or LDAP server. The id should remain "attributeRepository" though.
|
||||
+-->
|
||||
<bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao"
|
||||
p:backingMap-ref="attrRepoBackingMap" />
|
||||
|
||||
<util:map id="attrRepoBackingMap">
|
||||
<entry key="uid" value="uid" />
|
||||
<entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
|
||||
<entry key="groupMembership" value="groupMembership" />
|
||||
</util:map>
|
||||
|
||||
<!--
|
||||
Sample, in-memory data store for the ServiceRegistry. A real implementation
|
||||
would probably want to replace this with the JPA-backed ServiceRegistry DAO
|
||||
The name of this bean should remain "serviceRegistryDao".
|
||||
+-->
|
||||
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"
|
||||
p:registeredServices-ref="registeredServicesList" />
|
||||
|
||||
<util:list id="registeredServicesList">
|
||||
<bean class="org.jasig.cas.services.RegexRegisteredService"
|
||||
p:id="0" p:name="HTTP and IMAP" p:description="Allows HTTP(S) and IMAP(S) protocols"
|
||||
p:serviceId="^(https?|imaps?)://.*" p:evaluationOrder="10000001"
|
||||
p:allowedToProxy="true"/>
|
||||
<!--
|
||||
Use the following definition instead of the above to further restrict access
|
||||
to services within your domain (including sub domains).
|
||||
Note that example.com must be replaced with the domain you wish to permit.
|
||||
This example also demonstrates the configuration of an attribute filter
|
||||
that only allows for attributes whose length is 3.
|
||||
-->
|
||||
<!--
|
||||
<bean class="org.jasig.cas.services.RegexRegisteredService">
|
||||
<property name="id" value="1" />
|
||||
<property name="name" value="HTTP and IMAP on example.com" />
|
||||
<property name="description" value="Allows HTTP(S) and IMAP(S) protocols on example.com" />
|
||||
<property name="serviceId" value="^(https?|imaps?)://([A-Za-z0-9_-]+\.)*example\.com/.*" />
|
||||
<property name="evaluationOrder" value="0" />
|
||||
<property name="attributeFilter">
|
||||
<bean class="org.jasig.cas.services.support.RegisteredServiceRegexAttributeFilter" c:regex="^\w{3}$" />
|
||||
</property>
|
||||
</bean>
|
||||
-->
|
||||
</util:list>
|
||||
|
||||
<bean id="auditTrailManager" class="com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager" />
|
||||
|
||||
<bean id="healthCheckMonitor" class="org.jasig.cas.monitor.HealthCheckMonitor" p:monitors-ref="monitorsList" />
|
||||
|
||||
<util:list id="monitorsList">
|
||||
<bean class="org.jasig.cas.monitor.MemoryMonitor" p:freeMemoryWarnThreshold="10" />
|
||||
<!--
|
||||
NOTE
|
||||
The following ticket registries support SessionMonitor:
|
||||
* DefaultTicketRegistry
|
||||
* JpaTicketRegistry
|
||||
Remove this monitor if you use an unsupported registry.
|
||||
-->
|
||||
<bean class="org.jasig.cas.monitor.SessionMonitor"
|
||||
p:ticketRegistry-ref="ticketRegistry"
|
||||
p:serviceTicketCountWarnThreshold="5000"
|
||||
p:sessionCountWarnThreshold="100000" />
|
||||
</util:list>
|
||||
</beans>
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to Jasig under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Jasig licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:c="http://www.springframework.org/schema/c"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
<description>
|
||||
This is the main Spring configuration file with some of the main "core" classes defined. You shouldn't really
|
||||
modify this unless you
|
||||
know what you're doing!
|
||||
</description>
|
||||
|
||||
<!--
|
||||
Including this aspectj-autoproxy element will cause spring to automatically
|
||||
create proxies around any beans defined in this file that match the pointcuts
|
||||
of any aspects defined in this file.
|
||||
-->
|
||||
<aop:aspectj-autoproxy/>
|
||||
|
||||
<!--
|
||||
Declare the TimingAspect that we want to weave into the other beans
|
||||
defined in this config file.
|
||||
-->
|
||||
<bean id="timingAspect" class="org.perf4j.log4j.aop.TimingAspect"/>
|
||||
|
||||
|
||||
<!--
|
||||
Message source for this context, loaded from localized "messages_xx" files.]
|
||||
|
||||
Disable the fallback mechanism to the system/JVM locale. By turning off this behavior, CAS
|
||||
will be able to revert back to the default language bundle that is "messages.properties"
|
||||
and will not rely on the JVM default locale which introduces the side effect of rendering
|
||||
the UI in the JVM locale by default.
|
||||
|
||||
Also, explicitly set the default encoding to be UTF-8 when parsing message bundles.
|
||||
The default, if not set, is none which forces ISO-8859-1 of java.util.ResourceBundle.
|
||||
-->
|
||||
<bean id="messageSource" class="org.jasig.cas.web.view.CasReloadableMessageBundle"
|
||||
p:basenames-ref="basenames" p:fallbackToSystemLocale="false" p:defaultEncoding="UTF-8"
|
||||
p:cacheSeconds="180" p:useCodeAsDefaultMessage="true" />
|
||||
|
||||
<util:list id="basenames">
|
||||
<value>classpath:custom_messages</value>
|
||||
<value>classpath:messages</value>
|
||||
</util:list>
|
||||
|
||||
<bean id="servicesManager" class="org.jasig.cas.services.DefaultServicesManagerImpl"
|
||||
c:serviceRegistryDao-ref="serviceRegistryDao" />
|
||||
|
||||
<!--
|
||||
Job to periodically reload services from service registry.
|
||||
This job is needed for a clustered CAS environment since service changes
|
||||
in one CAS node are not known to the other until a reload.
|
||||
-->
|
||||
<bean id="serviceRegistryReloaderJobDetail"
|
||||
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
|
||||
p:targetObject-ref="servicesManager"
|
||||
p:targetMethod="reload"/>
|
||||
|
||||
<bean id="periodicServiceRegistryReloaderTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"
|
||||
p:jobDetail-ref="serviceRegistryReloaderJobDetail"
|
||||
p:startDelay="${service.registry.quartz.reloader.startDelay:120000}"
|
||||
p:repeatInterval="${service.registry.quartz.reloader.repeatInterval:120000}"/>
|
||||
|
||||
|
||||
<bean id="noRedirectHttpClient" class="org.jasig.cas.util.SimpleHttpClient" parent="httpClient"
|
||||
p:followRedirects="false" />
|
||||
|
||||
<bean id="persistentIdGenerator"
|
||||
class="org.jasig.cas.authentication.principal.ShibbolethCompatiblePersistentIdGenerator"
|
||||
p:salt="casrocks"/>
|
||||
|
||||
<bean id="logoutManager" class="org.jasig.cas.logout.LogoutManagerImpl"
|
||||
c:servicesManager-ref="servicesManager"
|
||||
c:httpClient-ref="noRedirectHttpClient"
|
||||
c:logoutMessageBuilder-ref="logoutBuilder"
|
||||
p:disableSingleSignOut="${slo.callbacks.disabled:false}" />
|
||||
|
||||
<bean id="logoutBuilder" class="org.jasig.cas.logout.SamlCompliantLogoutMessageCreator" />
|
||||
|
||||
<!-- CentralAuthenticationService -->
|
||||
<bean id="centralAuthenticationService" class="org.jasig.cas.CentralAuthenticationServiceImpl">
|
||||
<constructor-arg index="0" ref="ticketRegistry"/>
|
||||
<constructor-arg index="1">
|
||||
<null />
|
||||
</constructor-arg>
|
||||
<constructor-arg index="2" ref="authenticationManager"/>
|
||||
<constructor-arg index="3" ref="ticketGrantingTicketUniqueIdGenerator"/>
|
||||
<constructor-arg index="4" ref="uniqueIdGeneratorsMap"/>
|
||||
<constructor-arg index="5" ref="grantingTicketExpirationPolicy"/>
|
||||
<constructor-arg index="6" ref="serviceTicketExpirationPolicy"/>
|
||||
<constructor-arg index="7" ref="servicesManager"/>
|
||||
<constructor-arg index="8" ref="logoutManager"/>
|
||||
<property name="persistentIdGenerator" ref="persistentIdGenerator"/>
|
||||
</bean>
|
||||
|
||||
<bean id="proxy10Handler" class="org.jasig.cas.ticket.proxy.support.Cas10ProxyHandler"/>
|
||||
|
||||
<bean id="proxy20Handler" class="org.jasig.cas.ticket.proxy.support.Cas20ProxyHandler"
|
||||
p:httpClient-ref="httpClient"
|
||||
p:uniqueTicketIdGenerator-ref="proxy20TicketUniqueIdGenerator"/>
|
||||
|
||||
<!-- ADVISORS -->
|
||||
<bean id="advisorAutoProxyCreator"
|
||||
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
|
||||
|
||||
<bean id="validationAnnotationBeanPostProcessor" class="org.jasig.cas.util.CustomBeanValidationPostProcessor"
|
||||
p:afterInitialization="true" />
|
||||
|
||||
<!-- The scheduler bean wires up any triggers that define scheduled tasks -->
|
||||
<bean id="scheduler" class="org.jasig.cas.util.AutowiringSchedulerFactoryBean"/>
|
||||
|
||||
|
||||
<bean id="httpClient" class="org.jasig.cas.util.SimpleHttpClient"
|
||||
p:readTimeout="5000"
|
||||
p:connectionTimeout="5000">
|
||||
<property name="executorService">
|
||||
<bean class="org.springframework.core.task.support.ExecutorServiceAdapter">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.core.task.SyncTaskExecutor"/>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>cas</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<name>cas</name>
|
||||
<description>cas</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
<organization>
|
||||
<name>spring.io</name>
|
||||
<url>http://spring.io/</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>rwinch</id>
|
||||
<name>Rob Winch</name>
|
||||
<email>rwinch@gopivotal.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
|
||||
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-security</url>
|
||||
</scm>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.2.5.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,8 @@
|
||||
# Properties file with server URL settings for remote access.
|
||||
# Applied by PropertyPlaceholderConfigurer from "clientContext.xml".
|
||||
#
|
||||
|
||||
serverName=localhost
|
||||
httpPort=8080
|
||||
contextPath=/spring-security-sample-contacts-filter
|
||||
rmiPort=1099
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||
|
||||
<!--
|
||||
- Contacts web application
|
||||
- Client application context
|
||||
-->
|
||||
|
||||
<beans>
|
||||
|
||||
<!-- Resolves ${...} placeholders from client.properties -->
|
||||
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="location"><value>client.properties</value></property>
|
||||
</bean>
|
||||
|
||||
<!-- Proxy for the RMI-exported ContactManager -->
|
||||
<!-- COMMENTED OUT BY DEFAULT TO AVOID CONFLICTS WITH APPLICATION SERVERS
|
||||
<bean id="rmiProxy" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
|
||||
<property name="serviceInterface">
|
||||
<value>sample.contact.ContactManager</value>
|
||||
</property>
|
||||
<property name="serviceUrl">
|
||||
<value>rmi://${serverName}:${rmiPort}/contactManager</value>
|
||||
</property>
|
||||
<property name="remoteInvocationFactory">
|
||||
<ref local="remoteInvocationFactory"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="remoteInvocationFactory" class="org.springframework.security.ui.rmi.ContextPropagatingRemoteInvocationFactory"/>
|
||||
-->
|
||||
|
||||
<!-- Proxy for the HTTP-invoker-exported ContactManager -->
|
||||
<!-- Spring's HTTP invoker uses Java serialization via HTTP -->
|
||||
<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
|
||||
<property name="serviceInterface">
|
||||
<value>sample.contact.ContactManager</value>
|
||||
</property>
|
||||
<property name="serviceUrl">
|
||||
<value>http://${serverName}:${httpPort}${contextPath}/remoting/ContactManager-httpinvoker</value>
|
||||
</property>
|
||||
<property name="httpInvokerRequestExecutor">
|
||||
<ref local="httpInvokerRequestExecutor"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Automatically propagates ContextHolder-managed Authentication principal
|
||||
and credentials to a HTTP invoker BASIC authentication header -->
|
||||
<bean id="httpInvokerRequestExecutor" class="org.springframework.security.core.context.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor"/>
|
||||
|
||||
<!-- Proxy for the Hessian-exported ContactManager
|
||||
<bean id="hessianProxy" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
|
||||
<property name="serviceInterface">
|
||||
<value>sample.contact.ContactManager</value>
|
||||
</property>
|
||||
<property name="serviceUrl">
|
||||
<value>http://${serverName}:${httpPort}${contextPath}/remoting/ContactManager-hessian</value>
|
||||
</property>
|
||||
</bean>
|
||||
-->
|
||||
|
||||
<!-- Proxy for the Burlap-exported ContactManager
|
||||
<bean id="burlapProxy" class="org.springframework.remoting.caucho.BurlapProxyFactoryBean">
|
||||
<property name="serviceInterface">
|
||||
<value>sample.contact.ContactManager</value>
|
||||
</property>
|
||||
<property name="serviceUrl">
|
||||
<value>http://${serverName}:${httpPort}${contextPath}/remoting/ContactManager-burlap</value>
|
||||
</property>
|
||||
</bean>
|
||||
-->
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,29 @@
|
||||
// Contacts sample build file
|
||||
|
||||
apply from: WAR_SAMPLE_GRADLE
|
||||
|
||||
dependencies {
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
compile project(':spring-security-core'),
|
||||
project(':spring-security-acl'),
|
||||
"org.springframework:spring-aop:$springVersion",
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-jdbc:$springVersion",
|
||||
"org.springframework:spring-tx:$springVersion",
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.springframework:spring-webmvc:$springVersion"
|
||||
|
||||
runtime project(':spring-security-web'),
|
||||
project(':spring-security-config'),
|
||||
project(':spring-security-taglibs'),
|
||||
"org.springframework:spring-context-support:$springVersion",
|
||||
jstlDependencies,
|
||||
"org.hsqldb:hsqldb:$hsqlVersion",
|
||||
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
|
||||
"ch.qos.logback:logback-classic:$logbackVersion",
|
||||
"net.sf.ehcache:ehcache:$ehcacheVersion"
|
||||
|
||||
integrationTestCompile gebDependencies
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-samples-xml-contacts</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<name>spring-security-samples-xml-contacts</name>
|
||||
<description>spring-security-samples-xml-contacts</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
<organization>
|
||||
<name>spring.io</name>
|
||||
<url>http://spring.io/</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>rwinch</id>
|
||||
<name>Rob Winch</name>
|
||||
<email>rwinch@gopivotal.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
|
||||
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-security</url>
|
||||
</scm>
|
||||
<properties>
|
||||
<m2eclipse.wtp.contextRoot>/sample</m2eclipse.wtp.contextRoot>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.2.5.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-acl</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet.jsp.jstl</groupId>
|
||||
<artifactId>javax.servlet.jsp.jstl-api</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.taglibs</groupId>
|
||||
<artifactId>taglibs-standard-jstlel</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-taglibs</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-httpclient</groupId>
|
||||
<artifactId>commons-httpclient</artifactId>
|
||||
<version>3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
<version>2.4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>2.4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.gebish</groupId>
|
||||
<artifactId>geb-spock</artifactId>
|
||||
<version>0.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-htmlunit-driver</artifactId>
|
||||
<version>2.44.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
<version>0.7-groovy-2.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<version>0.7-groovy-2.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import geb.spock.*
|
||||
import spock.lang.Stepwise
|
||||
import org.springframework.security.samples.pages.*
|
||||
|
||||
/**
|
||||
* Tests the CAS sample application using service tickets.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Stepwise
|
||||
class ContactsTests extends GebReportingSpec {
|
||||
def 'access home page with unauthenticated user success'() {
|
||||
when: 'Unauthenticated user accesses the Home Page'
|
||||
to HomePage
|
||||
then: 'The page is displayed'
|
||||
at HomePage
|
||||
}
|
||||
|
||||
def 'access manage page with unauthenticated user sends to login page'() {
|
||||
when: 'Unauthenticated user accesses the Manage Page'
|
||||
manage.click(LoginPage)
|
||||
then: 'The login page is displayed'
|
||||
at LoginPage
|
||||
}
|
||||
|
||||
def 'authenticated user is sent to original page'() {
|
||||
when: 'user authenticates'
|
||||
login()
|
||||
then: 'The manage page is displayed'
|
||||
at ContactsPage
|
||||
}
|
||||
|
||||
def 'add contact link works'() {
|
||||
when: 'user clicks add link'
|
||||
addContact.click(AddPage)
|
||||
then: 'The add page is displayed'
|
||||
at AddPage
|
||||
}
|
||||
|
||||
def 'add contact'() {
|
||||
when: 'add a contact'
|
||||
addContact
|
||||
then: 'The add page is displayed'
|
||||
at ContactsPage
|
||||
and: 'The new contact is displayed'
|
||||
contacts.find { it.email == 'rob@example.com' }?.name == 'Rob Winch'
|
||||
}
|
||||
|
||||
def 'delete contact'() {
|
||||
when: 'delete a contact'
|
||||
contacts.find { it.email == 'rob@example.com' }.delete()
|
||||
then: 'Delete confirmation displayed'
|
||||
at DeleteConfirmPage
|
||||
when: 'View Manage Page'
|
||||
manage.click()
|
||||
then: 'New contact has been removed'
|
||||
!contacts.find { it.email == 'rob@example.com' }
|
||||
}
|
||||
|
||||
def 'authenticated user logs out'() {
|
||||
when: 'user logs out'
|
||||
logout.click()
|
||||
then: 'the default logout success page is displayed'
|
||||
at HomePage
|
||||
when: 'Unauthenticated user accesses the Manage Page'
|
||||
via ContactsPage
|
||||
then: 'The login page is displayed'
|
||||
at LoginPage
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.pages
|
||||
|
||||
import geb.Page
|
||||
|
||||
/**
|
||||
* The login page.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class AddPage extends Page {
|
||||
static url = 'add'
|
||||
static at = { assert driver.title == 'Add New Contact'; true}
|
||||
static content = {
|
||||
addContact(required:false) { name = 'Rob Winch', email = 'rob@example.com'->
|
||||
addForm.name = name
|
||||
addForm.email = email
|
||||
submit.click()
|
||||
}
|
||||
addForm { $('form') }
|
||||
submit { $('input', type: 'submit') }
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class ContactsPage extends Page {
|
||||
static url = 'secure/'
|
||||
static at = { assert driver.title == 'Your Contacts'; true}
|
||||
static content = {
|
||||
addContact(to: AddPage) { $('a', text: 'Add') }
|
||||
contacts { moduleList Contact, $("table tr").tail() }
|
||||
logout { $("input[type=submit]", value: "Logoff") }
|
||||
}
|
||||
}
|
||||
|
||||
class Contact extends Module {
|
||||
static content = {
|
||||
cell { $("td", it) }
|
||||
id { cell(0).text().toInteger() }
|
||||
name { cell(1).text() }
|
||||
email { cell(2).text() }
|
||||
delete { cell(3).$('a').click() }
|
||||
adminPermission { cell(4).$('a') }
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.pages
|
||||
|
||||
import geb.Page
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class DeleteConfirmPage extends Page {
|
||||
static at = { assert driver.title == 'Deletion completed'; true}
|
||||
static content = {
|
||||
manage(to: ContactsPage) { $('a', text: 'Manage') }
|
||||
}
|
||||
}
|
||||
+33
@@ -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.pages;
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class HomePage extends Page {
|
||||
static url = ''
|
||||
static at = { assert driver.title == 'Contacts Security Demo'; true}
|
||||
static content = {
|
||||
manage(to: [ContactsPage,LoginPage]) { $('a', text: 'Manage') }
|
||||
debug { $('a', text: 'Debug').click() }
|
||||
frames { $('a', text: 'Frames').click() }
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.pages;
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The login page.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class LoginPage extends Page {
|
||||
static url = 'login'
|
||||
static at = { assert driver.title == 'Login'; true}
|
||||
static content = {
|
||||
login(required:false) { user='rod', password='koala' ->
|
||||
loginForm.username = user
|
||||
loginForm.password = password
|
||||
submit.click()
|
||||
}
|
||||
loginForm { $('form') }
|
||||
submit { $('input', type: 'submit') }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.contact;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
@Controller
|
||||
public class AddDeleteContactController {
|
||||
@Autowired
|
||||
private ContactManager contactManager;
|
||||
private final Validator validator = new WebContactValidator();
|
||||
|
||||
/**
|
||||
* Displays the "add contact" form.
|
||||
*/
|
||||
@RequestMapping(value = "/secure/add.htm", method = RequestMethod.GET)
|
||||
public ModelAndView addContactDisplay() {
|
||||
return new ModelAndView("add", "webContact", new WebContact());
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder binder) {
|
||||
System.out.println("A binder for object: " + binder.getObjectName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the submission of the contact form, creating a new instance if the username
|
||||
* and email are valid.
|
||||
*/
|
||||
@RequestMapping(value = "/secure/add.htm", method = RequestMethod.POST)
|
||||
public String addContact(WebContact form, BindingResult result) {
|
||||
validator.validate(form, result);
|
||||
|
||||
if (result.hasErrors()) {
|
||||
return "add";
|
||||
}
|
||||
|
||||
Contact contact = new Contact(form.getName(), form.getEmail());
|
||||
contactManager.create(contact);
|
||||
|
||||
return "redirect:/secure/index.htm";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/secure/del.htm", method = RequestMethod.GET)
|
||||
public ModelAndView handleRequest(@RequestParam("contactId") int contactId) {
|
||||
Contact contact = contactManager.getById(Long.valueOf(contactId));
|
||||
contactManager.delete(contact);
|
||||
|
||||
return new ModelAndView("deleted", "contact", contact);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
|
||||
/**
|
||||
* Model object for add permission use case.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class AddPermission {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
public Contact contact;
|
||||
public Integer permission = BasePermission.READ.getMask();
|
||||
public String recipient;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public Contact getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
public Integer getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
public void setContact(Contact contact) {
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
public void setPermission(Integer permission) {
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
public void setRecipient(String recipient) {
|
||||
this.recipient = recipient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.ValidationUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
* Validates {@link AddPermission}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class AddPermissionValidator implements Validator {
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean supports(Class clazz) {
|
||||
return clazz.equals(AddPermission.class);
|
||||
}
|
||||
|
||||
public void validate(Object obj, Errors errors) {
|
||||
AddPermission addPermission = (AddPermission) obj;
|
||||
|
||||
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "permission", "err.permission",
|
||||
"Permission is required. *");
|
||||
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "recipient", "err.recipient",
|
||||
"Recipient is required. *");
|
||||
|
||||
if (addPermission.getPermission() != null) {
|
||||
int permission = addPermission.getPermission().intValue();
|
||||
|
||||
if ((permission != BasePermission.ADMINISTRATION.getMask())
|
||||
&& (permission != BasePermission.READ.getMask())
|
||||
&& (permission != BasePermission.DELETE.getMask())) {
|
||||
errors.rejectValue("permission", "err.permission.invalid",
|
||||
"The indicated permission is invalid. *");
|
||||
}
|
||||
}
|
||||
|
||||
if (addPermission.getRecipient() != null) {
|
||||
if (addPermission.getRecipient().length() > 100) {
|
||||
errors.rejectValue("recipient", "err.recipient.length",
|
||||
"The recipient is too long (maximum 100 characters). *");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.contact;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionFactory;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PermissionFactory;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Web controller to handle <tt>Permission</tt> administration functions - adding and
|
||||
* deleting permissions for contacts.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
@Controller
|
||||
@SessionAttributes("addPermission")
|
||||
public final class AdminPermissionController implements MessageSourceAware {
|
||||
@Autowired
|
||||
private AclService aclService;
|
||||
@Autowired
|
||||
private ContactManager contactManager;
|
||||
private MessageSourceAccessor messages;
|
||||
private final Validator addPermissionValidator = new AddPermissionValidator();
|
||||
private final PermissionFactory permissionFactory = new DefaultPermissionFactory();
|
||||
|
||||
/**
|
||||
* Displays the permission admin page for a particular contact.
|
||||
*/
|
||||
@RequestMapping(value = "/secure/adminPermission.htm", method = RequestMethod.GET)
|
||||
public ModelAndView displayAdminPage(@RequestParam("contactId") int contactId) {
|
||||
Contact contact = contactManager.getById(Long.valueOf(contactId));
|
||||
Acl acl = aclService.readAclById(new ObjectIdentityImpl(contact));
|
||||
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("contact", contact);
|
||||
model.put("acl", acl);
|
||||
|
||||
return new ModelAndView("adminPermission", "model", model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the "add permission" page for a contact.
|
||||
*/
|
||||
@RequestMapping(value = "/secure/addPermission.htm", method = RequestMethod.GET)
|
||||
public ModelAndView displayAddPermissionPageForContact(
|
||||
@RequestParam("contactId") int contactId) {
|
||||
Contact contact = contactManager.getById(new Long(contactId));
|
||||
|
||||
AddPermission addPermission = new AddPermission();
|
||||
addPermission.setContact(contact);
|
||||
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("addPermission", addPermission);
|
||||
model.put("recipients", listRecipients());
|
||||
model.put("permissions", listPermissions());
|
||||
|
||||
return new ModelAndView("addPermission", model);
|
||||
}
|
||||
|
||||
@InitBinder("addPermission")
|
||||
public void initBinder(WebDataBinder binder) {
|
||||
binder.setAllowedFields("recipient", "permission");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles submission of the "add permission" form.
|
||||
*/
|
||||
@RequestMapping(value = "/secure/addPermission.htm", method = RequestMethod.POST)
|
||||
public String addPermission(AddPermission addPermission, BindingResult result,
|
||||
ModelMap model) {
|
||||
addPermissionValidator.validate(addPermission, result);
|
||||
|
||||
if (result.hasErrors()) {
|
||||
model.put("recipients", listRecipients());
|
||||
model.put("permissions", listPermissions());
|
||||
|
||||
return "addPermission";
|
||||
}
|
||||
|
||||
PrincipalSid sid = new PrincipalSid(addPermission.getRecipient());
|
||||
Permission permission = permissionFactory.buildFromMask(addPermission
|
||||
.getPermission());
|
||||
|
||||
try {
|
||||
contactManager.addPermission(addPermission.getContact(), sid, permission);
|
||||
}
|
||||
catch (DataAccessException existingPermission) {
|
||||
existingPermission.printStackTrace();
|
||||
result.rejectValue("recipient", "err.recipientExistsForContact",
|
||||
"Addition failure.");
|
||||
|
||||
model.put("recipients", listRecipients());
|
||||
model.put("permissions", listPermissions());
|
||||
return "addPermission";
|
||||
}
|
||||
|
||||
return "redirect:/secure/index.htm";
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a permission
|
||||
*/
|
||||
@RequestMapping(value = "/secure/deletePermission.htm")
|
||||
public ModelAndView deletePermission(@RequestParam("contactId") int contactId,
|
||||
@RequestParam("sid") String sid, @RequestParam("permission") int mask) {
|
||||
|
||||
Contact contact = contactManager.getById(new Long(contactId));
|
||||
|
||||
Sid sidObject = new PrincipalSid(sid);
|
||||
Permission permission = permissionFactory.buildFromMask(mask);
|
||||
|
||||
contactManager.deletePermission(contact, sidObject, permission);
|
||||
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("contact", contact);
|
||||
model.put("sid", sidObject);
|
||||
model.put("permission", permission);
|
||||
|
||||
return new ModelAndView("deletePermission", "model", model);
|
||||
}
|
||||
|
||||
private Map<Integer, String> listPermissions() {
|
||||
Map<Integer, String> map = new LinkedHashMap<Integer, String>();
|
||||
map.put(Integer.valueOf(BasePermission.ADMINISTRATION.getMask()),
|
||||
messages.getMessage("select.administer", "Administer"));
|
||||
map.put(Integer.valueOf(BasePermission.READ.getMask()),
|
||||
messages.getMessage("select.read", "Read"));
|
||||
map.put(Integer.valueOf(BasePermission.DELETE.getMask()),
|
||||
messages.getMessage("select.delete", "Delete"));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, String> listRecipients() {
|
||||
Map<String, String> map = new LinkedHashMap<String, String>();
|
||||
map.put("", messages.getMessage("select.pleaseSelect", "-- please select --"));
|
||||
|
||||
for (String recipient : contactManager.getAllRecipients()) {
|
||||
map.put(recipient, recipient);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.context.support.FileSystemXmlApplicationContext;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
/**
|
||||
* Demonstrates accessing the {@link ContactManager} via remoting protocols.
|
||||
* <p>
|
||||
* Based on Spring's JPetStore sample, written by Juergen Hoeller.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class ClientApplication {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
// ~ Constructors
|
||||
// ===================================================================================================
|
||||
|
||||
public ClientApplication(ListableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void invokeContactManager(Authentication authentication, int nrOfCalls) {
|
||||
StopWatch stopWatch = new StopWatch(nrOfCalls + " ContactManager call(s)");
|
||||
Map<String, ContactManager> contactServices = this.beanFactory.getBeansOfType(
|
||||
ContactManager.class, true, true);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
for (String beanName : contactServices.keySet()) {
|
||||
Object object = this.beanFactory.getBean("&" + beanName);
|
||||
|
||||
try {
|
||||
System.out.println("Trying to find setUsername(String) method on: "
|
||||
+ object.getClass().getName());
|
||||
|
||||
Method method = object.getClass().getMethod("setUsername",
|
||||
new Class[] { String.class });
|
||||
System.out.println("Found; Trying to setUsername(String) to "
|
||||
+ authentication.getPrincipal());
|
||||
method.invoke(object, authentication.getPrincipal());
|
||||
}
|
||||
catch (NoSuchMethodException ignored) {
|
||||
System.out
|
||||
.println("This client proxy factory does not have a setUsername(String) method");
|
||||
}
|
||||
catch (IllegalAccessException ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
catch (InvocationTargetException ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
System.out.println("Trying to find setPassword(String) method on: "
|
||||
+ object.getClass().getName());
|
||||
|
||||
Method method = object.getClass().getMethod("setPassword",
|
||||
new Class[] { String.class });
|
||||
method.invoke(object, authentication.getCredentials());
|
||||
System.out.println("Found; Trying to setPassword(String) to "
|
||||
+ authentication.getCredentials());
|
||||
}
|
||||
catch (NoSuchMethodException ignored) {
|
||||
System.out
|
||||
.println("This client proxy factory does not have a setPassword(String) method");
|
||||
}
|
||||
catch (IllegalAccessException ignored) {
|
||||
}
|
||||
catch (InvocationTargetException ignored) {
|
||||
}
|
||||
|
||||
ContactManager remoteContactManager = contactServices.get(beanName);
|
||||
System.out.println("Calling ContactManager '" + beanName + "'");
|
||||
|
||||
stopWatch.start(beanName);
|
||||
|
||||
List<Contact> contacts = null;
|
||||
|
||||
for (int i = 0; i < nrOfCalls; i++) {
|
||||
contacts = remoteContactManager.getAll();
|
||||
}
|
||||
|
||||
stopWatch.stop();
|
||||
|
||||
if (contacts.size() != 0) {
|
||||
for (Contact contact : contacts) {
|
||||
System.out.println("Contact: " + contact);
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.out.println("No contacts found which this user has permission to");
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.println(stopWatch.prettyPrint());
|
||||
}
|
||||
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String username = System.getProperty("username", "");
|
||||
String password = System.getProperty("password", "");
|
||||
String nrOfCallsString = System.getProperty("nrOfCalls", "");
|
||||
|
||||
if ("".equals(username) || "".equals(password)) {
|
||||
System.out
|
||||
.println("You need to specify the user ID to use, the password to use, and optionally a number of calls "
|
||||
+ "using the username, password, and nrOfCalls system properties respectively. eg for user rod, "
|
||||
+ "use: -Dusername=rod -Dpassword=koala' for a single call per service and "
|
||||
+ "use: -Dusername=rod -Dpassword=koala -DnrOfCalls=10 for ten calls per service.");
|
||||
System.exit(-1);
|
||||
}
|
||||
else {
|
||||
int nrOfCalls = 1;
|
||||
|
||||
if (!"".equals(nrOfCallsString)) {
|
||||
nrOfCalls = Integer.parseInt(nrOfCallsString);
|
||||
}
|
||||
|
||||
ListableBeanFactory beanFactory = new FileSystemXmlApplicationContext(
|
||||
"clientContext.xml");
|
||||
ClientApplication client = new ClientApplication(beanFactory);
|
||||
|
||||
client.invokeContactManager(new UsernamePasswordAuthenticationToken(username,
|
||||
password), nrOfCalls);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Represents a contact.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class Contact implements Serializable {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private Long id;
|
||||
private String email;
|
||||
private String name;
|
||||
|
||||
// ~ Constructors
|
||||
// ===================================================================================================
|
||||
|
||||
public Contact(String name, String email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Contact() {
|
||||
}
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
/**
|
||||
* @return Returns the email.
|
||||
*/
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the id.
|
||||
*/
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param email The email to set.
|
||||
*/
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name The name to set.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString() + ": ");
|
||||
sb.append("Id: " + this.getId() + "; ");
|
||||
sb.append("Name: " + this.getName() + "; ");
|
||||
sb.append("Email: " + this.getEmail());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to the application's persistence layer.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public interface ContactDao {
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void create(Contact contact);
|
||||
|
||||
public void delete(Long contactId);
|
||||
|
||||
public List<Contact> findAll();
|
||||
|
||||
public List<String> findAllPrincipals();
|
||||
|
||||
public List<String> findAllRoles();
|
||||
|
||||
public Contact getById(Long id);
|
||||
|
||||
public void update(Contact contact);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.support.JdbcDaoSupport;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link ContactDao} that uses Spring's JdbcTemplate.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class ContactDaoSpring extends JdbcDaoSupport implements ContactDao {
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void create(final Contact contact) {
|
||||
getJdbcTemplate().update("insert into contacts values (?, ?, ?)",
|
||||
new PreparedStatementSetter() {
|
||||
public void setValues(PreparedStatement ps) throws SQLException {
|
||||
ps.setLong(1, contact.getId());
|
||||
ps.setString(2, contact.getName());
|
||||
ps.setString(3, contact.getEmail());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void delete(final Long contactId) {
|
||||
getJdbcTemplate().update("delete from contacts where id = ?",
|
||||
new PreparedStatementSetter() {
|
||||
public void setValues(PreparedStatement ps) throws SQLException {
|
||||
ps.setLong(1, contactId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void update(final Contact contact) {
|
||||
getJdbcTemplate().update(
|
||||
"update contacts set contact_name = ?, address = ? where id = ?",
|
||||
new PreparedStatementSetter() {
|
||||
public void setValues(PreparedStatement ps) throws SQLException {
|
||||
ps.setString(1, contact.getName());
|
||||
ps.setString(2, contact.getEmail());
|
||||
ps.setLong(3, contact.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<Contact> findAll() {
|
||||
return getJdbcTemplate().query(
|
||||
"select id, contact_name, email from contacts order by id",
|
||||
new RowMapper<Contact>() {
|
||||
public Contact mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return mapContact(rs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<String> findAllPrincipals() {
|
||||
return getJdbcTemplate().queryForList(
|
||||
"select username from users order by username", String.class);
|
||||
}
|
||||
|
||||
public List<String> findAllRoles() {
|
||||
return getJdbcTemplate().queryForList(
|
||||
"select distinct authority from authorities order by authority",
|
||||
String.class);
|
||||
}
|
||||
|
||||
public Contact getById(Long id) {
|
||||
List<Contact> list = getJdbcTemplate().query(
|
||||
"select id, contact_name, email from contacts where id = ? order by id",
|
||||
new RowMapper<Contact>() {
|
||||
public Contact mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return mapContact(rs);
|
||||
}
|
||||
}, id);
|
||||
|
||||
if (list.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (Contact) list.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
private Contact mapContact(ResultSet rs) throws SQLException {
|
||||
Contact contact = new Contact();
|
||||
contact.setId(new Long(rs.getLong("id")));
|
||||
contact.setName(rs.getString("contact_name"));
|
||||
contact.setEmail(rs.getString("email"));
|
||||
|
||||
return contact;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import org.springframework.security.access.prepost.PostFilter;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface for the application's services layer.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public interface ContactManager {
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
@PreAuthorize("hasPermission(#contact, admin)")
|
||||
public void addPermission(Contact contact, Sid recipient, Permission permission);
|
||||
|
||||
@PreAuthorize("hasPermission(#contact, admin)")
|
||||
public void deletePermission(Contact contact, Sid recipient, Permission permission);
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
public void create(Contact contact);
|
||||
|
||||
@PreAuthorize("hasPermission(#contact, 'delete') or hasPermission(#contact, admin)")
|
||||
public void delete(Contact contact);
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, admin)")
|
||||
public List<Contact> getAll();
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
public List<String> getAllRecipients();
|
||||
|
||||
@PreAuthorize("hasPermission(#id, 'sample.contact.Contact', read) or "
|
||||
+ "hasPermission(#id, 'sample.contact.Contact', admin)")
|
||||
public Contact getById(Long id);
|
||||
|
||||
public Contact getRandomContact();
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.model.AccessControlEntry;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.MutableAclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.context.support.ApplicationObjectSupport;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Concrete implementation of {@link ContactManager}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@Transactional
|
||||
public class ContactManagerBackend extends ApplicationObjectSupport implements
|
||||
ContactManager, InitializingBean {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private ContactDao contactDao;
|
||||
private MutableAclService mutableAclService;
|
||||
private int counter = 1000;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(contactDao, "contactDao required");
|
||||
Assert.notNull(mutableAclService, "mutableAclService required");
|
||||
}
|
||||
|
||||
public void addPermission(Contact contact, Sid recipient, Permission permission) {
|
||||
MutableAcl acl;
|
||||
ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId());
|
||||
|
||||
try {
|
||||
acl = (MutableAcl) mutableAclService.readAclById(oid);
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
acl = mutableAclService.createAcl(oid);
|
||||
}
|
||||
|
||||
acl.insertAce(acl.getEntries().size(), permission, recipient, true);
|
||||
mutableAclService.updateAcl(acl);
|
||||
|
||||
logger.debug("Added permission " + permission + " for Sid " + recipient
|
||||
+ " contact " + contact);
|
||||
}
|
||||
|
||||
public void create(Contact contact) {
|
||||
// Create the Contact itself
|
||||
contact.setId(new Long(counter++));
|
||||
contactDao.create(contact);
|
||||
|
||||
// Grant the current principal administrative permission to the contact
|
||||
addPermission(contact, new PrincipalSid(getUsername()),
|
||||
BasePermission.ADMINISTRATION);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Created contact " + contact
|
||||
+ " and granted admin permission to recipient " + getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(Contact contact) {
|
||||
contactDao.delete(contact.getId());
|
||||
|
||||
// Delete the ACL information as well
|
||||
ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId());
|
||||
mutableAclService.deleteAcl(oid, false);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Deleted contact " + contact + " including ACL permissions");
|
||||
}
|
||||
}
|
||||
|
||||
public void deletePermission(Contact contact, Sid recipient, Permission permission) {
|
||||
ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId());
|
||||
MutableAcl acl = (MutableAcl) mutableAclService.readAclById(oid);
|
||||
|
||||
// Remove all permissions associated with this particular recipient (string
|
||||
// equality to KISS)
|
||||
List<AccessControlEntry> entries = acl.getEntries();
|
||||
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
if (entries.get(i).getSid().equals(recipient)
|
||||
&& entries.get(i).getPermission().equals(permission)) {
|
||||
acl.deleteAce(i);
|
||||
}
|
||||
}
|
||||
|
||||
mutableAclService.updateAcl(acl);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Deleted contact " + contact + " ACL permissions for recipient "
|
||||
+ recipient);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<Contact> getAll() {
|
||||
logger.debug("Returning all contacts");
|
||||
|
||||
return contactDao.findAll();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<String> getAllRecipients() {
|
||||
logger.debug("Returning all recipients");
|
||||
|
||||
return contactDao.findAllPrincipals();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Contact getById(Long id) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Returning contact with id: " + id);
|
||||
}
|
||||
|
||||
return contactDao.getById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a public method.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Contact getRandomContact() {
|
||||
logger.debug("Returning random contact");
|
||||
|
||||
Random rnd = new Random();
|
||||
List<Contact> contacts = contactDao.findAll();
|
||||
int getNumber = rnd.nextInt(contacts.size());
|
||||
|
||||
return contacts.get(getNumber);
|
||||
}
|
||||
|
||||
protected String getUsername() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (auth.getPrincipal() instanceof UserDetails) {
|
||||
return ((UserDetails) auth.getPrincipal()).getUsername();
|
||||
}
|
||||
else {
|
||||
return auth.getPrincipal().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public void setContactDao(ContactDao contactDao) {
|
||||
this.contactDao = contactDao;
|
||||
}
|
||||
|
||||
public void setMutableAclService(MutableAclService mutableAclService) {
|
||||
this.mutableAclService = mutableAclService;
|
||||
}
|
||||
|
||||
public void update(Contact contact) {
|
||||
contactDao.update(contact);
|
||||
|
||||
logger.debug("Updated contact " + contact);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.security.acls.domain.AclImpl;
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.MutableAclService;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Populates the Contacts in-memory database with contact and ACL information.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class DataSourcePopulator implements InitializingBean {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
JdbcTemplate template;
|
||||
private MutableAclService mutableAclService;
|
||||
final Random rnd = new Random();
|
||||
TransactionTemplate tt;
|
||||
final String[] firstNames = { "Bob", "Mary", "James", "Jane", "Kristy", "Kirsty",
|
||||
"Kate", "Jeni", "Angela", "Melanie", "Kent", "William", "Geoff", "Jeff",
|
||||
"Adrian", "Amanda", "Lisa", "Elizabeth", "Prue", "Richard", "Darin",
|
||||
"Phillip", "Michael", "Belinda", "Samantha", "Brian", "Greg", "Matthew" };
|
||||
final String[] lastNames = { "Smith", "Williams", "Jackson", "Rictor", "Nelson",
|
||||
"Fitzgerald", "McAlpine", "Sutherland", "Abbott", "Hall", "Edwards", "Gates",
|
||||
"Black", "Brown", "Gray", "Marwell", "Booch", "Johnson", "McTaggart",
|
||||
"Parklin", "Findlay", "Robinson", "Giugni", "Lang", "Chi", "Carmichael" };
|
||||
private int createEntities = 50;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(mutableAclService, "mutableAclService required");
|
||||
Assert.notNull(template, "dataSource required");
|
||||
Assert.notNull(tt, "platformTransactionManager required");
|
||||
|
||||
// Set a user account that will initially own all the created data
|
||||
Authentication authRequest = new UsernamePasswordAuthenticationToken("rod",
|
||||
"koala", AuthorityUtils.createAuthorityList("ROLE_IGNORED"));
|
||||
SecurityContextHolder.getContext().setAuthentication(authRequest);
|
||||
|
||||
try {
|
||||
template.execute("DROP TABLE CONTACTS");
|
||||
template.execute("DROP TABLE AUTHORITIES");
|
||||
template.execute("DROP TABLE USERS");
|
||||
template.execute("DROP TABLE ACL_ENTRY");
|
||||
template.execute("DROP TABLE ACL_OBJECT_IDENTITY");
|
||||
template.execute("DROP TABLE ACL_CLASS");
|
||||
template.execute("DROP TABLE ACL_SID");
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.out.println("Failed to drop tables: " + e.getMessage());
|
||||
}
|
||||
|
||||
template.execute("CREATE TABLE ACL_SID("
|
||||
+ "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,"
|
||||
+ "PRINCIPAL BOOLEAN NOT NULL," + "SID VARCHAR_IGNORECASE(100) NOT NULL,"
|
||||
+ "CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));");
|
||||
template.execute("CREATE TABLE ACL_CLASS("
|
||||
+ "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,"
|
||||
+ "CLASS VARCHAR_IGNORECASE(100) NOT NULL,"
|
||||
+ "CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));");
|
||||
template.execute("CREATE TABLE ACL_OBJECT_IDENTITY("
|
||||
+ "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,"
|
||||
+ "OBJECT_ID_CLASS BIGINT NOT NULL,"
|
||||
+ "OBJECT_ID_IDENTITY BIGINT NOT NULL,"
|
||||
+ "PARENT_OBJECT BIGINT,"
|
||||
+ "OWNER_SID BIGINT,"
|
||||
+ "ENTRIES_INHERITING BOOLEAN NOT NULL,"
|
||||
+ "CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY),"
|
||||
+ "CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),"
|
||||
+ "CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),"
|
||||
+ "CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));");
|
||||
template.execute("CREATE TABLE ACL_ENTRY("
|
||||
+ "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,"
|
||||
+ "ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL,"
|
||||
+ "MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL,"
|
||||
+ "AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER),"
|
||||
+ "CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID),"
|
||||
+ "CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));");
|
||||
|
||||
template.execute("CREATE TABLE USERS(USERNAME VARCHAR_IGNORECASE(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR_IGNORECASE(500) NOT NULL,ENABLED BOOLEAN NOT NULL);");
|
||||
template.execute("CREATE TABLE AUTHORITIES(USERNAME VARCHAR_IGNORECASE(50) NOT NULL,AUTHORITY VARCHAR_IGNORECASE(50) NOT NULL,CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME));");
|
||||
template.execute("CREATE UNIQUE INDEX IX_AUTH_USERNAME ON AUTHORITIES(USERNAME,AUTHORITY);");
|
||||
|
||||
template.execute("CREATE TABLE CONTACTS(ID BIGINT NOT NULL PRIMARY KEY, CONTACT_NAME VARCHAR_IGNORECASE(50) NOT NULL, EMAIL VARCHAR_IGNORECASE(50) NOT NULL)");
|
||||
|
||||
/*
|
||||
* Passwords encoded using MD5, NOT in Base64 format, with null as salt Encoded
|
||||
* password for rod is "koala" Encoded password for dianne is "emu" Encoded
|
||||
* password for scott is "wombat" Encoded password for peter is "opal" (but user
|
||||
* is disabled) Encoded password for bill is "wombat" Encoded password for bob is
|
||||
* "wombat" Encoded password for jane is "wombat"
|
||||
*/
|
||||
template.execute("INSERT INTO USERS VALUES('rod','$2a$10$75pBjapg4Nl8Pzd.3JRnUe7PDJmk9qBGwNEJDAlA3V.dEJxcDKn5O',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('dianne','$2a$04$bCMEyxrdF/7sgfUiUJ6Ose2vh9DAMaVBldS1Bw2fhi1jgutZrr9zm',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('scott','$2a$06$eChwvzAu3TSexnC3ynw4LOSw1qiEbtNItNeYv5uI40w1i3paoSfLu',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('peter','$2a$04$8.H8bCMROLF4CIgd7IpeQ.tcBXLP5w8iplO0n.kCIkISwrIgX28Ii',FALSE);");
|
||||
template.execute("INSERT INTO USERS VALUES('bill','$2a$04$8.H8bCMROLF4CIgd7IpeQ.3khQlPVNWbp8kzSQqidQHGFurim7P8O',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('bob','$2a$06$zMgxlMf01SfYNcdx7n4NpeFlAGU8apCETz/i2C7VlYWu6IcNyn4Ay',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('jane','$2a$05$ZrdS7yMhCZ1J.AAidXZhCOxdjD8LO/dhlv4FJzkXA6xh9gdEbBT/u',TRUE);");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_SUPERVISOR');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('dianne','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('scott','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('peter','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('bill','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('bob','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('jane','ROLE_USER');");
|
||||
|
||||
template.execute("INSERT INTO contacts VALUES (1, 'John Smith', 'john@somewhere.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (2, 'Michael Citizen', 'michael@xyz.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (3, 'Joe Bloggs', 'joe@demo.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (4, 'Karen Sutherland', 'karen@sutherland.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (5, 'Mitchell Howard', 'mitchell@abcdef.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (6, 'Rose Costas', 'rose@xyz.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (7, 'Amanda Smith', 'amanda@abcdef.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (8, 'Cindy Smith', 'cindy@smith.com');");
|
||||
template.execute("INSERT INTO contacts VALUES (9, 'Jonathan Citizen', 'jonathan@xyz.com');");
|
||||
|
||||
for (int i = 10; i < createEntities; i++) {
|
||||
String[] person = selectPerson();
|
||||
template.execute("INSERT INTO contacts VALUES (" + i + ", '" + person[2]
|
||||
+ "', '" + person[0].toLowerCase() + "@" + person[1].toLowerCase()
|
||||
+ ".com');");
|
||||
}
|
||||
|
||||
// Create acl_object_identity rows (and also acl_class rows as needed
|
||||
for (int i = 1; i < createEntities; i++) {
|
||||
final ObjectIdentity objectIdentity = new ObjectIdentityImpl(Contact.class,
|
||||
new Long(i));
|
||||
tt.execute(new TransactionCallback<Object>() {
|
||||
public Object doInTransaction(TransactionStatus arg0) {
|
||||
mutableAclService.createAcl(objectIdentity);
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Now grant some permissions
|
||||
grantPermissions(1, "rod", BasePermission.ADMINISTRATION);
|
||||
grantPermissions(2, "rod", BasePermission.READ);
|
||||
grantPermissions(3, "rod", BasePermission.READ);
|
||||
grantPermissions(3, "rod", BasePermission.WRITE);
|
||||
grantPermissions(3, "rod", BasePermission.DELETE);
|
||||
grantPermissions(4, "rod", BasePermission.ADMINISTRATION);
|
||||
grantPermissions(4, "dianne", BasePermission.ADMINISTRATION);
|
||||
grantPermissions(4, "scott", BasePermission.READ);
|
||||
grantPermissions(5, "dianne", BasePermission.ADMINISTRATION);
|
||||
grantPermissions(5, "dianne", BasePermission.READ);
|
||||
grantPermissions(6, "dianne", BasePermission.READ);
|
||||
grantPermissions(6, "dianne", BasePermission.WRITE);
|
||||
grantPermissions(6, "dianne", BasePermission.DELETE);
|
||||
grantPermissions(6, "scott", BasePermission.READ);
|
||||
grantPermissions(7, "scott", BasePermission.ADMINISTRATION);
|
||||
grantPermissions(8, "dianne", BasePermission.ADMINISTRATION);
|
||||
grantPermissions(8, "dianne", BasePermission.READ);
|
||||
grantPermissions(8, "scott", BasePermission.READ);
|
||||
grantPermissions(9, "scott", BasePermission.ADMINISTRATION);
|
||||
grantPermissions(9, "scott", BasePermission.READ);
|
||||
grantPermissions(9, "scott", BasePermission.WRITE);
|
||||
grantPermissions(9, "scott", BasePermission.DELETE);
|
||||
|
||||
// Now expressly change the owner of the first ten contacts
|
||||
// We have to do this last, because "rod" owns all of them (doing it sooner would
|
||||
// prevent ACL updates)
|
||||
// Note that ownership has no impact on permissions - they're separate (ownership
|
||||
// only allows ACl editing)
|
||||
changeOwner(5, "dianne");
|
||||
changeOwner(6, "dianne");
|
||||
changeOwner(7, "scott");
|
||||
changeOwner(8, "dianne");
|
||||
changeOwner(9, "scott");
|
||||
|
||||
String[] users = { "bill", "bob", "jane" }; // don't want to mess around with
|
||||
// consistent sample data
|
||||
Permission[] permissions = { BasePermission.ADMINISTRATION, BasePermission.READ,
|
||||
BasePermission.DELETE };
|
||||
|
||||
for (int i = 10; i < createEntities; i++) {
|
||||
String user = users[rnd.nextInt(users.length)];
|
||||
Permission permission = permissions[rnd.nextInt(permissions.length)];
|
||||
grantPermissions(i, user, permission);
|
||||
|
||||
String user2 = users[rnd.nextInt(users.length)];
|
||||
Permission permission2 = permissions[rnd.nextInt(permissions.length)];
|
||||
grantPermissions(i, user2, permission2);
|
||||
}
|
||||
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
private void changeOwner(int contactNumber, String newOwnerUsername) {
|
||||
AclImpl acl = (AclImpl) mutableAclService.readAclById(new ObjectIdentityImpl(
|
||||
Contact.class, new Long(contactNumber)));
|
||||
acl.setOwner(new PrincipalSid(newOwnerUsername));
|
||||
updateAclInTransaction(acl);
|
||||
}
|
||||
|
||||
public int getCreateEntities() {
|
||||
return createEntities;
|
||||
}
|
||||
|
||||
private void grantPermissions(int contactNumber, String recipientUsername,
|
||||
Permission permission) {
|
||||
AclImpl acl = (AclImpl) mutableAclService.readAclById(new ObjectIdentityImpl(
|
||||
Contact.class, new Long(contactNumber)));
|
||||
acl.insertAce(acl.getEntries().size(), permission, new PrincipalSid(
|
||||
recipientUsername), true);
|
||||
updateAclInTransaction(acl);
|
||||
}
|
||||
|
||||
private String[] selectPerson() {
|
||||
String firstName = firstNames[rnd.nextInt(firstNames.length)];
|
||||
String lastName = lastNames[rnd.nextInt(lastNames.length)];
|
||||
|
||||
return new String[] { firstName, lastName, firstName + " " + lastName };
|
||||
}
|
||||
|
||||
public void setCreateEntities(int createEntities) {
|
||||
this.createEntities = createEntities;
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.template = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
public void setMutableAclService(MutableAclService mutableAclService) {
|
||||
this.mutableAclService = mutableAclService;
|
||||
}
|
||||
|
||||
public void setPlatformTransactionManager(
|
||||
PlatformTransactionManager platformTransactionManager) {
|
||||
this.tt = new TransactionTemplate(platformTransactionManager);
|
||||
}
|
||||
|
||||
private void updateAclInTransaction(final MutableAcl acl) {
|
||||
tt.execute(new TransactionCallback<Object>() {
|
||||
public Object doInTransaction(TransactionStatus arg0) {
|
||||
mutableAclService.updateAcl(acl);
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.contact;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Controller which handles simple, single request use cases such as index pages and
|
||||
* contact deletion.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
@Controller
|
||||
public class IndexController {
|
||||
private final static Permission[] HAS_DELETE = new Permission[] {
|
||||
BasePermission.DELETE, BasePermission.ADMINISTRATION };
|
||||
private final static Permission[] HAS_ADMIN = new Permission[] { BasePermission.ADMINISTRATION };
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
@Autowired
|
||||
private ContactManager contactManager;
|
||||
@Autowired
|
||||
private PermissionEvaluator permissionEvaluator;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
/**
|
||||
* The public index page, used for unauthenticated users.
|
||||
*/
|
||||
@RequestMapping(value = "/hello.htm", method = RequestMethod.GET)
|
||||
public ModelAndView displayPublicIndex() {
|
||||
Contact rnd = contactManager.getRandomContact();
|
||||
|
||||
return new ModelAndView("hello", "contact", rnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* The index page for an authenticated user.
|
||||
* <p>
|
||||
* This controller displays a list of all the contacts for which the current user has
|
||||
* read or admin permissions. It makes a call to {@link ContactManager#getAll()} which
|
||||
* automatically filters the returned list using Spring Security's ACL mechanism (see
|
||||
* the expression annotations on this interface for the details).
|
||||
* <p>
|
||||
* In addition to rendering the list of contacts, the view will also include a "Del"
|
||||
* or "Admin" link beside the contact, depending on whether the user has the
|
||||
* corresponding permissions (admin permission is assumed to imply delete here). This
|
||||
* information is stored in the model using the injected {@link PermissionEvaluator}
|
||||
* instance. The implementation should be an instance of
|
||||
* {@link AclPermissionEvaluator} or one which is compatible with Spring Security's
|
||||
* ACL module.
|
||||
*/
|
||||
@RequestMapping(value = "/secure/index.htm", method = RequestMethod.GET)
|
||||
public ModelAndView displayUserContacts() {
|
||||
List<Contact> myContactsList = contactManager.getAll();
|
||||
Map<Contact, Boolean> hasDelete = new HashMap<Contact, Boolean>(
|
||||
myContactsList.size());
|
||||
Map<Contact, Boolean> hasAdmin = new HashMap<Contact, Boolean>(
|
||||
myContactsList.size());
|
||||
|
||||
Authentication user = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
for (Contact contact : myContactsList) {
|
||||
hasDelete.put(contact, Boolean.valueOf(permissionEvaluator.hasPermission(
|
||||
user, contact, HAS_DELETE)));
|
||||
hasAdmin.put(contact, Boolean.valueOf(permissionEvaluator.hasPermission(user,
|
||||
contact, HAS_ADMIN)));
|
||||
}
|
||||
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("contacts", myContactsList);
|
||||
model.put("hasDeletePermission", hasDelete);
|
||||
model.put("hasAdminPermission", hasAdmin);
|
||||
|
||||
return new ModelAndView("index", "model", model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
/**
|
||||
* An object that represents user-editable sections of a {@link Contact}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class WebContact {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private String email;
|
||||
private String name;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
* Validates {@link WebContact}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class WebContactValidator implements Validator {
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean supports(Class clazz) {
|
||||
return clazz.equals(WebContact.class);
|
||||
}
|
||||
|
||||
public void validate(Object obj, Errors errors) {
|
||||
WebContact wc = (WebContact) obj;
|
||||
|
||||
if ((wc.getName() == null) || (wc.getName().length() < 3)
|
||||
|| (wc.getName().length() > 50)) {
|
||||
errors.rejectValue("name", "err.name", "Name 3-50 characters is required. *");
|
||||
}
|
||||
|
||||
if ((wc.getEmail() == null) || (wc.getEmail().length() < 3)
|
||||
|| (wc.getEmail().length() > 50)) {
|
||||
errors.rejectValue("email", "err.email",
|
||||
"Email 3-50 characters is required. *");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
|
||||
|
||||
<!--
|
||||
- Application context containing the ACL beans.
|
||||
-
|
||||
-->
|
||||
|
||||
<!-- ========= ACL SERVICE DEFINITIONS ========= -->
|
||||
|
||||
<bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
|
||||
<property name="cacheManager">
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
|
||||
</property>
|
||||
<property name="cacheName" value="aclCache"/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
|
||||
<constructor-arg value="ROLE_ACL_ADMIN"/>
|
||||
</bean>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
|
||||
<constructor-arg ref="dataSource"/>
|
||||
<constructor-arg ref="aclCache"/>
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
|
||||
<constructor-arg value="ROLE_ADMINISTRATOR"/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
|
||||
<constructor-arg ref="dataSource"/>
|
||||
<constructor-arg ref="lookupStrategy"/>
|
||||
<constructor-arg ref="aclCache"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
- Application context containing business beans.
|
||||
-
|
||||
- Used by all artifacts.
|
||||
-
|
||||
-->
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
|
||||
|
||||
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
|
||||
<property name="basename" value="classpath:org/springframework/security/messages"/>
|
||||
</bean>
|
||||
|
||||
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
|
||||
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
|
||||
<property name="url" value="jdbc:hsqldb:mem:test"/>
|
||||
<!-- <value>jdbc:hsqldb:hsql://localhost/acl</value> -->
|
||||
<property name="username" value="sa"/>
|
||||
<property name="password" value=""/>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
||||
<bean id="dataSourcePopulator" class="sample.contact.DataSourcePopulator">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="mutableAclService" ref="aclService"/>
|
||||
<property name="platformTransactionManager" ref="transactionManager"/>
|
||||
</bean>
|
||||
|
||||
<bean id="contactManager" class="sample.contact.ContactManagerBackend">
|
||||
<property name="contactDao">
|
||||
<bean class="sample.contact.ContactDaoSpring">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="mutableAclService" ref="aclService"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
- Application context containing authentication, channel
|
||||
- security and web URI beans.
|
||||
-
|
||||
- Only used by "filter" artifact.
|
||||
-
|
||||
-->
|
||||
|
||||
<b:beans xmlns="http://www.springframework.org/schema/security"
|
||||
xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
|
||||
|
||||
<global-method-security pre-post-annotations="enabled">
|
||||
<expression-handler ref="expressionHandler"/>
|
||||
</global-method-security>
|
||||
|
||||
<http realm="Contacts Realm" use-expressions="false">
|
||||
<intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
|
||||
<intercept-url pattern="/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
|
||||
<intercept-url pattern="/hello.htm" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
|
||||
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
|
||||
<intercept-url pattern="/switchuser.jsp" access="ROLE_SUPERVISOR"/>
|
||||
<intercept-url pattern="/login/impersonate" access="ROLE_SUPERVISOR"/>
|
||||
<intercept-url pattern="/**" access="ROLE_USER"/>
|
||||
|
||||
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
|
||||
<http-basic/>
|
||||
<logout logout-success-url="/index.jsp"/>
|
||||
<remember-me />
|
||||
<headers/>
|
||||
<csrf/>
|
||||
<custom-filter ref="switchUserProcessingFilter" position="SWITCH_USER_FILTER"/>
|
||||
</http>
|
||||
|
||||
<b:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
|
||||
|
||||
<authentication-manager>
|
||||
<authentication-provider>
|
||||
<password-encoder ref="encoder"/>
|
||||
<jdbc-user-service data-source-ref="dataSource"/>
|
||||
</authentication-provider>
|
||||
</authentication-manager>
|
||||
|
||||
<!-- Automatically receives AuthenticationEvent messages -->
|
||||
<b:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener"/>
|
||||
|
||||
<!-- Filter used to switch the user context. Note: the switch and exit url must be secured
|
||||
based on the role granted the ability to 'switch' to another user -->
|
||||
<!-- In this example 'rod' has ROLE_SUPERVISOR that can switch to regular ROLE_USER(s) -->
|
||||
<b:bean id="switchUserProcessingFilter" class="org.springframework.security.web.authentication.switchuser.SwitchUserFilter" autowire="byType">
|
||||
<b:property name="targetUrl" value="/secure/index.htm"/>
|
||||
</b:bean>
|
||||
|
||||
<b:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
|
||||
<b:property name="permissionEvaluator" ref="permissionEvaluator"/>
|
||||
<b:property name="permissionCacheOptimizer">
|
||||
<b:bean class="org.springframework.security.acls.AclPermissionCacheOptimizer">
|
||||
<b:constructor-arg ref="aclService"/>
|
||||
</b:bean>
|
||||
</b:property>
|
||||
</b:bean>
|
||||
|
||||
<b:bean id="permissionEvaluator" class="org.springframework.security.acls.AclPermissionEvaluator">
|
||||
<b:constructor-arg ref="aclService"/>
|
||||
</b:bean>
|
||||
|
||||
</b:beans>
|
||||
@@ -0,0 +1,14 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- <logger name="org.springframework.security" level="DEBUG"/> -->
|
||||
|
||||
<root level="WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,6 @@
|
||||
err.name=Name 3-50 characters is required.
|
||||
err.email=Email 3-50 characters is required.
|
||||
err.permission=Permission is required.
|
||||
err.recipient=Recipient is required.
|
||||
err.permission.invalid=The indicated permission is invalid.
|
||||
err.recipient.length=The recipient is too long (maximum 100 characters).
|
||||
@@ -0,0 +1,27 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
|
||||
<!-- ========================== WEB DEFINITIONS ======================= -->
|
||||
|
||||
<context:component-scan base-package="sample.contact"/>
|
||||
<context:annotation-config />
|
||||
|
||||
<mvc:annotation-driven/>
|
||||
<mvc:view-controller path="/frames.htm" view-name="/frames"/>
|
||||
|
||||
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
|
||||
<property name="basename" value="messages"/>
|
||||
</bean>
|
||||
|
||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
||||
<property name="prefix" value="/WEB-INF/jsp/"/>
|
||||
<property name="suffix" value=".jsp"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,42 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
<html>
|
||||
<head><title>Add New Contact</title></head>
|
||||
<body>
|
||||
<h1>Add Contact</h1>
|
||||
<form method="post">
|
||||
<table width="95%" bgcolor="f8f8ff" border="0" cellspacing="0" cellpadding="5">
|
||||
<tr>
|
||||
<td alignment="right" width="20%">Name:</td>
|
||||
<spring:bind path="webContact.name">
|
||||
<td width="20%">
|
||||
<input type="text" name="name" value="<c:out value="${status.value}"/>">
|
||||
</td>
|
||||
<td width="60%">
|
||||
<font color="red"><c:out value="${status.errorMessage}"/></font>
|
||||
</td>
|
||||
</spring:bind>
|
||||
</tr>
|
||||
<tr>
|
||||
<td alignment="right" width="20%">Email:</td>
|
||||
<spring:bind path="webContact.email">
|
||||
<td width="20%">
|
||||
<input type="text" name="email" value="<c:out value="${status.value}"/>">
|
||||
</td>
|
||||
<td width="60%">
|
||||
<font color="red"><c:out value="${status.errorMessage}"/></font>
|
||||
</td>
|
||||
</spring:bind>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<spring:hasBindErrors name="webContact">
|
||||
<b>Please fix all errors!</b>
|
||||
</spring:hasBindErrors>
|
||||
<br><br>
|
||||
|
||||
<input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/>
|
||||
<input name="execute" type="submit" alignment="center" value="Execute">
|
||||
</form>
|
||||
<a href="<c:url value="../hello.htm"/>">Home</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,56 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
<html>
|
||||
<head><title>Add Permission</title></head>
|
||||
<body>
|
||||
<h1>Add Permission</h1>
|
||||
<form method="post">
|
||||
<table width="95%" bgcolor="f8f8ff" border="0" cellspacing="0" cellpadding="5">
|
||||
<tr>
|
||||
<td alignment="right" width="20%">Contact:</td>
|
||||
<td width="60%"><c:out value="${addPermission.contact}"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td alignment="right" width="20%">Recipient:</td>
|
||||
<spring:bind path="addPermission.recipient">
|
||||
<td width="20%">
|
||||
<select name="<c:out value="${status.expression}"/>">
|
||||
<c:forEach var="thisRecipient" items="${recipients}">
|
||||
<option <c:if test="${thisRecipient.key == status.value}">selected</c:if> value="<c:out value="${thisRecipient.key}"/>">
|
||||
<c:out value="${thisRecipient.value}"/></option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
</td>
|
||||
<td width="60%">
|
||||
<font color="red"><c:out value="${status.errorMessage}"/></font>
|
||||
</td>
|
||||
</spring:bind>
|
||||
</tr>
|
||||
<tr>
|
||||
<td alignment="right" width="20%">Permission:</td>
|
||||
<spring:bind path="addPermission.permission">
|
||||
<td width="20%">
|
||||
<select name="<c:out value="${status.expression}"/>">
|
||||
<c:forEach var="thisPermission" items="${permissions}">
|
||||
<option <c:if test="${thisPermission.key == status.value}">selected</c:if> value="<c:out value="${thisPermission.key}"/>">
|
||||
<c:out value="${thisPermission.value}"/></option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
</td>
|
||||
<td width="60%">
|
||||
<font color="red"><c:out value="${status.errorMessage}"/></font>
|
||||
</td>
|
||||
</spring:bind>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<spring:hasBindErrors name="webContact">
|
||||
<b>Please fix all errors!</b>
|
||||
</spring:hasBindErrors>
|
||||
<br><br>
|
||||
<input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/>
|
||||
<input name="execute" type="submit" alignment="center" value="Execute">
|
||||
</form>
|
||||
<p>
|
||||
<A HREF="<c:url value="adminPermission.htm"><c:param name="contactId" value="${addPermission.contact.id}"/></c:url>">Admin Permission</A> <a href="<c:url value="index.htm"/>">Manage</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
|
||||
<html>
|
||||
<head><title>Administer Permissions</title></head>
|
||||
<body>
|
||||
<h1>Administer Permissions</h1>
|
||||
<p>
|
||||
<code>
|
||||
<c:out value="${model.contact}"/>
|
||||
</code>
|
||||
</p>
|
||||
<table cellpadding="3" border="0">
|
||||
<c:forEach var="acl" items="${model.acl.entries}">
|
||||
<tr>
|
||||
<td>
|
||||
<code>
|
||||
<c:out value="${acl}"/>
|
||||
</code>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<c:url value="deletePermission.htm"><c:param name="contactId" value="${model.contact.id}"/><c:param name="sid" value="${acl.sid.principal}"/><c:param name="permission" value="${acl.permission.mask}"/></c:url>">Del</a>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
<p>
|
||||
<a href="<c:url value="addPermission.htm"><c:param name="contactId" value="${model.contact.id}"/></c:url>">Add Permission</a> <a href="<c:url value="index.htm"/>">Manage</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,20 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
|
||||
<html>
|
||||
<head><title>Permission Deleted</title></head>
|
||||
<body>
|
||||
<h1>Permission Deleted</h1>
|
||||
<P>
|
||||
<code>
|
||||
<c:out value="${model.contact}"/>
|
||||
</code>
|
||||
<P>
|
||||
<code>
|
||||
<c:out value="${model.sid}"/>
|
||||
</code>
|
||||
<code>
|
||||
<c:out value="${model.permission}"/>
|
||||
</code>
|
||||
<p><a href="<c:url value="index.htm"/>">Manage</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
|
||||
<html>
|
||||
<head><title>Deletion completed</title></head>
|
||||
<body>
|
||||
<h1>Deleted</h1>
|
||||
<P>
|
||||
<code>
|
||||
<c:out value="${contact}"/>
|
||||
</code>
|
||||
<p><a href="<c:url value="index.htm"/>">Manage</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Frames</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>This contains frames, but the frames will not be loaded due to the <a href="http://tools.ietf.org/html/draft-ietf-websec-x-frame-options">X-Frame-Options</a>
|
||||
being specified as denied. This protects against <a href="http://en.wikipedia.org/wiki/Clickjacking">clickjacking attacks</a></p>
|
||||
<iframe src="./hello.htm" width="500" height="500"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
|
||||
<html>
|
||||
<head><title>Contacts Security Demo</title></head>
|
||||
<body>
|
||||
<h1>Contacts Security Demo</h1>
|
||||
<P>Contacts demonstrates the following central Spring Security capabilities:
|
||||
<ul>
|
||||
<li><b>Role-based security</b>. Each principal is a member of certain roles,
|
||||
which are used to restrict access to certain secure objects.</li>
|
||||
<li><b>Domain object instance security</b>. The <code>Contact</code>, the
|
||||
main domain object in the application, has an access control list (ACL)
|
||||
that indicates who is allowed read, administer and delete the object.</li>
|
||||
<li><b>Method invocation security</b>. The <code>ContactManager</code> service
|
||||
layer bean has a number of secured (protected) and public (unprotected)
|
||||
methods.</li>
|
||||
<li><b>Web request security</b>. The <code>/secure</code> URI path is protected
|
||||
by Spring Security from principals not holding the
|
||||
<code>ROLE_USER</code> granted authority.</li>
|
||||
<li><b>Security unaware application objects</b>. None of the objects
|
||||
are aware of the security being implemented by Spring Security. *</li>
|
||||
<li><b>Security taglib usage</b>. All of the JSPs use Spring Security's
|
||||
taglib to evaluate security information. *</li>
|
||||
<li><b>Fully declarative security</b>. Every capability is configured in
|
||||
the application context using standard Spring Security classes. *</li>
|
||||
<li><b>Database-sourced security data</b>. All of the user, role and ACL
|
||||
information is obtained from an in-memory JDBC-compliant database.</li>
|
||||
<li><b>Integrated form-based and BASIC authentication</b>. Any BASIC
|
||||
authentication header is detected and used for authentication. Normal
|
||||
interactive form-based authentication is used by default.</li>
|
||||
<li><b>Remember-me services</b>. Spring Security's pluggable remember-me
|
||||
strategy is demonstrated, with a corresponding checkbox on the login form.</li>
|
||||
</ul>
|
||||
|
||||
* As the application provides an "ACL Administration" use case, those
|
||||
classes are necessarily aware of security. But no business use cases are.
|
||||
|
||||
<p>Please excuse the lack of look 'n' feel polish in this application.
|
||||
It is about security, after all! :-)
|
||||
|
||||
<p>To demonstrate a public method on <code>ContactManager</code>,
|
||||
here's a random <code>Contact</code>:
|
||||
<p>
|
||||
<code>
|
||||
<c:out value="${contact}"/>
|
||||
</code>
|
||||
<p>Get started by clicking "Manage"...
|
||||
<p><A HREF="<c:url value="secure/index.htm"/>">Manage</a>
|
||||
<a href="<c:url value="secure/debug.jsp"/>">Debug</a>
|
||||
<a href="<c:url value="./frames.htm"/>">Frames</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
|
||||
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
|
||||
|
||||
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
|
||||
<%@ page pageEncoding="UTF-8" %>
|
||||
@@ -0,0 +1,37 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
|
||||
<html>
|
||||
<head><title>Your Contacts</title></head>
|
||||
<body>
|
||||
<h1><security:authentication property="principal.username"/>'s Contacts</h1>
|
||||
<P>
|
||||
<table cellpadding=3 border=0>
|
||||
<tr><td><b>id</b></td><td><b>Name</b></td><td><b>Email</b></td></tr>
|
||||
<c:forEach var="contact" items="${model.contacts}" >
|
||||
<tr>
|
||||
<td>
|
||||
<c:out value="${contact.id}"/>
|
||||
</td>
|
||||
<td>
|
||||
<c:out value="${contact.name}"/>
|
||||
</td>
|
||||
<td>
|
||||
<c:out value="${contact.email}"/>
|
||||
</td>
|
||||
<c:if test="${model.hasDeletePermission[contact]}">
|
||||
<td><a href="<c:url value="del.htm"><c:param name="contactId" value="${contact.id}"/></c:url>">Del</a></td>
|
||||
</c:if>
|
||||
<c:if test="${model.hasAdminPermission[contact]}">
|
||||
<td><a href="<c:url value="adminPermission.htm"><c:param name="contactId" value="${contact.id}"/></c:url>">Admin Permission</a></td>
|
||||
</c:if>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
<p><a href="<c:url value="add.htm"/>">Add</a> </p>
|
||||
|
||||
<form action="<c:url value="/logout"/>" method="post">
|
||||
<input type="submit" value="Logoff"/> (also clears any remember-me cookie)
|
||||
<security:csrfInput/>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||
|
||||
<!--
|
||||
- Contacts web application
|
||||
-->
|
||||
<beans>
|
||||
|
||||
<!-- RMI exporter for the ContactManager -->
|
||||
<!-- This could just as easily have been in
|
||||
applicationContext-common-business.xml, because it doesn't rely on
|
||||
DispatcherServlet or indeed any other HTTP services. It's in this
|
||||
application context simply for logical placement with other
|
||||
remoting exporters. -->
|
||||
<!-- COMMENTED OUT BY DEFAULT TO AVOID CONFLICTS WITH APPLICATION SERVERS
|
||||
<bean id="contactManager-rmi" class="org.springframework.remoting.rmi.RmiServiceExporter">
|
||||
<property name="service"><ref bean="contactManager"/></property>
|
||||
<property name="serviceInterface">
|
||||
<value>sample.contact.ContactManager</value>
|
||||
</property>
|
||||
<property name="serviceName"><value>contactManager</value></property>
|
||||
<property name="registryPort"><value>1099</value></property>
|
||||
</bean>
|
||||
-->
|
||||
|
||||
<!-- HTTP invoker exporter for the ContactManager -->
|
||||
<!-- Spring's HTTP invoker uses Java serialization via HTTP -->
|
||||
<bean name="/ContactManager-httpinvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
|
||||
<property name="service" ref="contactManager"/>
|
||||
<property name="serviceInterface" value="sample.contact.ContactManager"/>
|
||||
</bean>
|
||||
|
||||
<!-- Hessian exporter for the ContactManager -->
|
||||
<!-- Hessian is a slim binary HTTP remoting protocol -->
|
||||
<!--
|
||||
<bean name="/ContactManager-hessian" class="org.springframework.remoting.caucho.HessianServiceExporter">
|
||||
<property name="service" ref="contactManager"/>
|
||||
<property name="serviceInterface" value="sample.contact.ContactManager"/>
|
||||
</bean>
|
||||
-->
|
||||
<!-- Burlap exporter for the ContactManager -->
|
||||
<!-- Burlap is a slim XML-based HTTP remoting protocol -->
|
||||
<!--
|
||||
<bean name="/ContactManager-burlap" class="org.springframework.remoting.caucho.BurlapServiceExporter">
|
||||
<property name="service" ref="contactManager"/>
|
||||
<property name="serviceInterface" value="sample.contact.ContactManager"/>
|
||||
</bean>
|
||||
-->
|
||||
</beans>
|
||||
@@ -0,0 +1,311 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
|
||||
|
||||
<taglib>
|
||||
|
||||
<tlib-version>1.1.1</tlib-version>
|
||||
|
||||
<jsp-version>1.2</jsp-version>
|
||||
|
||||
<short-name>Spring</short-name>
|
||||
|
||||
<uri>http://www.springframework.org/tags</uri>
|
||||
|
||||
<description>Spring Framework JSP Tag Library. Authors: Rod Johnson, Juergen Hoeller</description>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>htmlEscape</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.HtmlEscapeTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Sets default HTML escape value for the current page.
|
||||
Overrides a "defaultHtmlEscape" context-param in web.xml, if any.
|
||||
</description>
|
||||
|
||||
<attribute>
|
||||
<name>defaultHtmlEscape</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>escapeBody</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.EscapeBodyTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Escapes its enclosed body content, applying HTML escaping and/or JavaScript escaping.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<attribute>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>message</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.MessageTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Retrieves the message with the given code, or text if code isn't resolvable.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<attribute>
|
||||
<name>code</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>arguments</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>text</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>theme</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.ThemeTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Retrieves the theme message with the given code, or text if code isn't resolvable.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<attribute>
|
||||
<name>code</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>arguments</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>text</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>hasBindErrors</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.BindErrorsTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Provides Errors instance in case of bind errors.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<variable>
|
||||
<name-given>errors</name-given>
|
||||
<variable-class>org.springframework.validation.Errors</variable-class>
|
||||
</variable>
|
||||
|
||||
<attribute>
|
||||
<name>name</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>nestedPath</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.NestedPathTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Sets a nested path to be used by the bind tag's path.
|
||||
</description>
|
||||
|
||||
<variable>
|
||||
<name-given>nestedPath</name-given>
|
||||
<variable-class>java.lang.String</variable-class>
|
||||
</variable>
|
||||
|
||||
<attribute>
|
||||
<name>path</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>bind</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.BindTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Provides BindStatus object for the given bind path.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<variable>
|
||||
<name-given>status</name-given>
|
||||
<variable-class>org.springframework.web.servlet.support.BindStatus</variable-class>
|
||||
</variable>
|
||||
|
||||
<attribute>
|
||||
<name>path</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>ignoreNestedPath</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>transform</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.TransformTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Provides transformation of variables to Strings, using an appropriate
|
||||
custom PropertyEditor from BindTag (can only be used inside BindTag).
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<attribute>
|
||||
<name>value</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
</taglib>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
- Contacts web application
|
||||
-
|
||||
-->
|
||||
|
||||
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
|
||||
<display-name>Contacts Sample Application</display-name>
|
||||
|
||||
<!--
|
||||
- Location of the XML file that defines the root application context
|
||||
- Applied by ContextLoaderListener.
|
||||
-->
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>
|
||||
classpath:applicationContext-common-business.xml
|
||||
classpath:applicationContext-common-authorization.xml
|
||||
classpath:applicationContext-security.xml
|
||||
</param-value>
|
||||
</context-param>
|
||||
|
||||
<!-- Nothing below here needs to be modified -->
|
||||
|
||||
<context-param>
|
||||
<param-name>webAppRootKey</param-name>
|
||||
<param-value>contacts.root</param-value>
|
||||
</context-param>
|
||||
|
||||
<filter>
|
||||
<filter-name>localizationFilter</filter-name>
|
||||
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>localizationFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!--
|
||||
- Loads the root application context of this web app at startup.
|
||||
- The application context is then available via
|
||||
- WebApplicationContextUtils.getWebApplicationContext(servletContext).
|
||||
-->
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<!--
|
||||
- Provides core MVC application controller. See contacts-servlet.xml.
|
||||
-->
|
||||
<servlet>
|
||||
<servlet-name>contacts</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<!--
|
||||
- Provides web services endpoint. See remoting-servlet.xml.
|
||||
-->
|
||||
<servlet>
|
||||
<servlet-name>remoting</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<load-on-startup>2</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>contacts</servlet-name>
|
||||
<url-pattern>*.htm</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>remoting</servlet-name>
|
||||
<url-pattern>/remoting/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
</web-app>
|
||||
@@ -0,0 +1,22 @@
|
||||
<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %>
|
||||
<%@ page import="org.springframework.security.core.Authentication" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Access Denied</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Sorry, access is denied</h1>
|
||||
|
||||
<p>
|
||||
<%= request.getAttribute("SPRING_SECURITY_403_EXCEPTION")%>
|
||||
</p>
|
||||
<p>
|
||||
<% Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth != null) { %>
|
||||
Authentication object as a String: <%= auth.toString() %><br /><br />
|
||||
<% } %>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<title>Access denied!</title>
|
||||
<h1>Access Denied</h1>
|
||||
<p>We're sorry, but you are not authorized to perform the requested operation.</p>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %>
|
||||
|
||||
<%@ page import="org.springframework.security.core.Authentication" %>
|
||||
<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %>
|
||||
<%@ page pageEncoding="UTF-8" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Exit User</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Exit User</h1>
|
||||
|
||||
<c:if test="${not empty param.login_error}">
|
||||
<font color="red">
|
||||
Your 'Exit User' attempt was not successful, try again.<br/><br/>
|
||||
Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>
|
||||
</font>
|
||||
</c:if>
|
||||
|
||||
<form action="<c:url value='logout/impersonate'/>" method="POST">
|
||||
<table>
|
||||
<tr><td>Current User:</td><td>
|
||||
|
||||
<%
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth != null) { %>
|
||||
|
||||
<%= auth.getPrincipal().toString() %>
|
||||
|
||||
<% } %>
|
||||
</td></tr>
|
||||
<tr><td colspan='2'><input name="exit" type="submit" value="Exit"></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
<%@ include file="/WEB-INF/jsp/include.jsp" %>
|
||||
|
||||
<%-- Redirected because we can't set the welcome page to a virtual URL. --%>
|
||||
<c:redirect url="/hello.htm"/>
|
||||
@@ -0,0 +1,47 @@
|
||||
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ page pageEncoding="UTF-8" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Login</title>
|
||||
</head>
|
||||
|
||||
<body onload="document.f.username.focus();">
|
||||
<h1>Login</h1>
|
||||
|
||||
<p>Valid users:
|
||||
<p>
|
||||
<p>username <b>rod</b>, password <b>koala</b>
|
||||
<p>username <b>dianne</b>, password <b>emu</b>
|
||||
<p>username <b>scott</b>, password <b>wombat</b>
|
||||
<p>username <b>peter</b>, password <b>opal</b> (user disabled)
|
||||
<p>username <b>bill</b>, password <b>wombat</b>
|
||||
<p>username <b>bob</b>, password <b>wombat</b>
|
||||
<p>username <b>jane</b>, password <b>wombat</b>
|
||||
<p>
|
||||
|
||||
<p>Locale is: <%= request.getLocale() %></p>
|
||||
<%-- this form-login-page form is also used as the
|
||||
form-error-page to ask for a login again.
|
||||
--%>
|
||||
<c:if test="${not empty param.login_error}">
|
||||
<font color="red">
|
||||
Your login attempt was not successful, try again.<br/><br/>
|
||||
Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>.
|
||||
</font>
|
||||
</c:if>
|
||||
|
||||
<form name="f" action="<c:url value='login'/>" method="POST">
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>'/></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'></td></tr>
|
||||
<tr><td><input type="checkbox" name="remember-me"></td><td>Don't ask for my password for two weeks</td></tr>
|
||||
|
||||
<tr><td colspan='2'><input name="submit" type="submit"></td></tr>
|
||||
<tr><td colspan='2'><input name="reset" type="reset"></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %>
|
||||
<%@ page import="org.springframework.security.core.Authentication" %>
|
||||
<%@ page import="org.springframework.security.core.GrantedAuthority" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Security Debug Information</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3>Security Debug Information</h3>
|
||||
|
||||
<%
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth != null) { %>
|
||||
<p>
|
||||
Authentication object is of type: <em><%= auth.getClass().getName() %></em>
|
||||
</p>
|
||||
<p>
|
||||
Authentication object as a String: <br/><br/><%= auth.toString() %>
|
||||
</p>
|
||||
|
||||
Authentication object holds the following granted authorities:<br /><br />
|
||||
<%
|
||||
for (GrantedAuthority authority : auth.getAuthorities()) { %>
|
||||
<%= authority %> (<em>getAuthority()</em>: <%= authority.getAuthority() %>)<br />
|
||||
<% }
|
||||
%>
|
||||
|
||||
<p><b>Success! Your web filters appear to be properly configured!</b></p>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
Authentication object is null.<br />
|
||||
This is an error and your Spring Security application will not operate properly until corrected.<br /><br />
|
||||
<% }
|
||||
%>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %>
|
||||
<%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %>
|
||||
<%@ page import="org.springframework.security.core.AuthenticationException" %>
|
||||
<%@ page pageEncoding="UTF-8" %>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Switch User</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Switch to User</h1>
|
||||
|
||||
<h3>Valid users:</h3>
|
||||
|
||||
<p>username <b>rod</b>, password <b>koala</b></p>
|
||||
<p>username <b>dianne</b>, password <b>emu</b></p>
|
||||
<p>username <b>scott</b>, password <b>wombat</b></p>
|
||||
<p>username <b>bill</b>, password <b>wombat</b></p>
|
||||
<p>username <b>bob</b>, password <b>wombat</b></p>
|
||||
<p>username <b>jane</b>, password <b>wombat</b></p>
|
||||
<%-- this form-login-page form is also used as the
|
||||
form-error-page to ask for a login again.
|
||||
--%>
|
||||
<c:if test="${not empty param.login_error}">
|
||||
<p>
|
||||
<font color="red">
|
||||
Your 'su' attempt was not successful, try again.<br/>
|
||||
</font>
|
||||
</p>
|
||||
</c:if>
|
||||
|
||||
<form action="<c:url value='login/impersonate'/>" method="POST">
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username'></td></tr>
|
||||
<tr><td colspan='2'><input name="switch" type="submit" value="Switch to User"></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,99 @@
|
||||
$Id$
|
||||
|
||||
CAS requires HTTPS be used for all operations, with the certificate used
|
||||
having been signed by a certificate in the cacerts files shipped with Java.
|
||||
|
||||
If you're using a HTTPS certificate signed by a well known authority
|
||||
(like Verisign), you can safely ignore the procedure below (although you
|
||||
might find the troubleshooting section at the end helpful).
|
||||
|
||||
The following demonstrates how to create a self-signed certificate and add
|
||||
it to the cacerts file. If you just want to use the certificate we have
|
||||
already created and shipped with Spring Security, you
|
||||
can skip directly to step 3.
|
||||
|
||||
|
||||
1. keytool -keystore keystore -alias acegisecurity -genkey -keyalg RSA -validity 9999 -storepass password -keypass password
|
||||
|
||||
What is your first and last name?
|
||||
[Unknown]: localhost
|
||||
What is the name of your organizational unit?
|
||||
[Unknown]: Spring Security
|
||||
What is the name of your organization?
|
||||
[Unknown]: TEST CERTIFICATE ONLY. DO NOT USE IN PRODUCTION.
|
||||
What is the name of your City or Locality?
|
||||
[Unknown]:
|
||||
What is the name of your State or Province?
|
||||
[Unknown]:
|
||||
What is the two-letter country code for this unit?
|
||||
[Unknown]:
|
||||
Is CN=localhost, OU=Spring Security, O=TEST CERTIFICATE ONLY. D
|
||||
O NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown correct?
|
||||
[no]: yes
|
||||
|
||||
|
||||
2. keytool -export -v -rfc -alias acegisecurity -file acegisecurity.txt -keystore keystore -storepass password
|
||||
|
||||
3. copy acegisecurity.txt %JAVA_HOME%\lib\security
|
||||
|
||||
4. copy keystore %YOUR_WEB_CONTAINER_LOCATION%
|
||||
|
||||
NOTE: You will need to configure your web container as appropriate.
|
||||
We recommend you test the certificate works by visiting
|
||||
https://localhost:8443. When prompted by your browser, select to
|
||||
install the certificate.
|
||||
|
||||
5. cd %JAVA_HOME%\lib\security
|
||||
|
||||
6. keytool -import -v -file acegisecurity.txt -keypass password -keystore cacerts -storepass changeit -alias acegisecurity
|
||||
|
||||
Owner: CN=localhost, OU=Spring Security, O=TEST CERTIFICATE ONL
|
||||
Y. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown
|
||||
Issuer: CN=localhost, OU=Spring Security, O=TEST CERTIFICATE ON
|
||||
LY. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown
|
||||
Serial number: 4080daf4
|
||||
Valid from: Sat Apr 17 07:21:24 GMT 2004 until: Tue Sep 02 07:21:24 GMT 2031
|
||||
Certificate fingerprints:
|
||||
MD5: B4:AC:A8:24:34:99:F1:A9:F8:1D:A5:6C:BF:0A:34:FA
|
||||
SHA1: F1:E6:B1:3A:01:39:2D:CF:06:FA:82:AB:86:0D:77:9D:06:93:D6:B0
|
||||
Trust this certificate? [no]: yes
|
||||
Certificate was added to keystore
|
||||
[Saving cacerts]
|
||||
|
||||
|
||||
7. Finished. You can now run the sample application as if you purchased a
|
||||
properly signed certificate. For production applications, of course you should
|
||||
use an appropriately signed certificate so your web visitors will trust it
|
||||
(such as issued by Thawte, Verisign etc).
|
||||
|
||||
TROUBLESHOOTING
|
||||
|
||||
* First of all, most CAS-Acegi Security problems are because of untrusted
|
||||
SSL certificates. So it's important to understand why. Most people can
|
||||
load the Acegi Security webapp, get redirected to the CAS server, then
|
||||
after login they get redirected back to the Acegi Security webapp and
|
||||
receive a failure. This is because the CAS server redirects to something
|
||||
like https://server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ
|
||||
which causes the "service ticket" (the "ticket" parameter) to be validated.
|
||||
net.sf.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator
|
||||
performs service ticket validation by delegation to CAS'
|
||||
ProxyTicketValidator class. The ProxyTicketValidator class will perform a
|
||||
HTTPS connection from the web server running the Acegi Security webapp
|
||||
(server3.company.com) above to the CAS server. If for some reason the
|
||||
web server keystore does not trust the HTTPS certificate presented by the
|
||||
CAS server, you will receive various failures as discussed below. NB: This
|
||||
has NOTHING to do with client-side (browser) certificates. You need to
|
||||
correct the trust between the two webserver keystores alone.
|
||||
|
||||
* A "sun.security.validator.ValidatorException: No trusted certificate
|
||||
found" indicates the cacerts is not being used or it did not correctly
|
||||
import the certificate. To rule out your web container replacing or in
|
||||
some way modifying the trust manager, set the
|
||||
CasProxyTicketValidator.trustStore property to the full file system
|
||||
location to your cacerts file.
|
||||
|
||||
* If your web container is ignoring your cacerts file, double-check it
|
||||
is stored in $JAVA_HOME\lib\security\cacerts. $JAVA_HOME might be
|
||||
pointing to the SDK, not JRE. In that case, copy
|
||||
$JAVA_HOME\jre\lib\security\cacerts to $JAVA_HOME\lib\security\cacerts
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 sample.contact;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Tests {@link ContactManager}.
|
||||
*
|
||||
* @author David Leal
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@ContextConfiguration(locations = { "/applicationContext-security.xml",
|
||||
"/applicationContext-common-authorization.xml",
|
||||
"/applicationContext-common-business.xml" })
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ContactManagerTests {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
@Autowired
|
||||
protected ContactManager contactManager;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
void assertContainsContact(long id, List<Contact> contacts) {
|
||||
for (Contact contact : contacts) {
|
||||
if (contact.getId().equals(Long.valueOf(id))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fail("List of contacts should have contained: " + id);
|
||||
}
|
||||
|
||||
void assertDoestNotContainContact(long id, List<Contact> contacts) {
|
||||
for (Contact contact : contacts) {
|
||||
if (contact.getId().equals(Long.valueOf(id))) {
|
||||
fail("List of contact should NOT (but did) contain: " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the first <code>Contact</code> of the exact name specified.
|
||||
* <p>
|
||||
* Uses the {@link ContactManager#getAll()} method.
|
||||
*
|
||||
* @param id Identify of the contact to locate (must be an exact match)
|
||||
*
|
||||
* @return the domain or <code>null</code> if not found
|
||||
*/
|
||||
Contact getContact(String id) {
|
||||
for (Contact contact : contactManager.getAll()) {
|
||||
if (contact.getId().equals(id)) {
|
||||
return contact;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void makeActiveUser(String username) {
|
||||
String password = "";
|
||||
|
||||
if ("rod".equals(username)) {
|
||||
password = "koala";
|
||||
}
|
||||
else if ("dianne".equals(username)) {
|
||||
password = "emu";
|
||||
}
|
||||
else if ("scott".equals(username)) {
|
||||
password = "wombat";
|
||||
}
|
||||
else if ("peter".equals(username)) {
|
||||
password = "opal";
|
||||
}
|
||||
|
||||
Authentication authRequest = new UsernamePasswordAuthenticationToken(username,
|
||||
password);
|
||||
SecurityContextHolder.getContext().setAuthentication(authRequest);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDianne() {
|
||||
makeActiveUser("dianne"); // has ROLE_USER
|
||||
|
||||
List<Contact> contacts = contactManager.getAll();
|
||||
assertThat(contacts).hasSize(4);
|
||||
|
||||
assertContainsContact(4, contacts);
|
||||
assertContainsContact(5, contacts);
|
||||
assertContainsContact(6, contacts);
|
||||
assertContainsContact(8, contacts);
|
||||
|
||||
assertDoestNotContainContact(1, contacts);
|
||||
assertDoestNotContainContact(2, contacts);
|
||||
assertDoestNotContainContact(3, contacts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testrod() {
|
||||
makeActiveUser("rod"); // has ROLE_SUPERVISOR
|
||||
|
||||
List<Contact> contacts = contactManager.getAll();
|
||||
|
||||
assertThat(contacts).hasSize(4);
|
||||
|
||||
assertContainsContact(1, contacts);
|
||||
assertContainsContact(2, contacts);
|
||||
assertContainsContact(3, contacts);
|
||||
assertContainsContact(4, contacts);
|
||||
|
||||
assertDoestNotContainContact(5, contacts);
|
||||
|
||||
Contact c1 = contactManager.getById(new Long(4));
|
||||
|
||||
contactManager.deletePermission(c1, new PrincipalSid("bob"),
|
||||
BasePermission.ADMINISTRATION);
|
||||
contactManager.addPermission(c1, new PrincipalSid("bob"),
|
||||
BasePermission.ADMINISTRATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScott() {
|
||||
makeActiveUser("scott"); // has ROLE_USER
|
||||
|
||||
List<Contact> contacts = contactManager.getAll();
|
||||
|
||||
assertThat(contacts).hasSize(5);
|
||||
|
||||
assertContainsContact(4, contacts);
|
||||
assertContainsContact(6, contacts);
|
||||
assertContainsContact(7, contacts);
|
||||
assertContainsContact(8, contacts);
|
||||
assertContainsContact(9, contacts);
|
||||
|
||||
assertDoestNotContainContact(1, contacts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.security" level="${sec.log.level}:-WARN"/>
|
||||
|
||||
<root level="${root.level}:-WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-security-core'),
|
||||
project(':spring-security-acl'),
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-tx:$springVersion",
|
||||
"org.springframework:spring-jdbc:$springVersion"
|
||||
|
||||
testCompile "org.springframework:spring-context:$springVersion"
|
||||
|
||||
runtime project(':spring-security-config'),
|
||||
"org.hsqldb:hsqldb:$hsqlVersion",
|
||||
"org.springframework:spring-context-support:$springVersion"
|
||||
|
||||
optional "net.sf.ehcache:ehcache:$ehcacheVersion"
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-samples-xml-dms</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<name>spring-security-samples-xml-dms</name>
|
||||
<description>spring-security-samples-xml-dms</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
<organization>
|
||||
<name>spring.io</name>
|
||||
<url>http://spring.io/</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>rwinch</id>
|
||||
<name>Rob Winch</name>
|
||||
<email>rwinch@gopivotal.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
|
||||
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-security</url>
|
||||
</scm>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.2.5.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-acl</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Ben Alex
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractElement {
|
||||
/** The name of this token (a filename or directory segment name */
|
||||
private String name;
|
||||
|
||||
/** The parent of this token (a directory, or null if referring to root) */
|
||||
private AbstractElement parent;
|
||||
|
||||
/** The database identifier for this object (null if not persisted) */
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* Constructor to use to represent a root element. A root element has an id of -1.
|
||||
*/
|
||||
protected AbstractElement() {
|
||||
this.name = "/";
|
||||
this.parent = null;
|
||||
this.id = new Long(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to use to represent a non-root element.
|
||||
*
|
||||
* @param name name for this element (required, cannot be "/")
|
||||
* @param parent for this element (required, cannot be null)
|
||||
*/
|
||||
protected AbstractElement(String name, AbstractElement parent) {
|
||||
Assert.hasText(name, "Name required");
|
||||
Assert.notNull(parent, "Parent required");
|
||||
Assert.notNull(parent.getId(),
|
||||
"The parent must have been saved in order to create a child");
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of this token (never null, although will be "/" if root, otherwise
|
||||
* it won't include separators)
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public AbstractElement getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fully-qualified name of this element, including any parents
|
||||
*/
|
||||
public String getFullName() {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
AbstractElement currentElement = this;
|
||||
while (currentElement != null) {
|
||||
strings.add(0, currentElement.getName());
|
||||
currentElement = currentElement.getParent();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String lastCharacter = null;
|
||||
for (Iterator<String> i = strings.iterator(); i.hasNext();) {
|
||||
String token = i.next();
|
||||
if (!"/".equals(lastCharacter) && lastCharacter != null) {
|
||||
sb.append("/");
|
||||
}
|
||||
sb.append(token);
|
||||
lastCharacter = token.substring(token.length() - 1);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Populates the DMS in-memory database with document and ACL information.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class DataSourcePopulator implements InitializingBean {
|
||||
protected static final int LEVEL_NEGATE_READ = 0;
|
||||
protected static final int LEVEL_GRANT_READ = 1;
|
||||
protected static final int LEVEL_GRANT_WRITE = 2;
|
||||
protected static final int LEVEL_GRANT_ADMIN = 3;
|
||||
protected JdbcTemplate template;
|
||||
protected DocumentDao documentDao;
|
||||
|
||||
public DataSourcePopulator(DataSource dataSource, DocumentDao documentDao) {
|
||||
Assert.notNull(dataSource, "DataSource required");
|
||||
Assert.notNull(documentDao, "DocumentDao required");
|
||||
this.template = new JdbcTemplate(dataSource);
|
||||
this.documentDao = documentDao;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// ACL tables
|
||||
template.execute("CREATE TABLE ACL_SID(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,PRINCIPAL BOOLEAN NOT NULL,SID VARCHAR_IGNORECASE(100) NOT NULL,CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));");
|
||||
template.execute("CREATE TABLE ACL_CLASS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,CLASS VARCHAR_IGNORECASE(100) NOT NULL,CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));");
|
||||
template.execute("CREATE TABLE ACL_OBJECT_IDENTITY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,OBJECT_ID_CLASS BIGINT NOT NULL,OBJECT_ID_IDENTITY BIGINT NOT NULL,PARENT_OBJECT BIGINT,OWNER_SID BIGINT,ENTRIES_INHERITING BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY),CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));");
|
||||
template.execute("CREATE TABLE ACL_ENTRY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL,MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL,AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER),CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID),CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));");
|
||||
|
||||
// Normal authentication tables
|
||||
template.execute("CREATE TABLE USERS(USERNAME VARCHAR_IGNORECASE(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR_IGNORECASE(500) NOT NULL,ENABLED BOOLEAN NOT NULL);");
|
||||
template.execute("CREATE TABLE AUTHORITIES(USERNAME VARCHAR_IGNORECASE(50) NOT NULL,AUTHORITY VARCHAR_IGNORECASE(50) NOT NULL,CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME));");
|
||||
template.execute("CREATE UNIQUE INDEX IX_AUTH_USERNAME ON AUTHORITIES(USERNAME,AUTHORITY);");
|
||||
|
||||
// Document management system business tables
|
||||
template.execute("CREATE TABLE DIRECTORY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY, DIRECTORY_NAME VARCHAR_IGNORECASE(50) NOT NULL, PARENT_DIRECTORY_ID BIGINT)");
|
||||
template.execute("CREATE TABLE FILE(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY, FILE_NAME VARCHAR_IGNORECASE(50) NOT NULL, CONTENT VARCHAR_IGNORECASE(1024), PARENT_DIRECTORY_ID BIGINT)");
|
||||
|
||||
// Populate the authentication and role tables
|
||||
template.execute("INSERT INTO USERS VALUES('rod','$2a$10$75pBjapg4Nl8Pzd.3JRnUe7PDJmk9qBGwNEJDAlA3V.dEJxcDKn5O',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('dianne','$2a$04$bCMEyxrdF/7sgfUiUJ6Ose2vh9DAMaVBldS1Bw2fhi1jgutZrr9zm',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('scott','$2a$06$eChwvzAu3TSexnC3ynw4LOSw1qiEbtNItNeYv5uI40w1i3paoSfLu',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('peter','$2a$04$8.H8bCMROLF4CIgd7IpeQ.tcBXLP5w8iplO0n.kCIkISwrIgX28Ii',FALSE);");
|
||||
template.execute("INSERT INTO USERS VALUES('bill','$2a$04$8.H8bCMROLF4CIgd7IpeQ.3khQlPVNWbp8kzSQqidQHGFurim7P8O',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('bob','$2a$06$zMgxlMf01SfYNcdx7n4NpeFlAGU8apCETz/i2C7VlYWu6IcNyn4Ay',TRUE);");
|
||||
template.execute("INSERT INTO USERS VALUES('jane','$2a$05$ZrdS7yMhCZ1J.AAidXZhCOxdjD8LO/dhlv4FJzkXA6xh9gdEbBT/u',TRUE);");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_SUPERVISOR');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('dianne','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('scott','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('peter','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('bill','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('bob','ROLE_USER');");
|
||||
template.execute("INSERT INTO AUTHORITIES VALUES('jane','ROLE_USER');");
|
||||
|
||||
// Now create an ACL entry for the root directory
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken("rod", "ignored", AuthorityUtils
|
||||
.createAuthorityList(("ROLE_IGNORED"))));
|
||||
|
||||
addPermission(documentDao, Directory.ROOT_DIRECTORY, "ROLE_USER",
|
||||
LEVEL_GRANT_WRITE);
|
||||
|
||||
// Now go off and create some directories and files for our users
|
||||
createSampleData("rod", "koala");
|
||||
createSampleData("dianne", "emu");
|
||||
createSampleData("scott", "wombat");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory for the user, and a series of sub-directories. The root
|
||||
* directory is the parent for the user directory. The sub-directories are
|
||||
* "confidential" and "shared". The ROLE_USER will be given read and write access to
|
||||
* "shared".
|
||||
*/
|
||||
private void createSampleData(String username, String password) {
|
||||
Assert.notNull(documentDao, "DocumentDao required");
|
||||
Assert.hasText(username, "Username required");
|
||||
|
||||
Authentication auth = new UsernamePasswordAuthenticationToken(username, password);
|
||||
|
||||
try {
|
||||
// Set the SecurityContextHolder ThreadLocal so any subclasses
|
||||
// automatically know which user is operating
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
// Create the home directory first
|
||||
Directory home = new Directory(username, Directory.ROOT_DIRECTORY);
|
||||
documentDao.create(home);
|
||||
addPermission(documentDao, home, username, LEVEL_GRANT_ADMIN);
|
||||
addPermission(documentDao, home, "ROLE_USER", LEVEL_GRANT_READ);
|
||||
createFiles(documentDao, home);
|
||||
|
||||
// Now create the confidential directory
|
||||
Directory confid = new Directory("confidential", home);
|
||||
documentDao.create(confid);
|
||||
addPermission(documentDao, confid, "ROLE_USER", LEVEL_NEGATE_READ);
|
||||
createFiles(documentDao, confid);
|
||||
|
||||
// Now create the shared directory
|
||||
Directory shared = new Directory("shared", home);
|
||||
documentDao.create(shared);
|
||||
addPermission(documentDao, shared, "ROLE_USER", LEVEL_GRANT_READ);
|
||||
addPermission(documentDao, shared, "ROLE_USER", LEVEL_GRANT_WRITE);
|
||||
createFiles(documentDao, shared);
|
||||
}
|
||||
finally {
|
||||
// Clear the SecurityContextHolder ThreadLocal so future calls are
|
||||
// guaranteed to be clean
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
private void createFiles(DocumentDao documentDao, Directory parent) {
|
||||
Assert.notNull(documentDao, "DocumentDao required");
|
||||
Assert.notNull(parent, "Parent required");
|
||||
int countBeforeInsert = documentDao.findElements(parent).length;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
File file = new File("file_" + i + ".txt", parent);
|
||||
documentDao.create(file);
|
||||
}
|
||||
Assert.isTrue(countBeforeInsert + 10 == documentDao.findElements(parent).length,
|
||||
"Failed to increase count by 10");
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclass to add permissions.
|
||||
*
|
||||
* @param documentDao that will presumably offer methods to enable the operation to be
|
||||
* completed
|
||||
* @param element to the subject of the new permissions
|
||||
* @param recipient to receive permission (if it starts with ROLE_ it is assumed to be
|
||||
* a GrantedAuthority, else it is a username)
|
||||
* @param level based on the static final integer fields on this class
|
||||
*/
|
||||
protected void addPermission(DocumentDao documentDao, AbstractElement element,
|
||||
String recipient, int level) {
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ben Alex
|
||||
*
|
||||
*/
|
||||
public class Directory extends AbstractElement {
|
||||
public static final Directory ROOT_DIRECTORY = new Directory();
|
||||
|
||||
private Directory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Directory(String name, Directory parent) {
|
||||
super(name, parent);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Directory[fullName='" + getFullName() + "'; name='" + getName()
|
||||
+ "'; id='" + getId() + "'; parent='" + getParent() + "']";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ben Alex
|
||||
*
|
||||
*/
|
||||
public interface DocumentDao {
|
||||
/**
|
||||
* Creates an entry in the database for the element.
|
||||
*
|
||||
* @param element an unsaved element (the "id" will be updated after method is
|
||||
* invoked)
|
||||
*/
|
||||
public void create(AbstractElement element);
|
||||
|
||||
/**
|
||||
* Removes a file from the database for the specified element.
|
||||
*
|
||||
* @param file the file to remove (cannot be null)
|
||||
*/
|
||||
public void delete(File file);
|
||||
|
||||
/**
|
||||
* Modifies a file in the database.
|
||||
*
|
||||
* @param file the file to update (cannot be null)
|
||||
*/
|
||||
public void update(File file);
|
||||
|
||||
/**
|
||||
* Locates elements in the database which appear under the presented directory
|
||||
*
|
||||
* @param directory the directory (cannot be null - use
|
||||
* {@link Directory#ROOT_DIRECTORY} for root)
|
||||
* @return zero or more elements in the directory (an empty array may be returned -
|
||||
* never null)
|
||||
*/
|
||||
public AbstractElement[] findElements(Directory directory);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.support.JdbcDaoSupport;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Basic JDBC implementation of {@link DocumentDao}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class DocumentDaoImpl extends JdbcDaoSupport implements DocumentDao {
|
||||
|
||||
private static final String INSERT_INTO_DIRECTORY = "insert into directory(directory_name, parent_directory_id) values (?,?)";
|
||||
private static final String INSERT_INTO_FILE = "insert into file(file_name, content, parent_directory_id) values (?,?,?)";
|
||||
private static final String SELECT_FROM_DIRECTORY = "select id from directory where parent_directory_id = ?";
|
||||
private static final String SELECT_FROM_DIRECTORY_NULL = "select id from directory where parent_directory_id is null";
|
||||
private static final String SELECT_FROM_FILE = "select id, file_name, content, parent_directory_id from file where parent_directory_id = ?";
|
||||
private static final String SELECT_FROM_DIRECTORY_SINGLE = "select id, directory_name, parent_directory_id from directory where id = ?";
|
||||
private static final String DELETE_FROM_FILE = "delete from file where id = ?";
|
||||
private static final String UPDATE_FILE = "update file set content = ? where id = ?";
|
||||
private static final String SELECT_IDENTITY = "call identity()";
|
||||
|
||||
private Long obtainPrimaryKey() {
|
||||
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
|
||||
"Transaction must be running");
|
||||
return getJdbcTemplate().queryForObject(SELECT_IDENTITY, Long.class);
|
||||
}
|
||||
|
||||
public void create(AbstractElement element) {
|
||||
Assert.notNull(element, "Element required");
|
||||
Assert.isNull(element.getId(), "Element has previously been saved");
|
||||
if (element instanceof Directory) {
|
||||
Directory directory = (Directory) element;
|
||||
Long parentId = directory.getParent() == null ? null : directory.getParent()
|
||||
.getId();
|
||||
getJdbcTemplate().update(INSERT_INTO_DIRECTORY,
|
||||
new Object[] { directory.getName(), parentId });
|
||||
FieldUtils.setProtectedFieldValue("id", directory, obtainPrimaryKey());
|
||||
}
|
||||
else if (element instanceof File) {
|
||||
File file = (File) element;
|
||||
Long parentId = file.getParent() == null ? null : file.getParent().getId();
|
||||
getJdbcTemplate().update(INSERT_INTO_FILE,
|
||||
new Object[] { file.getName(), file.getContent(), parentId });
|
||||
FieldUtils.setProtectedFieldValue("id", file, obtainPrimaryKey());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported AbstractElement");
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(File file) {
|
||||
Assert.notNull(file, "File required");
|
||||
Assert.notNull(file.getId(), "File ID required");
|
||||
getJdbcTemplate().update(DELETE_FROM_FILE, new Object[] { file.getId() });
|
||||
}
|
||||
|
||||
/** Executes recursive SQL as needed to build a full Directory hierarchy of objects */
|
||||
private Directory getDirectoryWithImmediateParentPopulated(final Long id) {
|
||||
return getJdbcTemplate().queryForObject(SELECT_FROM_DIRECTORY_SINGLE,
|
||||
new Object[] { id }, new RowMapper<Directory>() {
|
||||
public Directory mapRow(ResultSet rs, int rowNumber)
|
||||
throws SQLException {
|
||||
Long parentDirectoryId = new Long(rs
|
||||
.getLong("parent_directory_id"));
|
||||
Directory parentDirectory = Directory.ROOT_DIRECTORY;
|
||||
if (parentDirectoryId != null
|
||||
&& !parentDirectoryId.equals(new Long(-1))) {
|
||||
// Need to go and lookup the parent, so do that first
|
||||
parentDirectory = getDirectoryWithImmediateParentPopulated(parentDirectoryId);
|
||||
}
|
||||
Directory directory = new Directory(rs
|
||||
.getString("directory_name"), parentDirectory);
|
||||
FieldUtils.setProtectedFieldValue("id", directory,
|
||||
new Long(rs.getLong("id")));
|
||||
return directory;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public AbstractElement[] findElements(Directory directory) {
|
||||
Assert.notNull(directory,
|
||||
"Directory required (the ID can be null to refer to root)");
|
||||
if (directory.getId() == null) {
|
||||
List<Directory> directories = getJdbcTemplate().query(
|
||||
SELECT_FROM_DIRECTORY_NULL, new RowMapper<Directory>() {
|
||||
public Directory mapRow(ResultSet rs, int rowNumber)
|
||||
throws SQLException {
|
||||
return getDirectoryWithImmediateParentPopulated(new Long(rs
|
||||
.getLong("id")));
|
||||
}
|
||||
});
|
||||
return (AbstractElement[]) directories.toArray(new AbstractElement[] {});
|
||||
}
|
||||
List<AbstractElement> directories = getJdbcTemplate().query(
|
||||
SELECT_FROM_DIRECTORY, new Object[] { directory.getId() },
|
||||
new RowMapper<AbstractElement>() {
|
||||
public Directory mapRow(ResultSet rs, int rowNumber)
|
||||
throws SQLException {
|
||||
return getDirectoryWithImmediateParentPopulated(new Long(rs
|
||||
.getLong("id")));
|
||||
}
|
||||
});
|
||||
List<File> files = getJdbcTemplate().query(SELECT_FROM_FILE,
|
||||
new Object[] { directory.getId() }, new RowMapper<File>() {
|
||||
public File mapRow(ResultSet rs, int rowNumber) throws SQLException {
|
||||
Long parentDirectoryId = new Long(rs
|
||||
.getLong("parent_directory_id"));
|
||||
Directory parentDirectory = null;
|
||||
if (parentDirectoryId != null) {
|
||||
parentDirectory = getDirectoryWithImmediateParentPopulated(parentDirectoryId);
|
||||
}
|
||||
File file = new File(rs.getString("file_name"), parentDirectory);
|
||||
FieldUtils.setProtectedFieldValue("id", file,
|
||||
new Long(rs.getLong("id")));
|
||||
return file;
|
||||
}
|
||||
});
|
||||
// Add the File elements after the Directory elements
|
||||
directories.addAll(files);
|
||||
return (AbstractElement[]) directories.toArray(new AbstractElement[] {});
|
||||
}
|
||||
|
||||
public void update(File file) {
|
||||
Assert.notNull(file, "File required");
|
||||
Assert.notNull(file.getId(), "File ID required");
|
||||
getJdbcTemplate().update(UPDATE_FILE,
|
||||
new Object[] { file.getContent(), file.getId() });
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class File extends AbstractElement {
|
||||
/** Content of the file, which can be null */
|
||||
private String content;
|
||||
|
||||
public File(String name, Directory parent) {
|
||||
super(name, parent);
|
||||
Assert.isTrue(!parent.equals(Directory.ROOT_DIRECTORY),
|
||||
"Cannot insert File into root directory");
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "File[fullName='" + getFullName() + "'; name='" + getName() + "'; id='"
|
||||
+ getId() + "'; content=" + getContent() + "'; parent='" + getParent()
|
||||
+ "']";
|
||||
}
|
||||
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms.secured;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.domain.GrantedAuthoritySid;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.MutableAclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import sample.dms.AbstractElement;
|
||||
import sample.dms.DataSourcePopulator;
|
||||
import sample.dms.DocumentDao;
|
||||
|
||||
public class SecureDataSourcePopulator extends DataSourcePopulator {
|
||||
|
||||
private MutableAclService aclService;
|
||||
|
||||
public SecureDataSourcePopulator(DataSource dataSource,
|
||||
SecureDocumentDao documentDao, MutableAclService aclService) {
|
||||
super(dataSource, documentDao);
|
||||
Assert.notNull(aclService, "MutableAclService required");
|
||||
this.aclService = aclService;
|
||||
}
|
||||
|
||||
protected void addPermission(DocumentDao documentDao, AbstractElement element,
|
||||
String recipient, int level) {
|
||||
Assert.notNull(documentDao, "DocumentDao required");
|
||||
Assert.isInstanceOf(SecureDocumentDao.class, documentDao,
|
||||
"DocumentDao should have been a SecureDocumentDao");
|
||||
Assert.notNull(element, "Element required");
|
||||
Assert.hasText(recipient, "Recipient required");
|
||||
Assert.notNull(SecurityContextHolder.getContext().getAuthentication(),
|
||||
"SecurityContextHolder must contain an Authentication");
|
||||
|
||||
// We need SecureDocumentDao to assign different permissions
|
||||
// SecureDocumentDao dao = (SecureDocumentDao) documentDao;
|
||||
|
||||
// We need to construct an ACL-specific Sid. Note the prefix contract is defined
|
||||
// on the superclass method's JavaDocs
|
||||
Sid sid = null;
|
||||
if (recipient.startsWith("ROLE_")) {
|
||||
sid = new GrantedAuthoritySid(recipient);
|
||||
}
|
||||
else {
|
||||
sid = new PrincipalSid(recipient);
|
||||
}
|
||||
|
||||
// We need to identify the target domain object and create an ObjectIdentity for
|
||||
// it
|
||||
// This works because AbstractElement has a "getId()" method
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(element);
|
||||
// ObjectIdentity identity = new ObjectIdentityImpl(element.getClass(),
|
||||
// element.getId()); // equivalent
|
||||
|
||||
// Next we need to create a Permission
|
||||
Permission permission = null;
|
||||
if (level == LEVEL_NEGATE_READ || level == LEVEL_GRANT_READ) {
|
||||
permission = BasePermission.READ;
|
||||
}
|
||||
else if (level == LEVEL_GRANT_WRITE) {
|
||||
permission = BasePermission.WRITE;
|
||||
}
|
||||
else if (level == LEVEL_GRANT_ADMIN) {
|
||||
permission = BasePermission.ADMINISTRATION;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported LEVEL_");
|
||||
}
|
||||
|
||||
// Attempt to retrieve the existing ACL, creating an ACL if it doesn't already
|
||||
// exist for this ObjectIdentity
|
||||
MutableAcl acl = null;
|
||||
try {
|
||||
acl = (MutableAcl) aclService.readAclById(identity);
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
acl = aclService.createAcl(identity);
|
||||
Assert.notNull(acl, "Acl could not be retrieved or created");
|
||||
}
|
||||
|
||||
// Now we have an ACL, add another ACE to it
|
||||
if (level == LEVEL_NEGATE_READ) {
|
||||
acl.insertAce(acl.getEntries().size(), permission, sid, false); // not
|
||||
// granting
|
||||
}
|
||||
else {
|
||||
acl.insertAce(acl.getEntries().size(), permission, sid, true); // granting
|
||||
}
|
||||
|
||||
// Finally, persist the modified ACL
|
||||
aclService.updateAcl(acl);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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 sample.dms.secured;
|
||||
|
||||
import sample.dms.DocumentDao;
|
||||
|
||||
/**
|
||||
* Extends the {@link DocumentDao} and introduces ACL-related methods.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*
|
||||
*/
|
||||
public interface SecureDocumentDao extends DocumentDao {
|
||||
/**
|
||||
* @return all the usernames existing in the system.
|
||||
*/
|
||||
public String[] getUsers();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user