From 4f993d95b5ac49e4839a29a4924e61f8d678b1f8 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 1 Oct 2012 14:46:37 -0500 Subject: [PATCH] Updates for 3.0.x autorepo support --- .gitignore | 23 +- acl/pom.xml | 2 +- acl/template.mf | 21 +- aspects/pom.xml | 4 +- aspects/template.mf | 16 ++ build.gradle | 203 ++++-------------- buildSrc/build.gradle | 31 ++- buildSrc/src/main/groovy/TarUpload.groovy | 75 ------- .../main/groovy/aspectj/AspectJPlugin.groovy | 108 ++++++++++ .../main/groovy/bundlor/BundlorPlugin.groovy | 150 +++++++++++++ .../main/groovy/docbook/DocbookPlugin.groovy | 49 ++++- .../src/main/groovy/emma/EmmaPlugin.groovy | 114 ++++++++++ buildSrc/src/main/groovy/gae/GaePlugin.groovy | 26 +++ .../gradle-plugins/aspectj.properties | 1 + .../gradle-plugins/bundlor.properties | 1 + .../META-INF/gradle-plugins/emma.properties | 1 + .../META-INF/gradle-plugins/gae.properties | 1 + cas/cas.gradle | 2 +- cas/pom.xml | 2 +- cas/template.mf | 24 +-- config/config.gradle | 19 +- config/pom.xml | 2 +- ...LdapProviderBeanDefinitionParserTests.java | 0 .../LdapServerBeanDefinitionParserTests.java | 31 ++- ...pUserServiceBeanDefinitionParserTests.java | 44 ++-- config/template.mf | 32 +-- core/core.gradle | 6 +- core/pom.xml | 2 +- core/template.mf | 28 +-- docs/docs.gradle | 143 ++++++++++++ docs/faq/faq.gradle | 17 -- docs/manual/manual.gradle | 19 -- docs/manual/src/docbook/springsecurity.xml | 2 +- gradle.properties | 1 + gradle/aspectj.gradle | 47 ---- gradle/bundlor.gradle | 31 --- gradle/ide-integration.gradle | 63 ++++++ gradle/javaprojects.gradle | 127 +++++++++-- gradle/maven-deployment.gradle | 51 +++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 46670 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 ++++++++++++++ gradlew.bat | 90 ++++++++ itest/context/itest-context.gradle | 17 ++ itest/context/pom.xml | 2 +- ...amespaceWithMultipleInterceptorsTests.java | 0 .../HttpPathParameterStrippingTests.java | 0 .../integration/MultiAnnotationTests.java | 0 .../SEC933ApplicationContextTests.java | 0 .../SEC936ApplicationContextTests.java | 0 .../integration/StubUserRepository.java | 0 .../PythonInterpreterBasedSecurityTests.java | 0 .../FilterChainPerformanceTests.java | 18 +- .../ProtectPointcutPerformanceTests.java | 0 .../filter-chain-performance-app-context.xml | 0 .../resources/http-extra-fsi-app-context.xml | 0 .../http-path-param-stripping-app-context.xml | 0 .../resources/log4j.properties | 0 .../multi-sec-annotation-app-context.xml | 0 ...otect-pointcut-performance-app-context.xml | 0 .../python-method-access-app-context.xml | 0 .../resources/sec-933-app-context.xml | 0 .../resources/sec-936-app-context.xml | 0 .../resources/someMethod.py | 0 itest/misc/pom.xml | 2 +- itest/pom.xml | 4 +- itest/web/itest-web.gradle | 5 +- itest/web/pom.xml | 2 +- ldap/ldap.gradle | 35 ++- ldap/pom.xml | 2 +- .../ldap/AbstractLdapIntegrationTests.java | 49 +++++ .../ldap/ApacheDSServerIntegrationTests.java} | 77 +++---- ...faultSpringSecurityContextSourceTests.java | 0 .../ldap/SpringSecurityLdapTemplateTests.java | 0 .../BindAuthenticatorTests.java | 0 .../PasswordComparisonAuthenticatorTests.java | 0 .../DefaultLdapAuthoritiesPopulatorTests.java | 0 .../ppolicy/OpenLDAPIntegrationTestSuite.java | 0 .../FilterBasedLdapUserSearchTests.java | 0 .../ldap/server/ApacheDSContainerTests.java | 0 .../DefaultLdapAuthoritiesPopulatorTests.java | 161 ++++++++++++++ .../LdapUserDetailsManagerTests.java | 0 .../resources/logback-test.xml | 18 ++ .../resources/test-server.ldif | 0 ldap/template.mf | 28 +-- openid/openid.gradle | 12 +- openid/pom.xml | 2 +- openid/template.mf | 14 +- parent/parent.gradle | 1 + pom.xml => parent/pom.xml | 2 +- readme.txt | 28 ++- samples/aspectj/pom.xml | 80 ------- samples/cas/Readme.txt | 57 +---- samples/cas/cas.gradle | 9 - samples/cas/pom.xml | 16 -- samples/cas/sample/cassample.gradle | 125 +++++++++++ samples/cas/{client => sample}/pom.xml | 2 +- .../src/main/java/Dummy.java | 0 .../WEB-INF/applicationContext-security.xml | 0 .../webapp/WEB-INF/classes/log4j.properties | 0 .../src/main/webapp/WEB-INF/web.xml | 0 .../src/main/webapp/cas-logout.jsp | 0 .../src/main/webapp/casfailed.jsp | 0 .../src/main/webapp/index.jsp | 0 .../src/main/webapp/secure/extreme/index.jsp | 0 .../src/main/webapp/secure/index.jsp | 0 samples/cas/server/casserver.gradle | 61 ++++++ samples/cas/server/pom.xml | 47 ---- .../src/main/webapp/WEB-INF/classes/log4j.xml | 17 ++ .../zzzhttpClientCustomization.xml | 22 ++ samples/certificates/server.jks | Bin 3488 -> 84658 bytes samples/contacts/contacts.gradle | 19 +- samples/contacts/pom.xml | 144 ------------- samples/dms/dms.gradle | 16 ++ samples/dms/pom.xml | 54 ----- samples/ldap/ldap.gradle | 27 +++ samples/ldap/pom.xml | 106 --------- samples/openid/openid.gradle | 2 + samples/openid/pom.xml | 103 --------- samples/pom.xml | 38 ---- samples/preauth/pom.xml | 89 -------- samples/preauth/preauth.gradle | 27 +++ samples/runall.sh | 2 +- samples/tutorial/pom.xml | 2 +- samples/tutorial/readme.txt | 8 +- samples/tutorial/tutorial.gradle | 33 ++- sandbox/heavyduty/pom.xml | 194 ----------------- sandbox/webflow/pom.xml | 96 --------- settings.gradle | 45 ++-- taglibs/pom.xml | 4 +- taglibs/taglibs.gradle | 3 +- taglibs/template.mf | 24 ++- web/pom.xml | 2 +- web/template.mf | 36 ++-- web/web.gradle | 18 +- 135 files changed, 2104 insertions(+), 1682 deletions(-) create mode 100644 aspects/template.mf delete mode 100644 buildSrc/src/main/groovy/TarUpload.groovy create mode 100644 buildSrc/src/main/groovy/aspectj/AspectJPlugin.groovy create mode 100644 buildSrc/src/main/groovy/bundlor/BundlorPlugin.groovy create mode 100644 buildSrc/src/main/groovy/emma/EmmaPlugin.groovy create mode 100644 buildSrc/src/main/groovy/gae/GaePlugin.groovy create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/aspectj.properties create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/bundlor.properties create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/emma.properties create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/gae.properties rename config/src/{test => integration-test}/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParserTests.java (100%) rename config/src/{test => integration-test}/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java (61%) rename config/src/{test => integration-test}/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java (82%) create mode 100644 docs/docs.gradle delete mode 100644 docs/faq/faq.gradle delete mode 100644 docs/manual/manual.gradle create mode 100644 gradle.properties delete mode 100644 gradle/aspectj.gradle delete mode 100644 gradle/bundlor.gradle create mode 100644 gradle/ide-integration.gradle create mode 100644 gradle/maven-deployment.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 itest/context/itest-context.gradle rename itest/context/src/{test => integration-test}/java/org/springframework/security/integration/HttpNamespaceWithMultipleInterceptorsTests.java (100%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/integration/HttpPathParameterStrippingTests.java (100%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/integration/MultiAnnotationTests.java (100%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/integration/SEC933ApplicationContextTests.java (100%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/integration/SEC936ApplicationContextTests.java (100%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/integration/StubUserRepository.java (100%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/integration/python/PythonInterpreterBasedSecurityTests.java (100%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/performance/FilterChainPerformanceTests.java (94%) rename itest/context/src/{test => integration-test}/java/org/springframework/security/performance/ProtectPointcutPerformanceTests.java (100%) rename itest/context/src/{test => integration-test}/resources/filter-chain-performance-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/http-extra-fsi-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/http-path-param-stripping-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/log4j.properties (100%) rename itest/context/src/{test => integration-test}/resources/multi-sec-annotation-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/protect-pointcut-performance-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/python-method-access-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/sec-933-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/sec-936-app-context.xml (100%) rename itest/context/src/{test => integration-test}/resources/someMethod.py (100%) create mode 100644 ldap/src/integration-test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java rename ldap/src/{test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java => integration-test/java/org/springframework/security/ldap/ApacheDSServerIntegrationTests.java} (58%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java (100%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java (100%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java (100%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticatorTests.java (100%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java (100%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/ppolicy/OpenLDAPIntegrationTestSuite.java (100%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java (100%) rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/server/ApacheDSContainerTests.java (100%) create mode 100644 ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java rename ldap/src/{test => integration-test}/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java (100%) create mode 100644 ldap/src/integration-test/resources/logback-test.xml rename ldap/src/{test => integration-test}/resources/test-server.ldif (100%) create mode 100644 parent/parent.gradle rename pom.xml => parent/pom.xml (99%) delete mode 100644 samples/aspectj/pom.xml delete mode 100644 samples/cas/cas.gradle delete mode 100644 samples/cas/pom.xml create mode 100644 samples/cas/sample/cassample.gradle rename samples/cas/{client => sample}/pom.xml (99%) rename samples/cas/{client => sample}/src/main/java/Dummy.java (100%) rename samples/cas/{client => sample}/src/main/webapp/WEB-INF/applicationContext-security.xml (100%) rename samples/cas/{client => sample}/src/main/webapp/WEB-INF/classes/log4j.properties (100%) rename samples/cas/{client => sample}/src/main/webapp/WEB-INF/web.xml (100%) rename samples/cas/{client => sample}/src/main/webapp/cas-logout.jsp (100%) rename samples/cas/{client => sample}/src/main/webapp/casfailed.jsp (100%) rename samples/cas/{client => sample}/src/main/webapp/index.jsp (100%) rename samples/cas/{client => sample}/src/main/webapp/secure/extreme/index.jsp (100%) rename samples/cas/{client => sample}/src/main/webapp/secure/index.jsp (100%) create mode 100644 samples/cas/server/casserver.gradle delete mode 100644 samples/cas/server/pom.xml create mode 100644 samples/cas/server/src/main/webapp/WEB-INF/classes/log4j.xml create mode 100644 samples/cas/server/src/main/webapp/WEB-INF/spring-configuration/zzzhttpClientCustomization.xml delete mode 100644 samples/contacts/pom.xml create mode 100644 samples/dms/dms.gradle delete mode 100644 samples/dms/pom.xml create mode 100644 samples/ldap/ldap.gradle delete mode 100644 samples/ldap/pom.xml delete mode 100644 samples/openid/pom.xml delete mode 100644 samples/pom.xml delete mode 100644 samples/preauth/pom.xml create mode 100644 samples/preauth/preauth.gradle delete mode 100755 sandbox/heavyduty/pom.xml delete mode 100644 sandbox/webflow/pom.xml diff --git a/.gitignore b/.gitignore index 19f72f531d..e56d60133e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,21 @@ target/ -.classpath -.project -.settings/ */src/*/java/META-INF -bin/ -build/ +*/src/META-INF/ +*/src/*/java/META-INF/ +.classpath +.springBeans +.project +.DS_Store +.settings/ +.idea/ out/ +bin/ intellij/ +build/ +*.log +*.log.* +*.iml *.ipr *.iws -*.log -*.log.1 -.DS_Store -*.iml .gradle/ -gradle.properties atlassian-ide-plugin.xml diff --git a/acl/pom.xml b/acl/pom.xml index 4ca4662779..3181e4c0c4 100644 --- a/acl/pom.xml +++ b/acl/pom.xml @@ -3,7 +3,7 @@ spring-security-parent org.springframework.security - 3.0.8.CI-SNAPSHOT + @pomVersion@ 4.0.0 org.springframework.security diff --git a/acl/template.mf b/acl/template.mf index 7abb563aca..d92dd43b47 100644 --- a/acl/template.mf +++ b/acl/template.mf @@ -9,14 +9,15 @@ Ignored-Existing-Headers: Import-Package, Export-Package Import-Template: - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.springframework.security.core.*;version="[${version}, 3.1.0)", - org.springframework.security.access.*;version="[${version}, 3.1.0)", - org.springframework.security.util.*;version="[${version}, 3.1.0)", - org.springframework.context.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.dao.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.jdbc.core.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.transaction.support.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.util.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional, + org.aopalliance.*;version="${aopAllianceRange}", + org.apache.commons.logging.*;version="${cloggingRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.access.*;version="${secRange}", + org.springframework.security.util.*;version="${secRange}", + org.springframework.context.*;version="${springRange}";resolution:=optional, + org.springframework.dao.*;version="${springRange}";resolution:=optional, + org.springframework.jdbc.core.*;version="${springRange}";resolution:=optional, + org.springframework.transaction.support.*;version="${springRange}";resolution:=optional, + org.springframework.util.*;version="${springRange}";resolution:=optional, + net.sf.ehcache.*;version="${ehcacheRange}";resolution:=optional, javax.sql.*;version="0";resolution:=optional diff --git a/aspects/pom.xml b/aspects/pom.xml index d328d0ae18..1ddc2555af 100644 --- a/aspects/pom.xml +++ b/aspects/pom.xml @@ -5,7 +5,7 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ jar spring-security-aspects @@ -18,7 +18,7 @@ org.springframework.security spring-security-core - 3.0.8.CI-SNAPSHOT + @pomVersion@ diff --git a/aspects/template.mf b/aspects/template.mf new file mode 100644 index 0000000000..5f43a718e0 --- /dev/null +++ b/aspects/template.mf @@ -0,0 +1,16 @@ +Implementation-Title: org.springframework.security.aspects +Implementation-Version: ${version} +Bundle-SymbolicName: org.springframework.security.aspects +Bundle-Name: Spring Security Aspects +Bundle-Vendor: SpringSource +Bundle-ManifestVersion: 2 +Bundle-Version: ${version} +Ignored-Existing-Headers: + Import-Package, + Export-Package +Import-Template: + org.aspectj.*;version="${aspectjRange}";resolution:=optional, + org.apache.commons.logging.*;version="${cloggingRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.access.intercept.aspectj;version="${secRange}", + org.springframework.beans.factory;version="${springRange}" diff --git a/build.gradle b/build.gradle index fba90870d3..efddbb1e0f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,120 +1,58 @@ apply plugin: 'base' +description = 'Spring Security' allprojects { - version = '3.0.8.CI-SNAPSHOT' - releaseBuild = version.endsWith('RELEASE') - snapshotBuild = version.endsWith('SNAPSHOT') + ext.releaseBuild = version.endsWith('RELEASE') + ext.snapshotBuild = version.endsWith('SNAPSHOT') group = 'org.springframework.security' repositories { - mavenRepo name:'Local', urls: "file://" + System.properties['user.home'] + "/.m2/repository" mavenCentral() - mavenRepo name: 'SpringSource Milestone Repo', urls: 'http://repository.springsource.com/maven/bundles/milestone' - mavenRepo name: 'SpringSource Maven Snapshot Repo', urls: 'http://maven.springframework.org/snapshot/' - mavenRepo name: 'SpringSource Enterprise Release', urls: 'http://repository.springsource.com/maven/bundles/release' - mavenRepo name: 'SpringSource Enterprise External', urls: 'http://repository.springsource.com/maven/bundles/external' } } +// Set up different subproject lists for individual configuration +ext.javaProjects = subprojects.findAll { project -> project.name != 'docs' && project.name != 'faq' && project.name != 'manual' } +ext.sampleProjects = subprojects.findAll { project -> project.name.startsWith('spring-security-samples') } +ext.itestProjects = subprojects.findAll { project -> project.name.startsWith('itest') } +ext.coreModuleProjects = javaProjects - sampleProjects - itestProjects +ext.aspectjProjects = [project(':spring-security-aspects'), project(':spring-security-samples-aspectj')] configure(javaProjects) { apply from: "$rootDir/gradle/javaprojects.gradle" - apply from: "$rootDir/gradle/maven.gradle" } configure(coreModuleProjects) { - apply from: "$rootDir/gradle/bundlor.gradle" // Gives better names in structure101 jar diagram - sourceSets.main.classesDir = new File(buildDir, "classes/" + project.name.substring("spring-security".length() + 1)) + sourceSets.main.output.classesDir = new File(buildDir, "classes/" + project.name.substring("spring-security".length() + 1)) + apply plugin: 'bundlor' + bundlor.expansions = bundlorProperties + apply from: "$rootDir/gradle/maven-deployment.gradle" + apply plugin: 'emma' +} + +task coreBuild { + dependsOn coreModuleProjects*.tasks*.matching { task -> task.name == 'build' } } configure (aspectjProjects) { - apply from: "$rootDir/gradle/aspectj.gradle" + apply plugin: 'aspectj' } -configurations { - antlibs -} - -dependencies { - antlibs "org.springframework.build:org.springframework.build.aws.ant:3.0.3.RELEASE", - "net.java.dev.jets3t:jets3t:0.6.1" -} - -task apidocs(type: Javadoc) { - destinationDir = new File(buildDir, 'apidocs') - title = "Spring Security $version API" - optionsFile = file("$buildDir/tmp/javadoc.options") - - source coreModuleProjects.collect {project -> - project.sourceSets.main.allJava - } - - classpath = files(coreModuleProjects.collect {project -> - project.sourceSets.main.compileClasspath - }) -} - -task docSiteLogin(type: Login) { - if (project.hasProperty('sshHost')) { - host = project.property('sshHost') - } -} - -// Define remoteSiteDir and sshHost in gradle.properties -def remoteDocsDir = null - -if (hasProperty('remoteSiteDir')) { - remoteDocsDir="$remoteSiteDir/docs/3.0.x" -} - -task uploadApidocs(type: TarUpload) { - dependsOn apidocs - classifier = 'apidocs' - remoteDir = remoteDocsDir - login = docSiteLogin - - into('apidocs') { - from apidocs.destinationDir - } -} - -def docsDir = new File(project(':manual').buildDir, 'docs') - -task uploadDoc(type: TarUpload) { - dependsOn ':manual:doc' - classifier = 'doc' - remoteDir = remoteDocsDir - login = docSiteLogin - - into('reference') { - from docsDir - } -} - -task uploadFaq(type: TarUpload) { - dependsOn ':faq:docbookHtmlSingle' - classifier = 'faq' - if (project.hasProperty('remoteSiteDir')) { - remoteDir = project.property('remoteSiteDir') - } - login = docSiteLogin - - def faqDir = new File(project(':faq').buildDir, 'docs') - - into('faq') { - from faqDir - } -} +// Task for creating the distro zip task dist(type: Zip) { + dependsOn subprojects*.tasks*.matching { task -> task.name == 'assemble' || task.name.endsWith('Zip') } + classifier = 'dist' + evaluationDependsOn(':docs') def zipRootDir = "${project.name}-$version" into(zipRootDir) { - into('docs/apidocs') { - from apidocs.destinationDir + from(rootDir) { + include '*.txt' } - into('docs/reference') { - from docsDir + into('docs') { + with(project(':docs').apiSpec) + with(project(':docs:manual').spec) } into('dist') { from coreModuleProjects.collect {project -> project.libsDir } @@ -124,83 +62,14 @@ task dist(type: Zip) { } } -dist { - dependsOn apidocs, ':manual:doc', subprojects.collect { "$it.path:assemble" } - doLast { - ant.checksum(file: archivePath, algorithm: 'SHA1', fileext: '.sha1') - } +artifacts { + archives dist + archives project(':docs').docsZip + archives project(':docs').schemaZip } -task uploadDist(type: UploadDist) { - archiveFile = dist.archivePath - shaFile = "${dist.archivePath}.sha1" as File - archiveName = dist.archiveName - classpath = configurations.antlibs +apply from: "$rootDir/gradle/ide-integration.gradle" + +task wrapper(type: Wrapper) { + gradleVersion = '1.1' } - -def getJavaProjects() { - subprojects.findAll {project -> project.name != 'faq' && project.name != 'manual' } -} - -def getSampleProjects() { - subprojects.findAll {project -> project.name.startsWith('spring-security-samples') } -} - -def getItestProjects() { - subprojects.findAll {project -> project.name.startsWith('itest') } -} - -def getCoreModuleProjects() { - javaProjects - sampleProjects - itestProjects - aspectjProjects -} - -def getAspectjProjects() { - subprojects.findAll {project -> project.name == 'spring-security-aspects' || project.name == 'spring-security-samples-aspectj'} -} - -class UploadDist extends DefaultTask { - @InputFile - File shaFile - - @InputFile - File archiveFile - - @Input - String archiveName - - @InputFiles - def classpath - - @TaskAction - def upload() { - def accessKey = project.s3AccessKey - def secretKey = project.s3SecretAccessKey - def version = project.version - - project.ant { - taskdef(resource: 'org/springframework/build/aws/ant/antlib.xml', classpath: classpath.asPath) - s3(accessKey: accessKey, secretKey: secretKey) { - upload(bucketName: 'dist.springframework.org', file: archiveFile, - toFile: releaseType() + "/SEC/${archiveName}", publicRead: 'true') { - metadata(name: 'project.name', value: 'Spring Security') - metadata(name: 'release.type', value: releaseType()) - metadata(name: 'bundle.version', value: version) - metadata(name: 'package.file.name', value: archiveName) - } - upload(bucketName: 'dist.springframework.org', file: shaFile, - toFile: releaseType() + "/SEC/${archiveName}.sha1", publicRead: 'true') - } - } - } - - def releaseType() { - if (project.releaseBuild) { - 'release' - } else if (project.snapshotBuild) { - 'snapshot' - } else { - 'milestone' - } - } -} - diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index c0cdf8c470..97d8b139a8 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,11 +1,18 @@ apply plugin: 'groovy' repositories { - mavenRepo name:'localRepo', urls: "file://" + System.properties['user.home'] + "/.m2/repository" mavenCentral() - mavenRepo name:'Shibboleth Repo', urls:'http://shibboleth.internet2.edu/downloads/maven2' + maven { + name = 'SpringSource Enterprise Release' + url = 'http://repository.springsource.com/maven/bundles/release' + } + maven { + name = 'SpringSource Enterprise External' + url = 'http://repository.springsource.com/maven/bundles/external' + } } +// Docbook Plugin dependencies { def fopDeps = [ 'org.apache.xmlgraphics:fop:0.95-1@jar', 'org.apache.xmlgraphics:xmlgraphics-commons:1.3', @@ -17,15 +24,31 @@ dependencies { 'org.apache.avalon.framework:avalon-framework-api:4.3.1'] groovy localGroovy() compile gradleApi(), - 'org.apache.xerces:resolver:2.9.1', + 'xml-resolver:xml-resolver:1.2', + 'xerces:xercesImpl:2.9.1', 'saxon:saxon:6.5.3', - 'org.apache.xerces:xercesImpl:2.9.1', + 'net.java.dev.jets3t:jets3t:0.6.1', fopDeps runtime 'net.sf.xslthl:xslthl:2.0.1', 'net.sf.docbook:docbook-xsl:1.75.2:ns-resources@zip' } +// GAE +dependencies { + compile 'com.google.appengine:appengine-tools-sdk:1.4.2' +} + +dependencies{ + compile "emma:emma:2.0.5312" +} + +// Bundlor +dependencies { + compile 'com.springsource.bundlor:com.springsource.bundlor:1.0.0.RELEASE', + 'com.springsource.bundlor:com.springsource.bundlor.blint:1.0.0.RELEASE' +} + task ide(type: Copy) { from configurations.runtime into 'ide' diff --git a/buildSrc/src/main/groovy/TarUpload.groovy b/buildSrc/src/main/groovy/TarUpload.groovy deleted file mode 100644 index ab0234865e..0000000000 --- a/buildSrc/src/main/groovy/TarUpload.groovy +++ /dev/null @@ -1,75 +0,0 @@ -import org.gradle.api.DefaultTask; -import org.gradle.api.tasks.*; -import org.gradle.api.tasks.bundling.Tar; -import org.gradle.api.tasks.bundling.Compression; - -/** - * Extends the Tar task, uploading the created archive to a remote directory, unpacking and deleting it. - * Requires Ant ssh (jsch) support. - */ -class TarUpload extends Tar { - @Input - String remoteDir - Login login - @Input - String host - - TarUpload() { - compression = Compression.BZIP2 - if (project.configurations.findByName('antjsch') == null) { - project.configurations.add('antjsch') - project.dependencies { - antjsch 'org.apache.ant:ant-jsch:1.8.1' - } - def classpath = project.configurations.antjsch.asPath - project.ant { - taskdef(name: 'scp', classname: 'org.apache.tools.ant.taskdefs.optional.ssh.Scp', classpath: classpath) - taskdef(name: 'sshexec', classname: 'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec', classpath: classpath) - } - } - } - - @TaskAction - void copy() { - super.copy(); - upload(); - } - - def upload() { - String username = login.username - String password = login.password - String host = login.host - project.ant { - scp(file: archivePath, todir: "$username@$host:$remoteDir", password: password) - sshexec(host: host, username: username, password: password, command: "cd $remoteDir && tar -xjf $archiveName") - sshexec(host: host, username: username, password: password, command: "rm $remoteDir/$archiveName") - } - } - - void setLogin(Login login) { - dependsOn(login) - this.login = login - this.host = login.host - } -} - -/** - * Stores login information for a remote host. - */ -class Login extends DefaultTask { - @Input - String host - String username - String password - - @TaskAction - login() { - def console = System.console() - if (console) { - username = console.readLine("\nPlease enter the ssh username for host '$host': ") - password = new String(console.readPassword("Please enter the ssh password for '$host': ")) - } else { - logger.error "Unable to access System.console()." - } - } -} diff --git a/buildSrc/src/main/groovy/aspectj/AspectJPlugin.groovy b/buildSrc/src/main/groovy/aspectj/AspectJPlugin.groovy new file mode 100644 index 0000000000..2e0751545a --- /dev/null +++ b/buildSrc/src/main/groovy/aspectj/AspectJPlugin.groovy @@ -0,0 +1,108 @@ +package aspectj + +import org.gradle.api.Project +import org.gradle.api.Plugin +import org.gradle.api.tasks.TaskAction +import org.gradle.api.logging.LogLevel +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.SourceSet +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException + +import org.gradle.plugins.ide.eclipse.GenerateEclipseProject +import org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath +import org.gradle.plugins.ide.eclipse.EclipsePlugin +import org.gradle.plugins.ide.eclipse.model.BuildCommand +import org.gradle.plugins.ide.eclipse.model.ProjectDependency + +/** + * + * @author Luke Taylor + */ +class AspectJPlugin implements Plugin { + + void apply(Project project) { + if (!project.hasProperty('aspectjVersion')) { + throw new GradleException("You must set the property 'aspectjVersion' before applying the aspectj plugin") + } + + if (project.configurations.findByName('ajtools') == null) { + project.configurations.add('ajtools') + project.dependencies { + ajtools "org.aspectj:aspectjtools:${project.aspectjVersion}" + compile "org.aspectj:aspectjrt:${project.aspectjVersion}" + } + } + + if (project.configurations.findByName('aspectpath') == null) { + project.configurations.add('aspectpath') + } + + project.tasks.add(name: 'compileJava', overwrite: true, description: 'Compiles AspectJ Source', type: Ajc) { + dependsOn project.processResources + sourceSet = project.sourceSets.main + inputs.files(sourceSet.java.srcDirs) + outputs.dir(sourceSet.output.classesDir) + aspectPath = project.configurations.aspectpath + } + + project.tasks.add(name: 'compileTestJava', overwrite: true, description: 'Compiles AspectJ Test Source', type: Ajc) { + dependsOn project.processTestResources, project.compileJava, project.jar + sourceSet = project.sourceSets.test + inputs.files(sourceSet.java.srcDirs) + outputs.dir(sourceSet.output.classesDir) + aspectPath = project.files(project.configurations.aspectpath, project.jar.archivePath) + } + + project.tasks.withType(GenerateEclipseProject) { + project.eclipse.project.file.whenMerged { p -> + p.natures.add(0, 'org.eclipse.ajdt.ui.ajnature') + p.buildCommands = [new BuildCommand('org.eclipse.ajdt.core.ajbuilder')] + } + } + + project.tasks.withType(GenerateEclipseClasspath) { + project.eclipse.classpath.file.whenMerged { classpath -> + def entries = classpath.entries.findAll { it instanceof ProjectDependency}.findAll { entry -> + def projectPath = entry.path.replaceAll('/',':') +println projectPath + project.rootProject.allprojects.find{ p-> + if(p.plugins.findPlugin(EclipsePlugin)) { + println " checking " + p.eclipse.project.name + return p.eclipse.project.name == projectPath && p.plugins.findPlugin(AspectJPlugin) + } + false + } + } + entries.each { entry-> + entry.entryAttributes.put('org.eclipse.ajdt.aspectpath','org.eclipse.ajdt.aspectpath') + } + } + } + } +} + +class Ajc extends DefaultTask { + SourceSet sourceSet + FileCollection aspectPath + + Ajc() { + logging.captureStandardOutput(LogLevel.INFO) + } + + @TaskAction + def compile() { + logger.info("Running ajc ...") + ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: project.configurations.ajtools.asPath) + ant.iajc(classpath: sourceSet.compileClasspath.asPath, fork: 'true', destDir: sourceSet.output.classesDir.absolutePath, + source: project.convention.plugins.java.sourceCompatibility, + target: project.convention.plugins.java.targetCompatibility, + aspectPath: aspectPath.asPath, sourceRootCopyFilter: '**/*.java', showWeaveInfo: 'true') { + sourceroots { + sourceSet.java.srcDirs.each { + pathelement(location: it.absolutePath) + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/bundlor/BundlorPlugin.groovy b/buildSrc/src/main/groovy/bundlor/BundlorPlugin.groovy new file mode 100644 index 0000000000..a48e1248e9 --- /dev/null +++ b/buildSrc/src/main/groovy/bundlor/BundlorPlugin.groovy @@ -0,0 +1,150 @@ +package bundlor + +import com.springsource.bundlor.ClassPath +import com.springsource.bundlor.ManifestGenerator +import com.springsource.bundlor.ManifestWriter +import com.springsource.bundlor.blint.ManifestValidator +import com.springsource.bundlor.blint.support.DefaultManifestValidatorContributorsFactory +import com.springsource.bundlor.blint.support.StandardManifestValidator +import com.springsource.bundlor.support.DefaultManifestGeneratorContributorsFactory +import com.springsource.bundlor.support.StandardManifestGenerator +import com.springsource.bundlor.support.classpath.FileSystemClassPath +import com.springsource.bundlor.support.manifestwriter.FileSystemManifestWriter +import com.springsource.bundlor.support.properties.EmptyPropertiesSource +import com.springsource.bundlor.support.properties.FileSystemPropertiesSource +import com.springsource.bundlor.support.properties.PropertiesPropertiesSource +import com.springsource.bundlor.support.properties.PropertiesSource +import com.springsource.bundlor.util.BundleManifestUtils +import com.springsource.util.parser.manifest.ManifestContents +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.FileCollection +import org.gradle.api.logging.LogLevel +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +/** + * @author Luke Taylor + */ +class BundlorPlugin implements Plugin { + void apply(Project project) { + Task bundlor = project.tasks.add('bundlor', Bundlor.class) + bundlor.setDescription('Generates OSGi manifest using bundlor tool') + bundlor.dependsOn(project.classes) + project.jar.dependsOn bundlor + } +} + +public class Bundlor extends DefaultTask { + @InputFile + @Optional + File manifestTemplate + + @OutputDirectory + File bundlorDir = new File("${project.buildDir}/bundlor") + + @OutputFile + File manifest = project.file("${bundlorDir}/META-INF/MANIFEST.MF") + + @Input + Map expansions = [:] + + @InputFile + @Optional + File osgiProfile + + @InputFiles + @Optional + FileCollection inputPaths + + @Input + boolean failOnWarnings = false + + Bundlor() { + manifestTemplate = new File(project.projectDir, 'template.mf') + + if (!manifestTemplate.exists()) { + logger.info("No bundlor template for project " + project.name) + manifestTemplate = null + } + + inputPaths = project.files(project.sourceSets.main.output.classesDir) + + if (manifestTemplate != null) { + project.jar.manifest.from manifest + project.jar.inputs.files manifest + } + } + + @TaskAction + void createManifest() { + if (manifestTemplate == null) { + return; + } + + logging.captureStandardOutput(LogLevel.INFO) + + project.mkdir(bundlorDir) + + //String inputPath = project.sourceSets.main.classesDir + + List inputClassPath = [] as List; + + ManifestWriter manifestWriter = new FileSystemManifestWriter(project.file(bundlorDir.absolutePath)); + ManifestContents mfTemplate = BundleManifestUtils.getManifest(manifestTemplate); + + inputPaths.each {f -> + inputClassPath.add(new FileSystemClassPath(f)) + } + + // Must be a better way of doing this... + Properties p = new Properties() + expansions.each {entry -> + p.setProperty(entry.key, entry.value as String) + } + + PropertiesSource expansionProps = new PropertiesPropertiesSource(p) + PropertiesSource osgiProfileProps = osgiProfile == null ? new EmptyPropertiesSource() : + new FileSystemPropertiesSource(osgiProfile); + + ManifestGenerator manifestGenerator = new StandardManifestGenerator( + DefaultManifestGeneratorContributorsFactory.create(expansionProps, osgiProfileProps)); + + ManifestContents mf = manifestGenerator.generate(mfTemplate, inputClassPath.toArray(new ClassPath[inputClassPath.size()])); + + try { + manifestWriter.write(mf); + } finally { + manifestWriter.close(); + } + + ManifestValidator manifestValidator = new StandardManifestValidator(DefaultManifestValidatorContributorsFactory.create()); + + List warnings = manifestValidator.validate(mf); + + if (warnings.isEmpty()) { + return + } + + logger.warn("Bundlor Warnings:"); + for (String warning : warnings) { + logger.warn(" " + warning); + } + + if (failOnWarnings) { + throw new GradleException("Bundlor returned warnings. Please fix manifest template at " + manifestTemplate.absolutePath + " and try again.") + } + } + + def inputPath(FileCollection paths) { + inputPaths = project.files(inputPaths, paths) + } +} diff --git a/buildSrc/src/main/groovy/docbook/DocbookPlugin.groovy b/buildSrc/src/main/groovy/docbook/DocbookPlugin.groovy index 24e84de9ad..d9c699c061 100644 --- a/buildSrc/src/main/groovy/docbook/DocbookPlugin.groovy +++ b/buildSrc/src/main/groovy/docbook/DocbookPlugin.groovy @@ -48,6 +48,9 @@ class DocbookPlugin implements Plugin { Task docbookFoPdf = project.tasks.add("docbookFoPdf", DocbookFoPdf.class); docbookFoPdf.setDescription('Generates PDF output'); docbookFoPdf.extension = 'fo' + + Task docbook = project.tasks.add("docbook", DefaultTask.class); + docbook.dependsOn (docbookHtml, docbookHtmlSingle, docbookFoPdf) } } @@ -69,6 +72,8 @@ public class Docbook extends DefaultTask { String admonGraphicsPath; + String imgSrcPath; + @InputDirectory File sourceDirectory = new File(project.getProjectDir(), "src/docbook"); @@ -87,7 +92,7 @@ public class Docbook extends DefaultTask { factory.setXIncludeAware(XIncludeAware); docsDir.mkdirs(); - File srcFile = new File(sourceDirectory, sourceFileName); + File srcFile = new File(filterDocbookSources(sourceDirectory), sourceFileName); String outputFilename = srcFile.getName().substring(0, srcFile.getName().length() - 4) + suffix + '.' + extension; File outputFile = new File(getDocsDir(), outputFilename); @@ -112,11 +117,15 @@ public class Docbook extends DefaultTask { } transformer.setParameter("highlight.xslthl.config", new File(highlightingDir, "xslthl-config.xml").toURI().toURL()); + } - if (admonGraphicsPath != null) { - transformer.setParameter("admon.graphics", "1"); - transformer.setParameter("admon.graphics.path", admonGraphicsPath); - } + if (admonGraphicsPath != null) { + transformer.setParameter("admon.graphics", "1"); + transformer.setParameter("admon.graphics.path", admonGraphicsPath); + } + + if (imgSrcPath != null) { + transformer.setParameter("img.src.path", imgSrcPath); } preTransform(transformer, srcFile, outputFile); @@ -126,6 +135,32 @@ public class Docbook extends DefaultTask { postTransform(outputFile); } + /** + * @param sourceDir directory of unfiltered sources + * @return directory of filtered sources + * @author Chris Beams + */ + private File filterDocbookSources(File sourceDir) { + def docbookWorkDir = new File("${project.buildDir}/reference-work") + + docbookWorkDir.mkdirs() + + // copy everything but springsecurity.xml + project.copy { + into(docbookWorkDir) + from(sourceDir) { exclude '**/springsecurity.xml' } + } + // copy index.xml and expand ${...} variables along the way + // e.g.: ${version} needs to be replaced in the header + project.copy { + into(docbookWorkDir) + from(sourceDir) { include '**/springsecurity.xml' } + expand(version: "${project.version}") + } + + return docbookWorkDir + } + private void extractHighlightFiles(File toDir) { URLClassLoader cl = (URLClassLoader) getClass().getClassLoader(); URL[] urls = cl.getURLs(); @@ -254,9 +289,9 @@ class DocbookFoPdf extends Docbook { } } - if (!foFile.delete()) { +/* if (!foFile.delete()) { logger.warn("Failed to delete 'fo' file " + foFile); - } + }*/ } private File getPdfOutputFile(File foFile) { diff --git a/buildSrc/src/main/groovy/emma/EmmaPlugin.groovy b/buildSrc/src/main/groovy/emma/EmmaPlugin.groovy new file mode 100644 index 0000000000..f325019468 --- /dev/null +++ b/buildSrc/src/main/groovy/emma/EmmaPlugin.groovy @@ -0,0 +1,114 @@ +package emma; + +import org.gradle.api.* +import org.gradle.api.tasks.testing.Test +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.Input +import com.vladium.emma.instr.InstrProcessor +import com.vladium.emma.report.ReportProcessor +import org.gradle.api.tasks.InputFiles +import com.vladium.util.XProperties; + +/** + * + * @author Luke Taylor + */ +class EmmaPlugin implements Plugin { + + void apply(Project project) { + Project rootProject = project.rootProject + def emmaMetaDataFile = "${rootProject.buildDir}/emma/emma.em" + def emmaCoverageFile = "${rootProject.buildDir}/emma/emma.ec" + + if (project.configurations.findByName('emma_rt') == null) { + project.configurations.add('emma_rt') + project.dependencies { + emma_rt 'emma:emma:2.0.5312' + } + } + + project.task('emmaInstrument') { + dependsOn project.classes + + doFirst { + InstrProcessor processor = InstrProcessor.create (); + String[] classesDirPath = [project.sourceSets.main.output.classesDir.absolutePath] + + processor.setInstrPath(classesDirPath, false); + processor.setOutMode(InstrProcessor.OutMode.OUT_MODE_COPY); + processor.setInstrOutDir("${project.buildDir}/emma/classes"); + processor.setMetaOutFile(emmaMetaDataFile); + processor.setMetaOutMerge(true); + //processor.setInclExclFilter (null); + processor.run(); + } + } + + // Modify test tasks in the project to generate coverage data + project.afterEvaluate { + if (project.hasProperty('coverage') && ['on','true'].contains(project.properties.coverage)) { + project.tasks.withType(Test.class).each { task -> + task.dependsOn project.emmaInstrument + task.configure() { + jvmArgs '-Dsec.log.level=DEBUG', "-Demma.coverage.out.file=$emmaCoverageFile" + jvmArgs '-Demma.verbosity.level=quiet' + } + task.doFirst { + classpath = project.files("${project.buildDir}/emma/classes") + project.configurations.emma_rt + classpath + } + } + } + } + + List reportTasks = rootProject.getTasksByName('coverageReport', false) as List; + CoverageReport task; + + if (reportTasks.isEmpty()) { + task = rootProject.tasks.add('coverageReport', CoverageReport.class); + task.dataPath = [emmaMetaDataFile, emmaCoverageFile]; + } else { + task = reportTasks[0]; + } + + task.modules.add(project); + } +} + +class CoverageReport extends DefaultTask { + @Input + List modules = []; + + @Input + String[] dataPath; + + @TaskAction + void generateReport() { + def buildDir = project.rootProject.buildDir + + if (!buildDir.exists()) { + throw new GradleException("No coverage data. Run gradle with -Pcoverage=on if using coverageReport"); + } + + ReportProcessor processor = ReportProcessor.create (); + processor.setDataPath(dataPath) + + def srcPath = [] + modules.each {module-> + module.sourceSets.main.java.srcDirs.each { + srcPath.add(it.absolutePath) + } + } + + processor.setSourcePath(srcPath as String[]); + + + def types = ['txt', 'html'] + processor.setReportTypes(types as String[]); + XProperties properties = new XProperties(); + properties.setProperty('report.html.out.file', "$buildDir/emma/coverage.html"); + properties.setProperty('report.txt.out.file', "$buildDir/emma/coverage.txt"); + processor.setPropertyOverrides(properties) + + processor.run() + } +} diff --git a/buildSrc/src/main/groovy/gae/GaePlugin.groovy b/buildSrc/src/main/groovy/gae/GaePlugin.groovy new file mode 100644 index 0000000000..b131c8c316 --- /dev/null +++ b/buildSrc/src/main/groovy/gae/GaePlugin.groovy @@ -0,0 +1,26 @@ +package gae; + +import com.google.appengine.tools.admin.AppCfg +import org.gradle.api.*; + +class GaePlugin implements Plugin { + public void apply(Project project) { + if (!project.hasProperty('appEngineSdkRoot')) { + println "'appEngineSdkRoot' must be set in gradle.properties" + } else { + System.setProperty('appengine.sdk.root', project.property('appEngineSdkRoot')) + } + + File explodedWar = new File(project.buildDir, "gae-exploded") + + project.task('gaeDeploy') << { + AppCfg.main("update", explodedWar.toString()) + } + + project.gaeDeploy.dependsOn project.war + + project.war.doLast { + ant.unzip(src: project.war.archivePath, dest: explodedWar) + } + } +} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/aspectj.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/aspectj.properties new file mode 100644 index 0000000000..3e8a23c083 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/aspectj.properties @@ -0,0 +1 @@ +implementation-class=aspectj.AspectJPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/bundlor.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/bundlor.properties new file mode 100644 index 0000000000..483572f837 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/bundlor.properties @@ -0,0 +1 @@ +implementation-class=bundlor.BundlorPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/emma.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/emma.properties new file mode 100644 index 0000000000..29a6d56444 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/emma.properties @@ -0,0 +1 @@ +implementation-class=emma.EmmaPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/gae.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/gae.properties new file mode 100644 index 0000000000..d6c44963b6 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/gae.properties @@ -0,0 +1 @@ +implementation-class=gae.GaePlugin \ No newline at end of file diff --git a/cas/cas.gradle b/cas/cas.gradle index 7b4945de3e..291c09d2ba 100644 --- a/cas/cas.gradle +++ b/cas/cas.gradle @@ -10,4 +10,4 @@ dependencies { "net.sf.ehcache:ehcache:$ehcacheVersion" provided 'javax.servlet:servlet-api:2.5' -} \ No newline at end of file +} \ No newline at end of file diff --git a/cas/pom.xml b/cas/pom.xml index 9334bf1b62..dc0b373ef9 100644 --- a/cas/pom.xml +++ b/cas/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ spring-security-cas-client Spring Security - CAS support diff --git a/cas/template.mf b/cas/template.mf index 06da716830..b508b16119 100644 --- a/cas/template.mf +++ b/cas/template.mf @@ -5,18 +5,18 @@ Bundle-Name: Spring Security CAS Bundle-Vendor: SpringSource Bundle-Version: ${version} Bundle-ManifestVersion: 2 -Ignored-Existing-Headers: +Ignored-Existing-Headers: Import-Package, Export-Package -Import-Template: - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.jasig.cas.client.*;version="[3.1.1,3.2)", - org.springframework.security.core.*;version="[${version}, 3.1.0)", - org.springframework.security.authentication.*;version="[${version}, 3.1.0)", - org.springframework.security.web.*;version="[${version}, 3.1.0)", - org.springframework.beans.factory;version="[${spring.version}, 3.1.0)", - org.springframework.context.*;version="[${spring.version}, 3.1.0)", - org.springframework.dao;version="[${spring.version}, 3.1.0)", - org.springframework.util;version="[${spring.version}, 3.1.0)", - net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional, +Import-Template: + org.apache.commons.logging.*;version="${cloggingRange}", + org.jasig.cas.client.*;version="${casRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.authentication.*;version="${secRange}", + org.springframework.security.web.*;version="${secRange}", + org.springframework.beans.factory;version="${springRange}", + org.springframework.context.*;version="${springRange}", + org.springframework.dao;version="${springRange}", + org.springframework.util;version="${springRange}", + net.sf.ehcache.*;version="${ehcacheRange}";resolution:=optional, javax.servlet.*;version="0" diff --git a/config/config.gradle b/config/config.gradle index 87661e85a1..7e7275cfba 100644 --- a/config/config.gradle +++ b/config/config.gradle @@ -17,8 +17,23 @@ dependencies { testCompile project(':spring-security-ldap'), project(':spring-security-openid'), - files(this.project(':spring-security-core').sourceSets.test.classesDir), + project(':spring-security-core').sourceSets.test.output, 'javax.annotation:jsr250-api:1.0', "org.springframework.ldap:spring-ldap-core:$springLdapVersion", - "org.springframework:spring-jdbc:$springVersion" + "org.springframework:spring-expression:$springVersion", + "org.springframework:spring-jdbc:$springVersion", + "org.springframework:spring-tx:$springVersion", + 'org.spockframework:spock-core:0.6-groovy-1.8', + "org.slf4j:jcl-over-slf4j:$slf4jVersion" + testCompile('org.openid4java:openid4java-nodeps:0.9.6') { + exclude group: 'com.google.code.guice', module: 'guice' + } + + + testRuntime "hsqldb:hsqldb:$hsqlVersion", + "cglib:cglib-nodep:2.2" +} + +integrationTest { + systemProperties['apacheDSWorkDir'] = "${buildDir}/apacheDSWork" } diff --git a/config/pom.xml b/config/pom.xml index 120ac463e2..0fc76059bd 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ jar spring-security-config diff --git a/config/src/test/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParserTests.java b/config/src/integration-test/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParserTests.java similarity index 100% rename from config/src/test/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParserTests.java rename to config/src/integration-test/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParserTests.java diff --git a/config/src/test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java b/config/src/integration-test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java similarity index 61% rename from config/src/test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java rename to config/src/integration-test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java index 72f44f9f13..ed6bf73ec9 100644 --- a/config/src/test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java +++ b/config/src/integration-test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java @@ -1,14 +1,31 @@ +/* + * Copyright 2002-2012 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.config.ldap; +import static org.junit.Assert.*; + import org.junit.After; import org.junit.Test; import org.springframework.ldap.core.LdapTemplate; import org.springframework.security.config.BeanIds; import org.springframework.security.config.util.InMemoryXmlApplicationContext; import org.springframework.security.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.server.ApacheDSContainer; +import org.springframework.test.util.ReflectionTestUtils; /** * @author Luke Taylor + * @author Rob Winch */ public class LdapServerBeanDefinitionParserTests { InMemoryXmlApplicationContext appCtx; @@ -23,7 +40,7 @@ public class LdapServerBeanDefinitionParserTests { @Test public void embeddedServerCreationContainsExpectedContextSourceAndData() { - appCtx = new InMemoryXmlApplicationContext(""); + appCtx = new InMemoryXmlApplicationContext(""); DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx.getBean(BeanIds.CONTEXT_SOURCE); @@ -35,8 +52,8 @@ public class LdapServerBeanDefinitionParserTests { @Test public void useOfUrlAttributeCreatesCorrectContextSource() { // Create second "server" with a url pointing at embedded one - appCtx = new InMemoryXmlApplicationContext("" + - ""); + appCtx = new InMemoryXmlApplicationContext("" + + ""); // Check the default context source is still there. appCtx.getBean(BeanIds.CONTEXT_SOURCE); @@ -58,6 +75,12 @@ public class LdapServerBeanDefinitionParserTests { template.lookup("uid=pg,ou=gorillas"); } + @Test + public void defaultLdifFileIsSuccessful() { + appCtx = new InMemoryXmlApplicationContext( + ""); + ApacheDSContainer dsContainer = appCtx.getBean(ApacheDSContainer.class); - + assertEquals("classpath*:*.ldif", ReflectionTestUtils.getField(dsContainer, "ldifResources")); + } } diff --git a/config/src/test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java b/config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java similarity index 82% rename from config/src/test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java rename to config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java index 2f1d44fe1d..d862ca4e9d 100644 --- a/config/src/test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java +++ b/config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java @@ -1,17 +1,24 @@ +/* + * Copyright 2002-2012 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.config.ldap; import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.*; import static org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser.*; -import java.util.Set; - -import org.junit.After; -import org.junit.Test; -import org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser; +import org.junit.*; import org.springframework.security.config.util.InMemoryXmlApplicationContext; import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; @@ -24,8 +31,11 @@ import org.springframework.security.ldap.userdetails.Person; import org.springframework.security.ldap.userdetails.PersonContextMapper; import org.w3c.dom.Element; +import java.util.*; + /** * @author Luke Taylor + * @author Rob Winch */ public class LdapUserServiceBeanDefinitionParserTests { private InMemoryXmlApplicationContext appCtx; @@ -50,12 +60,12 @@ public class LdapUserServiceBeanDefinitionParserTests { @Test public void minimalConfigurationIsParsedOk() throws Exception { - setContext(""); + setContext(""); } @Test public void userServiceReturnsExpectedData() throws Exception { - setContext(""); + setContext(""); UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS"); UserDetails ben = uds.loadUserByUsername("ben"); @@ -70,7 +80,7 @@ public class LdapUserServiceBeanDefinitionParserTests { setContext(""); + " group-search-filter='member={0}' />"); UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS"); UserDetails joe = uds.loadUserByUsername("Joe Smeth"); @@ -86,7 +96,7 @@ public class LdapUserServiceBeanDefinitionParserTests { " group-search-filter='member={0}' role-prefix='PREFIX_'/>" + ""); + " group-search-filter='member={0}' role-prefix='none'/>"); UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS"); UserDetails ben = uds.loadUserByUsername("ben"); @@ -101,21 +111,21 @@ public class LdapUserServiceBeanDefinitionParserTests { @Test public void differentGroupRoleAttributeWorksAsExpected() throws Exception { - setContext(""); + setContext(""); UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS"); UserDetails ben = uds.loadUserByUsername("ben"); Set authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities()); assertEquals(3, authorities.size()); - assertTrue(authorities.contains(new GrantedAuthorityImpl("ROLE_DEVELOPER"))); + assertTrue(authorities.contains("ROLE_DEVELOPER")); } @Test public void isSupportedByAuthenticationProviderElement() { setContext( - "" + + "" + "" + " " + " " + @@ -126,7 +136,7 @@ public class LdapUserServiceBeanDefinitionParserTests { @Test public void personContextMapperIsSupported() { setContext( - "" + + "" + ""); UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS"); UserDetails ben = uds.loadUserByUsername("ben"); @@ -136,7 +146,7 @@ public class LdapUserServiceBeanDefinitionParserTests { @Test public void inetOrgContextMapperIsSupported() { setContext( - "" + + "" + ""); UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS"); UserDetails ben = uds.loadUserByUsername("ben"); @@ -146,7 +156,7 @@ public class LdapUserServiceBeanDefinitionParserTests { @Test public void externalContextMapperIsSupported() { setContext( - "" + + "" + "" + ""); diff --git a/config/template.mf b/config/template.mf index 251edc81d8..cc76467216 100644 --- a/config/template.mf +++ b/config/template.mf @@ -5,22 +5,24 @@ Bundle-Name: Spring Security Namespace Configuration Bundle-Vendor: SpringSource Bundle-Version: ${version} Bundle-ManifestVersion: 2 -Ignored-Existing-Headers: +Ignored-Existing-Headers: Import-Package, Export-Package -Import-Template: - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.aspectj.*;version="[1.6.0, 1.7.0)";resolution:=optional, - org.springframework.security.access.*;version="[${version}, 3.1.0)", - org.springframework.security.authentication.*;version="[${version}, 3.1.0)", - org.springframework.security.core.*;version="[${version}, 3.1.0)", - org.springframework.security.util;version="[${version}, 3.1.0)", - org.springframework.security.web.*;version="[${version}, 3.1.0)";resolution:=optional, - org.springframework.aop.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.beans.*;version="[${spring.version}, 3.1.0)", - org.springframework.context.*;version="[${spring.version}, 3.1.0)", - org.springframework.core.*;version="[${spring.version}, 3.1.0)", - org.springframework.util.*;version="[${spring.version}, 3.1.0)", - javax.servlet;version="0";resolution:=optional, +Import-Template: + org.apache.commons.logging.*;version="${cloggingRange}", + org.aspectj.*;version="${aspectjRange}";resolution:=optional, + org.springframework.security.access.*;version="${secRange}", + org.springframework.security.authentication.*;version="${secRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.util;version="${secRange}", + org.springframework.security.provisioning;version="${secRange}", + org.springframework.security.web.*;version="${secRange}";resolution:=optional, + org.springframework.aop.*;version="${springRange}";resolution:=optional, + org.springframework.beans.*;version="${springRange}", + org.springframework.context.*;version="${springRange}", + org.springframework.core.*;version="${springRange}", + org.springframework.web.*;version="${springRange}", + org.springframework.util.*;version="${springRange}", + javax.servlet.*;version="0";resolution:=optional, javax.naming.directory;version="0";resolution:=optional, org.w3c.dom;version="0";resolution:=optional diff --git a/core/core.gradle b/core/core.gradle index 4e7fc54aa6..27036e68c0 100644 --- a/core/core.gradle +++ b/core/core.gradle @@ -17,5 +17,9 @@ dependencies { runtime 'hsqldb:hsqldb:1.8.0.10' testCompile 'commons-collections:commons-collections:3.2', - "org.springframework:spring-test:$springVersion" + "org.springframework:spring-test:$springVersion", + "org.slf4j:jcl-over-slf4j:$slf4jVersion" + + testRuntime "hsqldb:hsqldb:$hsqlVersion", + "cglib:cglib-nodep:$cglibVersion" } diff --git a/core/pom.xml b/core/pom.xml index 9aafc0e420..badb511fb9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ jar spring-security-core diff --git a/core/template.mf b/core/template.mf index 81918e516b..4560dd006c 100644 --- a/core/template.mf +++ b/core/template.mf @@ -9,20 +9,20 @@ Ignored-Existing-Headers: Import-Package, Export-Package Import-Template: - org.aopalliance.*;version="[1.0.0, 2.0.0)", - org.aspectj.*;version="[1.6.0, 1.7.0)";resolution:=optional, - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.springframework.aop.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.beans.*;version="[${spring.version}, 3.1.0)", - org.springframework.context.*;version="[${spring.version}, 3.1.0)", - org.springframework.core.*;version="[${spring.version}, 3.1.0)", - org.springframework.expression.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.remoting.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.dao.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.jdbc.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.transaction.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.util;version="[${spring.version}, 3.1.0)", - net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional, + org.aopalliance.*;version="${aopAllianceRange}", + org.aspectj.*;version="${aspectjRange}";resolution:=optional, + org.apache.commons.logging.*;version="${cloggingRange}", + org.springframework.aop.*;version="${springRange}";resolution:=optional, + org.springframework.beans.*;version="${springRange}", + org.springframework.context.*;version="${springRange}", + org.springframework.core.*;version="${springRange}", + org.springframework.expression.*;version="${springRange}";resolution:=optional, + org.springframework.remoting.*;version="${springRange}";resolution:=optional, + org.springframework.dao.*;version="${springRange}";resolution:=optional, + org.springframework.jdbc.*;version="${springRange}";resolution:=optional, + org.springframework.transaction.*;version="${springRange}";resolution:=optional, + org.springframework.util;version="${springRange}", + net.sf.ehcache.*;version="${ehcacheRange}";resolution:=optional, javax.annotation.security.*;version="0";resolution:=optional, javax.crypto.*;version="0";resolution:=optional, javax.security.auth.*;version="0";resolution:=optional, diff --git a/docs/docs.gradle b/docs/docs.gradle new file mode 100644 index 0000000000..685ac7dbd6 --- /dev/null +++ b/docs/docs.gradle @@ -0,0 +1,143 @@ + // Docbook and Javadoc building and uploading tasks +apply plugin: 'base' + +task docs { + dependsOn 'manual:docbook', 'faq:docbookHtmlSingle', 'apidocs' +} + +subprojects { + apply plugin: 'base' + apply plugin: 'docbook' + + docbookHtmlSingle.stylesheet = new File(projectDir, 'src/xsl/html-single-custom.xsl') +} + +project('faq') { + defaultTasks 'docbookHtmlSingle' + [docbookHtml, docbookFoPdf, docbookHtmlSingle]*.sourceFileName = 'faq.xml' + docbookHtmlSingle.suffix = '' + + ext.spec = copySpec { + into ('faq') { + from("$buildDir/docs") + from("$projectDir/src/resources") + } + } +} + +project('manual') { + defaultTasks 'docbookHtml', 'docbookHtmlSingle', 'docbookFoPdf' + [docbookHtml, docbookFoPdf, docbookHtmlSingle]*.sourceFileName = 'springsecurity.xml' + + docbookHtml.stylesheet = new File(projectDir, 'src/xsl/html-custom.xsl') + docbookHtmlSingle.stylesheet = new File(projectDir, 'src/xsl/html-single-custom.xsl') + docbookFoPdf.stylesheet = new File(projectDir, 'src/xsl/pdf-custom.xsl') + def imagesDir = new File(projectDir, 'src/docbook/images'); +// docbookFoPdf.admonGraphicsPath = "${imagesDir}/" + docbookFoPdf.imgSrcPath = "${projectDir}/src/docbook/" + + ext.spec = copySpec { + into ('reference') { + from("$buildDir/docs") + from("$projectDir/src/resources") + } + into ('reference/images') { + from (imagesDir) + } + } +} + +task reference (type: Copy) { + dependsOn 'manual:docbook' + destinationDir = buildDir + with(project('manual').spec) +} + +task apidocs(type: Javadoc) { + destinationDir = new File(buildDir, 'apidocs') + title = "Spring Security $version API" + + source coreModuleProjects.collect { project -> + project.sourceSets.main.allJava + } + + classpath = files(coreModuleProjects.collect { project -> + project.sourceSets.main.compileClasspath + }) +} + +apidocs.options.outputLevel = org.gradle.external.javadoc.JavadocOutputLevel.QUIET + +apidocs.options.links = [ + "http://static.springframework.org/spring/docs/3.0.x/javadoc-api", + "http://static.springsource.org/spring-ldap/docs/1.3.x/apidocs/", + "http://download.oracle.com/javase/6/docs/api/" +] + +apidocs.options.groups = [ + 'Spring Security Core':[ + 'org.springframework.security.core*', + 'org.springframework.security.authentication*', + 'org.springframework.security.access*', + 'org.springframework.security.remoting*', + 'org.springframework.security.provisioning*', + 'org.springframework.security.util*'], + 'Spring Security Web':['org.springframework.security.web*'], + 'Spring Security LDAP':['org.springframework.security.ldap*'], + 'Spring Security Crypto':['org.springframework.security.crypto*'], + 'Spring Security OpenID':['org.springframework.security.openid*'], + 'Spring Security CAS':['org.springframework.security.cas*'], + 'Spring Security ACL':['org.springframework.security.acls*'], + 'Spring Security Config':['org.springframework.security.config*'], + 'Spring Security Taglibs':['org.springframework.security.taglibs*'], + +] + +ext.apiSpec = copySpec { + into('apidocs') { + from(apidocs.destinationDir) + } +} + +assemble.dependsOn = [apidocs, 'manual:docbook'] + +task docsZip(type: Zip) { + dependsOn docs + group = 'Distribution' + baseName = rootProject.name + classifier = 'docs' + description = "Builds -${classifier} archive containing api and reference " + + "for deployment at static.springframework.org/spring-security/site/docs." + + with(project(':docs').apiSpec) + with(project(':docs:manual').spec) + with(project(':docs:faq').spec) +} + +task schemaZip(type: Zip) { + group = 'Distribution' + baseName = rootProject.name + classifier = 'schema' + description = "Builds -${classifier} archive containing all " + + "XSDs for deployment at static.springframework.org/schema." + + coreModuleProjects.each { module -> + def Properties schemas = new Properties(); + + module.sourceSets.main.resources.find { + it.path.endsWith('META-INF/spring.schemas') + }?.withInputStream { schemas.load(it) } + + for (def key : schemas.keySet()) { + def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') + assert shortName != key + File xsdFile = module.sourceSets.main.resources.find { + it.path.endsWith(schemas.get(key)) + } + assert xsdFile != null + into (shortName) { + from xsdFile.path + } + } + } +} diff --git a/docs/faq/faq.gradle b/docs/faq/faq.gradle deleted file mode 100644 index 2680a455e3..0000000000 --- a/docs/faq/faq.gradle +++ /dev/null @@ -1,17 +0,0 @@ -apply plugin: 'base' -apply plugin: 'docbook' - -defaultTasks 'docbookHtmlSingle' - -[docbookHtml, docbookFoPdf, docbookHtmlSingle]*.sourceFileName = 'faq.xml' - -docbookHtmlSingle.stylesheet = new File(projectDir, 'src/xsl/html-single-custom.xsl') -docbookHtmlSingle.suffix = '' - -docbookHtmlSingle.doLast { - resourcesDir = new File(projectDir, 'src/resources') - ant { - docsDir = new File(buildDir, 'docs') - copy(toDir: docsDir) {fileset(dir: resourcesDir)} - } -} diff --git a/docs/manual/manual.gradle b/docs/manual/manual.gradle deleted file mode 100644 index a1f7b6f28a..0000000000 --- a/docs/manual/manual.gradle +++ /dev/null @@ -1,19 +0,0 @@ -apply plugin: 'base' -apply plugin: 'docbook' - -[docbookHtml, docbookFoPdf, docbookHtmlSingle]*.sourceFileName = 'springsecurity.xml'; - -docbookHtml.stylesheet = new File(projectDir, 'src/xsl/html-custom.xsl') -docbookHtmlSingle.stylesheet = new File(projectDir, 'src/xsl/html-single-custom.xsl') -docbookFoPdf.stylesheet = new File(projectDir, 'src/xsl/pdf-custom.xsl') -def imagesDir = new File(projectDir, 'src/docbook/images'); -docbookFoPdf.admonGraphicsPath = "${imagesDir}/" - -task doc (dependsOn: [docbookHtml, docbookHtmlSingle, docbookFoPdf]) << { - resourcesDir = new File(projectDir, 'src/resources') - ant { - docsDir = new File(buildDir, 'docs') - copy(toDir: docsDir) {fileset(dir: resourcesDir)} - copy(toDir: new File(docsDir, 'images')) {fileset(dir: imagesDir)} - } -} diff --git a/docs/manual/src/docbook/springsecurity.xml b/docs/manual/src/docbook/springsecurity.xml index 3028a3bc1b..82c77c0ebb 100644 --- a/docs/manual/src/docbook/springsecurity.xml +++ b/docs/manual/src/docbook/springsecurity.xml @@ -10,7 +10,7 @@ Spring Security - 3.0.7.RELEASE + ${version} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000..d8b19fe0ce --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +version=3.0.8.CI-SNAPSHOT \ No newline at end of file diff --git a/gradle/aspectj.gradle b/gradle/aspectj.gradle deleted file mode 100644 index 0d7cab37e4..0000000000 --- a/gradle/aspectj.gradle +++ /dev/null @@ -1,47 +0,0 @@ -apply plugin: 'java' - -configurations { - ajtools - aspectpath -} - -dependencies { - ajtools "org.aspectj:aspectjtools:$aspectjVersion" - compile "org.aspectj:aspectjrt:$aspectjVersion" -} - -task compileJava(overwrite: true, description: 'Compiles AspectJ Source', type: Ajc) { - dependsOn processResources - sourceSet = sourceSets.main - aspectPath = configurations.aspectpath -} - -task compileTestJava(overwrite: true, description: 'Compiles AspectJ Test Source', type: Ajc) { - dependsOn processTestResources, compileJava, jar - sourceSet = sourceSets.test - aspectPath = files(configurations.aspectpath, jar.archivePath) -} - -class Ajc extends DefaultTask { - @Input - SourceSet sourceSet - - @Input - FileCollection aspectPath - - @TaskAction - def compile() { - println "Running ajc ..." - ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: project.configurations.ajtools.asPath) - ant.iajc(classpath: sourceSet.compileClasspath.asPath, fork: 'true', destDir: sourceSet.classesDir.absolutePath, - source: project.convention.plugins.java.sourceCompatibility, - target: project.convention.plugins.java.targetCompatibility, - aspectPath: aspectPath.asPath, sourceRootCopyFilter: '**/*.java', showWeaveInfo: 'true') { - sourceroots { - sourceSet.java.srcDirs.each { - pathelement(location: it.absolutePath) - } - } - } - } -} \ No newline at end of file diff --git a/gradle/bundlor.gradle b/gradle/bundlor.gradle deleted file mode 100644 index b081c9e1d4..0000000000 --- a/gradle/bundlor.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: 'java' - -configurations { - bundlor -} - -dependencies { - bundlor 'com.springsource.bundlor:com.springsource.bundlor.ant:1.0.0.RELEASE', - 'com.springsource.bundlor:com.springsource.bundlor:1.0.0.RELEASE', - 'com.springsource.bundlor:com.springsource.bundlor.blint:1.0.0.RELEASE' -} - -task bundlor(dependsOn: compileJava) { - onlyIf { - dependsOnTaskDidWork() - } - doFirst { - ant.taskdef(resource: 'com/springsource/bundlor/ant/antlib.xml', classpath: configurations.bundlor.asPath) - File template = new File(projectDir, 'template.mf') - mkdir("$buildDir/bundlor") - if (template.exists()) { - ant.bundlor(inputPath: sourceSets.main.classesDir, outputPath: "$buildDir/bundlor", manifestTemplatePath: template) { - property(name: 'version', value: "$version") - property(name: 'spring.version', value: "$springVersion") - } - jar.manifest.from("$buildDir/bundlor/META-INF/MANIFEST.MF") - } - } -} - -jar.dependsOn bundlor diff --git a/gradle/ide-integration.gradle b/gradle/ide-integration.gradle new file mode 100644 index 0000000000..aa1821b535 --- /dev/null +++ b/gradle/ide-integration.gradle @@ -0,0 +1,63 @@ +apply plugin: 'idea' + +configure(javaProjects) { + apply plugin: 'eclipse-wtp' + + eclipse.classpath.downloadSources = true + + // GRADLE-1116 + project.eclipse.classpath.file.whenMerged { classpath -> + classpath.entries.removeAll { entry -> entry.path.endsWith('/build/classes/test') } + } + + // GRADLE-1422 + project.eclipseClasspath.doFirst { + // delay adding whenMerged till the entryAttributes are added (must be the last whenMerged) + project.eclipse.classpath.file.whenMerged { classpath -> + def includeDeps = project.configurations.getByName('runtime').collect {f -> f.absolutePath } as Set + classpath.entries.each { cp -> + if(cp instanceof org.gradle.plugins.ide.eclipse.model.Library) { + def include = includeDeps.contains(cp.path) + def attr = 'org.eclipse.jst.component.dependency' + if(!include) { + cp.entryAttributes.remove(attr) + } + } + } + } + } + + tasks.withType(org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent) { + project.eclipse.classpath.file.whenMerged { classpath-> + project.eclipse.wtp.component.file.whenMerged { wtpComponent -> + wtpComponent.contextPath = project.tasks.findByName('jettyRun')?.contextPath?.replaceFirst('/','') + } + } + } +} + +// STS-2723 +project(':spring-security-samples-aspectj') { + task afterEclipseImport { + ext.srcFile = file('.classpath') + inputs.file srcFile + outputs.dir srcFile + + onlyIf { srcFile.exists() } + + doLast { + def classpath = new XmlParser().parse(srcFile) + + classpath.classpathentry.findAll{ it.@path == '/spring-security-aspects' }.each { node -> + if(node.children().size() == 0) { + def attrs = new Node(node,'attributes') + def adjtAttr = new Node(attrs,'attributes',[name: 'org.eclipse.ajdt.aspectpath', value: 'org.eclipse.ajdt.aspectpath']) + node.appendNode(adjtAttr) + } + } + + def writer = new FileWriter(srcFile) + new XmlNodePrinter(new PrintWriter(writer)).print(classpath) + } + } +} diff --git a/gradle/javaprojects.gradle b/gradle/javaprojects.gradle index 7fd31440dd..a255bb526d 100644 --- a/gradle/javaprojects.gradle +++ b/gradle/javaprojects.gradle @@ -1,24 +1,73 @@ apply plugin: 'java' apply plugin: 'eclipse' -springVersion = '3.0.3.RELEASE' -springLdapVersion = '1.3.0.RELEASE' -ehcacheVersion = '1.6.2' -aspectjVersion = '1.6.8' -apacheDsVersion = '1.5.5' -jstlVersion = '1.1.2' -jettyVersion = '6.1.22' -hsqlVersion = '1.8.0.10' -slf4jVersion = '1.6.1' -logbackVersion = '0.9.29' +sourceCompatibility = 1.5 +targetCompatibility = 1.5 +ext.springVersion = '3.0.7.RELEASE' +ext.springLdapVersion = '1.3.1.RELEASE' +ext.ehcacheVersion = '1.6.2' +ext.aspectjVersion = '1.6.10' +ext.apacheDsVersion = '1.5.5' +ext.jstlVersion = '1.2' +ext.jettyVersion = '6.1.26' +ext.hsqlVersion = '1.8.0.10' +ext.slf4jVersion = '1.6.1' +ext.logbackVersion = '0.9.29' +ext.cglibVersion = '2.2' +ext.powerMockVersion = '1.4.12' +ext.bundlorProperties = [ + version: version, + secRange: "[$version, 3.1.0)", + springRange: "[$springVersion, 3.1.0)", + aspectjRange: '[1.6.0, 1.7.0)', + casRange: '[3.1.1, 3.2.0)', + cloggingRange: '[1.0.4, 2.0.0)', + ehcacheRange: '[1.4.1, 2.5.0)', + openid4javaRange: '[0.9.5, 1.0.0)', + springLdapRange: '[1.3.0,1.4.0)', + apacheDSRange: '[1.5.5, 1.6)', + apacheDSSharedRange: '[0.9.15, 1.0)', + ldapSdkRange: '[4.1, 5.0)', + aopAllianceRange: '[1.0.0, 2.0.0)' +] configurations { + // Configuration which is ONLY used for compileJava and will not be inherited by any others + // Revisit post Gradle 1.0 + compileOnly + // Used to identify deps which should be marked as "provided" in maven poms provided - compile.extendsFrom provided + testCompile.extendsFrom provided + compile.transitive = false + testCompile.transitive = false } +// Integration test setup +configurations { + integrationTestCompile { + extendsFrom testCompile + } + integrationTestRuntime { + extendsFrom integrationTestCompile, testRuntime + } +} +sourceSets { + integrationTest { + java.srcDir file('src/integration-test/java') + resources.srcDir file('src/integration-test/resources') + compileClasspath = sourceSets.main.output + sourceSets.test.output + configurations.integrationTestCompile + runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime + } +} +task integrationTest(type: Test, dependsOn: jar) { + testClassesDir = sourceSets.integrationTest.output.classesDir + logging.captureStandardOutput(LogLevel.INFO) + classpath = sourceSets.integrationTest.runtimeClasspath + maxParallelForks = 1 +// testReport = false +} dependencies { - compile 'commons-logging:commons-logging:1.1.1' + compileOnly 'commons-logging:commons-logging:1.1.1' compile ("org.springframework:spring-core:$springVersion") { exclude(group: 'commons-logging', module: 'commons-logging') @@ -31,12 +80,60 @@ dependencies { 'org.hamcrest:hamcrest-core:1.1', 'org.hamcrest:hamcrest-library:1.1', "org.springframework:spring-test:$springVersion" + // Use slf4j/logback for logging + testRuntime "org.slf4j:jcl-over-slf4j:$slf4jVersion", + "ch.qos.logback:logback-classic:$logbackVersion" } +[configurations.runtime, configurations.default]*.exclude(module: 'commons-logging') +sourceSets.main.compileClasspath += configurations.compileOnly +sourceSets.main.compileClasspath += configurations.provided +[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' test { - onlyIf { - !project.hasProperty('skipTests') + jvmArgs = ['-ea', '-Xmx500m'] + maxParallelForks = guessMaxForks() + logging.captureStandardOutput(LogLevel.INFO) + testReport = false +} +def guessMaxForks() { + int processors = Runtime.runtime.availableProcessors() + return Math.max(2, (int) (processors / 2)) +} +javadoc { + title = "Spring Security $version API" + source = sourceSets.main.allJava + classpath += configurations.compileOnly + configurations.provided + options { + memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED + author = true + header = project.name + outputLevel = org.gradle.external.javadoc.JavadocOutputLevel.QUIET + links = [ + "http://static.springframework.org/spring/docs/3.0.x/javadoc-api", + "http://static.springsource.org/spring-ldap/docs/1.3.x/apidocs/", + "http://download.oracle.com/javase/6/docs/api/" + ] + groups = [ + 'Spring Security Core':[ + 'org.springframework.security.core*', + 'org.springframework.security.authentication*', + 'org.springframework.security.access*', + 'org.springframework.security.remoting*', + 'org.springframework.security.provisioning*', + 'org.springframework.security.util*'], + 'Spring Security Web':['org.springframework.security.web*'], + 'Spring Security LDAP':['org.springframework.security.ldap*'], + 'Spring Security Crypto':['org.springframework.security.crypto*'], + 'Spring Security OpenID':['org.springframework.security.openid*'], + 'Spring Security CAS':['org.springframework.security.cas*'], + 'Spring Security ACL':['org.springframework.security.acls*'], + 'Spring Security Config':['org.springframework.security.config*'], + 'Spring Security Taglibs':['org.springframework.security.taglibs*'], + ] } - jvmArgs = ['-ea', '-Xms128m', '-Xmx500m', '-XX:MaxPermSize=128m'] } +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} diff --git a/gradle/maven-deployment.gradle b/gradle/maven-deployment.gradle new file mode 100644 index 0000000000..bf68c10679 --- /dev/null +++ b/gradle/maven-deployment.gradle @@ -0,0 +1,51 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +apply plugin: 'maven' + +// Create a source jar for uploading +task sourceJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.java.srcDirs + include '**/*.java', '**/*.aj' +} + +// Configuration for SpringSource s3 maven deployer +configurations { + deployerJars +} +dependencies { + deployerJars "org.springframework.build.aws:org.springframework.build.aws.maven:3.0.0.RELEASE" +} + +// Remove the archive configuration from the runtime configuration, so that anything added to archives +// (such as the source jar) is no longer included in the runtime classpath +configurations.default.extendsFrom = [configurations.runtime] as Set +// Add the main jar into the default configuration +artifacts { 'default' jar } + +install { + customizePom(repositories.mavenInstaller.pom, project) +} + +if(project != project(":spring-security-parent")) { + install.dependsOn ':spring-security-parent:install' + artifacts { + archives sourceJar + archives javadocJar + } +} + +task generatePom(type: Copy) { + from 'pom.xml' + into 'build/' + filter(ReplaceTokens, tokens: [pomVersion : project.properties.version]) +} +install.dependsOn generatePom + +def customizePom(pom, gradleProject) { + pom.withXml { provider -> + def builder = provider.asString() + builder.length = 0 // delete existing content + builder.append(file("build/pom.xml").text) + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7b359d719bf96879170b6887c0bbb2e02b00ac34 GIT binary patch literal 46670 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..9b3cdd7c3e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Oct 02 08:45:52 CDT 2012 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..e7a5be4ba1 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="-Xmx1024M -XX:MaxPermSize=256M" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" +APP_HOME="`pwd -P`" +cd "$SAVED" + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..bbc4099e94 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS=-Xmx1024M -XX:MaxPermSize=256M + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/itest/context/itest-context.gradle b/itest/context/itest-context.gradle new file mode 100644 index 0000000000..9a35296564 --- /dev/null +++ b/itest/context/itest-context.gradle @@ -0,0 +1,17 @@ +System.setProperty('python.cachedir.skip', 'true') + +dependencies { + compile project(':spring-security-core'), + 'aopalliance:aopalliance:1.0', + 'org.python:jython:2.5.0', + "org.springframework:spring-context:$springVersion", + "org.springframework:spring-aop:$springVersion", + "org.springframework:spring-tx:$springVersion", + "org.springframework:spring-beans:$springVersion" + + testCompile project(':spring-security-web'), + 'javax.servlet:servlet-api:2.5', + "org.springframework:spring-web:$springVersion" + testRuntime project(':spring-security-config') + +} diff --git a/itest/context/pom.xml b/itest/context/pom.xml index c91c93f4b4..701d05d93c 100644 --- a/itest/context/pom.xml +++ b/itest/context/pom.xml @@ -4,7 +4,7 @@ org.springframework.security spring-security-itest - 3.0.8.CI-SNAPSHOT + @pomVersion@ spring-security-itest-context Spring Security - Miscellaneous Application Context Integration Tests diff --git a/itest/context/src/test/java/org/springframework/security/integration/HttpNamespaceWithMultipleInterceptorsTests.java b/itest/context/src/integration-test/java/org/springframework/security/integration/HttpNamespaceWithMultipleInterceptorsTests.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/integration/HttpNamespaceWithMultipleInterceptorsTests.java rename to itest/context/src/integration-test/java/org/springframework/security/integration/HttpNamespaceWithMultipleInterceptorsTests.java diff --git a/itest/context/src/test/java/org/springframework/security/integration/HttpPathParameterStrippingTests.java b/itest/context/src/integration-test/java/org/springframework/security/integration/HttpPathParameterStrippingTests.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/integration/HttpPathParameterStrippingTests.java rename to itest/context/src/integration-test/java/org/springframework/security/integration/HttpPathParameterStrippingTests.java diff --git a/itest/context/src/test/java/org/springframework/security/integration/MultiAnnotationTests.java b/itest/context/src/integration-test/java/org/springframework/security/integration/MultiAnnotationTests.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/integration/MultiAnnotationTests.java rename to itest/context/src/integration-test/java/org/springframework/security/integration/MultiAnnotationTests.java diff --git a/itest/context/src/test/java/org/springframework/security/integration/SEC933ApplicationContextTests.java b/itest/context/src/integration-test/java/org/springframework/security/integration/SEC933ApplicationContextTests.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/integration/SEC933ApplicationContextTests.java rename to itest/context/src/integration-test/java/org/springframework/security/integration/SEC933ApplicationContextTests.java diff --git a/itest/context/src/test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java b/itest/context/src/integration-test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java rename to itest/context/src/integration-test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java diff --git a/itest/context/src/test/java/org/springframework/security/integration/StubUserRepository.java b/itest/context/src/integration-test/java/org/springframework/security/integration/StubUserRepository.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/integration/StubUserRepository.java rename to itest/context/src/integration-test/java/org/springframework/security/integration/StubUserRepository.java diff --git a/itest/context/src/test/java/org/springframework/security/integration/python/PythonInterpreterBasedSecurityTests.java b/itest/context/src/integration-test/java/org/springframework/security/integration/python/PythonInterpreterBasedSecurityTests.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/integration/python/PythonInterpreterBasedSecurityTests.java rename to itest/context/src/integration-test/java/org/springframework/security/integration/python/PythonInterpreterBasedSecurityTests.java diff --git a/itest/context/src/test/java/org/springframework/security/performance/FilterChainPerformanceTests.java b/itest/context/src/integration-test/java/org/springframework/security/performance/FilterChainPerformanceTests.java similarity index 94% rename from itest/context/src/test/java/org/springframework/security/performance/FilterChainPerformanceTests.java rename to itest/context/src/integration-test/java/org/springframework/security/performance/FilterChainPerformanceTests.java index 9a3c32201e..a3be2ec8ef 100644 --- a/itest/context/src/test/java/org/springframework/security/performance/FilterChainPerformanceTests.java +++ b/itest/context/src/integration-test/java/org/springframework/security/performance/FilterChainPerformanceTests.java @@ -1,14 +1,6 @@ package org.springframework.security.performance; -import java.util.Arrays; -import java.util.List; - -import javax.servlet.http.HttpSession; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -26,6 +18,9 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.StopWatch; +import javax.servlet.http.HttpSession; +import java.util.*; + /** * * @author Luke Taylor @@ -34,8 +29,9 @@ import org.springframework.util.StopWatch; @ContextConfiguration(locations={"/filter-chain-performance-app-context.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class FilterChainPerformanceTests { - private static final int N_INVOCATIONS = 1000; - private static final int N_AUTHORITIES = 200; + // Adjust as required + private static final int N_INVOCATIONS = 1; // 1000 + private static final int N_AUTHORITIES = 2; // 200 private static StopWatch sw = new StopWatch("Filter Chain Performance Tests"); private final UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken("bob", "bobspassword", createRoles(N_AUTHORITIES)); diff --git a/itest/context/src/test/java/org/springframework/security/performance/ProtectPointcutPerformanceTests.java b/itest/context/src/integration-test/java/org/springframework/security/performance/ProtectPointcutPerformanceTests.java similarity index 100% rename from itest/context/src/test/java/org/springframework/security/performance/ProtectPointcutPerformanceTests.java rename to itest/context/src/integration-test/java/org/springframework/security/performance/ProtectPointcutPerformanceTests.java diff --git a/itest/context/src/test/resources/filter-chain-performance-app-context.xml b/itest/context/src/integration-test/resources/filter-chain-performance-app-context.xml similarity index 100% rename from itest/context/src/test/resources/filter-chain-performance-app-context.xml rename to itest/context/src/integration-test/resources/filter-chain-performance-app-context.xml diff --git a/itest/context/src/test/resources/http-extra-fsi-app-context.xml b/itest/context/src/integration-test/resources/http-extra-fsi-app-context.xml similarity index 100% rename from itest/context/src/test/resources/http-extra-fsi-app-context.xml rename to itest/context/src/integration-test/resources/http-extra-fsi-app-context.xml diff --git a/itest/context/src/test/resources/http-path-param-stripping-app-context.xml b/itest/context/src/integration-test/resources/http-path-param-stripping-app-context.xml similarity index 100% rename from itest/context/src/test/resources/http-path-param-stripping-app-context.xml rename to itest/context/src/integration-test/resources/http-path-param-stripping-app-context.xml diff --git a/itest/context/src/test/resources/log4j.properties b/itest/context/src/integration-test/resources/log4j.properties similarity index 100% rename from itest/context/src/test/resources/log4j.properties rename to itest/context/src/integration-test/resources/log4j.properties diff --git a/itest/context/src/test/resources/multi-sec-annotation-app-context.xml b/itest/context/src/integration-test/resources/multi-sec-annotation-app-context.xml similarity index 100% rename from itest/context/src/test/resources/multi-sec-annotation-app-context.xml rename to itest/context/src/integration-test/resources/multi-sec-annotation-app-context.xml diff --git a/itest/context/src/test/resources/protect-pointcut-performance-app-context.xml b/itest/context/src/integration-test/resources/protect-pointcut-performance-app-context.xml similarity index 100% rename from itest/context/src/test/resources/protect-pointcut-performance-app-context.xml rename to itest/context/src/integration-test/resources/protect-pointcut-performance-app-context.xml diff --git a/itest/context/src/test/resources/python-method-access-app-context.xml b/itest/context/src/integration-test/resources/python-method-access-app-context.xml similarity index 100% rename from itest/context/src/test/resources/python-method-access-app-context.xml rename to itest/context/src/integration-test/resources/python-method-access-app-context.xml diff --git a/itest/context/src/test/resources/sec-933-app-context.xml b/itest/context/src/integration-test/resources/sec-933-app-context.xml similarity index 100% rename from itest/context/src/test/resources/sec-933-app-context.xml rename to itest/context/src/integration-test/resources/sec-933-app-context.xml diff --git a/itest/context/src/test/resources/sec-936-app-context.xml b/itest/context/src/integration-test/resources/sec-936-app-context.xml similarity index 100% rename from itest/context/src/test/resources/sec-936-app-context.xml rename to itest/context/src/integration-test/resources/sec-936-app-context.xml diff --git a/itest/context/src/test/resources/someMethod.py b/itest/context/src/integration-test/resources/someMethod.py similarity index 100% rename from itest/context/src/test/resources/someMethod.py rename to itest/context/src/integration-test/resources/someMethod.py diff --git a/itest/misc/pom.xml b/itest/misc/pom.xml index 22493e7530..e1e580fd96 100644 --- a/itest/misc/pom.xml +++ b/itest/misc/pom.xml @@ -5,7 +5,7 @@ org.springframework.security spring-security-itest - 3.0.8.CI-SNAPSHOT + @pomVersion@ spring-security-itest-misc Spring Security - Miscellaneous Integration Tests diff --git a/itest/pom.xml b/itest/pom.xml index d79029124c..c2c1cddfd6 100644 --- a/itest/pom.xml +++ b/itest/pom.xml @@ -3,12 +3,12 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ spring-security-itest Spring Security - Integration Tests pom - 3.0.8.CI-SNAPSHOT + @pomVersion@ web diff --git a/itest/web/itest-web.gradle b/itest/web/itest-web.gradle index 6ba082f47d..4849b58bfc 100644 --- a/itest/web/itest-web.gradle +++ b/itest/web/itest-web.gradle @@ -21,10 +21,11 @@ dependencies { 'net.sourceforge.jwebunit:jwebunit-htmlunit-plugin:2.2' } -test { +integrationTest { useTestNG(); options { jvmArgs = ["-ea", '-Xms128m', '-Xmx500m'] systemProperties = ['webapp.dir': "$projectDir/src/main/webapp"] } -} \ No newline at end of file + maxParallelForks = 1 +} diff --git a/itest/web/pom.xml b/itest/web/pom.xml index 6679c138a5..6e746c93d3 100644 --- a/itest/web/pom.xml +++ b/itest/web/pom.xml @@ -4,7 +4,7 @@ org.springframework.security spring-security-itest - 3.0.8.CI-SNAPSHOT + @pomVersion@ spring-security-itest-web Spring Security - Web Integration Tests diff --git a/ldap/ldap.gradle b/ldap/ldap.gradle index 4bd8091054..4c9920d27a 100644 --- a/ldap/ldap.gradle +++ b/ldap/ldap.gradle @@ -1,25 +1,42 @@ // Ldap build file -test.exclude('**/OpenLDAPIntegrationTestSuite.class') -dependencies { - compile project(':spring-security-core'), - "org.springframework:spring-beans:$springVersion", - "org.springframework:spring-context:$springVersion", - "org.springframework:spring-tx:$springVersion", +def apacheds_libs = [ "org.apache.directory.server:apacheds-core:$apacheDsVersion", "org.apache.directory.server:apacheds-core-entry:$apacheDsVersion", "org.apache.directory.server:apacheds-protocol-shared:$apacheDsVersion", "org.apache.directory.server:apacheds-protocol-ldap:$apacheDsVersion", "org.apache.directory.server:apacheds-server-jndi:$apacheDsVersion", - 'org.apache.directory.shared:shared-ldap:0.9.15', + 'org.apache.directory.shared:shared-ldap:0.9.15' +] +dependencies { + compile project(':spring-security-core'), + "org.springframework:spring-beans:$springVersion", + "org.springframework:spring-context:$springVersion", + "org.springframework:spring-tx:$springVersion", 'ldapsdk:ldapsdk:4.1' + apacheds_libs.collect { + compile (it) { + exclude group: 'org.slf4j' + } + } compile ("org.springframework.ldap:spring-ldap-core:$springLdapVersion") { exclude(group: 'commons-logging', module: 'commons-logging') exclude(group: 'org.springframework', module: 'spring-core') exclude(group: 'org.springframework', module: 'spring-tx') exclude(group: 'org.springframework', module: 'spring-beans') } +} - runtime 'org.slf4j:slf4j-log4j12:1.4.3' -} \ No newline at end of file +integrationTest { + include('**/ApacheDSServerIntegrationTests.class') +// exclude('**/OpenLDAPIntegrationTestSuite.class') + maxParallelForks = 1 + systemProperties['apacheDSWorkDir'] = "${buildDir}/apacheDSWork" +} +// Runs a server for running the integration tests against (from an IDE, for example) +task(ldapServer, dependsOn: 'integrationTestClasses', type: JavaExec) { + classpath = sourceSets.integrationTest.runtimeClasspath + main = 'org.springframework.security.ldap.ApacheDSServerIntegrationTests' + systemProperties['apacheDSWorkDir'] = "${buildDir}/apacheDSWork" +} diff --git a/ldap/pom.xml b/ldap/pom.xml index ef2c592fac..2921a658e2 100644 --- a/ldap/pom.xml +++ b/ldap/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ jar spring-security-ldap diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java new file mode 100644 index 0000000000..4091295b41 --- /dev/null +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java @@ -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 org.springframework.security.ldap; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.springframework.ldap.core.support.BaseLdapPathContextSource; + +/** + * Based on class borrowed from Spring Ldap project. + * + * @author Luke Taylor + */ +public abstract class AbstractLdapIntegrationTests { + private static DefaultSpringSecurityContextSource contextSource; + + protected AbstractLdapIntegrationTests() { + } + + @BeforeClass + public static void startServer() throws Exception { + contextSource = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:53389/dc=springframework,dc=org"); + // OpenLDAP configuration +// contextSource = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:22389/dc=springsource,dc=com"); +// contextSource.setUserDn("cn=admin,dc=springsource,dc=com"); +// contextSource.setPassword("password"); + contextSource.afterPropertiesSet(); + } + + @Before + public void onSetUp() throws Exception { + } + + public BaseLdapPathContextSource getContextSource() { + return contextSource; + } +} diff --git a/ldap/src/test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/ApacheDSServerIntegrationTests.java similarity index 58% rename from ldap/src/test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/ApacheDSServerIntegrationTests.java index 29732d99d8..55c8b642cf 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/ApacheDSServerIntegrationTests.java @@ -1,59 +1,40 @@ -/* 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 org.springframework.security.ldap; -import javax.naming.Binding; -import javax.naming.ContextNotEmptyException; -import javax.naming.Name; -import javax.naming.NameNotFoundException; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; - -import org.apache.directory.server.protocol.shared.store.LdifFileLoader; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.springframework.core.io.ClassPathResource; -import org.springframework.ldap.core.DistinguishedName; -import org.springframework.ldap.core.support.BaseLdapPathContextSource; +import org.junit.*; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.springframework.security.ldap.authentication.BindAuthenticatorTests; +import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticatorTests; +import org.springframework.security.ldap.search.FilterBasedLdapUserSearchTests; import org.springframework.security.ldap.server.ApacheDSContainer; +import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulatorTests; +import org.springframework.security.ldap.userdetails.LdapUserDetailsManagerTests; /** - * Based on class borrowed from Spring Ldap project. - * * @author Luke Taylor */ -public abstract class AbstractLdapIntegrationTests { -// private static InMemoryXmlApplicationContext appContext; +@RunWith(Suite.class) +@Suite.SuiteClasses( { + BindAuthenticatorTests.class, + PasswordComparisonAuthenticatorTests.class, + FilterBasedLdapUserSearchTests.class, + DefaultLdapAuthoritiesPopulatorTests.class, + LdapUserDetailsManagerTests.class, + DefaultSpringSecurityContextSourceTests.class, + SpringSecurityLdapTemplateTests.class +} +) +public final class ApacheDSServerIntegrationTests { private static ApacheDSContainer server; - private static DefaultSpringSecurityContextSource contextSource; - - protected AbstractLdapIntegrationTests() { - } @BeforeClass public static void startServer() throws Exception { - contextSource = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:53389/dc=springframework,dc=org"); // OpenLDAP configuration // contextSource = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:22389/dc=springsource,dc=com"); // contextSource.setUserDn("cn=admin,dc=springsource,dc=com"); // contextSource.setPassword("password"); - contextSource.afterPropertiesSet(); server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif"); + server.setPort(53389); server.afterPropertiesSet(); } @@ -64,11 +45,15 @@ public abstract class AbstractLdapIntegrationTests { } } - @Before - public void onSetUp() throws Exception { + /** + * Main class to allow server to be started from gradle script + */ + public static void main(String[] args) throws Exception { + ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif"); + server.afterPropertiesSet(); } - +/* @After public final void reloadServerDataIfDirty() throws Exception { ClassPathResource ldifs = new ClassPathResource("test-server.ldif"); @@ -91,11 +76,6 @@ public abstract class AbstractLdapIntegrationTests { } } - public BaseLdapPathContextSource getContextSource() { - return contextSource; - } - - private void clearSubContexts(DirContext ctx, Name name) throws NamingException { NamingEnumeration enumeration = null; @@ -124,4 +104,5 @@ public abstract class AbstractLdapIntegrationTests { } } } + */ } diff --git a/ldap/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java diff --git a/ldap/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java diff --git a/ldap/src/test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java diff --git a/ldap/src/test/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticatorTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticatorTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticatorTests.java diff --git a/ldap/src/test/java/org/springframework/security/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java diff --git a/ldap/src/test/java/org/springframework/security/ldap/ppolicy/OpenLDAPIntegrationTestSuite.java b/ldap/src/integration-test/java/org/springframework/security/ldap/ppolicy/OpenLDAPIntegrationTestSuite.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/ppolicy/OpenLDAPIntegrationTestSuite.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/ppolicy/OpenLDAPIntegrationTestSuite.java diff --git a/ldap/src/test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java diff --git a/ldap/src/test/java/org/springframework/security/ldap/server/ApacheDSContainerTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/server/ApacheDSContainerTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/server/ApacheDSContainerTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/server/ApacheDSContainerTests.java diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java new file mode 100644 index 0000000000..3c1c085b75 --- /dev/null +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java @@ -0,0 +1,161 @@ +/* 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 org.springframework.security.ldap.userdetails; + + +import static org.junit.Assert.*; + +import org.junit.*; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.ldap.AbstractLdapIntegrationTests; + +import java.util.*; + + +/** + * + * @author Luke Taylor + */ +@SuppressWarnings({"deprecation"}) +public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegrationTests { + private DefaultLdapAuthoritiesPopulator populator; + //~ Methods ======================================================================================================== + + @Before + public void setUp() throws Exception { + populator = new DefaultLdapAuthoritiesPopulator(getContextSource(), "ou=groups"); + populator.setIgnorePartialResultException(false); + } + + @Test + public void defaultRoleIsAssignedWhenSet() { + populator.setDefaultRole("ROLE_USER"); + assertSame(getContextSource(), populator.getContextSource()); + + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("cn=notfound")); + + Collection authorities = populator.getGrantedAuthorities(ctx, "notfound"); + assertEquals(1, authorities.size()); + assertTrue(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_USER")); + } + + @Test + public void nullSearchBaseIsAccepted() throws Exception { + populator = new DefaultLdapAuthoritiesPopulator(getContextSource(), null); + populator.setDefaultRole("ROLE_USER"); + + Collection authorities = populator.getGrantedAuthorities( + new DirContextAdapter(new DistinguishedName("cn=notused")), "notused"); + assertEquals(1, authorities.size()); + assertTrue(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_USER")); + } + + @Test + public void groupSearchReturnsExpectedRoles() { + populator.setRolePrefix("ROLE_"); + populator.setGroupRoleAttribute("ou"); + populator.setSearchSubtree(true); + populator.setSearchSubtree(false); + populator.setConvertToUpperCase(true); + populator.setGroupSearchFilter("(member={0})"); + + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org")); + + Set authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "ben")); + + assertEquals("Should have 2 roles", 2, authorities.size()); + + assertTrue(authorities.contains("ROLE_DEVELOPER")); + assertTrue(authorities.contains("ROLE_MANAGER")); + } + + @Test + public void useOfUsernameParameterReturnsExpectedRoles() { + populator.setGroupRoleAttribute("ou"); + populator.setConvertToUpperCase(true); + populator.setGroupSearchFilter("(ou={1})"); + + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org")); + + Set authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "manager")); + + assertEquals("Should have 1 role", 1, authorities.size()); + assertTrue(authorities.contains("ROLE_MANAGER")); + } + + @Test + public void subGroupRolesAreNotFoundByDefault() { + populator.setGroupRoleAttribute("ou"); + populator.setConvertToUpperCase(true); + + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org")); + + Set authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "manager")); + + assertEquals("Should have 2 roles", 2, authorities.size()); + assertTrue(authorities.contains("ROLE_MANAGER")); + assertTrue(authorities.contains("ROLE_DEVELOPER")); + } + + @Test + public void subGroupRolesAreFoundWhenSubtreeSearchIsEnabled() { + populator.setGroupRoleAttribute("ou"); + populator.setConvertToUpperCase(true); + populator.setSearchSubtree(true); + + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org")); + + Set authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "manager")); + + assertEquals("Should have 3 roles", 3, authorities.size()); + assertTrue(authorities.contains("ROLE_MANAGER")); + assertTrue(authorities.contains("ROLE_SUBMANAGER")); + assertTrue(authorities.contains("ROLE_DEVELOPER")); + } + + @Test + public void extraRolesAreAdded() throws Exception { + populator = new DefaultLdapAuthoritiesPopulator(getContextSource(), null) { + @Override + protected Set getAdditionalRoles(DirContextOperations user, String username) { + return new HashSet(AuthorityUtils.createAuthorityList("ROLE_EXTRA")); + } + }; + + Collection authorities = populator.getGrantedAuthorities( + new DirContextAdapter(new DistinguishedName("cn=notused")), "notused"); + assertEquals(1, authorities.size()); + assertTrue(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_EXTRA")); + } + + @Test + public void userDnWithEscapedCharacterParameterReturnsExpectedRoles() { + populator.setGroupRoleAttribute("ou"); + populator.setConvertToUpperCase(true); + populator.setGroupSearchFilter("(member={0})"); + + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("cn=mouse\\, jerry,ou=people,dc=springframework,dc=org")); + + Set authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "notused")); + + assertEquals("Should have 1 role", 1, authorities.size()); + assertTrue(authorities.contains("ROLE_MANAGER")); + } +} diff --git a/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java similarity index 100% rename from ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java rename to ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java diff --git a/ldap/src/integration-test/resources/logback-test.xml b/ldap/src/integration-test/resources/logback-test.xml new file mode 100644 index 0000000000..60cd6fd4ee --- /dev/null +++ b/ldap/src/integration-test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + diff --git a/ldap/src/test/resources/test-server.ldif b/ldap/src/integration-test/resources/test-server.ldif similarity index 100% rename from ldap/src/test/resources/test-server.ldif rename to ldap/src/integration-test/resources/test-server.ldif diff --git a/ldap/template.mf b/ldap/template.mf index cfc3058d6e..971eb1aa3e 100644 --- a/ldap/template.mf +++ b/ldap/template.mf @@ -9,18 +9,18 @@ Ignored-Existing-Headers: Import-Package, Export-Package Import-Template: - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.apache.directory.server.*;version="[1.5.5, 1.6)";resolution:=optional, - org.apache.directory.shared.ldap.*;version="[0.9.15, 1.0)";resolution:=optional, - org.springframework.ldap.*;version="[1.3.0,1.4.0)", - org.springframework.security.core.*;version="[${version}, 3.1.0)", - org.springframework.security.authentication.*;version="[${version}, 3.1.0)", - org.springframework.security.provisioning.*;version="[${version}, 3.1.0)", - org.springframework.security.util;version="[${version}, 3.1.0)", - org.springframework.beans.*;version="[${spring.version}, 3.1.0)", - org.springframework.context.*;version="[${spring.version}, 3.1.0)", - org.springframework.core.io.*;version="[${spring.version}, 3.1.0)", - org.springframework.dao.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.util.*;version="[${spring.version}, 3.1.0)", + org.apache.commons.logging.*;version="${cloggingRange}", + org.apache.directory.server.*;version="${apacheDSRange}";resolution:=optional, + org.apache.directory.shared.ldap.*;version="${apacheDSSharedRange}";resolution:=optional, + org.springframework.ldap.*;version="${springLdapRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.authentication.*;version="${secRange}", + org.springframework.security.provisioning.*;version="${secRange}", + org.springframework.security.util;version="${secRange}", + org.springframework.beans.*;version="${springRange}", + org.springframework.context.*;version="${springRange}", + org.springframework.core.io.*;version="${springRange}", + org.springframework.dao.*;version="${springRange}";resolution:=optional, + org.springframework.util.*;version="${springRange}", javax.naming.*;version="0";resolution:=optional, - netscape.ldap.ber.stream;version="[4.1, 5.0)";resolution:=optional + netscape.ldap.ber.stream;version="${ldapSdkRange}";resolution:=optional diff --git a/openid/openid.gradle b/openid/openid.gradle index 814d480259..5de412a291 100644 --- a/openid/openid.gradle +++ b/openid/openid.gradle @@ -3,14 +3,20 @@ dependencies { compile project(':spring-security-core'), project(':spring-security-web'), - 'org.openid4java:openid4java-nodeps:0.9.5', "org.springframework:spring-aop:$springVersion", + "org.springframework:spring-tx:$springVersion", "org.springframework:spring-context:$springVersion", "org.springframework:spring-beans:$springVersion", - "org.springframework:spring-tx:$springVersion", "org.springframework:spring-web:$springVersion" + // openid4java has a compile time dep on guice with a group + // name which is different from the maven central one. + // We use the maven central version here instead. + compile('org.openid4java:openid4java-nodeps:0.9.6') { + exclude group: 'com.google.code.guice', module: 'guice' + } + compile 'com.google.inject:guice:2.0' provided 'javax.servlet:servlet-api:2.5' - runtime 'commons-httpclient:commons-httpclient:3.1' + runtime 'org.apache.httpcomponents:httpclient:4.1.1' } \ No newline at end of file diff --git a/openid/pom.xml b/openid/pom.xml index 4f731d1062..d22edf7fd8 100644 --- a/openid/pom.xml +++ b/openid/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ spring-security-openid Spring Security - OpenID support diff --git a/openid/template.mf b/openid/template.mf index f49295a006..030a9802f5 100644 --- a/openid/template.mf +++ b/openid/template.mf @@ -9,11 +9,11 @@ Ignored-Existing-Headers: Import-Package, Export-Package Import-Template: - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.springframework.security.core.*;version="[${version}, 3.1.0)", - org.springframework.security.authentication.*;version="[${version}, 3.1.0)", - org.springframework.security.web.*;version="[${version}, 3.1.0)", - org.springframework.beans.factory;version="[${spring.version}, 3.1.0)", - org.springframework.util;version="[${spring.version}, 3.1.0)", - org.openid4java.*;version="[0.9.5, 1.0.0)", + org.apache.commons.logging.*;version="${cloggingRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.authentication.*;version="${secRange}", + org.springframework.security.web.*;version="${secRange}", + org.springframework.beans.factory;version="${springRange}", + org.springframework.util;version="${springRange}", + org.openid4java.*;version="${openid4javaRange}", javax.servlet.*;version="0" diff --git a/parent/parent.gradle b/parent/parent.gradle new file mode 100644 index 0000000000..fa0bf62e5e --- /dev/null +++ b/parent/parent.gradle @@ -0,0 +1 @@ +[sourceJar,javadoc,javadocJar]*.enabled = false diff --git a/pom.xml b/parent/pom.xml similarity index 99% rename from pom.xml rename to parent/pom.xml index e6920d9623..b67abca411 100644 --- a/pom.xml +++ b/parent/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ Spring Security pom diff --git a/readme.txt b/readme.txt index eb79d9f553..e5f34379c6 100644 --- a/readme.txt +++ b/readme.txt @@ -6,19 +6,22 @@ OVERVIEW ------------------------------------------------------------------------------- -Spring Security provides security services for -The Spring Framework (http://www.springframework.org). +Spring Security provides security services for the Spring Framework +(http://www.springframework.org). Spring Security 3.1 requires Spring 3.0.3 as +a minimum and also requires Java 5. For a detailed list of features and access to the latest release, please visit http://www.springframework.org/projects/. +Spring Security is released under an Apache 2.0 license. See the accompanying +license.txt file. ------------------------------------------------------------------------------- BUILDING ------------------------------------------------------------------------------- -Spring Security is built using Maven. Please read the "Building from Source" page -at http://static.springframework.org/spring-security/site/. +Please read the "Building from Source" page at +http://static.springframework.org/spring-security/site/. ------------------------------------------------------------------------------- DOCUMENTATION @@ -33,7 +36,7 @@ QUICK START ------------------------------------------------------------------------------- We recommend you visit http://static.springframework.org/spring-security/site and -read the "Suggested Steps" page. +read the "Getting Started" page. ------------------------------------------------------------------------------- MAVEN REPOSITORY DOWNLOADS @@ -44,11 +47,11 @@ Release jars for the project are available from the central maven repository http://repo1.maven.org/maven2/org/springframework/security/ Note that milestone releases and snapshots are not uploaded to the central -repository, but can be obtained from te Spring milestone repository. -This blog article has full details on how to download milestone or snapshot -jars or use them in a Maven-based project build: - -http://blog.springsource.com/main/2007/09/18/maven-artifacts-2/ +repository, but can be obtained from the Spring milestone repository, using the +maven repository http://maven.springframework.org/snapshot/. You can't browse this +URL directly, but there is a separate browser interface. Check the downloads page +for more information +http://static.springsource.org/spring-security/site/downloads.html ------------------------------------------------------------------------------- @@ -69,4 +72,9 @@ located at the Spring Community's forum site: Links to the forums, and other useful resources are available from the web site. +------------------------------------------------------------------------------- +CONTRIBUTING +------------------------------------------------------------------------------- +Contributions are welcome. Please refer to the Contributor Guidelines for details + https://github.com/SpringSource/spring-security/wiki/Contributor-Guidelines \ No newline at end of file diff --git a/samples/aspectj/pom.xml b/samples/aspectj/pom.xml deleted file mode 100644 index ebf2aa976f..0000000000 --- a/samples/aspectj/pom.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - 4.0.0 - - org.springframework.security - spring-security-parent - 3.0.8.CI-SNAPSHOT - - spring-security-samples-aspectj - jar - Spring Security Sample AspectJ - - - - junit - junit - 4.6 - test - - - org.springframework.security - spring-security-config - ${project.version} - - - org.springframework.security - spring-security-aspects - ${project.version} - - - org.springframework - spring-test - test - - - - - - org.codehaus.mojo - aspectj-maven-plugin - 1.2 - - - - org.aspectj - aspectjrt - 1.6.8 - - - org.aspectj - aspectjtools - 1.6.8 - - - - - - compile - test-compile - - - - - - - org.springframework.security - spring-security-aspects - - - 1.5 - 1.5 - - - - - diff --git a/samples/cas/Readme.txt b/samples/cas/Readme.txt index 4df65e67ec..cfa67e6ad7 100644 --- a/samples/cas/Readme.txt +++ b/samples/cas/Readme.txt @@ -1,53 +1,12 @@ -There are two subdirectories in this project; +To run a CAS server and client application, just execute the command -server - this is not a real maven sub-project in the sense that it builds anything. It is just here to allow you to - conveniently run the CAS server using the maven Jetty plugin with our preconfigured SSL certificates. - -client - this contains the actual sample web application which uses the cas server for authentication. It uses the same - certificates. In practice, the CAS server would likely be running on a different machine and both client and - server would have different certificates issued to the server hostname. - -Running the CAS Server ------------------------ - -You first need to download the CAS server 3.3.5 distribution from - -http://www.ja-sig.org/products/cas/downloads/index.html - -You only need the modules/cas-server-webapp-3.3.5.war web application file from the distribution. Copy this to the -"server" directory inside the one that contains this readme file (i.e. copy it to samples/cas/server). - -You can then run the CAS server (from the same) by executing the maven command - -mvn jetty:run-war - -This will start the server on - -https://localhost:9443/cas - -If you point your browser at this URL, you should see the CAS login screen. - - -Running the Client Application -------------------------------- - -Leave the server running and start up a separate command window to run the sample application. Change to the directory -samples/cas/client and execute the command - -mvn jetty:run - - -This should start the sample application on - -http://localhost:8080/cas-sample/ - -Try to access the secure page (as with the other samples) and you should be redirected to the CAS server to log in. Note -that the sample authentication module that comes with the CAS server webapp will authenticate any user whose password -matches the username. So you have to log in here as rod/rod, dianne/dianne etc. Obviously the usernames must still match -those listed in the application's user-service. - - -$Id$ +./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). diff --git a/samples/cas/cas.gradle b/samples/cas/cas.gradle deleted file mode 100644 index ebd59e42aa..0000000000 --- a/samples/cas/cas.gradle +++ /dev/null @@ -1,9 +0,0 @@ -apply plugin: 'war' -apply plugin: 'jetty' - -dependencies { - runtime project(':spring-security-core'), - project(':spring-security-web'), - project(':spring-security-config'), - 'log4j:log4j:1.2.15@jar' -} \ No newline at end of file diff --git a/samples/cas/pom.xml b/samples/cas/pom.xml deleted file mode 100644 index 5627683141..0000000000 --- a/samples/cas/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - 4.0.0 - - org.springframework.security - spring-security-samples - 3.0.8.CI-SNAPSHOT - - org.springframework.security - spring-security-samples-cas - Spring Security - CAS Sample Parent - pom - - client - server - - diff --git a/samples/cas/sample/cassample.gradle b/samples/cas/sample/cassample.gradle new file mode 100644 index 0000000000..5dc4fa27c8 --- /dev/null +++ b/samples/cas/sample/cassample.gradle @@ -0,0 +1,125 @@ +// 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 = '7.1.6.v20100715' +def keystore = "$rootDir/samples/certificates/server.jks" +def password = 'password' + +configurations { + casServer + excludeModules.each {name -> + runtime.exclude module: name + } + + runtime.exclude group: 'org.aspectj' + + integrationTestCompile.extendsFrom groovy +} + +sourceSets { + test.resources.exclude 'GebConfig.groovy' + integrationTest.groovy.srcDir file('src/integration-test/groovy') +} + +eclipse.classpath.plusConfigurations += configurations.integrationTestRuntime + +dependencies { + groovy 'org.codehaus.groovy:groovy:1.8.7' + + providedCompile 'javax.servlet:servlet-api:2.5@jar' + + compile project(':spring-security-core'), + project(':spring-security-cas-client'), + "org.jasig.cas.client:cas-client-core:3.1.12" + + runtime project(':spring-security-web'), + project(':spring-security-config'), + "org.slf4j:jcl-over-slf4j:$slf4jVersion", + "ch.qos.logback:logback-classic:$logbackVersion" + + integrationTestCompile project(':spring-security-cas-client'), + 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.25.0', + 'org.spockframework:spock-core:0.6-groovy-1.8', + 'org.codehaus.geb:geb-spock:0.7.2', + 'commons-httpclient:commons-httpclient:3.1', + "org.eclipse.jetty:jetty-server:$jettyVersion", + "org.eclipse.jetty:jetty-servlet:$jettyVersion" +} + +[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-casserver:casServer') +} + +task casServer(dependsOn: ':spring-security-samples-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-casserver:casServerOverlay').logLevel = 'ERROR' + jettyRunWar.additionalRuntimeJars += file("src/integration-test/resources") + + jettyRunWar.daemon = true + jettyRunWar.httpConnector.port = availablePort() + jettyRunWar.httpsConnector.port = jettyRunWar.httpConnector.confidentialPort = availablePort() + casServer.httpsConnector.port = availablePort() + } +} + +def casServer() { + tasks.getByPath(':spring-security-samples-casserver:casServer') +} + +def availablePort() { + ServerSocket server = new ServerSocket(0) + int port = server.localPort + server.close() + port +} diff --git a/samples/cas/client/pom.xml b/samples/cas/sample/pom.xml similarity index 99% rename from samples/cas/client/pom.xml rename to samples/cas/sample/pom.xml index e398a5ce05..e28a6bd391 100644 --- a/samples/cas/client/pom.xml +++ b/samples/cas/sample/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-samples-cas - 3.0.8.CI-SNAPSHOT + @pomVersion@ org.springframework.security spring-security-samples-cas-client diff --git a/samples/cas/client/src/main/java/Dummy.java b/samples/cas/sample/src/main/java/Dummy.java similarity index 100% rename from samples/cas/client/src/main/java/Dummy.java rename to samples/cas/sample/src/main/java/Dummy.java diff --git a/samples/cas/client/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/cas/sample/src/main/webapp/WEB-INF/applicationContext-security.xml similarity index 100% rename from samples/cas/client/src/main/webapp/WEB-INF/applicationContext-security.xml rename to samples/cas/sample/src/main/webapp/WEB-INF/applicationContext-security.xml diff --git a/samples/cas/client/src/main/webapp/WEB-INF/classes/log4j.properties b/samples/cas/sample/src/main/webapp/WEB-INF/classes/log4j.properties similarity index 100% rename from samples/cas/client/src/main/webapp/WEB-INF/classes/log4j.properties rename to samples/cas/sample/src/main/webapp/WEB-INF/classes/log4j.properties diff --git a/samples/cas/client/src/main/webapp/WEB-INF/web.xml b/samples/cas/sample/src/main/webapp/WEB-INF/web.xml similarity index 100% rename from samples/cas/client/src/main/webapp/WEB-INF/web.xml rename to samples/cas/sample/src/main/webapp/WEB-INF/web.xml diff --git a/samples/cas/client/src/main/webapp/cas-logout.jsp b/samples/cas/sample/src/main/webapp/cas-logout.jsp similarity index 100% rename from samples/cas/client/src/main/webapp/cas-logout.jsp rename to samples/cas/sample/src/main/webapp/cas-logout.jsp diff --git a/samples/cas/client/src/main/webapp/casfailed.jsp b/samples/cas/sample/src/main/webapp/casfailed.jsp similarity index 100% rename from samples/cas/client/src/main/webapp/casfailed.jsp rename to samples/cas/sample/src/main/webapp/casfailed.jsp diff --git a/samples/cas/client/src/main/webapp/index.jsp b/samples/cas/sample/src/main/webapp/index.jsp similarity index 100% rename from samples/cas/client/src/main/webapp/index.jsp rename to samples/cas/sample/src/main/webapp/index.jsp diff --git a/samples/cas/client/src/main/webapp/secure/extreme/index.jsp b/samples/cas/sample/src/main/webapp/secure/extreme/index.jsp similarity index 100% rename from samples/cas/client/src/main/webapp/secure/extreme/index.jsp rename to samples/cas/sample/src/main/webapp/secure/extreme/index.jsp diff --git a/samples/cas/client/src/main/webapp/secure/index.jsp b/samples/cas/sample/src/main/webapp/secure/index.jsp similarity index 100% rename from samples/cas/client/src/main/webapp/secure/index.jsp rename to samples/cas/sample/src/main/webapp/secure/index.jsp diff --git a/samples/cas/server/casserver.gradle b/samples/cas/server/casserver.gradle new file mode 100644 index 0000000000..d3d98dc825 --- /dev/null +++ b/samples/cas/server/casserver.gradle @@ -0,0 +1,61 @@ +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:3.4.3.1@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) + } +} diff --git a/samples/cas/server/pom.xml b/samples/cas/server/pom.xml deleted file mode 100644 index 5291172600..0000000000 --- a/samples/cas/server/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - 4.0.0 - - org.springframework.security - spring-security-samples-cas - 3.0.8.CI-SNAPSHOT - - org.springframework.security - spring-security-samples-cas-server - Spring Security - CAS Server for CAS Sample Application - pom - - - - org.mortbay.jetty - maven-jetty-plugin - ${jetty.version} - - /cas - ${basedir}/cas-server-webapp-3.3.5.war - - - 9443 - ../../certificates/server.jks - password - password - ../../certificates/server.jks - password - true - false - - - - - javax.net.ssl.trustStore - ../../certificates/server.jks - - - javax.net.ssl.trustStorePassword - password - - - - - - - \ No newline at end of file diff --git a/samples/cas/server/src/main/webapp/WEB-INF/classes/log4j.xml b/samples/cas/server/src/main/webapp/WEB-INF/classes/log4j.xml new file mode 100644 index 0000000000..c1ce7fa6fd --- /dev/null +++ b/samples/cas/server/src/main/webapp/WEB-INF/classes/log4j.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/samples/cas/server/src/main/webapp/WEB-INF/spring-configuration/zzzhttpClientCustomization.xml b/samples/cas/server/src/main/webapp/WEB-INF/spring-configuration/zzzhttpClientCustomization.xml new file mode 100644 index 0000000000..5ba031b6e4 --- /dev/null +++ b/samples/cas/server/src/main/webapp/WEB-INF/spring-configuration/zzzhttpClientCustomization.xml @@ -0,0 +1,22 @@ + + + + Customizations to the CAS Server. The name starts with zzz to ensure it is the last file loaded to override other bean definitions. + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/certificates/server.jks b/samples/certificates/server.jks index f56cf2e837234e7dfb3e770a1e8fe952eedf28ec..aaa1119fff47e0c59ae84df117a9f1b23311f1be 100755 GIT binary patch literal 84658 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 delta 49 zcmdlqm34tM&%d|--ZC&SFoE#qU$sY=H@{5W!zuFj*t8X*Z}iGUa? - 4.0.0 - - org.springframework.security - spring-security-samples - 3.0.8.CI-SNAPSHOT - - spring-security-samples-contacts - Spring Security - Contacts sample - war - - - - org.springframework.security - spring-security-core - ${project.version} - - - org.springframework.security - spring-security-config - ${project.version} - - - org.springframework.security - spring-security-acl - ${project.version} - - - org.springframework.security - spring-security-taglibs - ${project.version} - - - org.springframework - spring-core - - - commons-logging - commons-logging - - - - - org.springframework - spring-webmvc - compile - - - org.springframework - spring-jdbc - compile - - - org.springframework - spring-aop - - - org.springframework - spring-test - test - - - javax.servlet - servlet-api - - - javax.servlet - jstl - - - taglibs - standard - - - net.sf.ehcache - ehcache - - - hessian - hessian - 3.0.1 - - - hsqldb - hsqldb - runtime - - - org.slf4j - slf4j-api - 1.6.0 - runtime - - - org.slf4j - jcl-over-slf4j - 1.6.0 - - - org.slf4j - slf4j-log4j12 - 1.6.0 - runtime - - - log4j - log4j - runtime - - - - - - - org.apache.maven.plugins - maven-war-plugin - - - org.mortbay.jetty - maven-jetty-plugin - ${jetty.version} - - /contacts - - 5 - - - - - - - - diff --git a/samples/dms/dms.gradle b/samples/dms/dms.gradle new file mode 100644 index 0000000000..a1c1363436 --- /dev/null +++ b/samples/dms/dms.gradle @@ -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'), + "hsqldb:hsqldb:$hsqlVersion", + "net.sf.ehcache:ehcache:$ehcacheVersion", + "org.springframework:spring-context-support:$springVersion" + +} diff --git a/samples/dms/pom.xml b/samples/dms/pom.xml deleted file mode 100644 index 6311c39e27..0000000000 --- a/samples/dms/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - 4.0.0 - - org.springframework.security - spring-security-samples - 3.0.8.CI-SNAPSHOT - - spring-security-samples-dms - Spring Security - DMS sample - - - org.springframework.security - spring-security-core - ${project.version} - - - org.springframework.security - spring-security-acl - ${project.version} - - - org.springframework.security - spring-security-config - ${project.version} - - - org.springframework - spring-jdbc - - - javax.servlet - servlet-api - - - org.springframework - spring-aop - runtime - - - org.springframework - spring-test - ${spring.version} - - - hsqldb - hsqldb - - - net.sf.ehcache - ehcache - runtime - - - diff --git a/samples/ldap/ldap.gradle b/samples/ldap/ldap.gradle new file mode 100644 index 0000000000..befbeb78a7 --- /dev/null +++ b/samples/ldap/ldap.gradle @@ -0,0 +1,27 @@ +// LDAP sample build file + +apply plugin: 'war' +apply plugin: 'jetty' + +def excludeModules = ['spring-security-acl', 'jsr250-api', 'ehcache', 'spring-jdbc', 'ldapsdk'] + +configurations { + excludeModules.each {name -> + runtime.exclude module: name + } + + runtime.exclude group: 'org.aspectj' +} + +dependencies { + + runtime project(':spring-security-web'), + project(':spring-security-config'), + project(':spring-security-ldap'), + "org.slf4j:jcl-over-slf4j:$slf4jVersion", + "ch.qos.logback:logback-classic:$logbackVersion" +} + +jettyRun { + contextPath = "/ldap" +} diff --git a/samples/ldap/pom.xml b/samples/ldap/pom.xml deleted file mode 100644 index 6b2b7de040..0000000000 --- a/samples/ldap/pom.xml +++ /dev/null @@ -1,106 +0,0 @@ - - 4.0.0 - - org.springframework.security - spring-security-samples - 3.0.8.CI-SNAPSHOT - - org.springframework.security - spring-security-samples-ldap - Spring Security - Ldap Sample - war - - - org.springframework.security - spring-security-ldap - ${project.version} - - - org.springframework.security - spring-security-config - ${project.version} - - - org.springframework.security - spring-security-web - ${project.version} - - - org.springframework - spring-core - - - commons-logging - commons-logging - - - - - org.springframework - spring-webmvc - ${spring.version} - - - org.springframework - spring-aop - runtime - - - org.apache.directory.server - apacheds-core - 1.5.5 - compile - - - org.apache.directory.server - apacheds-server-jndi - 1.5.5 - compile - - - org.slf4j - slf4j-api - 1.6.0 - runtime - - - org.slf4j - jcl-over-slf4j - 1.6.0 - runtime - - - org.slf4j - slf4j-log4j12 - 1.6.0 - runtime - - - log4j - log4j - runtime - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - - - org.mortbay.jetty - maven-jetty-plugin - ${jetty.version} - - /ldap - - - - - - diff --git a/samples/openid/openid.gradle b/samples/openid/openid.gradle index b5e017cf76..47e3ebd112 100644 --- a/samples/openid/openid.gradle +++ b/samples/openid/openid.gradle @@ -4,6 +4,8 @@ apply plugin: 'war' apply plugin: 'jetty' dependencies { + compile project(':spring-security-core'), + project(':spring-security-openid') providedCompile 'javax.servlet:servlet-api:2.5@jar' runtime project(':spring-security-config'), diff --git a/samples/openid/pom.xml b/samples/openid/pom.xml deleted file mode 100644 index 21d5df8c68..0000000000 --- a/samples/openid/pom.xml +++ /dev/null @@ -1,103 +0,0 @@ - - 4.0.0 - - org.springframework.security - spring-security-samples - 3.0.8.CI-SNAPSHOT - - org.springframework.security - spring-security-samples-openid - Spring Security - OpenID sample - war - - - org.springframework.security - spring-security-core - ${project.version} - - - org.springframework.security - spring-security-config - ${project.version} - - - org.springframework.security - spring-security-web - ${project.version} - - - org.springframework.security - spring-security-openid - ${project.version} - - - org.springframework - spring-core - - - commons-logging - commons-logging - - - - org.springframework - spring-webmvc - runtime - - - org.springframework - spring-jdbc - runtime - - - org.springframework - spring-aop - runtime - - - javax.servlet - jstl - - - taglibs - standard - - - org.slf4j - slf4j-api - 1.6.0 - runtime - - - org.slf4j - jcl-over-slf4j - 1.6.0 - runtime - - - org.slf4j - slf4j-log4j12 - 1.6.0 - runtime - - - log4j - log4j - runtime - - - - - - - org.mortbay.jetty - maven-jetty-plugin - ${jetty.version} - - /openid - - - - - - diff --git a/samples/pom.xml b/samples/pom.xml deleted file mode 100644 index 9a8fd4d322..0000000000 --- a/samples/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - 4.0.0 - - org.springframework.security - spring-security-parent - 3.0.8.CI-SNAPSHOT - - org.springframework.security - spring-security-samples - Spring Security - Samples - pom - - contacts - tutorial - dms - preauth - openid - ldap - cas - aspectj - - - - javax.servlet - servlet-api - - - javax.servlet - jstl - runtime - - - taglibs - standard - runtime - - - diff --git a/samples/preauth/pom.xml b/samples/preauth/pom.xml deleted file mode 100644 index 9ee13dc39f..0000000000 --- a/samples/preauth/pom.xml +++ /dev/null @@ -1,89 +0,0 @@ - - 4.0.0 - - org.springframework.security - spring-security-samples - 3.0.8.CI-SNAPSHOT - - org.springframework.security - spring-security-samples-preauth - Spring Security - Preauthentication sample - war - - - org.springframework.security - spring-security-core - ${project.version} - runtime - - - org.springframework - spring-core - - - commons-logging - commons-logging - - - - - org.springframework.security - spring-security-web - ${project.version} - runtime - - - org.springframework.security - spring-security-config - ${project.version} - runtime - - - org.springframework - spring-jdbc - runtime - - - org.slf4j - slf4j-api - 1.6.0 - runtime - - - org.slf4j - jcl-over-slf4j - 1.6.0 - runtime - - - org.slf4j - slf4j-log4j12 - 1.6.0 - runtime - - - log4j - log4j - runtime - - - - - - - org.mortbay.jetty - maven-jetty-plugin - ${jetty.version} - - /preauth - - - Preauth Realm - realm.properties - - - - - - - diff --git a/samples/preauth/preauth.gradle b/samples/preauth/preauth.gradle new file mode 100644 index 0000000000..e7218d8ef9 --- /dev/null +++ b/samples/preauth/preauth.gradle @@ -0,0 +1,27 @@ +// Preauth sample build file + +apply plugin: 'war' +apply plugin: 'jetty' + +def excludeModules = ['jsr250-api', 'ehcache', 'spring-jdbc', 'spring-tx'] + +configurations { + excludeModules.each {name -> + runtime.exclude module: name + } + + runtime.exclude group: 'org.aspectj' +} + +dependencies { + + runtime project(':spring-security-web'), + project(':spring-security-config'), + "org.slf4j:jcl-over-slf4j:$slf4jVersion", + "ch.qos.logback:logback-classic:$logbackVersion" +} + +jettyRun { + contextPath = "/preauth" + userRealms = [jettyRun.class.classLoader.loadClass('org.mortbay.jetty.security.HashUserRealm').newInstance('Preauth Realm', "$projectDir/realm.properties")] +} diff --git a/samples/runall.sh b/samples/runall.sh index 784734b98d..eb931a76a9 100755 --- a/samples/runall.sh +++ b/samples/runall.sh @@ -19,7 +19,7 @@ cleanup() { start_jetty() { - mvn -o jetty:run > runall.log & + gradle -d jettyRun > runall.log & until (grep "Started Jetty Server" runall.log) do echo "- Waiting for server to start... -" diff --git a/samples/tutorial/pom.xml b/samples/tutorial/pom.xml index 32e493487d..7f6a4f472b 100644 --- a/samples/tutorial/pom.xml +++ b/samples/tutorial/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-samples - 3.0.8.CI-SNAPSHOT + @pomVersion@ org.springframework.security spring-security-samples-tutorial diff --git a/samples/tutorial/readme.txt b/samples/tutorial/readme.txt index f14e775c8c..bfa6a48f5d 100644 --- a/samples/tutorial/readme.txt +++ b/samples/tutorial/readme.txt @@ -1,6 +1,10 @@ -Run the application directly from the checked out source tree, using the command +This is the most basic sample web application for Spring Security. -mvn jetty:run +If you have gradle installed, you can run the application directly from the checked out source tree, using the command + +gradle jettyRun This will start jetty on port 8080, with SSL support on port 8443. +The 'scripts' directory contains simple Unix command-line scripts for exercising the application and displaying the results. + diff --git a/samples/tutorial/tutorial.gradle b/samples/tutorial/tutorial.gradle index ec2a90d014..a98a54098f 100644 --- a/samples/tutorial/tutorial.gradle +++ b/samples/tutorial/tutorial.gradle @@ -3,6 +3,15 @@ apply plugin: 'war' apply plugin: 'jetty' +def excludeModules = ['spring-security-acl', 'jsr250-api', 'ehcache', 'spring-jdbc', 'spring-tx'] + +configurations { + excludeModules.each {name -> + runtime.exclude module: name + } + +} + dependencies { providedCompile 'javax.servlet:servlet-api:2.5@jar' @@ -10,10 +19,28 @@ dependencies { "org.springframework:spring-beans:$springVersion", "org.springframework:spring-web:$springVersion", "org.springframework:spring-webmvc:$springVersion", - "org.aspectj:aspectjrt:$aspectjVersion" + "org.aspectj:aspectjrt:$aspectjVersion", + "org.slf4j:slf4j-api:$slf4jVersion" runtime project(':spring-security-web'), project(':spring-security-config'), project(':spring-security-taglibs'), - 'log4j:log4j:1.2.15@jar' -} \ No newline at end of file + "javax.servlet:jstl:$jstlVersion", + "org.slf4j:jcl-over-slf4j:$slf4jVersion", + "ch.qos.logback:logback-core:$logbackVersion", + "ch.qos.logback:logback-classic:$logbackVersion" +} + +jettyRun { + contextPath = "/tutorial" + + 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 = "$rootDir/samples/certificates/server.jks" + httpsConnector.keyPassword = 'password' + + connectors = [httpConnector, httpsConnector] +} diff --git a/sandbox/heavyduty/pom.xml b/sandbox/heavyduty/pom.xml deleted file mode 100755 index bb7a20b95d..0000000000 --- a/sandbox/heavyduty/pom.xml +++ /dev/null @@ -1,194 +0,0 @@ - - 4.0.0 - org.springframework.security - spring-security-heavyduty - Spring Security - Heavy Duty Sample - war - 3.0.8.CI-SNAPSHOT - - - org.springframework.security - spring-security-web - ${spring.security.version} - - - org.springframework.security - spring-security-ldap - ${spring.security.version} - - - org.springframework.security - spring-security-config - ${spring.security.version} - - - org.springframework.security - spring-security-taglibs - ${spring.security.version} - - - org.springframework - spring-context-support - ${spring.version} - - - org.springframework - spring-web - ${spring.version} - - - org.springframework - spring-webmvc - ${spring.version} - - - org.springframework - spring-jdbc - runtime - ${spring.version} - - - org.springframework - spring-orm - ${spring.version} - - - org.freemarker - freemarker - runtime - 2.3.16 - - - hsqldb - hsqldb - 1.8.0.10 - compile - - - org.hibernate - hibernate-entitymanager - 3.4.0.GA - compile - - - commons-logging - commons-logging - - - - - net.sf.ehcache - ehcache - 1.6.2 - compile - - - javax.servlet - servlet-api - provided - 2.4 - - - javax.servlet - jstl - runtime - 1.1.2 - - - taglibs - standard - runtime - 1.1.2 - - - org.apache.directory.server - apacheds-core - 1.5.5 - compile - true - - - org.apache.directory.server - apacheds-server-jndi - 1.5.5 - compile - true - - - commons-collections - commons-collections - 3.2.1 - runtime - - - org.slf4j - slf4j-api - 1.6.0 - runtime - - - org.slf4j - slf4j-log4j12 - 1.6.0 - runtime - - - log4j - log4j - runtime - 1.2.14 - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - - - org.apache.maven.plugins - maven-eclipse-plugin - 2.5.1 - - true - 2.0 - - - - org.mortbay.jetty - maven-jetty-plugin - 6.1.22 - - /heavyduty - - - - 8080 - 8443 - - - 8443 - certificates/server.jks - password - password - certificates/server.jks - password - true - false - - - - - - - - 3.0.3.RELEASE - 3.0.6.RELEASE - - - diff --git a/sandbox/webflow/pom.xml b/sandbox/webflow/pom.xml deleted file mode 100644 index 9d72da6066..0000000000 --- a/sandbox/webflow/pom.xml +++ /dev/null @@ -1,96 +0,0 @@ - - 4.0.0 - org.springframework.security - spring-security-samples-webflow - Spring Security - Webflow sample - 3.0.8.CI-SNAPSHOT - war - - - org.springframework.security - spring-security-core - ${project.version} - - - org.springframework.security - spring-security-taglibs - ${project.version} - - - org.springframework.webflow - spring-webflow - 2.0.8.RELEASE - - - ognl - ognl - 2.7.3 - - - org.springframework - spring-web - 3.0.0.RELEASE - - - org.springframework - spring-core - 3.0.0.RELEASE - - - org.springframework - spring-context - 3.0.0.RELEASE - - - org.springframework - spring-webmvc - 3.0.0.RELEASE - - - javax.servlet - servlet-api - provided - 2.5 - - - org.freemarker - freemarker - runtime - 2.3.16 - - - log4j - log4j - runtime - 1.2.14 - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - - - org.mortbay.jetty - maven-jetty-plugin - 6.1.22 - - /webflow - - - 8080 - 8443 - - - - - - - - diff --git a/settings.gradle b/settings.gradle index 4151bf051a..e8b7c57205 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,60 +7,65 @@ def String[] modules = [ 'cas', 'openid', 'taglibs', - 'aspects' + 'aspects', + 'parent' ] def String[] samples = [ 'tutorial', 'contacts', 'openid', - 'aspectj' -] - -def String[] docs = [ - 'faq', - 'manual' + 'aspectj', + 'dms', + 'preauth', + 'cas/server', + 'cas/sample', + 'ldap' ] def String[] itest = [ - 'web' + 'web', + 'context' ] include modules modules.each {name -> - p = findProject(":${name}") + def p = findProject(":${name}") p.name = "spring-security-${name}" p.buildFileName = "${name}.gradle" } +def casClient = findProject(":spring-security-cas") +casClient.name = 'spring-security-cas-client' + include samples samples.each {name -> - p = findProject(":${name}") - p.name = "spring-security-samples-${name}" - p.buildFileName = "${name}.gradle" + def p = findProject(":${name}") + def fullName = name.replaceAll('/','') + p.name = "spring-security-samples-${fullName}" + p.buildFileName = "${fullName}.gradle" p.projectDir = new File(settingsDir, "samples/${name}"); } include itest itest.each { name -> - p = findProject(":${name}") + def p = findProject(":${name}") p.name = "itest-${name}" p.buildFileName = "itest-${name}.gradle" p.projectDir = new File(settingsDir, "itest/${name}"); } -include docs +include 'docs', 'docs:faq', 'docs:manual' -docs.each { name -> - p = findProject(":${name}") - p.buildFileName = "${name}.gradle" - p.projectDir = new File(settingsDir, "docs/${name}"); -} +def docs = findProject(':docs') +docs.buildFileName = 'docs.gradle' + +rootProject.name = 'spring-security' rootProject.children.each {project -> assert project.projectDir.isDirectory() assert project.buildFile.isFile() -} \ No newline at end of file +} diff --git a/taglibs/pom.xml b/taglibs/pom.xml index ec9b5db899..2fd889e07f 100644 --- a/taglibs/pom.xml +++ b/taglibs/pom.xml @@ -3,13 +3,13 @@ spring-security-parent org.springframework.security - 3.0.8.CI-SNAPSHOT + @pomVersion@ 4.0.0 org.springframework.security spring-security-taglibs Spring Security - JSP taglibs - 3.0.8.CI-SNAPSHOT + @pomVersion@ jar diff --git a/taglibs/taglibs.gradle b/taglibs/taglibs.gradle index 7765bf5f15..d631a4550f 100644 --- a/taglibs/taglibs.gradle +++ b/taglibs/taglibs.gradle @@ -5,11 +5,12 @@ dependencies { project(':spring-security-web'), project(':spring-security-acl'), "org.springframework:spring-beans:$springVersion", + "org.springframework:spring-aop:$springVersion", "org.springframework:spring-context:$springVersion", "org.springframework:spring-expression:$springVersion", "org.springframework:spring-web:$springVersion" provided 'javax.servlet:jsp-api:2.0', 'javax.servlet:servlet-api:2.5' - testRuntime "taglibs:standard:$jstlVersion" + testRuntime "javax.servlet:jstl:$jstlVersion" } \ No newline at end of file diff --git a/taglibs/template.mf b/taglibs/template.mf index 72d401ce61..bce2d3fbac 100644 --- a/taglibs/template.mf +++ b/taglibs/template.mf @@ -9,15 +9,17 @@ Ignored-Existing-Headers: Import-Package, Export-Package Import-Template: - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.springframework.security.core.*;version="[${version}, 3.1.0)", - org.springframework.security.web.*;version="[${version}, 3.1.0)", - org.springframework.security.access.*;version="[${version}, 3.1.0)", - org.springframework.security.util;version="[${version}, 3.1.0)", - org.springframework.security.acls.*;version="[${version}, 3.1.0)";resolution:=optional, - org.springframework.beans.*;version="[${spring.version}, 3.1.0)", - org.springframework.context.*;version="[${spring.version}, 3.1.0)", - org.springframework.expression;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.web.context.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.web.util.*;version="[${spring.version}, 3.1.0)", + org.apache.commons.logging.*;version="${cloggingRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.web.*;version="${secRange}", + org.springframework.security.access.*;version="${secRange}", + org.springframework.security.util;version="${secRange}", + org.springframework.security.acls.*;version="${secRange}";resolution:=optional, + org.springframework.core.*;version="${springRange}", + org.springframework.util;version="${springRange}", + org.springframework.beans.*;version="${springRange}", + org.springframework.context.*;version="${springRange}", + org.springframework.expression;version="${springRange}";resolution:=optional, + org.springframework.web.context.*;version="${springRange}";resolution:=optional, + org.springframework.web.util.*;version="${springRange}", javax.servlet.*;version="0" diff --git a/web/pom.xml b/web/pom.xml index 222360f9b0..780219467d 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -3,7 +3,7 @@ org.springframework.security spring-security-parent - 3.0.8.CI-SNAPSHOT + @pomVersion@ jar spring-security-web diff --git a/web/template.mf b/web/template.mf index 07655c62b8..3cec203cea 100644 --- a/web/template.mf +++ b/web/template.mf @@ -17,23 +17,25 @@ Ignored-Existing-Headers: Import-Package, Export-Package Import-Template: - org.apache.commons.logging.*;version="[1.0.4, 2.0.0)", - org.springframework.security.core.*;version="[${version}, 3.1.0)", - org.springframework.security.authentication.*;version="[${version}, 3.1.0)", - org.springframework.security.access.*;version="[${version}, 3.1.0)", - org.springframework.security.util;version="[${version}, 3.1.0)", - org.springframework.beans.*;version="[${spring.version}, 3.1.0)", - org.springframework.context.*;version="[${spring.version}, 3.1.0)", - org.springframework.core;version="[${spring.version}, 3.1.0)", - org.springframework.core.io;version="[${spring.version}, 3.1.0)", - org.springframework.dao;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.expression;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.expression.spel.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.jdbc.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.mock.web;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.web.context.*;version="[${spring.version}, 3.1.0)";resolution:=optional, - org.springframework.web.filter.*;version="[${spring.version}, 3.1.0)", - org.springframework.util;version="[${spring.version}, 3.1.0)";resolution:=optional, + org.apache.commons.logging.*;version="${cloggingRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.crypto.*;version="${secRange}", + org.springframework.security.authentication.*;version="${secRange}", + org.springframework.security.access.*;version="${secRange}", + org.springframework.security.util;version="${secRange}", + org.springframework.beans.*;version="${springRange}", + org.springframework.context.*;version="${springRange}", + org.springframework.core;version="${springRange}", + org.springframework.core.io;version="${springRange}", + org.springframework.dao;version="${springRange}";resolution:=optional, + org.springframework.expression;version="${springRange}";resolution:=optional, + org.springframework.expression.spel.*;version="${springRange}";resolution:=optional, + org.springframework.http.*;version="${springRange}", + org.springframework.jdbc.*;version="${springRange}";resolution:=optional, + org.springframework.mock.web;version="${springRange}";resolution:=optional, + org.springframework.web.context.*;version="${springRange}";resolution:=optional, + org.springframework.web.filter.*;version="${springRange}", + org.springframework.util;version="${springRange}";resolution:=optional, org.w3c.dom;version="0";resolution:=optional, org.xml.sax;version="0";resolution:=optional, javax.servlet.*;version="0", diff --git a/web/web.gradle b/web/web.gradle index a47e5531d7..edb246db02 100644 --- a/web/web.gradle +++ b/web/web.gradle @@ -1,20 +1,26 @@ -// Web module build file - dependencies { compile project(':spring-security-core'), 'aopalliance:aopalliance:1.0', - "org.aspectj:aspectjweaver:$aspectjVersion", "org.springframework:spring-aop:$springVersion", "org.springframework:spring-beans:$springVersion", "org.springframework:spring-context:$springVersion", "org.springframework:spring-expression:$springVersion", "org.springframework:spring-jdbc:$springVersion", "org.springframework:spring-tx:$springVersion", - "org.springframework:spring-web:$springVersion", - "org.springframework:spring-test:$springVersion" + "org.springframework:spring-web:$springVersion" provided 'javax.servlet:servlet-api:2.5' - testCompile 'commons-codec:commons-codec:1.3' + testCompile project(':spring-security-core').sourceSets.test.output, + 'commons-codec:commons-codec:1.3', + "org.slf4j:jcl-over-slf4j:$slf4jVersion", + "org.springframework:spring-test:$springVersion", + "org.powermock:powermock-core:$powerMockVersion", + "org.powermock:powermock-api-support:$powerMockVersion", + "org.powermock:powermock-module-junit4-common:$powerMockVersion", + "org.powermock:powermock-module-junit4:$powerMockVersion", + "org.powermock:powermock-api-mockito:$powerMockVersion", + "org.powermock:powermock-reflect:$powerMockVersion" + testRuntime "hsqldb:hsqldb:$hsqlVersion" }