From 5818336b4fa47953340d44064ec01be51753fc17 Mon Sep 17 00:00:00 2001 From: Brian Koehmstedt <1261658+bkoehm@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:59:07 -0700 Subject: [PATCH 01/30] Grails 7 --- .github/workflows/gradle.yml | 8 +- README.md | 1 + buildSrc/build.gradle | 2 +- examples/functional-test-app/build.gradle | 12 +- .../grails-app/conf/application.groovy | 12 ++ .../grails-app/views/error.gsp | 4 +- .../BasicAuthCacheUsersSecuritySpec.groovy | 151 ++++++++++++++++++ .../groovy/specs/RoleSpec.groovy | 1 + .../groovy/specs/UserSpec.groovy | 1 + .../com/testapp/MaintenanceModeFilter.groovy | 12 +- .../TestUserPasswordEncoderListener.groovy | 2 +- examples/integration-test-app/build.gradle | 2 +- .../springsecurity/SecurityTagLibSpec.groovy | 4 +- .../SpringSecurityUtilsIntegrationSpec.groovy | 6 +- .../com/test/AdditionalLogoutHandler.groovy | 4 +- gradle/buildsrc.libs.versions.toml | 6 +- gradle/groovy-config.gradle | 5 +- gradle/java-config.gradle | 2 +- gradle/libs.versions.toml | 60 +++---- gradle/wrapper/gradle-wrapper.properties | 2 +- plugin/build.gradle | 8 +- .../springsecurity/LoginController.groovy | 2 +- .../springsecurity/LogoutController.groovy | 2 +- .../SpringSecurityService.groovy | 2 +- .../springsecurity/SecurityTagLib.groovy | 2 +- .../grails-app/views/error.gsp | 4 +- .../grails-app/views/error.gsp | 4 +- .../grails-app/views/error.gsp | 4 +- .../s2-quickstart/grails-app/views/error.gsp | 4 +- .../SpringSecurityCoreGrailsPlugin.groovy | 22 +-- .../springsecurity/SpringSecurityUtils.groovy | 6 +- .../cache/SpringUserCacheFactoryBean.groovy | 58 +++++++ .../web/GrailsRedirectStrategy.groovy | 4 +- .../web/GrailsSecurityFilterChain.groovy | 4 +- .../web/SecurityRequestHolder.groovy | 4 +- .../web/SecurityRequestHolderFilter.groovy | 14 +- ...extHolderExceptionTranslationFilter.groovy | 12 +- .../AjaxAwareAccessDeniedHandler.groovy | 6 +- .../access/DefaultThrowableAnalyzer.groovy | 2 +- ...ailsWebInvocationPrivilegeEvaluator.groovy | 10 +- .../AbstractFilterInvocationDefinition.groovy | 8 +- ...nnotationFilterInvocationDefinition.groovy | 10 +- .../AjaxAwareAuthenticationEntryPoint.groovy | 6 +- ...axAwareAuthenticationFailureHandler.groovy | 6 +- ...axAwareAuthenticationSuccessHandler.groovy | 6 +- .../FilterProcessUrlRequestMatcher.groovy | 2 +- ...sernamePasswordAuthenticationFilter.groovy | 6 +- ...NullLogoutHandlerRememberMeServices.groovy | 4 +- .../logout/MutableLogoutFilter.groovy | 12 +- .../NullAuthenticationFailureHandler.groovy | 6 +- .../NullAuthenticationSuccessHandler.groovy | 6 +- .../web/filter/DebugFilter.groovy | 10 +- ...GrailsAnonymousAuthenticationFilter.groovy | 10 +- ...railsRememberMeAuthenticationFilter.groovy | 4 +- .../filter/HttpMethodOverrideDetector.groovy | 2 +- .../web/filter/IpAddressFilter.groovy | 12 +- .../AuthorityGroupAuthority.groovy.template | 2 +- .../templates/PersonAuthority.groovy.template | 2 +- .../PersonAuthorityGroup.groovy.template | 2 +- ...ordEncoderListenerWithSalt.groovy.template | 2 +- .../springsecurity/SecurityTestUtils.groovy | 2 +- .../SpringSecurityUtilsSpec.groovy | 6 +- .../SecurityRequestHolderFilterSpec.groovy | 2 +- .../logout/MutableLogoutFilterSpec.groovy | 2 +- .../web/filter/IpAddressFilterSpec.groovy | 2 +- 65 files changed, 418 insertions(+), 185 deletions(-) create mode 100644 examples/functional-test-app/src/integration-test/groovy/specs/BasicAuthCacheUsersSecuritySpec.groovy create mode 100644 plugin/src/main/groovy/grails/plugin/springsecurity/cache/SpringUserCacheFactoryBean.groovy diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3c394a088..b8be082b2 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - uses: gradle/wrapper-validation-action@v2 - uses: actions/setup-java@v4 - with: { java-version: 11, distribution: temurin } + with: { java-version: 17, distribution: temurin } - name: Run Tests uses: gradle/actions/setup-gradle@v3 env: @@ -36,11 +36,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - test-config: [ 'static', 'annotation', 'requestmap', 'basic', 'misc', 'putWithParams', 'bcrypt', 'issue503' ] + test-config: [ 'static', 'annotation', 'requestmap', 'basic', 'basicCacheUsers', 'misc', 'putWithParams', 'bcrypt', 'issue503' ] steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 - with: { java-version: 11, distribution: temurin } + with: { java-version: 17, distribution: temurin } - name: Run Tests uses: gradle/actions/setup-gradle@v3 env: @@ -58,7 +58,7 @@ jobs: - uses: actions/checkout@v4 - uses: gradle/wrapper-validation-action@v2 - uses: actions/setup-java@v4 - with: { java-version: 11, distribution: temurin } + with: { java-version: 17, distribution: temurin } - name: Run Build id: build uses: gradle/actions/setup-gradle@v3 diff --git a/README.md b/README.md index 6f96b4b3e..2a79f7aef 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ See [documentation](https://grails-plugins.github.io/grails-spring-security-core ### Branch structure +- `7.0.x` compatible with Grails 7 - `6.0.x` compatible with Grails 6 - `5.0.x` compatible with Grails 5 - `4.0.x` compatible with Grails 4 diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index f8da12941..f5f4d22ba 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -9,7 +9,7 @@ dependencies { implementation buildsrcLibs.grails.gradle.plugin, { // Grails Gradle plugin leaks groovy-xml onto compile classpath // which is causes a version conflict for Gradle - exclude group: 'org.codehaus.groovy', module: 'groovy-xml' + exclude group: 'org.apache.groovy', module: 'groovy-xml' } implementation buildsrcLibs.webdriver.binaries.gradle.plugin diff --git a/examples/functional-test-app/build.gradle b/examples/functional-test-app/build.gradle index 9b471fcc7..8185ded74 100644 --- a/examples/functional-test-app/build.gradle +++ b/examples/functional-test-app/build.gradle @@ -8,6 +8,12 @@ plugins { group = 'examples.test' +configurations { + all { + exclude group: 'io.micronaut', module:'micronaut-inject-groovy' + } +} + dependencies { implementation project(':spring-security-core') @@ -23,6 +29,7 @@ dependencies { implementation libs.spring.security.core implementation libs.spring.web + runtimeOnly libs.micronaut.jackson.databind runtimeOnly libs.gorm.hibernate5 runtimeOnly libs.grails.asset.pipeline runtimeOnly libs.grails.i18n @@ -35,9 +42,8 @@ dependencies { runtimeOnly libs.h2database runtimeOnly libs.tomcat.jdbc - compileOnly libs.micronaut.inject.groovy - compileOnly libs.javax.annotation.api - compileOnly libs.javax.servlet.api + compileOnly libs.jakarta.annotation.api + compileOnly libs.jakarta.servlet.api compileOnly libs.slf4j.nop // Prevent warnings about missing SLF4j implementation during GSP compilation } diff --git a/examples/functional-test-app/grails-app/conf/application.groovy b/examples/functional-test-app/grails-app/conf/application.groovy index 10fef5123..15f928f2a 100644 --- a/examples/functional-test-app/grails-app/conf/application.groovy +++ b/examples/functional-test-app/grails-app/conf/application.groovy @@ -49,6 +49,18 @@ switch (testconfig) { ] break + case 'basicCacheUsers': + grails.plugin.springsecurity.securityConfigType = 'Annotation' + grails.plugin.springsecurity.useBasicAuth = true + grails.plugin.springsecurity.basic.realmName = 'Grails Spring Security Basic Test Realm' + grails.plugin.springsecurity.filterChain.chainMap = [ + [pattern: '/secureclassannotated/**', filters: 'JOINED_FILTERS,-exceptionTranslationFilter'], + [pattern: '/**', filters: 'JOINED_FILTERS,-basicAuthenticationFilter,-basicExceptionTranslationFilter'] + ] + grails.plugin.springsecurity.cacheUsers = true + grails.plugin.springsecurity.providerManager.eraseCredentialsAfterAuthentication = false + break + case 'bcrypt': grails.plugin.springsecurity.securityConfigType = 'Annotation' grails.plugin.springsecurity.password.algorithm = 'bcrypt' diff --git a/examples/functional-test-app/grails-app/views/error.gsp b/examples/functional-test-app/grails-app/views/error.gsp index 419d66668..06820cf89 100644 --- a/examples/functional-test-app/grails-app/views/error.gsp +++ b/examples/functional-test-app/grails-app/views/error.gsp @@ -6,8 +6,8 @@ - - + +
    diff --git a/examples/functional-test-app/src/integration-test/groovy/specs/BasicAuthCacheUsersSecuritySpec.groovy b/examples/functional-test-app/src/integration-test/groovy/specs/BasicAuthCacheUsersSecuritySpec.groovy new file mode 100644 index 000000000..2be33d916 --- /dev/null +++ b/examples/functional-test-app/src/integration-test/groovy/specs/BasicAuthCacheUsersSecuritySpec.groovy @@ -0,0 +1,151 @@ +package specs + +import org.springframework.security.core.userdetails.UserCache +import pages.LoginPage +import pages.role.CreateRolePage +import pages.role.ListRolePage +import pages.role.ShowRolePage +import pages.user.CreateUserPage +import pages.user.ListUserPage +import pages.user.ShowUserPage +import spock.lang.IgnoreIf + +@IgnoreIf({ System.getProperty('TESTCONFIG') != 'basicCacheUsers' }) +class BasicAuthCacheUsersSecuritySpec extends AbstractSecuritySpec { + + private HttpURLConnection connection + UserCache userCache + + void 'create roles'() { + when: + to ListRolePage + + then: + roleRows.size() == 0 + + when: + newRoleButton.click() + + then: + at CreateRolePage + + when: + authority = 'ROLE_ADMIN' + createButton.click() + + then: + at ShowRolePage + + when: + to ListRolePage + + then: + roleRows.size() == 1 + + when: + newRoleButton.click() + + then: + at CreateRolePage + + when: + authority = 'ROLE_ADMIN2' + createButton.click() + + then: + at ShowRolePage + + when: + to ListRolePage + + then: + roleRows.size() == 2 + } + + void 'create users'() { + when: + to ListUserPage + + then: + userRows.size() == 0 + + when: + newUserButton.click() + + then: + at CreateUserPage + + when: + username = 'admin1' + password = 'password1' + $('#enabled').click() + $('#ROLE_ADMIN').click() + createButton.click() + + then: + at ShowUserPage + + when: + to ListUserPage + + then: + userRows.size() == 1 + + when: + newUserButton.click() + + then: + at CreateUserPage + + when: + username = 'admin2' + password = 'password2' + $('#enabled').click() + $('#ROLE_ADMIN').click() + $('#ROLE_ADMIN2').click() + createButton.click() + + then: + at ShowUserPage + + when: + to ListUserPage + + then: + userRows.size() == 2 + } + + @IgnoreIf({ !System.getProperty('geb.env') }) + void 'check userDetails caching'() { + + when: + go 'secureAnnotated' + + then: + at LoginPage + + when: + login 'admin1', 'password1' + + then: + assertContentContains 'you have ROLE_ADMIN' + + and: + userCache.getUserFromCache('admin1') + + cleanup: + logout() + } + + protected void logout() { + super.logout() + // cheesy, but the 'Authentication' header from basic auth + // isn't cleared, so this forces an invalid header + getWithAuth '', 'not_a_valid_username', '' + } + + private void getWithAuth(String path, String username, String password) { + String uri = new URI(baseUrlRequired).resolve(new URI(path)) + go uri.replace('http://', 'http://' + username + ':' + password + '@') + } +} diff --git a/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy b/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy index 1a23ab9a4..950c13855 100644 --- a/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy +++ b/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy @@ -9,6 +9,7 @@ import spock.lang.IgnoreIf @IgnoreIf({ !( System.getProperty('TESTCONFIG') == 'annotation' || System.getProperty('TESTCONFIG') == 'basic' || + System.getProperty('TESTCONFIG') == 'basicCacheUsers' || System.getProperty('TESTCONFIG') == 'requestmap' || System.getProperty('TESTCONFIG') == 'static') }) diff --git a/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy b/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy index b8415c099..77729618f 100644 --- a/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy +++ b/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy @@ -9,6 +9,7 @@ import spock.lang.IgnoreIf @IgnoreIf({ !( System.getProperty('TESTCONFIG') == 'annotation' || System.getProperty('TESTCONFIG') == 'basic' || + System.getProperty('TESTCONFIG') == 'basicCacheUsers' || System.getProperty('TESTCONFIG') == 'requestmap' || System.getProperty('TESTCONFIG') == 'static') }) diff --git a/examples/functional-test-app/src/main/groovy/com/testapp/MaintenanceModeFilter.groovy b/examples/functional-test-app/src/main/groovy/com/testapp/MaintenanceModeFilter.groovy index 570d910b3..1eb8a71ab 100644 --- a/examples/functional-test-app/src/main/groovy/com/testapp/MaintenanceModeFilter.groovy +++ b/examples/functional-test-app/src/main/groovy/com/testapp/MaintenanceModeFilter.groovy @@ -3,12 +3,12 @@ package com.testapp import groovy.util.logging.Slf4j import org.springframework.web.filter.GenericFilterBean -import javax.servlet.FilterChain -import javax.servlet.ServletException -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletException +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse /** * If registered, this filter results in an HttpStatus of 500 being returned to the client diff --git a/examples/functional-test-app/src/main/groovy/com/testapp/TestUserPasswordEncoderListener.groovy b/examples/functional-test-app/src/main/groovy/com/testapp/TestUserPasswordEncoderListener.groovy index f7551f43c..2e8fbd9f5 100644 --- a/examples/functional-test-app/src/main/groovy/com/testapp/TestUserPasswordEncoderListener.groovy +++ b/examples/functional-test-app/src/main/groovy/com/testapp/TestUserPasswordEncoderListener.groovy @@ -7,7 +7,7 @@ import org.grails.datastore.mapping.engine.event.PreUpdateEvent import org.springframework.beans.factory.annotation.Autowired import grails.events.annotation.gorm.Listener import groovy.transform.CompileStatic -import javax.annotation.PostConstruct +import jakarta.annotation.PostConstruct @CompileStatic class TestUserPasswordEncoderListener { diff --git a/examples/integration-test-app/build.gradle b/examples/integration-test-app/build.gradle index 1432dfa39..4c7dac804 100644 --- a/examples/integration-test-app/build.gradle +++ b/examples/integration-test-app/build.gradle @@ -33,7 +33,7 @@ dependencies { runtimeOnly libs.tomcat.jdbc compileOnly libs.micronaut.inject.groovy - compileOnly libs.javax.servlet.api + compileOnly libs.jakarta.servlet.api compileOnly libs.slf4j.nop testImplementation libs.spock.core diff --git a/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityTagLibSpec.groovy b/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityTagLibSpec.groovy index 893bf58cf..477da685b 100644 --- a/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityTagLibSpec.groovy +++ b/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityTagLibSpec.groovy @@ -34,8 +34,8 @@ import org.springframework.web.context.request.RequestContextHolder import spock.lang.Ignore import spock.lang.Shared -import javax.servlet.FilterChain -import javax.servlet.ServletContext +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletContext import java.security.Principal /** diff --git a/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy b/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy index 81fa0d968..d120cf660 100644 --- a/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy +++ b/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy @@ -36,9 +36,9 @@ import test.TestRole import test.TestUser import test.TestUserRole -import javax.servlet.FilterChain -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse /** * Integration tests for SpringSecurityUtils. diff --git a/examples/integration-test-app/src/main/groovy/com/test/AdditionalLogoutHandler.groovy b/examples/integration-test-app/src/main/groovy/com/test/AdditionalLogoutHandler.groovy index 59da3b1bd..9a9c5ffea 100644 --- a/examples/integration-test-app/src/main/groovy/com/test/AdditionalLogoutHandler.groovy +++ b/examples/integration-test-app/src/main/groovy/com/test/AdditionalLogoutHandler.groovy @@ -1,7 +1,7 @@ package com.test -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.security.core.Authentication import org.springframework.security.web.authentication.logout.LogoutHandler diff --git a/gradle/buildsrc.libs.versions.toml b/gradle/buildsrc.libs.versions.toml index 3177c3c6e..71c3ce95e 100644 --- a/gradle/buildsrc.libs.versions.toml +++ b/gradle/buildsrc.libs.versions.toml @@ -1,7 +1,7 @@ [versions] -asciidoctorj = '4.0.2' -asset-pipeline-gradle = '4.4.0' -grails-gradle-plugin = '6.1.2' +asciidoctorj = '4.0.3' +asset-pipeline-gradle = '5.0.1' +grails-gradle-plugin = '7.0.0-SNAPSHOT' webdriver-binaries = '3.2' spock = '2.3-groovy-3.0' diff --git a/gradle/groovy-config.gradle b/gradle/groovy-config.gradle index e40f8102f..362a10c4b 100644 --- a/gradle/groovy-config.gradle +++ b/gradle/groovy-config.gradle @@ -1,8 +1,9 @@ configurations.configureEach { resolutionStrategy.eachDependency { DependencyResolveDetails details -> - if (details.requested.group == 'org.codehaus.groovy') { + if ((details.requested.group == 'org.codehaus.groovy' || details.requested.group == 'org.apache.groovy') && details.requested.name != 'groovy-bom') { String groovyVersion = findProperty('groovyVersion') ?: libs.versions.groovy.get() - details.useVersion(groovyVersion) + details.useTarget(group: 'org.apache.groovy', name: details.requested.name, version: groovyVersion) + details.because "The dependency coordinates are changed in Apache Groovy 4, plus ensure version" } } } diff --git a/gradle/java-config.gradle b/gradle/java-config.gradle index 0bad9f3ac..3132d5647 100644 --- a/gradle/java-config.gradle +++ b/gradle/java-config.gradle @@ -1,5 +1,5 @@ java { - sourceCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 withJavadocJar() withSourcesJar() } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0dbdae2e1..c98dcfd02 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,32 +1,32 @@ [versions] -asset-pipeline = '4.3.0' +asset-pipeline = '5.0.1' commons-lang = '2.6' -ehcache = '2.10.9.2' -geb = '6.0' -gorm-hibernate5 = '8.0.3' -grails = '6.1.2' -grails-async-and-events = '5.0.2' -grails-datamapping = '8.0.3' -grails-gsp = '6.2.0' -grails-converters = '4.0.1' -grails-testing-support = '3.1.2' -groovy = '3.0.21' -h2database = '2.2.224' -javax-annotation-api = '1.3.2' -javax-servlet-api = '4.0.1' -micronaut = '3.10.4' -selenium = '4.18.1' -selenium-safari = '4.18.1' -slf4j = '1.7.36' -spock = '2.0-groovy-3.0' -spring = '5.3.33' -springboot = '2.7.18' -spring-security = '5.8.11' -tomcat = '9.0.87' +ehcache = '3.10.8' +geb = '7.0' +gorm-hibernate5 = '9.0.0-SNAPSHOT' +grails = '7.0.0-SNAPSHOT' +grails-async-and-events = '7.0.0-SNAPSHOT' +grails-datamapping = '9.0.0-SNAPSHOT' +grails-gsp = '7.0.0-SNAPSHOT' +grails-converters = '6.0.0-SNAPSHOT' +grails-testing-support = '4.0.0-SNAPSHOT' +groovy = '4.0.22' +h2database = '2.3.232' +jakarta-annotation-api = '3.0.0' +jakarta-servlet-api = '6.0.0' +micronaut = '4.5.3' +selenium = '4.19.1' +selenium-safari = '4.19.1' +slf4j = '2.0.16' +spock = '2.3-groovy-4.0' +spring = '6.1.12' +springboot = '3.3.3' +spring-security = '6.3.3' +tomcat = '10.1.29' [libraries] commons-lang = { module = 'commons-lang:commons-lang', version.ref = 'commons-lang' } -ehcache = { module = 'net.sf.ehcache:ehcache', version.ref = 'ehcache' } +ehcache = { module = 'org.ehcache:ehcache', version.ref = 'ehcache' } geb-core = { module = 'org.gebish:geb-core', version.ref = 'geb' } geb-spock = { module = 'org.gebish:geb-spock', version.ref = 'geb' } gorm-hibernate5 = { module = 'org.grails.plugins:hibernate5', version.ref = 'gorm-hibernate5' } @@ -39,7 +39,6 @@ grails-datastore-gorm = { module = 'org.grails:grails-datastore-gorm', version.r grails-domain = { module = 'org.grails:grails-plugin-domain-class', version.ref = 'grails' } grails-events-transform = { module = 'org.grails:grails-events-transform', version.ref = 'grails-async-and-events' } grails-gsp = { module = 'org.grails.plugins:gsp', version.ref = 'grails-gsp' } -grails-gorm-testing-support = { module = 'org.grails:grails-gorm-testing-support', version.ref = 'grails-testing-support' } grails-i18n = { module = 'org.grails:grails-plugin-i18n', version.ref = 'grails' } grails-interceptors = { module = 'org.grails:grails-plugin-interceptors', version.ref = 'grails' } grails-mimetypes = { module = 'org.grails:grails-plugin-mimetypes', version.ref = 'grails' } @@ -51,14 +50,14 @@ grails-testing-support-web = { module = 'org.grails:grails-web-testing-support', grails-urlmappings = { module = 'org.grails:grails-plugin-url-mappings', version.ref = 'grails' } grails-validation = { module = 'org.grails:grails-plugin-validation', version.ref = 'grails' } grails-web-common = { module = 'org.grails:grails-web-common', version.ref = 'grails' } -grails-web-testing-support = { module = 'org.grails:grails-web-testing-support', version.ref = 'grails-testing-support' } grails-web-urlmappings = { module = 'org.grails:grails-web-url-mappings', version.ref = 'grails' } -groovy-core = { module = 'org.codehaus.groovy:groovy', version.ref = 'groovy' } -javax-annotation-api = { module = 'javax.annotation:javax.annotation-api', version.ref = 'javax-annotation-api' } -javax-servlet-api = { module = 'javax.servlet:javax.servlet-api', version.ref = 'javax-servlet-api' } +groovy-core = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' } +jakarta-annotation-api = { module = 'jakarta.annotation:jakarta.annotation-api', version.ref = 'jakarta-annotation-api' } +jakarta-servlet-api = { module = 'jakarta.servlet:jakarta.servlet-api', version.ref = 'jakarta-servlet-api' } h2database = { module = 'com.h2database:h2', version.ref = 'h2database' } micronaut-httpclient = { module = 'io.micronaut:micronaut-http-client', version.ref = 'micronaut' } micronaut-inject-groovy = { module = 'io.micronaut:micronaut-inject-groovy', version.ref = 'micronaut' } +micronaut-jackson-databind = { module = 'io.micronaut:micronaut-jackson-databind', version.ref = 'micronaut' } selenium-api = { module = 'org.seleniumhq.selenium:selenium-api', version.ref = 'selenium' } selenium-chrome-driver = { module = 'org.seleniumhq.selenium:selenium-chrome-driver', version.ref = 'selenium' } selenium-chromium-driver = { module = 'org.seleniumhq.selenium:selenium-chromium-driver', version.ref = 'selenium' } @@ -85,10 +84,11 @@ spring-web = { module = 'org.springframework:spring-web', version.ref = 'spring' springboot-autoconfigure = { module = 'org.springframework.boot:spring-boot-autoconfigure', version.ref = 'springboot' } springboot-core = { module = 'org.springframework.boot:spring-boot', version.ref = 'springboot' } springboot-starter-logging = { module = 'org.springframework.boot:spring-boot-starter-logging', version.ref = 'springboot' } +springboot-starter-test = { module = 'org.springframework.boot:spring-boot-starter-test', version.ref = 'springboot' } springboot-starter-tomcat = { module = 'org.springframework.boot:spring-boot-starter-tomcat', version.ref = 'springboot' } tomcat-jdbc = { module = 'org.apache.tomcat:tomcat-jdbc', version.ref = 'tomcat' } [bundles] geb = ['geb-core', 'geb-spock'] -grails-testing-support = ['grails-testing-support-gorm', 'grails-testing-support-web'] +grails-testing-support = ['grails-testing-support-gorm', 'grails-testing-support-web', 'springboot-starter-test'] selenium = ['selenium-api', 'selenium-chrome-driver', 'selenium-chromium-driver', 'selenium-devtools-v85', 'selenium-firefox-driver', 'selenium-http', 'selenium-json', 'selenium-remote-driver', 'selenium-safari-driver', 'selenium-support'] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c7d437bbb..c44c2304c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/plugin/build.gradle b/plugin/build.gradle index f03a62481..a2c3246d1 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -9,6 +9,12 @@ plugins { group = 'org.grails.plugins' +configurations { + all { + exclude group: 'javax.servlet' + } +} + dependencies { api libs.grails.core @@ -38,7 +44,7 @@ dependencies { implementation libs.spring.tx compileOnly libs.groovy.core // Compile-time annotations - compileOnly libs.javax.servlet.api // Provided + compileOnly libs.jakarta.servlet.api // Provided compileOnly libs.slf4j.nop // Prevents warnings about missing slf4j implementation during compilation testImplementation libs.bundles.grails.testing.support diff --git a/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy b/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy index 1be4bdc64..bb503ec78 100644 --- a/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy +++ b/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy @@ -27,7 +27,7 @@ import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.web.WebAttributes import org.springframework.security.web.authentication.session.SessionAuthenticationException -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpServletResponse @Secured('permitAll') class LoginController { diff --git a/plugin/grails-app/controllers/grails/plugin/springsecurity/LogoutController.groovy b/plugin/grails-app/controllers/grails/plugin/springsecurity/LogoutController.groovy index 7540a20cc..864062bbe 100644 --- a/plugin/grails-app/controllers/grails/plugin/springsecurity/LogoutController.groovy +++ b/plugin/grails-app/controllers/grails/plugin/springsecurity/LogoutController.groovy @@ -17,7 +17,7 @@ package grails.plugin.springsecurity import org.springframework.security.access.annotation.Secured import org.springframework.security.web.RedirectStrategy -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpServletResponse @Secured('permitAll') class LogoutController { diff --git a/plugin/grails-app/services/grails/plugin/springsecurity/SpringSecurityService.groovy b/plugin/grails-app/services/grails/plugin/springsecurity/SpringSecurityService.groovy index c66111007..1f2f8bc00 100644 --- a/plugin/grails-app/services/grails/plugin/springsecurity/SpringSecurityService.groovy +++ b/plugin/grails-app/services/grails/plugin/springsecurity/SpringSecurityService.groovy @@ -22,7 +22,7 @@ import org.springframework.security.authentication.AuthenticationTrustResolver import org.springframework.security.core.Authentication import org.springframework.security.core.context.SecurityContextHolder as SCH -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequest /** * Utility methods. diff --git a/plugin/grails-app/taglib/grails/plugin/springsecurity/SecurityTagLib.groovy b/plugin/grails-app/taglib/grails/plugin/springsecurity/SecurityTagLib.groovy index 923635c12..f0042a43b 100644 --- a/plugin/grails-app/taglib/grails/plugin/springsecurity/SecurityTagLib.groovy +++ b/plugin/grails-app/taglib/grails/plugin/springsecurity/SecurityTagLib.groovy @@ -25,7 +25,7 @@ import org.springframework.security.core.Authentication import org.springframework.security.web.FilterInvocation import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator -import javax.servlet.FilterChain +import jakarta.servlet.FilterChain /** * Security tags. diff --git a/plugin/src/docs/code/s2-quickstart-old/grails-app/views/error.gsp b/plugin/src/docs/code/s2-quickstart-old/grails-app/views/error.gsp index 9a3bb8aa3..a2c4235ab 100644 --- a/plugin/src/docs/code/s2-quickstart-old/grails-app/views/error.gsp +++ b/plugin/src/docs/code/s2-quickstart-old/grails-app/views/error.gsp @@ -10,8 +10,8 @@ - - + +
      diff --git a/plugin/src/docs/code/s2-quickstart-requestmap/grails-app/views/error.gsp b/plugin/src/docs/code/s2-quickstart-requestmap/grails-app/views/error.gsp index 9a3bb8aa3..a2c4235ab 100644 --- a/plugin/src/docs/code/s2-quickstart-requestmap/grails-app/views/error.gsp +++ b/plugin/src/docs/code/s2-quickstart-requestmap/grails-app/views/error.gsp @@ -10,8 +10,8 @@ - - + +
        diff --git a/plugin/src/docs/code/s2-quickstart-rolegroup/grails-app/views/error.gsp b/plugin/src/docs/code/s2-quickstart-rolegroup/grails-app/views/error.gsp index 9a3bb8aa3..a2c4235ab 100644 --- a/plugin/src/docs/code/s2-quickstart-rolegroup/grails-app/views/error.gsp +++ b/plugin/src/docs/code/s2-quickstart-rolegroup/grails-app/views/error.gsp @@ -10,8 +10,8 @@ - - + +
          diff --git a/plugin/src/docs/code/s2-quickstart/grails-app/views/error.gsp b/plugin/src/docs/code/s2-quickstart/grails-app/views/error.gsp index 9a3bb8aa3..a2c4235ab 100644 --- a/plugin/src/docs/code/s2-quickstart/grails-app/views/error.gsp +++ b/plugin/src/docs/code/s2-quickstart/grails-app/views/error.gsp @@ -10,8 +10,8 @@ - - + +
            diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy index 1d32096de..fe6698786 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy @@ -20,6 +20,7 @@ import grails.plugin.springsecurity.access.vote.AuthenticatedVetoableDecisionMan import grails.plugin.springsecurity.access.vote.ClosureVoter import grails.plugin.springsecurity.authentication.GrailsAnonymousAuthenticationProvider import grails.plugin.springsecurity.authentication.NullAuthenticationEventPublisher +import grails.plugin.springsecurity.cache.SpringUserCacheFactoryBean import grails.plugin.springsecurity.userdetails.DefaultPostAuthenticationChecks import grails.plugin.springsecurity.userdetails.DefaultPreAuthenticationChecks import grails.plugin.springsecurity.userdetails.GormUserDetailsService @@ -58,8 +59,7 @@ import groovy.util.logging.Slf4j import org.grails.web.mime.HttpServletResponseExtension import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.boot.web.servlet.ServletListenerRegistrationBean -import org.springframework.cache.ehcache.EhCacheFactoryBean -import org.springframework.cache.ehcache.EhCacheManagerFactoryBean +import org.springframework.cache.jcache.JCacheCacheManager import org.springframework.core.Ordered import org.springframework.expression.spel.standard.SpelExpressionParser import org.springframework.security.access.event.LoggerListener @@ -79,7 +79,6 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent import org.springframework.security.core.context.SecurityContextHolder as SCH import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper -import org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache import org.springframework.security.core.userdetails.cache.NullUserCache import org.springframework.security.crypto.argon2.Argon2PasswordEncoder import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder @@ -131,7 +130,7 @@ import org.springframework.security.web.session.HttpSessionEventPublisher import org.springframework.security.web.util.matcher.AnyRequestMatcher import org.springframework.web.filter.FormContentFilter -import javax.servlet.DispatcherType +import jakarta.servlet.DispatcherType /** * @author Burt Beckwith @@ -600,16 +599,11 @@ to default to 'Annotation'; setting value to 'Annotation' // user details cache if (conf.cacheUsers) { log.trace 'Configuring user cache' - userCache(classFor('userCache', EhCacheBasedUserCache)) { - cache = ref('securityUserCache') - } - securityUserCache(classFor('securityUserCache', EhCacheFactoryBean)) { + userCache(classFor('userCache', SpringUserCacheFactoryBean)) { cacheManager = ref('cacheManager') cacheName = 'userCache' } - cacheManager(classFor('cacheManager', EhCacheManagerFactoryBean)) { - cacheManagerName = 'spring-security-core-user-cache-' + UUID.randomUUID() - } + cacheManager(classFor('cacheManager', JCacheCacheManager)) } else { userCache(classFor('userCache', NullUserCache)) @@ -1122,9 +1116,9 @@ to default to 'Annotation'; setting value to 'Annotation' (ENCODING_ID_MD4): new Md4PasswordEncoder(), (ENCODING_ID_MD5): messageDigestPasswordEncoderMD5, (ENCODING_ID_NOOP): NoOpPasswordEncoder.getInstance(), - (ENCODING_ID_PBKDF2): new Pbkdf2PasswordEncoder(), - (ENCODING_ID_SCRYPT): new SCryptPasswordEncoder(), - (ENCODING_ID_ARGON2): new Argon2PasswordEncoder(), + (ENCODING_ID_PBKDF2): Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8(), + (ENCODING_ID_SCRYPT): SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8(), + (ENCODING_ID_ARGON2): Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8(), (ENCODING_ID_SHA1): messageDigestPasswordEncoderSHA1, (ENCODING_IDSHA256): messageDigestPasswordEncoderSHA256, "sha256": new StandardPasswordEncoder()] diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index 116b18408..ac31b19d1 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -40,9 +40,9 @@ import org.springframework.security.web.savedrequest.SavedRequest import org.springframework.util.StringUtils import org.springframework.web.multipart.MultipartHttpServletRequest -import javax.servlet.Filter -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpSession +import jakarta.servlet.Filter +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpSession import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/cache/SpringUserCacheFactoryBean.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/cache/SpringUserCacheFactoryBean.groovy new file mode 100644 index 000000000..5f2a51e26 --- /dev/null +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/cache/SpringUserCacheFactoryBean.groovy @@ -0,0 +1,58 @@ +/* Copyright 2024 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 grails.plugin.springsecurity.cache + +import groovy.transform.CompileStatic +import org.springframework.beans.factory.FactoryBean +import org.springframework.beans.factory.InitializingBean +import org.springframework.cache.jcache.JCacheCache +import org.springframework.cache.jcache.JCacheCacheManager +import org.springframework.security.core.userdetails.User +import org.springframework.security.core.userdetails.cache.SpringCacheBasedUserCache +import org.springframework.util.Assert + +import javax.cache.configuration.Configuration +import javax.cache.configuration.MutableConfiguration + +@CompileStatic +class SpringUserCacheFactoryBean implements FactoryBean, InitializingBean { + + JCacheCacheManager cacheManager + String cacheName + Configuration cacheConfig + private SpringCacheBasedUserCache springUserCache + + @Override + SpringCacheBasedUserCache getObject() throws Exception { + springUserCache + } + + @Override + Class getObjectType() { + SpringCacheBasedUserCache + } + + @Override + void afterPropertiesSet() throws Exception { + Assert.notNull(cacheManager, "cacheManager mandatory") + Assert.notNull(cacheName, "cacheName mandatory") + if (!cacheConfig) { + cacheConfig = new MutableConfiguration() + .setTypes(String, User) + .setStoreByValue(false) + } + springUserCache = new SpringCacheBasedUserCache(new JCacheCache(cacheManager.cacheManager.createCache(cacheName, cacheConfig))) + } +} diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsRedirectStrategy.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsRedirectStrategy.groovy index d04bc050d..ba5569ee8 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsRedirectStrategy.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsRedirectStrategy.groovy @@ -20,8 +20,8 @@ import org.springframework.security.web.PortResolver import org.springframework.security.web.RedirectStrategy import org.springframework.security.web.util.UrlUtils -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse /** * Builds absolute urls when using header check channel security to prevent the diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsSecurityFilterChain.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsSecurityFilterChain.groovy index 461fb095e..bb463ec82 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsSecurityFilterChain.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/GrailsSecurityFilterChain.groovy @@ -20,8 +20,8 @@ import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.util.matcher.AntPathRequestMatcher import org.springframework.security.web.util.matcher.RequestMatcher -import javax.servlet.Filter -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.Filter +import jakarta.servlet.http.HttpServletRequest /** * Based on org.springframework.security.web.DefaultSecurityFilterChain which is final. diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolder.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolder.groovy index 41d9e308d..5db996234 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolder.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolder.groovy @@ -14,8 +14,8 @@ */ package grails.plugin.springsecurity.web -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import groovy.transform.CompileStatic diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolderFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolderFilter.groovy index fa207e969..7450f69ad 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolderFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/SecurityRequestHolderFilter.groovy @@ -20,13 +20,13 @@ import org.springframework.security.web.PortMapper import org.springframework.security.web.PortResolver import org.springframework.web.filter.GenericFilterBean -import javax.servlet.FilterChain -import javax.servlet.ServletException -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletRequestWrapper -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletException +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequestWrapper +import jakarta.servlet.http.HttpServletResponse /** * Stores the request and response in the {@link SecurityRequestHolder}. Also wraps the request in a diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/UpdateRequestContextHolderExceptionTranslationFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/UpdateRequestContextHolderExceptionTranslationFilter.groovy index f5c3e0c1e..72e8c9396 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/UpdateRequestContextHolderExceptionTranslationFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/UpdateRequestContextHolderExceptionTranslationFilter.groovy @@ -23,12 +23,12 @@ import org.springframework.security.web.access.ExceptionTranslationFilter import org.springframework.security.web.savedrequest.RequestCache import org.springframework.web.context.request.RequestContextHolder -import javax.servlet.FilterChain -import javax.servlet.ServletException -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletException +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse /** * Replaces the current GrailsWebRequest with one that delegates to the real current instance but uses the request and diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/AjaxAwareAccessDeniedHandler.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/AjaxAwareAccessDeniedHandler.groovy index f2269d711..a9f94403b 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/AjaxAwareAccessDeniedHandler.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/AjaxAwareAccessDeniedHandler.groovy @@ -14,9 +14,9 @@ */ package grails.plugin.springsecurity.web.access -import javax.servlet.ServletException -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.ServletException +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.beans.factory.InitializingBean import org.springframework.security.access.AccessDeniedException diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/DefaultThrowableAnalyzer.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/DefaultThrowableAnalyzer.groovy index 89107f397..59cae7008 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/DefaultThrowableAnalyzer.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/DefaultThrowableAnalyzer.groovy @@ -14,7 +14,7 @@ */ package grails.plugin.springsecurity.web.access -import javax.servlet.ServletException +import jakarta.servlet.ServletException import org.springframework.security.web.util.ThrowableAnalyzer import org.springframework.security.web.util.ThrowableCauseExtractor diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/GrailsWebInvocationPrivilegeEvaluator.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/GrailsWebInvocationPrivilegeEvaluator.groovy index 266b9fac4..659e9c2b2 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/GrailsWebInvocationPrivilegeEvaluator.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/GrailsWebInvocationPrivilegeEvaluator.groovy @@ -20,11 +20,11 @@ import java.lang.reflect.InvocationHandler import java.lang.reflect.Method import java.lang.reflect.Proxy -import javax.servlet.FilterChain -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.security.access.AccessDeniedException import org.springframework.security.access.ConfigAttribute diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AbstractFilterInvocationDefinition.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AbstractFilterInvocationDefinition.groovy index 7a4dc7e72..1651549a2 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AbstractFilterInvocationDefinition.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AbstractFilterInvocationDefinition.groovy @@ -19,7 +19,7 @@ import org.springframework.web.util.UrlPathHelper import java.util.concurrent.CopyOnWriteArrayList -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequest import org.springframework.context.support.MessageSourceAccessor import org.springframework.http.HttpMethod @@ -175,11 +175,11 @@ abstract class AbstractFilterInvocationDefinition implements FilterInvocationSec } /** - * Resolve the URI from {@link javax.servlet.http.HttpServletRequest} - * @param request The {@link javax.servlet.http.HttpServletRequest} + * Resolve the URI from {@link jakarta.servlet.http.HttpServletRequest} + * @param request The {@link jakarta.servlet.http.HttpServletRequest} * * @return The resolved URI string - * @deprecated Use {@link org.springframework.web.util.UrlPathHelper#getRequestUri(javax.servlet.http.HttpServletRequest request)} and {@link #stripContextPath} instead + * @deprecated Use {@link org.springframework.web.util.UrlPathHelper#getRequestUri(jakarta.servlet.http.HttpServletRequest request)} and {@link #stripContextPath} instead */ @Deprecated protected String calculateUri(HttpServletRequest request) { diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AnnotationFilterInvocationDefinition.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AnnotationFilterInvocationDefinition.groovy index e66478712..02e0795f9 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AnnotationFilterInvocationDefinition.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/access/intercept/AnnotationFilterInvocationDefinition.groovy @@ -44,9 +44,11 @@ import org.springframework.util.ReflectionUtils import org.springframework.util.StringUtils import org.springframework.web.context.ServletContextAware -import javax.servlet.ServletContext -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.RequestDispatcher +import jakarta.servlet.ServletContext +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse + import java.lang.annotation.Annotation import java.lang.reflect.AccessibleObject import java.lang.reflect.Constructor @@ -93,7 +95,7 @@ class AnnotationFilterInvocationDefinition extends AbstractFilterInvocationDefin existingRequest = WebUtils.retrieveGrailsWebRequest() } catch (IllegalStateException e) { - if (request.getAttribute('javax.servlet.error.status_code') == 404) { + if (request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) == 404) { ERROR404 } else { diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPoint.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPoint.groovy index 4cbadcdf9..b02f3fe2c 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPoint.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPoint.groovy @@ -14,9 +14,9 @@ */ package grails.plugin.springsecurity.web.authentication -import javax.servlet.ServletException -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.ServletException +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.security.core.AuthenticationException import org.springframework.security.web.RedirectStrategy diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationFailureHandler.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationFailureHandler.groovy index c21483549..65e83c0c7 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationFailureHandler.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationFailureHandler.groovy @@ -14,9 +14,9 @@ */ package grails.plugin.springsecurity.web.authentication -import javax.servlet.ServletException -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.ServletException +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.beans.factory.InitializingBean import org.springframework.security.core.AuthenticationException diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationSuccessHandler.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationSuccessHandler.groovy index 90ac41ec6..06beb6ff3 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationSuccessHandler.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationSuccessHandler.groovy @@ -14,9 +14,9 @@ */ package grails.plugin.springsecurity.web.authentication -import javax.servlet.ServletException -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.ServletException +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.security.core.Authentication import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/FilterProcessUrlRequestMatcher.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/FilterProcessUrlRequestMatcher.groovy index ca6fe7b91..15072c8c2 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/FilterProcessUrlRequestMatcher.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/FilterProcessUrlRequestMatcher.groovy @@ -14,7 +14,7 @@ */ package grails.plugin.springsecurity.web.authentication -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequest import org.springframework.security.web.util.UrlUtils import org.springframework.security.web.util.matcher.RequestMatcher diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/GrailsUsernamePasswordAuthenticationFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/GrailsUsernamePasswordAuthenticationFilter.groovy index ae135ecac..071af5284 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/GrailsUsernamePasswordAuthenticationFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/GrailsUsernamePasswordAuthenticationFilter.groovy @@ -20,9 +20,9 @@ import org.springframework.security.core.Authentication import org.springframework.security.core.AuthenticationException import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse -import javax.servlet.http.HttpSession +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpSession /** * Extends the default {@link UsernamePasswordAuthenticationFilter} to store the diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/NullLogoutHandlerRememberMeServices.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/NullLogoutHandlerRememberMeServices.groovy index 0a41a67e6..02ed25e66 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/NullLogoutHandlerRememberMeServices.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/NullLogoutHandlerRememberMeServices.groovy @@ -14,8 +14,8 @@ */ package grails.plugin.springsecurity.web.authentication -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.security.core.Authentication import org.springframework.security.web.authentication.NullRememberMeServices diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilter.groovy index 7accbd451..7a6f5cbb2 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilter.groovy @@ -16,12 +16,12 @@ package grails.plugin.springsecurity.web.authentication.logout import groovy.util.logging.Slf4j -import javax.servlet.FilterChain -import javax.servlet.ServletException -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletException +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.security.core.Authentication import org.springframework.security.core.context.SecurityContextHolder diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationFailureHandler.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationFailureHandler.groovy index 9d1955b3f..2f4ae2f15 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationFailureHandler.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationFailureHandler.groovy @@ -18,9 +18,9 @@ import groovy.transform.CompileStatic import org.springframework.security.core.AuthenticationException import org.springframework.security.web.authentication.AuthenticationFailureHandler -import javax.servlet.ServletException -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.ServletException +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse /** * @author Burt Beckwith diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationSuccessHandler.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationSuccessHandler.groovy index e738bada7..59367f9fd 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationSuccessHandler.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/authentication/preauth/x509/NullAuthenticationSuccessHandler.groovy @@ -18,9 +18,9 @@ import groovy.transform.CompileStatic import org.springframework.security.core.Authentication import org.springframework.security.web.authentication.AuthenticationSuccessHandler -import javax.servlet.ServletException -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.ServletException +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse /** * @author Burt Beckwith diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/DebugFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/DebugFilter.groovy index 587254938..7dc65a2a0 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/DebugFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/DebugFilter.groovy @@ -22,11 +22,11 @@ import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.util.UrlUtils import org.springframework.web.filter.GenericFilterBean -import javax.servlet.* -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletRequestWrapper -import javax.servlet.http.HttpServletResponse -import javax.servlet.http.HttpSession +import jakarta.servlet.* +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequestWrapper +import jakarta.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpSession /** * Based on the package-scope org.springframework.security.config.debug.DebugFilter. diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsAnonymousAuthenticationFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsAnonymousAuthenticationFilter.groovy index cb58c892f..01590d1e2 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsAnonymousAuthenticationFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsAnonymousAuthenticationFilter.groovy @@ -16,11 +16,11 @@ package grails.plugin.springsecurity.web.filter import groovy.util.logging.Slf4j -import javax.servlet.FilterChain -import javax.servlet.ServletException -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletException +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import jakarta.servlet.http.HttpServletRequest import org.springframework.security.authentication.AuthenticationDetailsSource import org.springframework.security.core.Authentication diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsRememberMeAuthenticationFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsRememberMeAuthenticationFilter.groovy index 84f4ff66b..789933522 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsRememberMeAuthenticationFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/GrailsRememberMeAuthenticationFilter.groovy @@ -14,8 +14,8 @@ */ package grails.plugin.springsecurity.web.filter -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.core.Authentication diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/HttpMethodOverrideDetector.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/HttpMethodOverrideDetector.groovy index 7dc6cc1e5..8938d76f9 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/HttpMethodOverrideDetector.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/HttpMethodOverrideDetector.groovy @@ -3,7 +3,7 @@ package grails.plugin.springsecurity.web.filter import groovy.transform.CompileStatic import org.springframework.util.Assert -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequest @CompileStatic class HttpMethodOverrideDetector { diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilter.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilter.groovy index e63d1680e..63aa8f81d 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilter.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilter.groovy @@ -24,12 +24,12 @@ import org.springframework.security.web.util.matcher.IpAddressMatcher import org.springframework.util.AntPathMatcher import org.springframework.web.filter.GenericFilterBean -import javax.servlet.FilterChain -import javax.servlet.ServletException -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletException +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse /** * Blocks access to protected resources based on IP address. Sends 404 rather than diff --git a/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template b/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template index 48ef0234c..11c32c142 100644 --- a/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template +++ b/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template @@ -2,7 +2,7 @@ package ${packageName} import grails.gorm.DetachedCriteria import groovy.transform.ToString -import org.codehaus.groovy.util.HashCodeHelper +import org.apache.groovy.util.HashCodeHelper import grails.compiler.GrailsCompileStatic @GrailsCompileStatic diff --git a/plugin/src/main/templates/PersonAuthority.groovy.template b/plugin/src/main/templates/PersonAuthority.groovy.template index 940b9fba7..a96855cc3 100644 --- a/plugin/src/main/templates/PersonAuthority.groovy.template +++ b/plugin/src/main/templates/PersonAuthority.groovy.template @@ -3,7 +3,7 @@ package ${packageName} import grails.gorm.DetachedCriteria import grails.compiler.GrailsCompileStatic import groovy.transform.ToString -import org.codehaus.groovy.util.HashCodeHelper +import org.apache.groovy.util.HashCodeHelper @GrailsCompileStatic @ToString(cache=true, includeNames=true, includePackage=false) diff --git a/plugin/src/main/templates/PersonAuthorityGroup.groovy.template b/plugin/src/main/templates/PersonAuthorityGroup.groovy.template index f79fba658..63ca66da0 100644 --- a/plugin/src/main/templates/PersonAuthorityGroup.groovy.template +++ b/plugin/src/main/templates/PersonAuthorityGroup.groovy.template @@ -2,7 +2,7 @@ package ${packageName} import grails.gorm.DetachedCriteria import groovy.transform.ToString -import org.codehaus.groovy.util.HashCodeHelper +import org.apache.groovy.util.HashCodeHelper import grails.compiler.GrailsCompileStatic @GrailsCompileStatic diff --git a/plugin/src/main/templates/PersonPasswordEncoderListenerWithSalt.groovy.template b/plugin/src/main/templates/PersonPasswordEncoderListenerWithSalt.groovy.template index c8da3caf1..2a0abc342 100644 --- a/plugin/src/main/templates/PersonPasswordEncoderListenerWithSalt.groovy.template +++ b/plugin/src/main/templates/PersonPasswordEncoderListenerWithSalt.groovy.template @@ -7,7 +7,7 @@ import org.grails.datastore.mapping.engine.event.PreUpdateEvent import grails.events.annotation.gorm.Listener import org.springframework.beans.factory.annotation.Autowired import groovy.transform.CompileStatic -import javax.annotation.PostConstruct +import jakarta.annotation.PostConstruct @CompileStatic class ${userClassName}PasswordEncoderListener { diff --git a/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTestUtils.groovy b/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTestUtils.groovy index 3424f209e..2fda94dbc 100644 --- a/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTestUtils.groovy +++ b/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTestUtils.groovy @@ -46,7 +46,7 @@ class SecurityTestUtils { * @return the authentication */ static Authentication authenticate(principal, credentials, List authorities) { - Authentication authentication = new TestingAuthenticationToken(principal, credentials, authorities) + Authentication authentication = authorities != null ? new TestingAuthenticationToken(principal, credentials, authorities) : new TestingAuthenticationToken(principal, credentials) authentication.authenticated = true SCH.context.authentication = authentication authentication diff --git a/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy b/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy index e4f9f8ca7..2bafcc75e 100644 --- a/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy +++ b/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy @@ -25,9 +25,9 @@ import org.springframework.security.web.savedrequest.DefaultSavedRequest import org.springframework.web.filter.GenericFilterBean import spock.lang.Unroll -import javax.servlet.FilterChain -import javax.servlet.ServletRequest -import javax.servlet.ServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse /** * @author Burt Beckwith diff --git a/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/SecurityRequestHolderFilterSpec.groovy b/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/SecurityRequestHolderFilterSpec.groovy index fc1ce25a4..1f698abe9 100644 --- a/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/SecurityRequestHolderFilterSpec.groovy +++ b/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/SecurityRequestHolderFilterSpec.groovy @@ -18,7 +18,7 @@ import grails.plugin.springsecurity.AbstractUnitSpec import grails.plugin.springsecurity.web.SecurityRequestHolder import grails.plugin.springsecurity.web.SecurityRequestHolderFilter -import javax.servlet.FilterChain +import jakarta.servlet.FilterChain /** * @author Burt Beckwith diff --git a/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilterSpec.groovy b/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilterSpec.groovy index 3c43e233c..d35a2e20d 100644 --- a/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilterSpec.groovy +++ b/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/logout/MutableLogoutFilterSpec.groovy @@ -21,7 +21,7 @@ import org.springframework.mock.web.MockHttpServletResponse import org.springframework.security.web.authentication.logout.LogoutHandler import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler -import javax.servlet.FilterChain +import jakarta.servlet.FilterChain /** * Unit tests for MutableLogoutFilter. diff --git a/plugin/src/test/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilterSpec.groovy b/plugin/src/test/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilterSpec.groovy index a4445e629..6b302d200 100644 --- a/plugin/src/test/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilterSpec.groovy +++ b/plugin/src/test/groovy/grails/plugin/springsecurity/web/filter/IpAddressFilterSpec.groovy @@ -16,7 +16,7 @@ package grails.plugin.springsecurity.web.filter import grails.plugin.springsecurity.AbstractUnitSpec -import javax.servlet.FilterChain +import jakarta.servlet.FilterChain /** * Unit tests for IpAddressFilter. From fb92b0d0ef122a77a084462f04d17e46b6ad1e23 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Mon, 23 Sep 2024 22:00:29 -0500 Subject: [PATCH 02/30] Restore shell scripts --- .../scripts/s2-create-persistent-token.groovy | 42 +++ .../s2-create-role-hierarchy-entry.groovy | 46 +++ plugin/src/main/scripts/s2-quickstart.groovy | 284 ++++++++++++++++++ 3 files changed, 372 insertions(+) create mode 100644 plugin/src/main/scripts/s2-create-persistent-token.groovy create mode 100644 plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy create mode 100644 plugin/src/main/scripts/s2-quickstart.groovy diff --git a/plugin/src/main/scripts/s2-create-persistent-token.groovy b/plugin/src/main/scripts/s2-create-persistent-token.groovy new file mode 100644 index 000000000..3ff7fdc2b --- /dev/null +++ b/plugin/src/main/scripts/s2-create-persistent-token.groovy @@ -0,0 +1,42 @@ +/* Copyright 2006-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import grails.codegen.model.Model + +description 'Creates a persistent token domain class for the Spring Security Core plugin', { + usage ''' +grails s2-create-persistent-token [DOMAIN CLASS NAME] + +Example: grails s2-create-persistent-token com.yourapp.PersistentLogin +''' + + argument name: 'Domain class name', description: 'The domain class full name with package' +} + +String fullClassName = args[0] +Model model = model(fullClassName) + +addStatus "\nCreating persistent token class $fullClassName" + +render template: template('PersistentLogin.groovy.template'), + destination: file("grails-app/domain/$model.packagePath/${model.simpleName}.groovy"), + model: model, overwrite: false + +file('grails-app/conf/application.groovy').withWriterAppend { BufferedWriter writer -> + writer.newLine() + writer.writeLine 'grails.plugin.springsecurity.rememberMe.persistent = true' + writer.writeLine "grails.plugin.springsecurity.rememberMe.persistentToken.domainClassName = '$fullClassName'" + writer.newLine() +} diff --git a/plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy b/plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy new file mode 100644 index 000000000..669f17b3f --- /dev/null +++ b/plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy @@ -0,0 +1,46 @@ +/* Copyright 2015-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import grails.codegen.model.Model + +/** + * @author fpape + * @author Burt Beckwith + */ + +description 'Creates a domain class for a persistent role hierarchy for the Spring Security Core plugin', { + usage ''' +grails s2-create-role-hierarchy-entry [DOMAIN CLASS NAME] + +Example: grails s2-create-role-hierarchy-entry com.yourapp.RoleHierarchyEntry +''' + + argument name: 'Domain class name', description: 'The domain class full name with package' +} + +String fullClassName = args[0] +Model model = model(fullClassName) + +addStatus "\nCreating role hierarchy entry class $fullClassName" + +render template: template('RoleHierarchyEntry.groovy.template'), + destination: file("grails-app/domain/$model.packagePath/${model.simpleName}.groovy"), + model: model, overwrite: false + +file('grails-app/conf/application.groovy').withWriterAppend { BufferedWriter writer -> + writer.newLine() + writer.writeLine "grails.plugin.springsecurity.roleHierarchyEntryClassName = '$fullClassName'" + writer.newLine() +} diff --git a/plugin/src/main/scripts/s2-quickstart.groovy b/plugin/src/main/scripts/s2-quickstart.groovy new file mode 100644 index 000000000..3d02fadc0 --- /dev/null +++ b/plugin/src/main/scripts/s2-quickstart.groovy @@ -0,0 +1,284 @@ +/* Copyright 2006-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import grails.codegen.model.Model +import groovy.transform.Field + +@Field String usageMessage = ''' + grails s2-quickstart [requestmap-class-name] [--groupClassName=group-class-name] +or grails s2-quickstart --uiOnly + +Example: grails s2-quickstart com.yourapp User Role +Example: grails s2-quickstart com.yourapp User Role --groupClassName=RoleGroup +Example: grails s2-quickstart com.yourapp Person Authority Requestmap +Example: grails s2-quickstart --uiOnly +''' + +@Field Map templateAttributes +@Field boolean uiOnly +@Field boolean salt + +description 'Creates domain classes and updates config settings for the Spring Security plugin', { + + usage usageMessage + + argument name: 'Domain class package', description: 'The package to use for the domain classes', required: false + argument name: 'User class name', description: 'The name of the User/Person class', required: false + argument name: 'Role class name', description: 'The name of the Role class', required: false + argument name: 'Requestmap class name', description: 'The name of the Requestmap class', required: false + + flag name: 'groupClassName', description: 'If specified, role/group classes will also be generated using the flag value as the role-group name' + flag name: 'uiOnly', description: 'If specified, no domain classes are created but the plugin settings are initialized (useful with LDAP, Mock, Shibboleth, etc.)' +} + +Model userModel +Model roleModel +Model requestmapModel +Model groupModel +uiOnly = flag('uiOnly') +salt = flag('salt') +if (uiOnly) { + addStatus '\nConfiguring Spring Security; not generating domain classes' +} +else { + + if (args.size() < 3) { + error 'Usage:' + usageMessage + return false + } + + String packageName = args[0] + String groupClassName = flag('groupClassName') + String groupClassNameMessage = '' + if (groupClassName) { + groupModel = model(packageName + '.' + groupClassName) + groupClassNameMessage = ", and role/group classes for '" + groupModel.simpleName + "'" + } + + userModel = model(packageName + '.' + args[1]) + roleModel = model(packageName + '.' + args[2]) + + String message = "Creating User class '" + userModel.simpleName + "'" + if (4 == args.size()) { + requestmapModel = model(packageName + '.' + args[3]) + message += ", Role class '" + roleModel.simpleName + "', and Requestmap class '" + requestmapModel.simpleName + "'" + groupClassNameMessage + } + else { + message += " and Role class '" + roleModel.simpleName + "'" + groupClassNameMessage + } + message += " in package '" + packageName + "'" + addStatus message + + templateAttributes = [ + packageName: userModel.packageName, + userClassName: userModel.simpleName, + userClassProperty: userModel.modelName, + roleClassName: roleModel.simpleName, + roleClassProperty: roleModel.modelName, + requestmapClassName: requestmapModel?.simpleName, + groupClassName: groupModel?.simpleName, + groupClassProperty: groupModel?.modelName] + + createDomains userModel, roleModel, requestmapModel, groupModel +} + +updateConfig userModel?.simpleName, roleModel?.simpleName, requestmapModel?.simpleName, userModel?.packageName, groupModel != null + +if (uiOnly) { + addStatus ''' +************************************************************ +* Your grails-app/conf/application.groovy has been updated * +* with security settings; please verify that the * +* values are correct. * +************************************************************ +''' +} +else { + addStatus ''' +************************************************************ +* Created security-related domain classes. Your * +* grails-app/conf/application.groovy has been updated with * +* the class names of the configured domain classes; * +* please verify that the values are correct. * +************************************************************ +''' +} + +private Map extractVersion(String versionString) { + def arr = versionString.split('\\.') + def v = [mayor: 0, minor: 0, bug: 0] + try { + if ( arr.size() >= 1) { + v.mayor = arr[0].toInteger() + } + if ( arr.size() >= 2) { + v.minor = arr[1].toInteger() + } + if ( arr.size() >= 3) { + v.bug = arr[2].toInteger() + } + } catch ( Exception e ) { + v = [mayor: 0, minor: 0, bug: 0] + } + v +} + +private boolean versionAfterOrEqualsToThreshold(String threshold, String value) { + if ( value == null ) { + return false + } + if ( value.startsWith(threshold) ) { + return true + } + + def va = extractVersion(value) + def vb = extractVersion(threshold) + def l = [va, vb] + l.sort { Map a, Map b -> + def compare = a.mayor <=> b.mayor + if ( compare != 0 ) { + return compare + } + compare = a.minor <=> b.minor + if ( compare != 0 ) { + return compare + } + a.bug <=> b.bug + } + def sortedValue = l[0].collect { k, v -> v }.join('.') + threshold.startsWith(sortedValue) +} + +private void createDomains(Model userModel, Model roleModel, Model requestmapModel, Model groupModel) { + + def props = new Properties() + file("gradle.properties")?.withInputStream { props.load(it) } + + final threshold = '6.0.10' + + boolean gormVersionAfterThreshold = versionAfterOrEqualsToThreshold(threshold, props.gormVersion ?: props.getProperty("gorm.version")) + + if ( gormVersionAfterThreshold ) { + generateFile 'PersonWithoutInjection', userModel.packagePath, userModel.simpleName + if ( salt ) { + generateFile 'PersonPasswordEncoderListenerWithSalt', userModel.packagePath, userModel.simpleName, "${userModel.simpleName}PasswordEncoderListener", 'src/main/groovy' + } else { + generateFile 'PersonPasswordEncoderListener', userModel.packagePath, userModel.simpleName, "${userModel.simpleName}PasswordEncoderListener", 'src/main/groovy' + } + def beansList = [[import: "import ${userModel.packageName}.${userModel.simpleName}PasswordEncoderListener", definition: "${userModel.propertyName}PasswordEncoderListener(${userModel.simpleName}PasswordEncoderListener)"]] + addBeans(beansList, 'grails-app/conf/spring/resources.groovy') + + } else { + if ( salt ) { + generateFile 'PersonWithSalt', userModel.packagePath, userModel.simpleName + } else { + generateFile 'Person', userModel.packagePath, userModel.simpleName + } + } + + generateFile 'Authority', roleModel.packagePath, roleModel.simpleName + generateFile 'PersonAuthority', roleModel.packagePath, userModel.simpleName + roleModel.simpleName + + if (requestmapModel) { + generateFile 'Requestmap', requestmapModel.packagePath, requestmapModel.simpleName + } + + if (groupModel) { + generateFile 'AuthorityGroup', groupModel.packagePath, groupModel.simpleName + generateFile 'PersonAuthorityGroup', groupModel.packagePath, userModel.simpleName + groupModel.simpleName + generateFile 'AuthorityGroupAuthority', groupModel.packagePath, groupModel.simpleName + roleModel.simpleName + } +} + +private void updateConfig(String userClassName, String roleClassName, String requestmapClassName, String packageName, boolean useRoleGroups) { + + file('grails-app/conf/application.groovy').withWriterAppend { BufferedWriter writer -> + writer.newLine() + writer.newLine() + writer.writeLine '// Added by the Spring Security Core plugin:' + if (!uiOnly) { + writer.writeLine "grails.plugin.springsecurity.userLookup.userDomainClassName = '${packageName}.$userClassName'" + writer.writeLine "grails.plugin.springsecurity.userLookup.authorityJoinClassName = '${packageName}.$userClassName$roleClassName'" + writer.writeLine "grails.plugin.springsecurity.authority.className = '${packageName}.$roleClassName'" + } + if (useRoleGroups) { + writer.writeLine "grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities'" + writer.writeLine 'grails.plugin.springsecurity.useRoleGroups = true' + } + if (requestmapClassName) { + writer.writeLine "grails.plugin.springsecurity.requestMap.className = '${packageName}.$requestmapClassName'" + writer.writeLine "grails.plugin.springsecurity.securityConfigType = 'Requestmap'" + } + writer.writeLine 'grails.plugin.springsecurity.controllerAnnotations.staticRules = [' + writer.writeLine "\t[pattern: '/', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/error', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/index', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/index.gsp', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/shutdown', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/assets/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/js/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/css/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/images/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/favicon.ico', access: ['permitAll']]" + writer.writeLine ']' + writer.newLine() + + writer.writeLine 'grails.plugin.springsecurity.filterChain.chainMap = [' + writer.writeLine "\t[pattern: '/assets/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/js/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/css/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/images/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/favicon.ico', filters: 'none']," + writer.writeLine "\t[pattern: '/**', filters: 'JOINED_FILTERS']" + writer.writeLine ']' + writer.newLine() + } +} + +private void generateFile(String templateName, String packagePath, String className, String fileName = null, String folder = 'grails-app/domain') { + render template(templateName + '.groovy.template'), + file("${folder}/$packagePath/${fileName ?: className}.groovy"), + templateAttributes, false +} + +private void addBeans(List beans, String pathname) { + def f = new File(pathname) + def lines = [] + beans.each { Map bean -> + lines << bean.import + } + if ( f.exists() ) { + f.eachLine { line, nb -> + lines << line + if ( line.contains('beans = {') ) { + beans.each { Map bean -> + lines << ' ' + bean.definition + } + } + } + } else { + lines << 'beans = {' + beans.each { Map bean -> + lines << ' ' + bean.definition + } + lines << '}' + } + + f.withWriter('UTF-8') { writer -> + lines.each { String line -> + writer.write "${line}${System.lineSeparator()}" + } + } +} From 27ae7c11137ce5316c5ef9d7396de603b9dd2df6 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 26 Sep 2024 13:35:24 -0700 Subject: [PATCH 03/30] HashCodeHelper in groovy 4 is still in org.codehaus namespace --- .../src/main/templates/AuthorityGroupAuthority.groovy.template | 2 +- plugin/src/main/templates/PersonAuthority.groovy.template | 2 +- plugin/src/main/templates/PersonAuthorityGroup.groovy.template | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template b/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template index 11c32c142..48ef0234c 100644 --- a/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template +++ b/plugin/src/main/templates/AuthorityGroupAuthority.groovy.template @@ -2,7 +2,7 @@ package ${packageName} import grails.gorm.DetachedCriteria import groovy.transform.ToString -import org.apache.groovy.util.HashCodeHelper +import org.codehaus.groovy.util.HashCodeHelper import grails.compiler.GrailsCompileStatic @GrailsCompileStatic diff --git a/plugin/src/main/templates/PersonAuthority.groovy.template b/plugin/src/main/templates/PersonAuthority.groovy.template index a96855cc3..940b9fba7 100644 --- a/plugin/src/main/templates/PersonAuthority.groovy.template +++ b/plugin/src/main/templates/PersonAuthority.groovy.template @@ -3,7 +3,7 @@ package ${packageName} import grails.gorm.DetachedCriteria import grails.compiler.GrailsCompileStatic import groovy.transform.ToString -import org.apache.groovy.util.HashCodeHelper +import org.codehaus.groovy.util.HashCodeHelper @GrailsCompileStatic @ToString(cache=true, includeNames=true, includePackage=false) diff --git a/plugin/src/main/templates/PersonAuthorityGroup.groovy.template b/plugin/src/main/templates/PersonAuthorityGroup.groovy.template index 63ca66da0..f79fba658 100644 --- a/plugin/src/main/templates/PersonAuthorityGroup.groovy.template +++ b/plugin/src/main/templates/PersonAuthorityGroup.groovy.template @@ -2,7 +2,7 @@ package ${packageName} import grails.gorm.DetachedCriteria import groovy.transform.ToString -import org.apache.groovy.util.HashCodeHelper +import org.codehaus.groovy.util.HashCodeHelper import grails.compiler.GrailsCompileStatic @GrailsCompileStatic From 75f5e19bdea9077901b17362bde909e48746d800 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 26 Sep 2024 14:16:55 -0700 Subject: [PATCH 04/30] --refresh-dependencies --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b8be082b2..35e86a398 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -29,6 +29,7 @@ jobs: with: arguments: | check + --refresh-dependencies -Dgeb.env=chromeHeadless functional-test: From cfcdbd8cc5df5a6a9a672b89047249b43b49a03f Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 26 Sep 2024 14:22:28 -0700 Subject: [PATCH 05/30] Temporarily ignore test --- .../grails/plugin/springsecurity/SecurityTagLibUnitSpec.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTagLibUnitSpec.groovy b/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTagLibUnitSpec.groovy index 091c6a708..d99ac6661 100644 --- a/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTagLibUnitSpec.groovy +++ b/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityTagLibUnitSpec.groovy @@ -6,6 +6,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.Authentication import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator +import spock.lang.Ignore import spock.lang.Specification class SecurityTagLibUnitSpec extends Specification implements TagLibUnitTest { @@ -92,6 +93,7 @@ class SecurityTagLibUnitSpec extends Specification implements TagLibUnitTest Date: Thu, 26 Sep 2024 14:32:50 -0700 Subject: [PATCH 06/30] --refresh-dependencies --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 35e86a398..4d5df5684 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -68,6 +68,7 @@ jobs: with: arguments: | spring-security-core:build + --refresh-dependencies -Dgeb.env=chromeHeadless - name: Publish Snapshot artifacts to Artifactory (repo.grails.org) From 3af0337b90c7791c5ed04dd6cbc4b74b621b3209 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 26 Sep 2024 18:15:30 -0700 Subject: [PATCH 07/30] Restore shell scripts --- .../scripts/s2-create-persistent-token.groovy | 42 +++ .../s2-create-role-hierarchy-entry.groovy | 46 +++ plugin/src/main/scripts/s2-quickstart.groovy | 284 ++++++++++++++++++ 3 files changed, 372 insertions(+) create mode 100644 plugin/src/main/scripts/s2-create-persistent-token.groovy create mode 100644 plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy create mode 100644 plugin/src/main/scripts/s2-quickstart.groovy diff --git a/plugin/src/main/scripts/s2-create-persistent-token.groovy b/plugin/src/main/scripts/s2-create-persistent-token.groovy new file mode 100644 index 000000000..3ff7fdc2b --- /dev/null +++ b/plugin/src/main/scripts/s2-create-persistent-token.groovy @@ -0,0 +1,42 @@ +/* Copyright 2006-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import grails.codegen.model.Model + +description 'Creates a persistent token domain class for the Spring Security Core plugin', { + usage ''' +grails s2-create-persistent-token [DOMAIN CLASS NAME] + +Example: grails s2-create-persistent-token com.yourapp.PersistentLogin +''' + + argument name: 'Domain class name', description: 'The domain class full name with package' +} + +String fullClassName = args[0] +Model model = model(fullClassName) + +addStatus "\nCreating persistent token class $fullClassName" + +render template: template('PersistentLogin.groovy.template'), + destination: file("grails-app/domain/$model.packagePath/${model.simpleName}.groovy"), + model: model, overwrite: false + +file('grails-app/conf/application.groovy').withWriterAppend { BufferedWriter writer -> + writer.newLine() + writer.writeLine 'grails.plugin.springsecurity.rememberMe.persistent = true' + writer.writeLine "grails.plugin.springsecurity.rememberMe.persistentToken.domainClassName = '$fullClassName'" + writer.newLine() +} diff --git a/plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy b/plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy new file mode 100644 index 000000000..669f17b3f --- /dev/null +++ b/plugin/src/main/scripts/s2-create-role-hierarchy-entry.groovy @@ -0,0 +1,46 @@ +/* Copyright 2015-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import grails.codegen.model.Model + +/** + * @author fpape + * @author Burt Beckwith + */ + +description 'Creates a domain class for a persistent role hierarchy for the Spring Security Core plugin', { + usage ''' +grails s2-create-role-hierarchy-entry [DOMAIN CLASS NAME] + +Example: grails s2-create-role-hierarchy-entry com.yourapp.RoleHierarchyEntry +''' + + argument name: 'Domain class name', description: 'The domain class full name with package' +} + +String fullClassName = args[0] +Model model = model(fullClassName) + +addStatus "\nCreating role hierarchy entry class $fullClassName" + +render template: template('RoleHierarchyEntry.groovy.template'), + destination: file("grails-app/domain/$model.packagePath/${model.simpleName}.groovy"), + model: model, overwrite: false + +file('grails-app/conf/application.groovy').withWriterAppend { BufferedWriter writer -> + writer.newLine() + writer.writeLine "grails.plugin.springsecurity.roleHierarchyEntryClassName = '$fullClassName'" + writer.newLine() +} diff --git a/plugin/src/main/scripts/s2-quickstart.groovy b/plugin/src/main/scripts/s2-quickstart.groovy new file mode 100644 index 000000000..3d02fadc0 --- /dev/null +++ b/plugin/src/main/scripts/s2-quickstart.groovy @@ -0,0 +1,284 @@ +/* Copyright 2006-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import grails.codegen.model.Model +import groovy.transform.Field + +@Field String usageMessage = ''' + grails s2-quickstart [requestmap-class-name] [--groupClassName=group-class-name] +or grails s2-quickstart --uiOnly + +Example: grails s2-quickstart com.yourapp User Role +Example: grails s2-quickstart com.yourapp User Role --groupClassName=RoleGroup +Example: grails s2-quickstart com.yourapp Person Authority Requestmap +Example: grails s2-quickstart --uiOnly +''' + +@Field Map templateAttributes +@Field boolean uiOnly +@Field boolean salt + +description 'Creates domain classes and updates config settings for the Spring Security plugin', { + + usage usageMessage + + argument name: 'Domain class package', description: 'The package to use for the domain classes', required: false + argument name: 'User class name', description: 'The name of the User/Person class', required: false + argument name: 'Role class name', description: 'The name of the Role class', required: false + argument name: 'Requestmap class name', description: 'The name of the Requestmap class', required: false + + flag name: 'groupClassName', description: 'If specified, role/group classes will also be generated using the flag value as the role-group name' + flag name: 'uiOnly', description: 'If specified, no domain classes are created but the plugin settings are initialized (useful with LDAP, Mock, Shibboleth, etc.)' +} + +Model userModel +Model roleModel +Model requestmapModel +Model groupModel +uiOnly = flag('uiOnly') +salt = flag('salt') +if (uiOnly) { + addStatus '\nConfiguring Spring Security; not generating domain classes' +} +else { + + if (args.size() < 3) { + error 'Usage:' + usageMessage + return false + } + + String packageName = args[0] + String groupClassName = flag('groupClassName') + String groupClassNameMessage = '' + if (groupClassName) { + groupModel = model(packageName + '.' + groupClassName) + groupClassNameMessage = ", and role/group classes for '" + groupModel.simpleName + "'" + } + + userModel = model(packageName + '.' + args[1]) + roleModel = model(packageName + '.' + args[2]) + + String message = "Creating User class '" + userModel.simpleName + "'" + if (4 == args.size()) { + requestmapModel = model(packageName + '.' + args[3]) + message += ", Role class '" + roleModel.simpleName + "', and Requestmap class '" + requestmapModel.simpleName + "'" + groupClassNameMessage + } + else { + message += " and Role class '" + roleModel.simpleName + "'" + groupClassNameMessage + } + message += " in package '" + packageName + "'" + addStatus message + + templateAttributes = [ + packageName: userModel.packageName, + userClassName: userModel.simpleName, + userClassProperty: userModel.modelName, + roleClassName: roleModel.simpleName, + roleClassProperty: roleModel.modelName, + requestmapClassName: requestmapModel?.simpleName, + groupClassName: groupModel?.simpleName, + groupClassProperty: groupModel?.modelName] + + createDomains userModel, roleModel, requestmapModel, groupModel +} + +updateConfig userModel?.simpleName, roleModel?.simpleName, requestmapModel?.simpleName, userModel?.packageName, groupModel != null + +if (uiOnly) { + addStatus ''' +************************************************************ +* Your grails-app/conf/application.groovy has been updated * +* with security settings; please verify that the * +* values are correct. * +************************************************************ +''' +} +else { + addStatus ''' +************************************************************ +* Created security-related domain classes. Your * +* grails-app/conf/application.groovy has been updated with * +* the class names of the configured domain classes; * +* please verify that the values are correct. * +************************************************************ +''' +} + +private Map extractVersion(String versionString) { + def arr = versionString.split('\\.') + def v = [mayor: 0, minor: 0, bug: 0] + try { + if ( arr.size() >= 1) { + v.mayor = arr[0].toInteger() + } + if ( arr.size() >= 2) { + v.minor = arr[1].toInteger() + } + if ( arr.size() >= 3) { + v.bug = arr[2].toInteger() + } + } catch ( Exception e ) { + v = [mayor: 0, minor: 0, bug: 0] + } + v +} + +private boolean versionAfterOrEqualsToThreshold(String threshold, String value) { + if ( value == null ) { + return false + } + if ( value.startsWith(threshold) ) { + return true + } + + def va = extractVersion(value) + def vb = extractVersion(threshold) + def l = [va, vb] + l.sort { Map a, Map b -> + def compare = a.mayor <=> b.mayor + if ( compare != 0 ) { + return compare + } + compare = a.minor <=> b.minor + if ( compare != 0 ) { + return compare + } + a.bug <=> b.bug + } + def sortedValue = l[0].collect { k, v -> v }.join('.') + threshold.startsWith(sortedValue) +} + +private void createDomains(Model userModel, Model roleModel, Model requestmapModel, Model groupModel) { + + def props = new Properties() + file("gradle.properties")?.withInputStream { props.load(it) } + + final threshold = '6.0.10' + + boolean gormVersionAfterThreshold = versionAfterOrEqualsToThreshold(threshold, props.gormVersion ?: props.getProperty("gorm.version")) + + if ( gormVersionAfterThreshold ) { + generateFile 'PersonWithoutInjection', userModel.packagePath, userModel.simpleName + if ( salt ) { + generateFile 'PersonPasswordEncoderListenerWithSalt', userModel.packagePath, userModel.simpleName, "${userModel.simpleName}PasswordEncoderListener", 'src/main/groovy' + } else { + generateFile 'PersonPasswordEncoderListener', userModel.packagePath, userModel.simpleName, "${userModel.simpleName}PasswordEncoderListener", 'src/main/groovy' + } + def beansList = [[import: "import ${userModel.packageName}.${userModel.simpleName}PasswordEncoderListener", definition: "${userModel.propertyName}PasswordEncoderListener(${userModel.simpleName}PasswordEncoderListener)"]] + addBeans(beansList, 'grails-app/conf/spring/resources.groovy') + + } else { + if ( salt ) { + generateFile 'PersonWithSalt', userModel.packagePath, userModel.simpleName + } else { + generateFile 'Person', userModel.packagePath, userModel.simpleName + } + } + + generateFile 'Authority', roleModel.packagePath, roleModel.simpleName + generateFile 'PersonAuthority', roleModel.packagePath, userModel.simpleName + roleModel.simpleName + + if (requestmapModel) { + generateFile 'Requestmap', requestmapModel.packagePath, requestmapModel.simpleName + } + + if (groupModel) { + generateFile 'AuthorityGroup', groupModel.packagePath, groupModel.simpleName + generateFile 'PersonAuthorityGroup', groupModel.packagePath, userModel.simpleName + groupModel.simpleName + generateFile 'AuthorityGroupAuthority', groupModel.packagePath, groupModel.simpleName + roleModel.simpleName + } +} + +private void updateConfig(String userClassName, String roleClassName, String requestmapClassName, String packageName, boolean useRoleGroups) { + + file('grails-app/conf/application.groovy').withWriterAppend { BufferedWriter writer -> + writer.newLine() + writer.newLine() + writer.writeLine '// Added by the Spring Security Core plugin:' + if (!uiOnly) { + writer.writeLine "grails.plugin.springsecurity.userLookup.userDomainClassName = '${packageName}.$userClassName'" + writer.writeLine "grails.plugin.springsecurity.userLookup.authorityJoinClassName = '${packageName}.$userClassName$roleClassName'" + writer.writeLine "grails.plugin.springsecurity.authority.className = '${packageName}.$roleClassName'" + } + if (useRoleGroups) { + writer.writeLine "grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities'" + writer.writeLine 'grails.plugin.springsecurity.useRoleGroups = true' + } + if (requestmapClassName) { + writer.writeLine "grails.plugin.springsecurity.requestMap.className = '${packageName}.$requestmapClassName'" + writer.writeLine "grails.plugin.springsecurity.securityConfigType = 'Requestmap'" + } + writer.writeLine 'grails.plugin.springsecurity.controllerAnnotations.staticRules = [' + writer.writeLine "\t[pattern: '/', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/error', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/index', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/index.gsp', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/shutdown', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/assets/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/js/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/css/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/images/**', access: ['permitAll']]," + writer.writeLine "\t[pattern: '/**/favicon.ico', access: ['permitAll']]" + writer.writeLine ']' + writer.newLine() + + writer.writeLine 'grails.plugin.springsecurity.filterChain.chainMap = [' + writer.writeLine "\t[pattern: '/assets/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/js/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/css/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/images/**', filters: 'none']," + writer.writeLine "\t[pattern: '/**/favicon.ico', filters: 'none']," + writer.writeLine "\t[pattern: '/**', filters: 'JOINED_FILTERS']" + writer.writeLine ']' + writer.newLine() + } +} + +private void generateFile(String templateName, String packagePath, String className, String fileName = null, String folder = 'grails-app/domain') { + render template(templateName + '.groovy.template'), + file("${folder}/$packagePath/${fileName ?: className}.groovy"), + templateAttributes, false +} + +private void addBeans(List beans, String pathname) { + def f = new File(pathname) + def lines = [] + beans.each { Map bean -> + lines << bean.import + } + if ( f.exists() ) { + f.eachLine { line, nb -> + lines << line + if ( line.contains('beans = {') ) { + beans.each { Map bean -> + lines << ' ' + bean.definition + } + } + } + } else { + lines << 'beans = {' + beans.each { Map bean -> + lines << ' ' + bean.definition + } + lines << '}' + } + + f.withWriter('UTF-8') { writer -> + lines.each { String line -> + writer.write "${line}${System.lineSeparator()}" + } + } +} From fa10e4a482b76c45f7eee115db38e8abd4391164 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 26 Sep 2024 23:43:25 -0700 Subject: [PATCH 08/30] Fix locale for authfail to be consistant with g:message FormatTagLib.resolveLocale --- .../plugin/springsecurity/LoginController.groovy | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy b/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy index bb503ec78..b58b0b9d3 100644 --- a/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy +++ b/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy @@ -15,6 +15,7 @@ package grails.plugin.springsecurity import grails.converters.JSON +import org.grails.web.servlet.mvc.GrailsWebRequest import org.springframework.context.MessageSource import org.springframework.security.access.annotation.Secured import org.springframework.security.authentication.AccountExpiredException @@ -104,18 +105,19 @@ class LoginController { String msg = '' def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION] if (exception) { + def locale = GrailsWebRequest.lookup().getLocale() ?: Locale.getDefault() if (exception instanceof AccountExpiredException) { - msg = messageSource.getMessage('springSecurity.errors.login.expired', null, "Account Expired", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.expired', null, "Account Expired", locale) } else if (exception instanceof CredentialsExpiredException) { - msg = messageSource.getMessage('springSecurity.errors.login.passwordExpired', null, "Password Expired", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.passwordExpired', null, "Password Expired", locale) } else if (exception instanceof DisabledException) { - msg = messageSource.getMessage('springSecurity.errors.login.disabled', null, "Account Disabled", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.disabled', null, "Account Disabled", locale) } else if (exception instanceof LockedException) { - msg = messageSource.getMessage('springSecurity.errors.login.locked', null, "Account Locked", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.locked', null, "Account Locked", locale) } else if (exception instanceof SessionAuthenticationException) { - msg = messageSource.getMessage('springSecurity.errors.login.max.sessions.exceeded', null, "Sorry, you have exceeded your maximum number of open sessions.", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.max.sessions.exceeded', null, "Sorry, you have exceeded your maximum number of open sessions.", locale) } else { - msg = messageSource.getMessage('springSecurity.errors.login.fail', null, "Authentication Failure", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.fail', null, "Authentication Failure", locale) } } From 31c525f3f4320971e490053283a0767dc4af2ec5 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 27 Sep 2024 08:45:03 -0700 Subject: [PATCH 09/30] Fix locale for authfail to be consistant with g:message FormatTagLib.resolveLocale --- .../plugin/springsecurity/LoginController.groovy | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy b/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy index 1be4bdc64..535a728d1 100644 --- a/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy +++ b/plugin/grails-app/controllers/grails/plugin/springsecurity/LoginController.groovy @@ -15,6 +15,7 @@ package grails.plugin.springsecurity import grails.converters.JSON +import org.grails.web.servlet.mvc.GrailsWebRequest import org.springframework.context.MessageSource import org.springframework.security.access.annotation.Secured import org.springframework.security.authentication.AccountExpiredException @@ -104,18 +105,19 @@ class LoginController { String msg = '' def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION] if (exception) { + def locale = GrailsWebRequest.lookup().getLocale() ?: Locale.getDefault() if (exception instanceof AccountExpiredException) { - msg = messageSource.getMessage('springSecurity.errors.login.expired', null, "Account Expired", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.expired', null, "Account Expired", locale) } else if (exception instanceof CredentialsExpiredException) { - msg = messageSource.getMessage('springSecurity.errors.login.passwordExpired', null, "Password Expired", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.passwordExpired', null, "Password Expired", locale) } else if (exception instanceof DisabledException) { - msg = messageSource.getMessage('springSecurity.errors.login.disabled', null, "Account Disabled", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.disabled', null, "Account Disabled", locale) } else if (exception instanceof LockedException) { - msg = messageSource.getMessage('springSecurity.errors.login.locked', null, "Account Locked", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.locked', null, "Account Locked", locale) } else if (exception instanceof SessionAuthenticationException) { - msg = messageSource.getMessage('springSecurity.errors.login.max.sessions.exceeded', null, "Sorry, you have exceeded your maximum number of open sessions.", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.max.sessions.exceeded', null, "Sorry, you have exceeded your maximum number of open sessions.", locale) } else { - msg = messageSource.getMessage('springSecurity.errors.login.fail', null, "Authentication Failure", request.locale) + msg = messageSource.getMessage('springSecurity.errors.login.fail', null, "Authentication Failure", locale) } } From 523bd5666b3fa236f30eb1fcf526a97a529e74eb Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Tue, 8 Oct 2024 11:34:06 -0700 Subject: [PATCH 10/30] Allow setting of FilterRegistrationBean according to documentation. Fixes #1027 --- .../plugin/springsecurity/SpringSecurityUtils.groovy | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index 116b18408..2a79fc9db 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -22,6 +22,7 @@ import groovy.transform.CompileDynamic import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.apache.commons.lang.StringEscapeUtils +import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.context.ApplicationContext import org.springframework.security.access.hierarchicalroles.RoleHierarchy import org.springframework.security.authentication.UsernamePasswordAuthenticationToken @@ -420,11 +421,14 @@ final class SpringSecurityUtils { assert !oldFilter, "Cannot register filter '$beanName' at position $order; '$oldFilter' is already registered in that position" - Filter filter = getBean(beanName) - configuredOrderedFilters[order] = filter + def filter = getBean(beanName) + if (filter instanceof FilterRegistrationBean) { + filter = ((FilterRegistrationBean) filter).filter + } + configuredOrderedFilters[order] = (Filter) filter List filterChains = getBean('securityFilterChains', List) - mergeFilterChains configuredOrderedFilters, filter, beanName, order, filterChains + mergeFilterChains configuredOrderedFilters, (Filter) filter, beanName, order, filterChains log.trace 'Client registered bean "{}" as a filter at order {}', beanName, order log.trace 'Updated filter chain: {}', filterChains From e5594b48dfc22612b8ea764a8bd58b538d7bf6a6 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Tue, 8 Oct 2024 14:31:22 -0700 Subject: [PATCH 11/30] Register SiteMesh 3 filter on security chain if detected. --- .../springsecurity/SpringSecurityCoreGrailsPlugin.groovy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy index fe6698786..5c52ade25 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy @@ -670,6 +670,12 @@ to default to 'Annotation'; setting value to 'Annotation' } log.trace 'Using SecurityContextHolder strategy {}', SCH.strategyName + // if sitemesh 3 is installed, an additional sitemesh 3 filter will need to be registered + // as part of the security filter chain so that pages are decorated using the security context + if (applicationContext.getBean('sitemesh3Secured')) { + SpringSecurityUtils.clientRegisterFilter('sitemesh3Secured', SecurityFilterPosition.FORM_CONTENT_FILTER.previous()) + } + // build filters here to give dependent plugins a chance to register some SortedMap filterNames = ReflectionUtils.findFilterChainNames(conf) def securityFilterChains = applicationContext.securityFilterChains From 18115e14c6772120231a682a7ca4e3cf8d61a9ce Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Tue, 8 Oct 2024 22:55:05 -0700 Subject: [PATCH 12/30] set username to grails-build --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3c394a088..92839e04f 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -10,8 +10,8 @@ on: - '[3-9]+.[3-9]+.x' workflow_dispatch: env: - GIT_USER_NAME: puneetbehl - GIT_USER_EMAIL: behlp@unityfoundation.io + GIT_USER_NAME: grails-build + GIT_USER_EMAIL: grails-build@users.noreply.github.com jobs: core-tests: From 09598445731c9766d9c983c108c91e0a1ac1be82 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Tue, 8 Oct 2024 23:57:14 -0700 Subject: [PATCH 13/30] Move sitemesh registration location --- .../springsecurity/SpringSecurityCoreGrailsPlugin.groovy | 6 ------ .../plugin/springsecurity/SpringSecurityUtils.groovy | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy index 5c52ade25..fe6698786 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy @@ -670,12 +670,6 @@ to default to 'Annotation'; setting value to 'Annotation' } log.trace 'Using SecurityContextHolder strategy {}', SCH.strategyName - // if sitemesh 3 is installed, an additional sitemesh 3 filter will need to be registered - // as part of the security filter chain so that pages are decorated using the security context - if (applicationContext.getBean('sitemesh3Secured')) { - SpringSecurityUtils.clientRegisterFilter('sitemesh3Secured', SecurityFilterPosition.FORM_CONTENT_FILTER.previous()) - } - // build filters here to give dependent plugins a chance to register some SortedMap filterNames = ReflectionUtils.findFilterChainNames(conf) def securityFilterChains = applicationContext.securityFilterChains diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index 03f0110dd..a54a1c7cc 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -782,6 +782,13 @@ final class SpringSecurityUtils { allConfiguredFilters[name] = filter SpringSecurityUtils.configuredOrderedFilters[order] = filter } + // if sitemesh 3 is installed, an additional sitemesh 3 filter will need to be registered + // as part of the security filter chain so that pages are decorated using the security context + FilterRegistrationBean sitemesh3Filter = (FilterRegistrationBean) applicationContext.getBean('sitemesh3Secured') + if (sitemesh3Filter) { + allConfiguredFilters['sitemesh3Secured'] = sitemesh3Filter.filter + SpringSecurityUtils.configuredOrderedFilters[SecurityFilterPosition.FORM_CONTENT_FILTER.previous().order] = sitemesh3Filter.filter + } log.trace 'Ordered filters: {}', SpringSecurityUtils.configuredOrderedFilters if (chainMap) { From 6ac0d9986be1594fc6a47cfa9e1e46b37adaa01a Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 10 Oct 2024 23:54:22 -0700 Subject: [PATCH 14/30] Filter order is completely different in Spring Security 5 --- gradle/libs.versions.toml | 1 + plugin/build.gradle | 1 + ...ion.groovy => SecurityFilterPosition.java} | 106 ++++++++++++------ .../springsecurity/SpringSecurityUtils.groovy | 2 +- .../SpringSecurityUtilsSpec.groovy | 6 + .../config/http/SecurityFiltersMapper.groovy | 5 + 6 files changed, 86 insertions(+), 35 deletions(-) rename plugin/src/main/groovy/grails/plugin/springsecurity/{SecurityFilterPosition.groovy => SecurityFilterPosition.java} (57%) create mode 100644 plugin/src/test/groovy/org/springframework/security/config/http/SecurityFiltersMapper.groovy diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0dbdae2e1..97670006c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -77,6 +77,7 @@ spring-context-core = { module = 'org.springframework:spring-context', version.r spring-context-support = { module = 'org.springframework:spring-context-support', version.ref = 'spring' } spring-expression = { module = 'org.springframework:spring-expression', version.ref = 'spring' } spring-security-core = { module = 'org.springframework.security:spring-security-core', version.ref = 'spring-security' } +spring-security-config = { module = 'org.springframework.security:spring-security-config', version.ref = 'spring-security' } spring-security-crypto = { module = 'org.springframework.security:spring-security-crypto', version.ref = 'spring-security' } spring-security-web = { module = 'org.springframework.security:spring-security-web', version.ref = 'spring-security' } spring-test = { module = 'org.springframework:spring-test', version.ref = 'spring' } diff --git a/plugin/build.gradle b/plugin/build.gradle index f03a62481..ceaecf0fa 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -44,6 +44,7 @@ dependencies { testImplementation libs.bundles.grails.testing.support testImplementation libs.spock.core testImplementation libs.spring.test + testImplementation libs.spring.security.config testRuntimeOnly libs.slf4j.nop // Prevents warnings about missing slf4j implementation during tests } diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.java similarity index 57% rename from plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.groovy rename to plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.java index 543770d25..06f1a1879 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.java @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package grails.plugin.springsecurity +package grails.plugin.springsecurity; /** * Stores the default order numbers of all Spring Security filters for use in configuration. @@ -23,63 +23,101 @@ * @author Burt Beckwith */ enum SecurityFilterPosition { - /** First */ + FIRST(Integer.MIN_VALUE), - /** HTTP/HTTPS channel filter */ + + DISABLE_ENCODE_URL_FILTER, + + FORCE_EAGER_SESSION_FILTER, + CHANNEL_FILTER, - /** Concurrent Sessions */ - CONCURRENT_SESSION_FILTER, - /** Populates the SecurityContextHolder */ + SECURITY_CONTEXT_FILTER, - /** Logout */ + + CONCURRENT_SESSION_FILTER, + + WEB_ASYNC_MANAGER_FILTER, + + HEADERS_FILTER, + + CORS_FILTER, + + SAML2_LOGOUT_REQUEST_FILTER, + + SAML2_LOGOUT_RESPONSE_FILTER, + + CSRF_FILTER, + + SAML2_LOGOUT_FILTER, + LOGOUT_FILTER, - /** x509 certs */ + + OAUTH2_AUTHORIZATION_REQUEST_FILTER, + + SAML2_AUTHENTICATION_REQUEST_FILTER, + X509_FILTER, - /** Pre-auth */ + PRE_AUTH_FILTER, - /** CAS */ + CAS_FILTER, - /** UsernamePasswordAuthenticationFilter */ + + OAUTH2_LOGIN_FILTER, + + SAML2_AUTHENTICATION_FILTER, + FORM_LOGIN_FILTER, - /** OpenID */ + OPENID_FILTER, - /** Not used, generates a dynamic login form */ + LOGIN_PAGE_FILTER, - /** Digest auth */ + + LOGOUT_PAGE_FILTER, + DIGEST_AUTH_FILTER, - /** Basic Auth */ + + BEARER_TOKEN_AUTH_FILTER, + BASIC_AUTH_FILTER, - /** saved request filter */ + REQUEST_CACHE_FILTER, - /** SecurityContextHolderAwareRequestFilter */ + SERVLET_API_SUPPORT_FILTER, - /** Remember-me cookie */ + + JAAS_API_SUPPORT_FILTER, + REMEMBER_ME_FILTER, - /** Anonymous auth */ + ANONYMOUS_FILTER, - /** SessionManagementFilter */ + + OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER, + + WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER, + SESSION_MANAGEMENT_FILTER, - /** Spring FormContentFilter allows www-url-form-encoded content-types to provide params in PUT requests */ - FORM_CONTENT_FILTER, - /** ExceptionTranslationFilter */ + EXCEPTION_TRANSLATION_FILTER, - /** FilterSecurityInterceptor */ + FILTER_SECURITY_INTERCEPTOR, - /** Switch user */ + SWITCH_USER_FILTER, - /** Last */ - LAST(Integer.MAX_VALUE) - private static final int INTERVAL = 100 + LAST(Integer.MAX_VALUE); + + private static final int INTERVAL = 100; - /** The position in the chain. */ - final int order + private final int order; - private SecurityFilterPosition() { - order = ordinal() * INTERVAL + SecurityFilterPosition() { + this.order = ordinal() * INTERVAL; } - private SecurityFilterPosition(int filterOrder) { - order = filterOrder + SecurityFilterPosition(int order) { + this.order = order; } + + public int getOrder() { + return this.order; + } + } diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index 2a79fc9db..9f1424898 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -762,7 +762,7 @@ final class SpringSecurityUtils { orderedNames[SecurityFilterPosition.SWITCH_USER_FILTER.order] = 'switchUserProcessingFilter' } - orderedNames[SecurityFilterPosition.FORM_CONTENT_FILTER.order] = 'formContentFilter' + orderedNames[SecurityFilterPosition.SWITCH_USER_FILTER.order+1] = 'formContentFilter' // add in filters contributed by secondary plugins orderedNames << SpringSecurityUtils.orderedFilters diff --git a/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy b/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy index e4f9f8ca7..92d10d631 100644 --- a/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy +++ b/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy @@ -17,6 +17,7 @@ package grails.plugin.springsecurity import grails.plugin.springsecurity.web.GrailsSecurityFilterChain import grails.plugin.springsecurity.web.SecurityRequestHolder import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl +import org.springframework.security.config.http.SecurityFiltersMapper import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.web.FilterChainProxy @@ -340,6 +341,11 @@ class SpringSecurityUtilsSpec extends AbstractUnitSpec { !SpringSecurityUtils.ifAnyGranted('ROLE_4') } + void 'SecurityFilterPosition order should match SecurityFilters'() { + expect: + SecurityFilterPosition.SWITCH_USER_FILTER.order == SecurityFiltersMapper.SWITCH_USER_FILTER.order + } + void 'private constructor'() { expect: SecurityTestUtils.testPrivateConstructor SpringSecurityUtils diff --git a/plugin/src/test/groovy/org/springframework/security/config/http/SecurityFiltersMapper.groovy b/plugin/src/test/groovy/org/springframework/security/config/http/SecurityFiltersMapper.groovy new file mode 100644 index 000000000..0c939e751 --- /dev/null +++ b/plugin/src/test/groovy/org/springframework/security/config/http/SecurityFiltersMapper.groovy @@ -0,0 +1,5 @@ +package org.springframework.security.config.http + +class SecurityFiltersMapper { + static final SWITCH_USER_FILTER = SecurityFilters.SWITCH_USER_FILTER +} \ No newline at end of file From 99e2d2ed8a9a8de05b96cbb3763146a64850ed82 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 11 Oct 2024 00:16:23 -0700 Subject: [PATCH 15/30] Fix integration test --- .../SpringSecurityUtilsIntegrationSpec.groovy | 20 +++++++++---------- .../springsecurity/SpringSecurityUtils.groovy | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy b/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy index 81fa0d968..03d550dcb 100644 --- a/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy +++ b/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsIntegrationSpec.groovy @@ -87,15 +87,15 @@ class SpringSecurityUtilsIntegrationSpec extends AbstractIntegrationSpec { expect: 10 == map.size() map[Integer.MIN_VALUE + 10] instanceof SecurityRequestHolderFilter - map[300] instanceof SecurityContextPersistenceFilter - map[400] instanceof MutableLogoutFilter - map[800] instanceof GrailsUsernamePasswordAuthenticationFilter - map[1400] instanceof SecurityContextHolderAwareRequestFilter - map[1500] instanceof GrailsRememberMeAuthenticationFilter - map[1600] instanceof GrailsAnonymousAuthenticationFilter - map[1800] instanceof FormContentFilter - map[1900] instanceof ExceptionTranslationFilter - map[2000] instanceof FilterSecurityInterceptor + map[SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order] instanceof SecurityContextPersistenceFilter + map[SecurityFilterPosition.LOGOUT_FILTER.order] instanceof MutableLogoutFilter + map[SecurityFilterPosition.FORM_LOGIN_FILTER.order] instanceof GrailsUsernamePasswordAuthenticationFilter + map[SecurityFilterPosition.SERVLET_API_SUPPORT_FILTER.order] instanceof SecurityContextHolderAwareRequestFilter + map[SecurityFilterPosition.REMEMBER_ME_FILTER.order] instanceof GrailsRememberMeAuthenticationFilter + map[SecurityFilterPosition.ANONYMOUS_FILTER.order] instanceof GrailsAnonymousAuthenticationFilter + map[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order-10] instanceof FormContentFilter + map[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order] instanceof ExceptionTranslationFilter + map[SecurityFilterPosition.FILTER_SECURITY_INTERCEPTOR.order] instanceof FilterSecurityInterceptor when: SpringSecurityUtils.clientRegisterFilter 'foo', SecurityFilterPosition.LOGOUT_FILTER @@ -123,7 +123,7 @@ class SpringSecurityUtilsIntegrationSpec extends AbstractIntegrationSpec { then: 11 == map.size() - map[410] instanceof DummyFilter + map[SecurityFilterPosition.LOGOUT_FILTER.order + 10] instanceof DummyFilter when: def filters = securityFilterChains[0].filters diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index 9f1424898..a8d7e521b 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -762,7 +762,7 @@ final class SpringSecurityUtils { orderedNames[SecurityFilterPosition.SWITCH_USER_FILTER.order] = 'switchUserProcessingFilter' } - orderedNames[SecurityFilterPosition.SWITCH_USER_FILTER.order+1] = 'formContentFilter' + orderedNames[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order-10] = 'formContentFilter' // add in filters contributed by secondary plugins orderedNames << SpringSecurityUtils.orderedFilters From cc4e5d2f393175ee59c71a83afa298334f58ac35 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 11 Oct 2024 00:30:51 -0700 Subject: [PATCH 16/30] add sdkmanrc --- .sdkmanrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .sdkmanrc diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 000000000..e2a1d58cf --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1 @@ +java=11.0.24-librca From d5748a3f32ed27170d7ce659d84c19c2f4aff493 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 11 Oct 2024 01:28:34 -0700 Subject: [PATCH 17/30] Java 17 --- .sdkmanrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.sdkmanrc b/.sdkmanrc index e2a1d58cf..215786fc3 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1 +1 @@ -java=11.0.24-librca +java=17.0.12-librca From 5ae99b3a14e5dbaf714df0014612612b200cb132 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 11 Oct 2024 01:29:09 -0700 Subject: [PATCH 18/30] Spring Security 6 --- .../grails/plugin/springsecurity/SecurityFilterPosition.java | 4 +--- .../grails/plugin/springsecurity/SpringSecurityUtils.groovy | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.java b/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.java index 06f1a1879..9676edd65 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.java +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SecurityFilterPosition.java @@ -22,7 +22,7 @@ * * @author Burt Beckwith */ -enum SecurityFilterPosition { +enum SecurityFilterPosition { FIRST(Integer.MIN_VALUE), @@ -68,8 +68,6 @@ enum SecurityFilterPosition { FORM_LOGIN_FILTER, - OPENID_FILTER, - LOGIN_PAGE_FILTER, LOGOUT_PAGE_FILTER, diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index 7e4a1cbdd..e9eb851fe 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -787,7 +787,7 @@ final class SpringSecurityUtils { FilterRegistrationBean sitemesh3Filter = (FilterRegistrationBean) applicationContext.getBean('sitemesh3Secured') if (sitemesh3Filter) { allConfiguredFilters['sitemesh3Secured'] = sitemesh3Filter.filter - SpringSecurityUtils.configuredOrderedFilters[SecurityFilterPosition.FORM_CONTENT_FILTER.previous().order] = sitemesh3Filter.filter + SpringSecurityUtils.configuredOrderedFilters[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order - 10] = sitemesh3Filter.filter } log.trace 'Ordered filters: {}', SpringSecurityUtils.configuredOrderedFilters From b0af3761b4a8816c8e964f015bf84b039805baed Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 11 Oct 2024 02:11:12 -0700 Subject: [PATCH 19/30] Ensure sitemesh 3 is in correct place in filter chain --- .../SpringSecurityCoreGrailsPlugin.groovy | 8 ++++++++ .../springsecurity/SpringSecurityUtils.groovy | 16 ++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy index fe6698786..eeb35c6c3 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy @@ -673,6 +673,14 @@ to default to 'Annotation'; setting value to 'Annotation' // build filters here to give dependent plugins a chance to register some SortedMap filterNames = ReflectionUtils.findFilterChainNames(conf) def securityFilterChains = applicationContext.securityFilterChains + + // if sitemesh 3 is installed, an additional sitemesh 3 filter will need to be registered + // as part of the security filter chain so that pages are decorated using the security context + def sitemesh3Filter = applicationContext.getBean('sitemesh3Secured') + if (sitemesh3Filter) { + filterNames[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order - 10] = 'sitemesh3Secured' + } + SpringSecurityUtils.buildFilterChains filterNames, conf.filterChain.chainMap ?: [], securityFilterChains, applicationContext log.trace 'Filter chain: {}', securityFilterChains diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index e9eb851fe..4edd9f627 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -778,16 +778,12 @@ final class SpringSecurityUtils { def allConfiguredFilters = [:] filterNames.each { Integer order, String name -> - Filter filter = applicationContext.getBean(name, Filter) - allConfiguredFilters[name] = filter - SpringSecurityUtils.configuredOrderedFilters[order] = filter - } - // if sitemesh 3 is installed, an additional sitemesh 3 filter will need to be registered - // as part of the security filter chain so that pages are decorated using the security context - FilterRegistrationBean sitemesh3Filter = (FilterRegistrationBean) applicationContext.getBean('sitemesh3Secured') - if (sitemesh3Filter) { - allConfiguredFilters['sitemesh3Secured'] = sitemesh3Filter.filter - SpringSecurityUtils.configuredOrderedFilters[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order - 10] = sitemesh3Filter.filter + def filter = applicationContext.getBean(name) + if (filter instanceof FilterRegistrationBean) { + filter = ((FilterRegistrationBean) filter).filter + } + allConfiguredFilters[name] = (Filter) filter + SpringSecurityUtils.configuredOrderedFilters[order] = (Filter) filter } log.trace 'Ordered filters: {}', SpringSecurityUtils.configuredOrderedFilters From a00c1a4746813bd61458d2587585877d75223a5b Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 11 Oct 2024 02:50:50 -0700 Subject: [PATCH 20/30] Temporarily disable javadoc --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 21464f77b..78b656112 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -70,6 +70,7 @@ jobs: spring-security-core:build --refresh-dependencies -Dgeb.env=chromeHeadless + -x javadoc - name: Publish Snapshot artifacts to Artifactory (repo.grails.org) uses: gradle/actions/setup-gradle@v3 From ddd8347f224315c3465a116fbaeb40fb1384967a Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 11 Oct 2024 02:54:19 -0700 Subject: [PATCH 21/30] Temporarily disable javadoc --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 78b656112..a65d26f87 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -83,6 +83,7 @@ jobs: arguments: | -Dorg.gradle.internal.publish.checksums.insecure=true spring-security-core:publish + -x javadoc - name: Generate Snapshot Documentation if: success() From d88550cf52354c97140484953216482e44e45fdc Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sat, 12 Oct 2024 16:06:56 -0500 Subject: [PATCH 22/30] Use containsBean in case sitemesh is not installed --- .../springsecurity/SpringSecurityCoreGrailsPlugin.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy index eeb35c6c3..174cbb755 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy @@ -676,8 +676,7 @@ to default to 'Annotation'; setting value to 'Annotation' // if sitemesh 3 is installed, an additional sitemesh 3 filter will need to be registered // as part of the security filter chain so that pages are decorated using the security context - def sitemesh3Filter = applicationContext.getBean('sitemesh3Secured') - if (sitemesh3Filter) { + if (applicationContext.containsBean('sitemesh3Secured')) { filterNames[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order - 10] = 'sitemesh3Secured' } From 5fbe1856380ea8e075124120baa908f9021d1943 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sat, 12 Oct 2024 22:50:44 -0500 Subject: [PATCH 23/30] remove commons-lang --- gradle/libs.versions.toml | 4 ++-- plugin/build.gradle | 14 +++++++------- .../springsecurity/SpringSecurityUtils.groovy | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e98abdd35..222c3ab69 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] asset-pipeline = '5.0.1' -commons-lang = '2.6' +commons-text = '1.1' ehcache = '3.10.8' geb = '7.0' gorm-hibernate5 = '9.0.0-SNAPSHOT' @@ -25,7 +25,7 @@ spring-security = '6.3.3' tomcat = '10.1.29' [libraries] -commons-lang = { module = 'commons-lang:commons-lang', version.ref = 'commons-lang' } +commons-text = { module = 'org.apache.commons:commons-text', version.ref = 'commons-text' } ehcache = { module = 'org.ehcache:ehcache', version.ref = 'ehcache' } geb-core = { module = 'org.gebish:geb-core', version.ref = 'geb' } geb-spock = { module = 'org.gebish:geb-spock', version.ref = 'geb' } diff --git a/plugin/build.gradle b/plugin/build.gradle index 0acd1a35a..e9e2a4f01 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -9,12 +9,12 @@ plugins { group = 'org.grails.plugins' -configurations { - all { - exclude group: 'javax.servlet' - } -} - +configurations { + all { + exclude group: 'javax.servlet' + } +} + dependencies { api libs.grails.core @@ -32,7 +32,7 @@ dependencies { api libs.spring.security.web api libs.spring.web - implementation libs.commons.lang + implementation libs.commons.text implementation libs.ehcache implementation libs.grails.bootstrap implementation libs.grails.converters diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy index 4edd9f627..a9074c14e 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityUtils.groovy @@ -21,7 +21,7 @@ import grails.util.Environment import groovy.transform.CompileDynamic import groovy.transform.CompileStatic import groovy.util.logging.Slf4j -import org.apache.commons.lang.StringEscapeUtils +import org.apache.commons.text.StringEscapeUtils import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.context.ApplicationContext import org.springframework.security.access.hierarchicalroles.RoleHierarchy @@ -617,7 +617,7 @@ final class SpringSecurityUtils { static String getLastUsername(HttpSession session) { String username = (String)session.getAttribute(SPRING_SECURITY_LAST_USERNAME_KEY) if (username) { - username = StringEscapeUtils.unescapeHtml(username) + username = StringEscapeUtils.unescapeHtml4(username) } username } From 5f1ded31e17b0ab5732b7c34a0d44f1ee6c8887a Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sun, 13 Oct 2024 01:34:27 -0500 Subject: [PATCH 24/30] commons-text 1.12.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 222c3ab69..079807dfc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] asset-pipeline = '5.0.1' -commons-text = '1.1' +commons-text = '1.12.0' ehcache = '3.10.8' geb = '7.0' gorm-hibernate5 = '9.0.0-SNAPSHOT' From 896ec4b5da16098dfe1bc96dd5710ae79ba061f5 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sun, 13 Oct 2024 01:41:39 -0500 Subject: [PATCH 25/30] remove unnecessary configuration --- plugin/build.gradle | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugin/build.gradle b/plugin/build.gradle index e9e2a4f01..cb7f9edf2 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -9,12 +9,6 @@ plugins { group = 'org.grails.plugins' -configurations { - all { - exclude group: 'javax.servlet' - } -} - dependencies { api libs.grails.core From d27a57fa59ec1f1d04b270cb5af0afa07e53ff81 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 24 Oct 2024 12:23:14 -0700 Subject: [PATCH 26/30] Reuse existing sitemesh filter for decoration --- .../springsecurity/SpringSecurityCoreGrailsPlugin.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy index 174cbb755..46465b8eb 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy @@ -674,10 +674,10 @@ to default to 'Annotation'; setting value to 'Annotation' SortedMap filterNames = ReflectionUtils.findFilterChainNames(conf) def securityFilterChains = applicationContext.securityFilterChains - // if sitemesh 3 is installed, an additional sitemesh 3 filter will need to be registered + // if sitemesh 3 is installed, the filter should be applied a second time // as part of the security filter chain so that pages are decorated using the security context - if (applicationContext.containsBean('sitemesh3Secured')) { - filterNames[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order - 10] = 'sitemesh3Secured' + if (applicationContext.containsBean('sitemesh')) { + filterNames[SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order - 10] = 'sitemesh' } SpringSecurityUtils.buildFilterChains filterNames, conf.filterChain.chainMap ?: [], securityFilterChains, applicationContext From 0a79bd9acc4343a84821c29b71ae0abc8beb81b4 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 24 Oct 2024 13:14:12 -0700 Subject: [PATCH 27/30] add jline for shell commands --- buildSrc/build.gradle | 1 + plugin/build.gradle | 1 + 2 files changed, 2 insertions(+) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index f5f4d22ba..034a10fa8 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -4,6 +4,7 @@ repositories { } dependencies { + implementation platform("org.grails:grails-bom:7.0.0-SNAPSHOT") implementation buildsrcLibs.asciidoctorj implementation buildsrcLibs.asset.pipeline.gradle implementation buildsrcLibs.grails.gradle.plugin, { diff --git a/plugin/build.gradle b/plugin/build.gradle index cb7f9edf2..da427faf1 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation libs.spring.core implementation libs.spring.security.crypto implementation libs.spring.tx + implementation "jline:jline" // for shell commands compileOnly libs.groovy.core // Compile-time annotations compileOnly libs.jakarta.servlet.api // Provided From 5b20efd9368424aec7d1582f74757a8ec0ceb21d Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 24 Oct 2024 13:15:02 -0700 Subject: [PATCH 28/30] change jline to compileOnly --- plugin/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/build.gradle b/plugin/build.gradle index da427faf1..5b5ca01f0 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -36,7 +36,7 @@ dependencies { implementation libs.spring.core implementation libs.spring.security.crypto implementation libs.spring.tx - implementation "jline:jline" // for shell commands + compileOnly "jline:jline" // for shell commands compileOnly libs.groovy.core // Compile-time annotations compileOnly libs.jakarta.servlet.api // Provided From c7e7725cf7d7a6e60412a4b9ea447eefda6e4ec7 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 31 Oct 2024 00:04:05 -0700 Subject: [PATCH 29/30] Set filterChain position to match default position used by Spring Security. Fixes #1033 --- .../SpringSecurityCoreGrailsPlugin.groovy | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy index 46465b8eb..0c1f90f73 100644 --- a/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy +++ b/plugin/src/main/groovy/grails/plugin/springsecurity/SpringSecurityCoreGrailsPlugin.groovy @@ -57,6 +57,7 @@ import grails.plugins.Plugin import grails.util.Metadata import groovy.util.logging.Slf4j import org.grails.web.mime.HttpServletResponseExtension +import org.springframework.boot.autoconfigure.security.SecurityProperties import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.boot.web.servlet.ServletListenerRegistrationBean import org.springframework.cache.jcache.JCacheCacheManager @@ -221,17 +222,7 @@ class SpringSecurityCoreGrailsPlugin extends Plugin { filter = ref('springSecurityFilterChain') urlPatterns = ['/*'] dispatcherTypes = EnumSet.of(DispatcherType.ERROR, DispatcherType.REQUEST) - - // The filter chain has to be after grailsWebRequestFilter, but its order changed - // in 3.1 (from Ordered.HIGHEST_PRECEDENCE + 30 (-2147483618) to - // FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER + 30 (30)) - String grailsVersion = Metadata.current.getGrailsVersion() - if (grailsVersion.startsWith('3.0')) { - order = Ordered.HIGHEST_PRECEDENCE + 100 - } - else { - order = 100 // FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER + 100 - } + order = SecurityProperties.DEFAULT_FILTER_ORDER } if (conf.useHttpSessionEventPublisher) { From e3accd924ce430eb39e0ed2b47a03663d34a7b5f Mon Sep 17 00:00:00 2001 From: Scott Murphy Date: Thu, 31 Oct 2024 00:17:50 -0700 Subject: [PATCH 30/30] Update README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 2a79f7aef..1eb0bac67 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,18 @@ See [documentation](https://grails-plugins.github.io/grails-spring-security-core - `4.0.x` compatible with Grails 4 - `3.3.x` compatible with Grails 3.3.x - `3.2.x` compatible with Grails 3.2.x + +Grails 7 requires disabling any Spring Security Auto Configurations you may have in your classpath. This can be done via annotation or `application.yml` +e.g. +```yml +spring: + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration + - org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration + - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration + - org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration + - org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration + - org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration + - org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration +```