diff --git a/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/CasSecuredApplication.java b/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/CasSecuredApplication.java index ca37535f2c..62da9cf725 100644 --- a/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/CasSecuredApplication.java +++ b/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/CasSecuredApplication.java @@ -48,14 +48,14 @@ public class CasSecuredApplication { public ServiceProperties serviceProperties() { logger.info("service properties"); ServiceProperties serviceProperties = new ServiceProperties(); - serviceProperties.setService("http://cas-client:8900/login/cas"); + serviceProperties.setService("http://localhost:8900/login/cas"); serviceProperties.setSendRenew(false); return serviceProperties; } @Bean public TicketValidator ticketValidator() { - return new Cas30ServiceTicketValidator("https://localhost:8443"); + return new Cas30ServiceTicketValidator("https://localhost:8443/cas"); } @Bean @@ -66,7 +66,7 @@ public class CasSecuredApplication { provider.setServiceProperties(serviceProperties); provider.setTicketValidator(ticketValidator); provider.setUserDetailsService( - s -> new User("test@test.com", "Mellon", true, true, true, true, + s -> new User("casuser", "Mellon", true, true, true, true, AuthorityUtils.createAuthorityList("ROLE_ADMIN"))); provider.setKey("CAS_PROVIDER_LOCALHOST_8900"); return provider; @@ -80,7 +80,7 @@ public class CasSecuredApplication { @Bean public LogoutFilter logoutFilter() { - LogoutFilter logoutFilter = new LogoutFilter("https://localhost:8443/logout", securityContextLogoutHandler()); + LogoutFilter logoutFilter = new LogoutFilter("https://localhost:8443/cas/logout", securityContextLogoutHandler()); logoutFilter.setFilterProcessesUrl("/logout/cas"); return logoutFilter; } diff --git a/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/config/WebSecurityConfig.java b/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/config/WebSecurityConfig.java index b0c3c68387..a3822b1b81 100644 --- a/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/config/WebSecurityConfig.java +++ b/security-modules/cas/cas-secured-app/src/main/java/com/baeldung/cassecuredapp/config/WebSecurityConfig.java @@ -1,15 +1,11 @@ package com.baeldung.cassecuredapp.config; import org.jasig.cas.client.session.SingleSignOutFilter; -import org.jasig.cas.client.validation.Cas30ServiceTicketValidator; -import org.jasig.cas.client.validation.TicketValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ProviderManager; import org.springframework.security.cas.ServiceProperties; import org.springframework.security.cas.authentication.CasAuthenticationProvider; import org.springframework.security.cas.web.CasAuthenticationEntryPoint; @@ -17,16 +13,12 @@ import org.springframework.security.cas.web.CasAuthenticationFilter; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.User; import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.logout.LogoutFilter; -import java.util.Collections; - @EnableWebSecurity -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { +public class WebSecurityConfig { private Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); private SingleSignOutFilter singleSignOutFilter; @@ -45,31 +37,28 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers( "/secured", "/login").authenticated() - .and() - .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()) - .and() - .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class) - .addFilterBefore(logoutFilter, LogoutFilter.class) - .csrf().ignoringAntMatchers("/exit/cas"); - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.authenticationProvider(casAuthenticationProvider); + .and() + .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()) + .and() + .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class) + .addFilterBefore(logoutFilter, LogoutFilter.class) + .csrf().ignoringAntMatchers("/exit/cas"); + return http.build(); } @Bean - @Override - protected AuthenticationManager authenticationManager() throws Exception { - return new ProviderManager(Collections.singletonList(casAuthenticationProvider)); + public AuthenticationManager authManager(HttpSecurity http) throws Exception { + return http.getSharedObject(AuthenticationManagerBuilder.class) + .authenticationProvider(casAuthenticationProvider) + .build(); } public AuthenticationEntryPoint authenticationEntryPoint() { CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint(); - entryPoint.setLoginUrl("https://localhost:8443/login"); + entryPoint.setLoginUrl("https://localhost:8443/cas/login"); entryPoint.setServiceProperties(serviceProperties); return entryPoint; } diff --git a/security-modules/cas/cas-server/.gitattributes b/security-modules/cas/cas-server/.gitattributes new file mode 100644 index 0000000000..8fc5677e1d --- /dev/null +++ b/security-modules/cas/cas-server/.gitattributes @@ -0,0 +1,6 @@ +# Set line endings to LF, even on Windows. Otherwise, execution within Docker fails. +# See https://help.github.com/articles/dealing-with-line-endings/ +*.sh text eol=lf +gradlew text eol=lf +*.cmd text eol=crlf +*.bat text eol=crlf diff --git a/security-modules/cas/cas-server/.github/dependabot.yml b/security-modules/cas/cas-server/.github/dependabot.yml new file mode 100644 index 0000000000..c8398bda95 --- /dev/null +++ b/security-modules/cas/cas-server/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: gradle + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/security-modules/cas/cas-server/.github/renovate.json b/security-modules/cas/cas-server/.github/renovate.json new file mode 100644 index 0000000000..58fa77f03c --- /dev/null +++ b/security-modules/cas/cas-server/.github/renovate.json @@ -0,0 +1,11 @@ +{ + "extends": [ + "config:base", + ":preserveSemverRanges", + ":rebaseStalePrs", + ":disableRateLimiting", + ":semanticCommits", + ":semanticCommitTypeAll(renovatebot)" + ], + "labels": ["dependencies", "bot"] +} diff --git a/security-modules/cas/cas-server/.github/workflows/build.yml b/security-modules/cas/cas-server/.github/workflows/build.yml new file mode 100644 index 0000000000..c585dbbb02 --- /dev/null +++ b/security-modules/cas/cas-server/.github/workflows/build.yml @@ -0,0 +1,39 @@ +name: Build + +env: + JAVA_OPTS: "-Xms512m -Xmx6048m -Xss128m -XX:ReservedCodeCacheSize=512m -server -XX:+UseG1GC" + GRADLE_OPTS: "-Xms512m -Xmx6048m -Xss128m -XX:ReservedCodeCacheSize=512m -server -XX:+UseG1GC" + TERM: xterm-256color + JDK_CURRENT: 17 + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + cancel-previous-runs: + runs-on: ubuntu-latest + timeout-minutes: 1 + steps: + - uses: styfle/cancel-workflow-action@0.11.0 + with: + access_token: ${{ github.token }} + build: + needs: cancel-previous-runs + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: ${{ env.JDK_CURRENT }} + distribution: 'temurin' + - name: Build + run: ./gradlew clean build + diff --git a/security-modules/cas/cas-server/.gitignore b/security-modules/cas/cas-server/.gitignore index 6121b5ea9d..1d6df4a3b9 100644 --- a/security-modules/cas/cas-server/.gitignore +++ b/security-modules/cas/cas-server/.gitignore @@ -1,3 +1,28 @@ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ .classpath !/.project .project @@ -18,4 +43,7 @@ bin/ *.log tmp/ ./apache-tomcat -apache-tomcat.zip \ No newline at end of file +apache-tomcat.zip +config-metadata.properties +node-modules +package-lock.json \ No newline at end of file diff --git a/security-modules/cas/cas-server/.mergify.yml b/security-modules/cas/cas-server/.mergify.yml deleted file mode 100644 index 4fcbdbe4ac..0000000000 --- a/security-modules/cas/cas-server/.mergify.yml +++ /dev/null @@ -1,32 +0,0 @@ -# -# Licensed to Apereo under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Apereo licenses this file to you under the Apache License, -# Version 2.0 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a -# copy of the License at the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -pull_request_rules: -- name: automatic merge by dependabot - conditions: - - status-success=continuous-integration/travis-ci/pr - - status-success=WIP - - "#changes-requested-reviews-by=0" - - base=master - - label=dependencies - actions: - merge: - method: merge - strict: true - delete_head_branch: \ No newline at end of file diff --git a/security-modules/cas/cas-server/.travis.yml b/security-modules/cas/cas-server/.travis.yml deleted file mode 100644 index 8347dd1719..0000000000 --- a/security-modules/cas/cas-server/.travis.yml +++ /dev/null @@ -1,62 +0,0 @@ -language: java -sudo: required -dist: trusty -services: - - docker -branches: - only: - - master -before_cache: - - rm -rf $HOME/.gradle/caches/5.*/ - - rm -rf $HOME/.gradle/caches/4.*/ - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - - find ~/.gradle/caches/ -name "*.lock" -type f -delete -cache: - bundler: false - cargo: false - directories: - - $HOME/.m2 - - $HOME/.npm/ - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ -env: - global: - - JAVA_OPTS="-Xms512m -Xmx4048m -Xss128m -XX:ReservedCodeCacheSize=512m -XX:+UseG1GC -Xverify:none -server" - - GRADLE_OPTS="-Xms512m -Xmx1024m -Xss128m -XX:ReservedCodeCacheSize=512m -XX:+UseG1GC -Xverify:none -server" -jdk: -- openjdk11 -before_install: -- echo -e "Configuring Gradle wrapper...\n" -- mkdir -p ~/.gradle && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties -- chmod -R 777 ./gradlew -- chmod -R 777 *.sh -install: true -stages: - - build - - validate - - docker -jobs: - include: - - stage: build - script: ./gradlew clean build --stacktrace --no-daemon --refresh-dependencies -Dorg.gradle.internal.http.socketTimeout=600000 -Dorg.gradle.internal.http.connectionTimeout=600000 - name: "Build CAS" - ############################################ - - stage: validate - script: ./gradlew downloadShell - name: "Download CAS Shell" - - stage: validate - script: ./gradlew listTemplateViews - name: "List CAS Template Views" - - stage: validate - script: ./gradlew explodeWar - name: "Unzip CAS Web Application" - ############################################ - - stage: docker - script: ./gradlew build jibDockerBuild --stacktrace --no-daemon --refresh-dependencies - name: "Build Docker Image via Jib" - - stage: docker - script: docker-compose build - name: "Build Docker Image via Docker Compose" - - stage: docker - script: ./docker-build.sh - name: "Build Docker Image" \ No newline at end of file diff --git a/security-modules/cas/cas-server/Dockerfile b/security-modules/cas/cas-server/Dockerfile index b2f15ef4c3..94072c1cc0 100644 --- a/security-modules/cas/cas-server/Dockerfile +++ b/security-modules/cas/cas-server/Dockerfile @@ -1,9 +1,13 @@ -FROM adoptopenjdk/openjdk11:alpine-slim AS overlay +ARG BASE_IMAGE="eclipse-temurin:17-jdk" +ARG EXT_BUILD_COMMANDS="" +ARG EXT_BUILD_OPTIONS="" + +FROM $BASE_IMAGE AS overlay RUN mkdir -p cas-overlay COPY ./src cas-overlay/src/ COPY ./gradle/ cas-overlay/gradle/ -COPY ./gradlew ./settings.gradle ./build.gradle ./gradle.properties /cas-overlay/ +COPY ./gradlew ./settings.gradle ./build.gradle ./gradle.properties ./lombok.config /cas-overlay/ RUN mkdir -p ~/.gradle \ && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties \ @@ -13,9 +17,9 @@ RUN mkdir -p ~/.gradle \ && ./gradlew --version; RUN cd cas-overlay \ - && ./gradlew clean build --parallel; + && ./gradlew clean build $EXT_BUILD_COMMANDS --parallel --no-daemon $EXT_BUILD_OPTIONS; -FROM adoptopenjdk/openjdk11:alpine-jre AS cas +FROM $BASE_IMAGE AS cas LABEL "Organization"="Apereo" LABEL "Description"="Apereo CAS" @@ -26,11 +30,11 @@ RUN cd / \ && mkdir -p /etc/cas/saml \ && mkdir -p cas-overlay; +COPY --from=overlay cas-overlay/build/libs/cas.war cas-overlay/ COPY etc/cas/ /etc/cas/ COPY etc/cas/config/ /etc/cas/config/ COPY etc/cas/services/ /etc/cas/services/ COPY etc/cas/saml/ /etc/cas/saml/ -COPY --from=overlay cas-overlay/build/libs/cas.war cas-overlay/ EXPOSE 8080 8443 diff --git a/security-modules/cas/cas-server/Procfile b/security-modules/cas/cas-server/Procfile new file mode 100644 index 0000000000..2c732c3138 --- /dev/null +++ b/security-modules/cas/cas-server/Procfile @@ -0,0 +1 @@ +web: java $JAVA_OPTS -jar build/libs/cas.war --server.port=$PORT --server.ssl.enabled=false diff --git a/security-modules/cas/cas-server/README.md b/security-modules/cas/cas-server/README.md index 6c9372a729..5d687e360d 100644 --- a/security-modules/cas/cas-server/README.md +++ b/security-modules/cas/cas-server/README.md @@ -1,2 +1,2 @@ # Relevant Articles -- [CAS SSO With Spring Security](https://www.baeldung.com/spring-security-cas-sso) +- [CAS SSO With Spring Security](https://www.baeldung.com/spring-security-cas-sso) \ No newline at end of file diff --git a/security-modules/cas/cas-server/build.gradle b/security-modules/cas/cas-server/build.gradle index 41381e2d8f..2eb347d998 100644 --- a/security-modules/cas/cas-server/build.gradle +++ b/security-modules/cas/cas-server/build.gradle @@ -1,106 +1,288 @@ +import org.apache.tools.ant.taskdefs.condition.* +import org.gradle.internal.logging.text.* +import org.apereo.cas.metadata.* +import java.nio.file.* +import org.gradle.internal.logging.text.* +import static org.gradle.internal.logging.text.StyledTextOutput.Style + buildscript { repositories { + if (project.privateRepoUrl) { + maven { + url project.privateRepoUrl + credentials { + username = project.privateRepoUsername + password = System.env.PRIVATE_REPO_TOKEN + } + } + } mavenLocal() mavenCentral() - jcenter() - maven { url "https://repo.spring.io/libs-milestone" } - maven { url "https://repo.spring.io/libs-snapshot" } - maven { url "https://plugins.gradle.org/m2/" } + gradlePluginPortal() + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots' + mavenContent { snapshotsOnly() } + } + maven { + url "https://repo.spring.io/milestone" + mavenContent { releasesOnly() } + } } dependencies { - classpath "de.undercouch:gradle-download-task:${project.gradleDownloadTaskVersion}" classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.springBootVersion}" - classpath "gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:${project.jibVersion}" - classpath "io.freefair.gradle:maven-plugin:${project.gradleMavenPluginVersion}" + classpath "io.freefair.gradle:maven-plugin:${project.gradleFreeFairPluginVersion}" + classpath "io.freefair.gradle:lombok-plugin:${project.gradleFreeFairPluginVersion}" + classpath "io.spring.gradle:dependency-management-plugin:${project.gradleDependencyManagementPluginVersion}" + classpath "com.google.cloud.tools:jib-gradle-plugin:${project.jibVersion}" + classpath "com.bmuschko:gradle-docker-plugin:${project.gradleDockerPluginVersion}" + + classpath "de.undercouch:gradle-download-task:${project.gradleDownloadTaskVersion}" + classpath "org.apereo.cas:cas-server-core-api-configuration-model:${project.'cas.version'}" + classpath "org.apereo.cas:cas-server-core-configuration-metadata-repository:${project.'cas.version'}" } } repositories { + if (project.privateRepoUrl) { + maven { + url project.privateRepoUrl + credentials { + username = project.privateRepoUsername + password = System.env.PRIVATE_REPO_TOKEN + } + } + } mavenLocal() mavenCentral() - jcenter() - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } - maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } - maven { url "https://repo.spring.io/milestone/" } - maven { url "https://repo.spring.io/snapshot/" } - maven { url "https://oss.jfrog.org/artifactory/oss-snapshot-local" } + maven { url 'https://oss.sonatype.org/content/repositories/releases' } + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots' + mavenContent { snapshotsOnly() } + } + maven { + url "https://repository.apache.org/content/repositories/snapshots" + mavenContent { snapshotsOnly() } + } + maven { + url 'https://build.shibboleth.net/nexus/content/repositories/releases/' + mavenContent { releasesOnly() } + } + maven { + url "https://build.shibboleth.net/nexus/content/repositories/snapshots" + mavenContent { snapshotsOnly() } + } + maven { + url "https://repo.spring.io/milestone" + mavenContent { releasesOnly() } + } + maven { + url "https://jitpack.io" + content { + includeGroupByRegex ".*wss4j.*" + } + mavenContent { releasesOnly() } + } } -def casServerVersion = project.'cas.version' -def casWebApplicationBinaryName = "cas.war" - -project.ext."casServerVersion" = casServerVersion -project.ext."casWebApplicationBinaryName" = casWebApplicationBinaryName apply plugin: "io.freefair.war-overlay" -apply from: rootProject.file("gradle/tasks.gradle") - apply plugin: "war" -apply plugin: "eclipse" -apply plugin: "idea" + +apply plugin: "org.springframework.boot" +apply plugin: "io.freefair.lombok" + apply from: rootProject.file("gradle/springboot.gradle") -apply from: rootProject.file("gradle/dockerjib.gradle") +apply plugin: "com.google.cloud.tools.jib" +apply plugin: "com.bmuschko.docker-remote-api" +apply from: rootProject.file("gradle/tasks.gradle") + + +configurations { + all { + resolutionStrategy { + cacheChangingModulesFor 0, "seconds" + cacheDynamicVersionsFor 0, "seconds" + preferProjectModules() + def failIfConflict = project.hasProperty("failOnVersionConflict") && Boolean.valueOf(project.getProperty("failOnVersionConflict")) + if (failIfConflict) { + failOnVersionConflict() + } + } + exclude(group: "cglib", module: "cglib") + exclude(group: "cglib", module: "cglib-full") + exclude(group: "org.slf4j", module: "slf4j-log4j12") + exclude(group: "org.slf4j", module: "slf4j-simple") + exclude(group: "org.slf4j", module: "jcl-over-slf4j") + exclude(group: "org.apache.logging.log4j", module: "log4j-to-slf4j") + } +} + +war { + entryCompression = ZipEntryCompression.STORED + enabled = false +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(project.targetCompatibility) + } +} + +bootBuildImage { + imageName = "${project.'containerImageOrg'}/${project.'containerImageName'}:${project.version}" +} + + +['jibDockerBuild', 'jibBuildTar', 'jib'].each { taskName -> + if (gradle.gradleVersion >= "8.0") { + getTasksByName(taskName, true).each(it -> { + it.notCompatibleWithConfigurationCache("Jib is not compatible with configuration cache"); + it.enabled = !gradle.startParameter.isConfigurationCacheRequested() + }) + } +} + +def imagePlatforms = project.dockerImagePlatform.split(",") +def dockerUsername = providers.systemProperty("dockerUsername").getOrNull() +def dockerPassword = providers.systemProperty("dockerPassword").getOrNull() +def imageTagPostFix = providers.systemProperty("dockerImageTagPostfix").getOrElse("") + +jib { + if (gradle.gradleVersion >= "8.0" && gradle.startParameter.isConfigurationCacheRequested()) { + def out = services.get(StyledTextOutputFactory).create("cas") + out.withStyle(Style.Info).println("You are seeing this message because the Gradle configuration cache is turned on") + out.withStyle(Style.Info).println("Running Jib tasks to produce Docker images will require the command-line option: --no-configuration-cache") + out.withStyle(Style.Info).println("Jib does not support the Gradle configuration cache; Please see https://github.com/GoogleContainerTools/jib/issues/3132") + out.withStyle(Style.Info).println("Jib tasks are disabled.") + } + from { + image = project.baseDockerImage + platforms { + imagePlatforms.each { + def given = it.split(":") + platform { + architecture = given[0] + os = given[1] + } + } + } + } + to { + image = "${project.'containerImageOrg'}/${project.'containerImageName'}:${project.version}" + /** + ecr-login: Amazon Elastic Container Registry (ECR) + gcr: Google Container Registry (GCR) + osxkeychain: Docker Hub + */ + credHelper = "osxkeychain" + if (dockerUsername != null && dockerPassword != null) { + auth { + username = "${dockerUsername}" + password = "${dockerPassword}" + } + } + tags = [project.version] + } + container { + creationTime = "USE_CURRENT_TIMESTAMP" + entrypoint = ['/docker/entrypoint.sh'] + ports = ['80', '443', '8080', '8443', '8444', '8761', '8888', '5000'] + labels = [version:project.version, name:project.name, group:project.group, org:project.containerImageOrg] + workingDirectory = '/docker/cas/war' + } + extraDirectories { + paths { + path { + from = file('src/main/jib') + } + path { + from = file('etc/cas') + into = '/etc/cas' + } + path { + from = file("build/libs") + into = "/docker/cas/war" + } + } + permissions = [ + '/docker/entrypoint.sh': '755' + ] + } + allowInsecureRegistries = project.allowInsecureRegistries +} + +import com.bmuschko.gradle.docker.tasks.image.* +tasks.register("casBuildDockerImage", DockerBuildImage) { + dependsOn("build") + + def imageTag = "${project.'cas.version'}" + inputDir = project.projectDir + images.add("apereo/cas:${imageTag}${imageTagPostFix}") + images.add("apereo/cas:latest${imageTagPostFix}") + if (dockerUsername != null && dockerPassword != null) { + username = dockerUsername + password = dockerPassword + } + doLast { + def out = services.get(StyledTextOutputFactory).create("cas") + out.withStyle(Style.Success).println("Built CAS images successfully.") + } +} + +tasks.register("casPushDockerImage", DockerPushImage) { + dependsOn("casBuildDockerImage") + + def imageTag = "${project.'cas.version'}" + images.add("apereo/cas:${imageTag}${imageTagPostFix}") + images.add("apereo/cas:latest${imageTagPostFix}") + + if (dockerUsername != null && dockerPassword != null) { + username = dockerUsername + password = dockerPassword + } + doLast { + def out = services.get(StyledTextOutputFactory).create("cas") + out.withStyle(Style.Success).println("Pushed CAS images successfully.") + } +} dependencies { - // Other CAS dependencies/modules may be listed here... - compile "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}" - compile "org.apereo.cas:cas-server-support-jdbc:${casServerVersion}" -} + /** + * Do NOT modify the lines below or else you will risk breaking dependency management. + */ + implementation enforcedPlatform("org.apereo.cas:cas-server-support-bom:${project.'cas.version'}") + implementation platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) -tasks.findByName("jibDockerBuild") - .dependsOn(copyWebAppIntoJib, copyConfigIntoJib) - .finalizedBy(deleteWebAppFromJib) + /** + * Do NOT modify the lines below or else you will risk breaking the build. + */ + implementation "org.apereo.cas:cas-server-core-api-configuration-model" + implementation "org.apereo.cas:cas-server-webapp-init" -tasks.findByName("jib") - .dependsOn(copyWebAppIntoJib, copyConfigIntoJib) - .finalizedBy(deleteWebAppFromJib) + developmentOnly "org.springframework.boot:spring-boot-devtools:${project.springBootVersion}" + + /** + * CAS dependencies and modules may be listed here. + * + * There is no need to specify the version number for each dependency + * since versions are all resolved and controlled by the dependency management + * plugin via the CAS bom. + **/ + implementation "org.apereo.cas:cas-server-support-rest" + implementation "org.apereo.cas:cas-server-support-json-service-registry" + implementation "org.apereo.cas:cas-server-support-jdbc" + + if (project.hasProperty("casModules")) { + def dependencies = project.getProperty("casModules").split(",") + dependencies.each { + def projectsToAdd = rootProject.subprojects.findAll {project -> + project.name == "cas-server-core-${it}" || project.name == "cas-server-support-${it}" + } + projectsToAdd.each {implementation it} + } + } -configurations.all { - resolutionStrategy { - cacheChangingModulesFor 0, "seconds" - cacheDynamicVersionsFor 0, "seconds" - - preferProjectModules() - - def failIfConflict = project.hasProperty("failOnVersionConflict") && Boolean.valueOf(project.getProperty("failOnVersionConflict")) - if (failIfConflict) { - failOnVersionConflict() - } - } + testImplementation "org.springframework.boot:spring-boot-starter-test" } -eclipse { - classpath { - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - downloadJavadoc = true - downloadSources = true - } -} - -bootWar { - entryCompression = ZipEntryCompression.STORED - overlays { - // https://docs.freefair.io/gradle-plugins/current/reference/#_io_freefair_war_overlay - // Note: The "excludes" property is only for files in the war dependency. - // If a jar is excluded from the war, it could be brought back into the final war as a dependency - // of non-war dependencies. Those should be excluded via normal gradle dependency exclusions. - cas { - from "org.apereo.cas:cas-server-webapp${project.appServer}:${casServerVersion}@war" - provided = false - //excludes = ["WEB-INF/lib/somejar-1.0*"] - } - } -} - - -wrapper { - distributionType = Wrapper.DistributionType.BIN - gradleVersion = "${project.gradleVersion}" -} diff --git a/security-modules/cas/cas-server/docker-build.sh b/security-modules/cas/cas-server/docker-build.sh deleted file mode 100644 index 8f2c2776bf..0000000000 --- a/security-modules/cas/cas-server/docker-build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -image_tag=(`cat gradle.properties | grep "cas.version" | cut -d= -f2`) - -echo "Building CAS docker image tagged as [$image_tag]" -# read -p "Press [Enter] to continue..." any_key; - -docker build --tag="org.apereo.cas/cas:$image_tag" . \ - && echo "Built CAS image successfully tagged as org.apereo.cas/cas:$image_tag" \ - && docker images "org.apereo.cas/cas:$image_tag" \ No newline at end of file diff --git a/security-modules/cas/cas-server/docker-push.sh b/security-modules/cas/cas-server/docker-push.sh deleted file mode 100644 index e04b107212..0000000000 --- a/security-modules/cas/cas-server/docker-push.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -read -p "Docker username: " docker_user -read -s -p "Docker password: " docker_psw - -echo "$docker_psw" | docker login --username "$docker_user" --password-stdin - -image_tag=(`cat gradle.properties | grep "cas.version" | cut -d= -f2`) - -echo "Pushing CAS docker image tagged as $image_tag to org.apereo.cas/cas..." -docker push org.apereo.cas/cas:"$image_tag" \ - && echo "Pushed org.apereo.cas/cas:$image_tag successfully."; \ No newline at end of file diff --git a/security-modules/cas/cas-server/docker-run.sh b/security-modules/cas/cas-server/docker-run.sh deleted file mode 100644 index f8627859f2..0000000000 --- a/security-modules/cas/cas-server/docker-run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -docker stop cas > /dev/null 2>&1 -docker rm cas > /dev/null 2>&1 -image_tag=(`cat gradle.properties | grep "cas.version" | cut -d= -f2`) -docker run -d -p 8080:8080 -p 8443:8443 --name="cas" org.apereo.cas/cas:"${image_tag}" -docker logs -f cas \ No newline at end of file diff --git a/security-modules/cas/cas-server/etc/cas/services/.donotdel b/security-modules/cas/cas-server/etc/cas/.ignore similarity index 100% rename from security-modules/cas/cas-server/etc/cas/services/.donotdel rename to security-modules/cas/cas-server/etc/cas/.ignore diff --git a/security-modules/cas/cas-server/etc/cas/config/cas.properties b/security-modules/cas/cas-server/etc/cas/config/cas.properties deleted file mode 100644 index a3be0e1388..0000000000 --- a/security-modules/cas/cas-server/etc/cas/config/cas.properties +++ /dev/null @@ -1,6 +0,0 @@ -cas.server.name=https://cas.example.org:8443 -cas.server.prefix=${cas.server.name}/cas - -logging.config: file:/etc/cas/config/log4j2.xml - -# cas.authn.accept.users= diff --git a/security-modules/cas/cas-server/etc/cas/config/log4j2.xml b/security-modules/cas/cas-server/etc/cas/config/log4j2.xml index 685dfab245..35dce6cc88 100644 --- a/security-modules/cas/cas-server/etc/cas/config/log4j2.xml +++ b/security-modules/cas/cas-server/etc/cas/config/log4j2.xml @@ -1,42 +1,85 @@ + /var/log - info warn info warn - debug warn warn warn warn warn warn + true + false + + casStackTraceFile + false + + - + + - + filePattern="${baseDir}/cas-%d{yyyy-MM-dd-HH}-%i.log.gz" + immediateFlush="false"> + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -45,75 +88,76 @@ + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/security-modules/cas/cas-server/etc/cas/saml/.gitkeep b/security-modules/cas/cas-server/etc/cas/saml/.gitkeep deleted file mode 100644 index 882c99944d..0000000000 --- a/security-modules/cas/cas-server/etc/cas/saml/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -This directory is references in the Dockerfile so it needs to be here. \ No newline at end of file diff --git a/security-modules/cas/cas-server/etc/cas/thekeystore b/security-modules/cas/cas-server/etc/cas/thekeystore deleted file mode 100644 index 78f49baf74..0000000000 Binary files a/security-modules/cas/cas-server/etc/cas/thekeystore and /dev/null differ diff --git a/security-modules/cas/cas-server/gradle.properties b/security-modules/cas/cas-server/gradle.properties index 28daacc245..c1af8aa0fa 100644 --- a/security-modules/cas/cas-server/gradle.properties +++ b/security-modules/cas/cas-server/gradle.properties @@ -1,28 +1,70 @@ -# Versions -cas.version=6.1.5 -springBootVersion=2.2.0.RELEASE -# Use -jetty, -undertow to other containers -# Or blank if you want to deploy to an external container -appServer=-tomcat -executable=false +version=6.6.9 +# CAS server version +cas.version=6.6.9 -gradleVersion=5.6.3 -tomcatVersion=9.0.30 +springBootVersion=2.7.11 +# The version of this overlay project group=org.apereo.cas +artifactId=cas-overlay sourceCompatibility=11 targetCompatibility=11 -jibVersion=1.7.0 +gradleFreeFairPluginVersion=8.1.0 +gradleDependencyManagementPluginVersion=1.1.2 -# Location of the downloaded CAS shell JAR -shellDir=build/libs -ivyVersion=2.4.0 -gradleDownloadTaskVersion=3.4.3 -gradleMavenPluginVersion=3.8.4 +# Used to build docker images +jibVersion=3.3.2 +gradleDockerPluginVersion=9.3.1 -# use without "-slim" in tag name if you want tools like jstack, adds about 100MB to image size -# (https://hub.docker.com/r/adoptopenjdk/openjdk11/tags/) -baseDockerImage=adoptopenjdk/openjdk11:alpine-jre +# Specify the coordinates of the container image to build via jib +containerImageOrg=apereo +containerImageName=cas + +baseDockerImage=eclipse-temurin:17-jdk allowInsecureRegistries=false +dockerImagePlatform=amd64:linux + +# Include launch script for executable WAR artifact +# Setting this to true allows the final web application +# to be fully executable on its own +executable=true + + + +# Use -tomcat, -jetty, -undertow for deployment to other embedded containers +# if the overlay application supports or provides the chosen type. +# You should set this to blank if you want to deploy to an external container. +# and want to set up, download and manage the container (i.e. Apache Tomcat) yourself. +appServer=-tomcat + +# Settings to generate keystore +# used by the build to assist with creating +# self-signed certificates for https endpoints +certDir=/etc/cas +serverKeystore=thekeystore +exportedServerCert=cas.crt +storeType=PKCS12 + +# Location of the downloaded CAS Shell JAR +shellDir=build/libs +ivyVersion=2.5.0 +gradleDownloadTaskVersion=4.1.1 + +tomcatVersion=10.1.11 + +# Include private repository +# override these in user properties or pass in values from env on command line +privateRepoUrl= +privateRepoUsername= + +# Gradle build settings +org.gradle.configureondemand=true +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.jvmargs=-Xms1024m -Xmx4048m -XX:TieredStopAtLevel=1 +org.gradle.unsafe.configuration-cache=false +org.gradle.unsafe.configuration-cache-problems=warn + + diff --git a/security-modules/cas/cas-server/gradle/dockerjib.gradle b/security-modules/cas/cas-server/gradle/dockerjib.gradle deleted file mode 100644 index dcd70e3875..0000000000 --- a/security-modules/cas/cas-server/gradle/dockerjib.gradle +++ /dev/null @@ -1,52 +0,0 @@ -apply plugin: "com.google.cloud.tools.jib" - -jib { - from { - image = project.baseDockerImage - } - to { - image = "${project.group}/${project.name}" - /** - ecr-login: Amazon Elastic Container Registry (ECR) - gcr: Google Container Registry (GCR) - osxkeychain: Docker Hub - */ - credHelper = "osxkeychain" - /** - auth { - username = "*******" - password = "*******" - } - tags = [casServerVersion] - */ - } - container { - useCurrentTimestamp = true - entrypoint = ['docker/entrypoint.sh'] - ports = ['80', '443', '8080', '8443'] - labels = [version:casServerVersion, name:project.name, group:project.group] - } - extraDirectories { - paths = 'src/main/jib' - permissions = [ - '/docker/entrypoint.sh': '755' - ] - } - allowInsecureRegistries = project.allowInsecureRegistries -} - -task copyWebAppIntoJib(type: Copy, group: "Docker", description: "Copy the web application into Docker image") { - dependsOn build - from "build/libs/${casWebApplicationBinaryName}" - into "src/main/jib/docker/cas/war" -} - -task copyConfigIntoJib(type: Copy, group: "Docker", description: "Copy the CAS configuration into Docker image") { - dependsOn build - from "etc/cas" - into "src/main/jib/docker/cas" -} - -task deleteWebAppFromJib(type: Delete, group: "Docker", description: "Explodes the CAS web application archive") { - delete "src/main/jib/docker/cas" -} \ No newline at end of file diff --git a/security-modules/cas/cas-server/gradle/springboot.gradle b/security-modules/cas/cas-server/gradle/springboot.gradle index b6a46b8940..4eca2eb384 100644 --- a/security-modules/cas/cas-server/gradle/springboot.gradle +++ b/security-modules/cas/cas-server/gradle/springboot.gradle @@ -1,15 +1,93 @@ -apply plugin: "org.springframework.boot" +apply plugin: "java" -bootRun.enabled = false -bootRun.onlyIf { return false } -tasks.remove(tasks['bootRun']) - -springBoot { - mainClassName = "org.apereo.cas.web.CasWebApplication" +sourceSets { + bootRunSources { + resources { + srcDirs new File("//etc/cas/templates/"), new File("${project.getProjectDir()}/src/main/resources/") + } + } } -bootWar { +configurations { + bootRunConfig { + extendsFrom compileClasspath + + exclude(group: "org.springframework.boot", module: "spring-boot-starter-logging") + exclude(group: "ch.qos.logback", module: "logback-core") + exclude(group: "ch.qos.logback", module: "logback-classic") + } +} + +dependencies { + bootRunConfig "org.apereo.cas:cas-server-core" + bootRunConfig "org.apereo.cas:cas-server-core-logging" + bootRunConfig "org.apereo.cas:cas-server-core-web" + bootRunConfig "org.apereo.cas:cas-server-core-webflow" + bootRunConfig "org.apereo.cas:cas-server-core-cookie" + bootRunConfig "org.apereo.cas:cas-server-core-logout" + bootRunConfig "org.apereo.cas:cas-server-core-authentication" + bootRunConfig "org.apereo.cas:cas-server-core-validation" + bootRunConfig "org.apereo.cas:cas-server-core-audit" + bootRunConfig "org.apereo.cas:cas-server-core-tickets" + bootRunConfig "org.apereo.cas:cas-server-core-services" + bootRunConfig "org.apereo.cas:cas-server-core-util" + + bootRunConfig "org.apereo.cas:cas-server-support-webconfig" + bootRunConfig "org.apereo.cas:cas-server-support-thymeleaf" + bootRunConfig "org.apereo.cas:cas-server-support-validation" + bootRunConfig "org.apereo.cas:cas-server-support-person-directory" + + bootRunConfig "org.apereo.cas:cas-server-webapp-resources" + bootRunConfig "org.apereo.cas:cas-server-webapp-init" + bootRunConfig "org.apereo.cas:cas-server-webapp-tomcat" + bootRunConfig "org.apereo.cas:cas-server-webapp-init-tomcat" + + bootRunConfig "org.springframework.cloud:spring-cloud-starter-bootstrap" + bootRunConfig "org.springframework.boot:spring-boot-devtools" +} + +bootRun { + classpath = configurations.bootRunConfig + sourceSets.main.compileClasspath + sourceSets.main.runtimeClasspath + sourceResources sourceSets.bootRunSources doFirst { + systemProperties = System.properties + } + + def list = [] + list.add("-XX:TieredStopAtLevel=1") + list.add("-Xverify:none") + list.add("--add-modules") + list.add("java.se") + list.add("--add-exports") + list.add("java.base/jdk.internal.ref=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.base/java.lang=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.base/java.nio=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.base/sun.nio.ch=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.management/sun.management=ALL-UNNAMED") + list.add("--add-opens") + list.add("jdk.management/com.sun.management.internal=ALL-UNNAMED") + list.add("-Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=n") + + jvmArgs = list + + def appArgList = [] + args = appArgList +} + +springBoot { + buildInfo() + mainClass = "org.apereo.cas.web.CasWebApplication" + + + + +} + + bootWar { def executable = project.hasProperty("executable") && Boolean.valueOf(project.getProperty("executable")) if (executable) { logger.info "Including launch script for executable WAR artifact" @@ -17,8 +95,44 @@ bootWar { } else { logger.info "WAR artifact is not marked as an executable" } - archiveName "${casWebApplicationBinaryName}" - baseName "cas" - excludeDevtools = true + + archiveFileName = "cas.war" + archiveBaseName = "cas" + + entryCompression = ZipEntryCompression.STORED + + /* + attachClasses = true + classesClassifier = 'classes' + archiveClasses = true + */ + + overlays { + /* + https://docs.freefair.io/gradle-plugins/current/reference/#_io_freefair_war_overlay + Note: The "excludes" property is only for files in the war dependency. + If a jar is excluded from the war, it could be brought back into the final war as a dependency + of non-war dependencies. Those should be excluded via normal gradle dependency exclusions. + */ + cas { + from "org.apereo.cas:cas-server-webapp${project.appServer}:${project.'cas.version'}@war" + + + + + + provided = false + excludes = ["WEB-INF/lib/servlet-api-2*.jar"] + + /* + excludes = ["WEB-INF/lib/somejar-1.0*"] + enableCompilation = true + includes = ["*.xyz"] + targetPath = "sub-path/bar" + skip = false + */ + } + } } -} \ No newline at end of file + +bootBuildInfo.mustRunAfter(compileJava) diff --git a/security-modules/cas/cas-server/gradle/tasks.gradle b/security-modules/cas/cas-server/gradle/tasks.gradle index 2babeb2d1f..9c868e50a7 100644 --- a/security-modules/cas/cas-server/gradle/tasks.gradle +++ b/security-modules/cas/cas-server/gradle/tasks.gradle @@ -1,75 +1,57 @@ -import org.apache.ivy.util.url.* -import org.apache.tools.ant.taskdefs.condition.Os -import org.gradle.api.tasks.Copy +import static org.gradle.internal.logging.text.StyledTextOutput.Style + +import org.apereo.cas.metadata.* +import org.gradle.internal.logging.text.* + +import groovy.json.* +import groovy.time.* import java.nio.file.* -import org.gradle.internal.logging.text.StyledTextOutputFactory; -import static org.gradle.internal.logging.text.StyledTextOutput.Style; +import java.util.* +import java.security.* buildscript { repositories { mavenLocal() mavenCentral() - jcenter() + gradlePluginPortal() + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots' + mavenContent { snapshotsOnly() } + } + maven { + url "https://repo.spring.io/milestone" + mavenContent { releasesOnly() } + } } dependencies { classpath "org.apache.ivy:ivy:${project.ivyVersion}" + classpath "org.apereo.cas:cas-server-core-configuration-metadata-repository:${project.'cas.version'}" } } - apply plugin: "de.undercouch.download" -def tomcatDirectory = "${buildDir}/apache-tomcat-${tomcatVersion}" -project.ext."tomcatDirectory" = tomcatDirectory - -def explodedDir="${buildDir}/cas" -def explodedResourcesDir="${buildDir}/cas-resources" -def resourceJarName = "cas-server-webapp-resources" - -task copyCasConfiguration(type: Copy, group: "build", description: "Copy the CAS configuration from this project to /etc/cas/config") { - from "etc/cas/config" - into new File('/etc/cas/config').absolutePath - doFirst { - new File('/etc/cas/config').mkdirs() - } -} - -task explodeWarOnly(type: Copy, group: "build", description: "Explodes the CAS web application archive") { - dependsOn 'build' - from zipTree("build/libs/${casWebApplicationBinaryName}") - into explodedDir -} - -task explodeWar(type: Copy, group: "build", description: "Explodes the CAS archive and resources jar from the CAS web application archive") { - dependsOn explodeWarOnly - from zipTree("${explodedDir}/WEB-INF/lib/${resourceJarName}-${casServerVersion}.jar") - into explodedResourcesDir -} - task run(group: "build", description: "Run the CAS web application in embedded container mode") { dependsOn 'build' doLast { - def casRunArgs = new ArrayList<>(Arrays.asList("-server -noverify -Xmx2048M -XX:+TieredCompilation -XX:TieredStopAtLevel=1".split(" "))) - if (project.hasProperty('args')) { - casRunArgs.addAll(project.args.split('\\s+')) - } - javaexec { - main = "-jar" + def casRunArgs = Arrays.asList("-server -noverify -Xmx2048M -XX:+TieredCompilation -XX:TieredStopAtLevel=1".split(" ")) + project.javaexec { jvmArgs = casRunArgs - args = ["build/libs/${casWebApplicationBinaryName}"] + classpath = project.files("build/libs/cas.war") + systemProperties = System.properties logger.info "Started ${commandLine}" } } } -task setExecutable(group: "build", description: "Configure the project to run in executable mode") { +task setExecutable(group: "CAS", description: "Configure the project to run in executable mode") { doFirst { project.setProperty("executable", "true") logger.info "Configuring the project as executable" } } -task executable(type:Exec, group: "build", description: "Run the CAS web application in standalone executable mode") { +task executable(type: Exec, group: "CAS", description: "Run the CAS web application in standalone executable mode") { dependsOn setExecutable, 'build' doFirst { workingDir "." @@ -81,66 +63,23 @@ task executable(type:Exec, group: "build", description: "Run the CAS web applica } } -task debug(group: "build", description: "Debug the CAS web application in embedded mode on port 5005") { + +task debug(group: "CAS", description: "Debug the CAS web application in embedded mode on port 5005") { dependsOn 'build' doLast { logger.info "Debugging process is started in a suspended state, listening on port 5005." def casArgs = Arrays.asList("-Xmx2048M".split(" ")) - javaexec { - main = "-jar" + project.javaexec { jvmArgs = casArgs debug = true - args = ["build/libs/${casWebApplicationBinaryName}"] + classpath = project.files("build/libs/cas.war") + systemProperties = System.properties logger.info "Started ${commandLine}" } } } -task downloadShell(group: "shell", description: "Download CAS shell jar from snapshot or release maven repo") { - doFirst { - mkdir "${project.shellDir}" - } - doLast { - def downloadFile - if (isRunningCasServerSnapshot(casServerVersion)) { - def snapshotDir = "https://oss.sonatype.org/content/repositories/snapshots/org/apereo/cas/cas-server-support-shell/${casServerVersion}/" - def files = new ApacheURLLister().listFiles(new URL(snapshotDir)) - files = files.sort{it.path} - files.each { - if (it.path.endsWith(".jar")) { - downloadFile = it - } - } - } else { - downloadFile = "https://repo1.maven.org/maven2/org/apereo/cas/cas-server-support-shell/${casServerVersion}/cas-server-support-shell-${casServerVersion}.jar" - } - logger.info "Downloading file: ${downloadFile}" - download { - src downloadFile - dest new File("${project.shellDir}", "cas-server-support-shell-${casServerVersion}.jar") - overwrite false - } - } -} - -task runShell(group: "shell", description: "Run the CAS shell") { - dependsOn downloadShell - doLast { - println "Run the following command to launch the shell:\n\tjava -jar ${project.shellDir}/cas-server-support-shell-${casServerVersion}.jar" - } -} - -task debugShell(group: "shell", description: "Run the CAS shell with debug options, wait for debugger on port 5005") { - dependsOn downloadShell - doLast { - println """ - Run the following command to launch the shell:\n\t - java -Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=y -jar ${project.shellDir}/cas-server-support-shell-${casServerVersion}.jar - """ - } -} - -task showConfiguration(group: "build", description: "Show configurations for each dependency, etc") { +task showConfiguration(group: "CAS", description: "Show configurations for each dependency, etc") { doLast() { def cfg = project.hasProperty("configuration") ? project.property("configuration") : "compile" configurations.getByName(cfg).each { println it } @@ -151,13 +90,13 @@ task allDependenciesInsight(group: "build", type: DependencyInsightReportTask, d task allDependencies(group: "build", type: DependencyReportTask, description: "Display a graph of all project dependencies") {} -task casVersion (group: "build", description: "Display the current CAS version") { +task casVersion(group: "CAS", description: "Display the current CAS version") { doFirst { def verbose = project.hasProperty("verbose") && Boolean.valueOf(project.getProperty("verbose")) if (verbose) { def out = services.get(StyledTextOutputFactory).create("CAS") println "******************************************************************" - out.withStyle(Style.Info).println "Apereo CAS $casServerVersion" + out.withStyle(Style.Info).println "Apereo CAS ${project.version}" out.withStyle(Style.Description).println "Enterprise Single SignOn for all earthlings and beyond" out.withStyle(Style.SuccessHeader).println "- GitHub: " out.withStyle(Style.Success).println "https://github.com/apereo/cas" @@ -167,25 +106,49 @@ task casVersion (group: "build", description: "Display the current CAS version") out.withStyle(Style.Success).println "https://apereo.github.io" println "******************************************************************" } else { - println casServerVersion + println project.version } } } -task createKeystore(group: "build", description: "Create CAS keystore") { +task springBootVersion(description: "Display current Spring Boot version") { + doLast { + println rootProject.springBootVersion + } +} + +task zip(type: Zip) { + from projectDir + exclude '**/.idea/**', '.gradle', 'tmp', '.git', '**/build/**', '**/bin/**', '**/out/**', '**/.settings/**' + destinationDirectory = buildDir + archiveFileName = "${project.name}.zip" + def zipFile = new File("${buildDir}/${archiveFileName}") + doLast { + if (zipFile.exists()) { + println "Zip archive is available at ${zipFile.absolutePath}" + } + } +} + +task createKeystore(group: "CAS", description: "Create CAS keystore") { + def dn = "CN=cas.example.org,OU=Example,OU=Org,C=US" + if (project.hasProperty("certificateDn")) { + dn = project.getProperty("certificateDn") + } + def subjectAltName = "dns:example.org,dns:localhost,ip:127.0.0.1" + if (project.hasProperty("certificateSubAltName")) { + subjectAltName = project.getProperty("certificateSubAltName") + } + doFirst { - mkdir "/etc/cas" + def certDir = project.getProperty("certDir") + def serverKeyStore = project.getProperty("serverKeystore") + def exportedServerCert = project.getProperty("exportedServerCert") + def storeType = project.getProperty("storeType") + def keystorePath = "$certDir/$serverKeyStore" + def serverCert = "$certDir/$exportedServerCert" - def keystorePath = "/etc/cas/thekeystore" - - def dn = "CN=cas.example.org,OU=Example,OU=Org,C=US" - if (project.hasProperty("certificateDn")) { - dn = project.getProperty("certificateDn") - } - def subjectAltName = "dns:example.org,dns:localhost,ip:127.0.0.1" - if (project.hasProperty("certificateSubAltName")) { - subjectAltName = project.getProperty("certificateSubAltName") - } + mkdir certDir // this will fail if thekeystore exists and has cert with cas alias already (so delete if you want to recreate) logger.info "Generating keystore for CAS with DN ${dn}" exec { @@ -194,65 +157,261 @@ task createKeystore(group: "build", description: "Create CAS keystore") { "-keyalg", "RSA", "-keypass", "changeit", "-storepass", "changeit", "-keystore", keystorePath, - "-dname", dn, "-ext", "SAN=${subjectAltName}" + "-dname", dn, "-ext", "SAN=${subjectAltName}", + "-storetype", storeType } logger.info "Exporting cert from keystore..." exec { workingDir "." commandLine "keytool", "-exportcert", "-alias", "cas", "-storepass", "changeit", "-keystore", keystorePath, - "-file", "/etc/cas/cas.cer" + "-file", serverCert } - logger.info "Import /etc/cas/cas.cer into your Java truststore (JAVA_HOME/lib/security/cacerts)" + logger.info "Import $serverCert into your Java truststore (\$JAVA_HOME/lib/security/cacerts)" } } -task listTemplateViews (group: "build", description: "List all CAS views") { - dependsOn explodeWar +task unzipWAR(type: Copy, group: "CAS", description: "Explodes the CAS web application archive") { + dependsOn 'build' + def destination = "${buildDir}/app" - doFirst { - fileTree(explodedResourcesDir).matching { - include "**/*.html" - } - .collect { it.name } - .toSorted() - .each { println it } + from zipTree("build/libs/cas.war") + into "${destination}" + doLast { + println "Unzipped WAR into ${destination}" } } -task getResource(group: "build", description: "Fetch a CAS resource and move it into the overlay") { - dependsOn explodeWar +task verifyRequiredJavaVersion { + def currentVersion = org.gradle.api.JavaVersion.current() + logger.info "Checking current Java version ${currentVersion} for required Java version ${project.targetCompatibility}" + if (!currentVersion.name.equalsIgnoreCase("${project.targetCompatibility}")) { + logger.warn("Careful: Current Java version ${currentVersion} does not match required Java version ${project.targetCompatibility}") + } +} + +task copyCasConfiguration(type: Copy, group: "CAS", + description: "Copy the CAS configuration from this project to /etc/cas/config") { + from "etc/cas/config" + into new File('/etc/cas/config').absolutePath + doFirst { + new File('/etc/cas/config').mkdirs() + } +} + + +def tomcatDirectory = "${buildDir}/apache-tomcat-${tomcatVersion}" +project.ext."tomcatDirectory" = tomcatDirectory + +def explodedDir = "${buildDir}/app" +def explodedResourcesDir = "${buildDir}/cas-resources" + +def resourcesJarName = "cas-server-webapp-resources" +def templateViewsJarName = "cas-server-support-thymeleaf" + +task unzip(type: Copy, group: "CAS", description: "Explodes the CAS archive and resources jar from the CAS web application archive") { + dependsOn unzipWAR + from zipTree("${explodedDir}/WEB-INF/lib/${templateViewsJarName}-${project.'cas.version'}.jar") + into explodedResourcesDir + + from zipTree("${explodedDir}/WEB-INF/lib/${resourcesJarName}-${project.'cas.version'}.jar") + into explodedResourcesDir + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + doLast { + println "Exploded WAR resources into ${explodedResourcesDir}" + } +} + +task downloadShell(group: "Shell", description: "Download CAS shell jar from snapshot or release maven repo", type: Download) { + def shellDir = project.providers.gradleProperty("shellDir").get() + def casVersion = project.providers.gradleProperty("cas.version").get() + def downloadFile + if (casVersion.contains("-SNAPSHOT")) { + def snapshotDir = "https://oss.sonatype.org/content/repositories/snapshots/org/apereo/cas/cas-server-support-shell/${casVersion}/" + def files = new org.apache.ivy.util.url.ApacheURLLister().listFiles(new URL(snapshotDir)) + files = files.sort { it.path } + files.each { + if (it.path.endsWith(".jar")) { + downloadFile = it + } + } + } else { + downloadFile = "https://repo1.maven.org/maven2/org/apereo/cas/cas-server-support-shell/${casVersion}/cas-server-support-shell-${casVersion}.jar" + } + new File("${shellDir}").mkdir() + logger.info "Downloading file: ${downloadFile}" + src downloadFile + dest new File("${shellDir}", "cas-server-support-shell-${casVersion}.jar") + overwrite false +} + +task runShell(group: "Shell", description: "Run the CAS shell") { + dependsOn downloadShell + def casVersion = project.providers.gradleProperty("cas.version").get() + doLast { + println "Run the following command to launch the shell:\n\tjava -jar ${project.shellDir}/cas-server-support-shell-${casVersion}.jar" + } +} + +task debugShell(group: "Shell", description: "Run the CAS shell with debug options, wait for debugger on port 5005") { + dependsOn downloadShell + def casVersion = project.providers.gradleProperty("cas.version").get() + doLast { + println """ + Run the following command to launch the shell:\n\t + java -Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=y -jar ${project.shellDir}/cas-server-support-shell-${casVersion}.jar + """ + } +} + +task listTemplateViews(group: "CAS", description: "List all CAS views") { + dependsOn unzip + + def templateViews = fileTree(explodedResourcesDir).matching { + include "**/*.html" + } + .collect { + return it.path.replace(explodedResourcesDir, "") + } + .toSorted() doFirst { - def resourceName = project.getProperty("resourceName") + templateViews.each { println it } + } +} - def results = fileTree(explodedResourcesDir).matching { +task getResource(group: "CAS", description: "Fetch a CAS resource and move it into the overlay") { + dependsOn unzip + + def resourceName = project.providers.gradleProperty("resourceName").getOrNull() + def resourcesDirectory = fileTree(explodedResourcesDir) + def projectDirectory = projectDir + + doFirst { + def results = resourcesDirectory.matching { include "**/${resourceName}.*" + include "**/${resourceName}" } if (results.isEmpty()) { println "No resources could be found matching ${resourceName}" return } if (results.size() > 1) { - println "Multiple resources found matching ${resourceName}: ${results}" + println "Multiple resources found matching ${resourceName}:\n" + results.each { + println "\t-" + it.path.replace(explodedResourcesDir, "") + } + println "\nNarrow down your search criteria and try again." return } def fromFile = explodedResourcesDir def resourcesDir = "src/main/resources" - mkdir resourcesDir + new File(resourcesDir).mkdir() def resourceFile = results[0].canonicalPath - def toResourceFile = resourceFile.replace(fromFile, resourcesDir) - - def parent = file(toResourceFile).getParent() - mkdir parent - - Files.copy(Paths.get(resourceFile), Paths.get(toResourceFile), StandardCopyOption.REPLACE_EXISTING) + def toResourceFile = new File("${projectDirectory}", resourceFile.replace(fromFile, resourcesDir)) + toResourceFile.getParentFile().mkdirs() + + Files.copy(Paths.get(resourceFile), Paths.get(toResourceFile.absolutePath), StandardCopyOption.REPLACE_EXISTING) println "Copied file ${resourceFile} to ${toResourceFile}" } } -def isRunningCasServerSnapshot(casServerVersion) { - return "${casServerVersion}".contains("-SNAPSHOT") -} \ No newline at end of file +task createTheme(group: "CAS", description: "Create theme directory structure in the overlay") { + def theme = project.providers.gradleProperty("theme").getOrNull() + + doFirst { + def builder = new FileTreeBuilder() + new File("src/main/resources/${theme}.properties").delete() + + builder.src { + main { + resources { + "static" { + themes { + "${theme}" { + css { + 'cas.css'('') + } + js { + 'cas.js'('') + } + images { + '.ignore'('') + } + } + } + } + + templates { + "${theme}" { + fragments { + + } + } + } + + "${theme}.properties"("""cas.standard.css.file=/themes/${theme}/css/cas.css +cas.standard.js.file=/themes/${theme}/js/cas.js + """) + } + } + } + } +} + +def skipValidation = project.hasProperty("validate") && project.property("validate").equals("false") +if (!skipValidation) { + task validateConfiguration(type: Copy, group: "CAS", + description: "Validate CAS configuration") { + def file = new File("${projectDir}/src/main/resources/application.properties") + if (file.exists()) { + throw new GradleException("This overlay project is overriding a CAS-supplied configuration file at ${file.path}. " + + "Overriding this file will disable all default CAS settings that are provided to the overlay, and " + + "generally has unintended side-effects. It's best to move your configuration inside an application.yml " + + "file, if you intend to keep the configuration bundled with the CAS web application. \n\nTo disable this " + + "validation step, run the build with -Pvalidate=false."); + } + } + processResources.dependsOn(validateConfiguration) +} + +task exportConfigMetadata(group: "CAS", description: "Export collection of CAS properties") { + def file = new File(project.rootDir, 'config-metadata.properties') + def queryType = ConfigurationMetadataCatalogQuery.QueryTypes.CAS + if (project.hasProperty("queryType")) { + queryType = ConfigurationMetadataCatalogQuery.QueryTypes.valueOf(project.findProperty("queryType")) + } + doLast { + file.withWriter('utf-8') { writer -> + def props = CasConfigurationMetadataCatalog.query( + ConfigurationMetadataCatalogQuery.builder() + .queryType(queryType) + .build()) + .properties() + props.each { property -> + writer.writeLine("# Type: ${property.type}"); + writer.writeLine("# Module: ${property.module}") + writer.writeLine("# Owner: ${property.owner}") + if (property.deprecationLevel != null) { + writer.writeLine("# This setting is deprecated with a severity level of ${property.deprecationLevel}.") + if (property.deprecationReason != null) { + writer.writeLine("# because ${property.deprecationReason}") + } + if (property.deprecationReason != null) { + writer.writeLine("# Replace with: ${property.deprecationReason}") + } + } + writer.writeLine("#") + def description = property.description.replace("\n", "\n# ").replace("\r", "") + description = org.apache.commons.text.WordUtils.wrap(description, 70, "\n# ", true) + writer.writeLine("# ${description}") + writer.writeLine("#") + writer.writeLine("# ${property.name}: ${property.defaultValue}") + writer.writeLine("") + } + } + println "Configuration metadata is available at ${file.absolutePath}" + } +} diff --git a/security-modules/cas/cas-server/gradle/wrapper/gradle-wrapper.properties b/security-modules/cas/cas-server/gradle/wrapper/gradle-wrapper.properties index f04d6a20ae..17a8ddce2d 100644 --- a/security-modules/cas/cas-server/gradle/wrapper/gradle-wrapper.properties +++ b/security-modules/cas/cas-server/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/security-modules/cas/cas-server/gradlew b/security-modules/cas/cas-server/gradlew index 83f2acfdc3..65dcd68d65 100644 --- a/security-modules/cas/cas-server/gradlew +++ b/security-modules/cas/cas-server/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,78 +17,113 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -105,84 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done fi +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/security-modules/cas/cas-server/gradlew.bat b/security-modules/cas/cas-server/gradlew.bat index 9618d8d960..93e3f59f13 100644 --- a/security-modules/cas/cas-server/gradlew.bat +++ b/security-modules/cas/cas-server/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,10 +25,14 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,38 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/security-modules/cas/cas-server/lombok.config b/security-modules/cas/cas-server/lombok.config new file mode 100644 index 0000000000..f562841cc3 --- /dev/null +++ b/security-modules/cas/cas-server/lombok.config @@ -0,0 +1,9 @@ +lombok.log.fieldName = LOGGER +lombok.log.fieldIsStatic=true + +lombok.toString.doNotUseGetters=true +lombok.equalsAndHashCode.doNotUseGetters=true + +lombok.addLombokGeneratedAnnotation = true + +config.stopBubbling=true diff --git a/security-modules/cas/cas-server/settings.gradle b/security-modules/cas/cas-server/settings.gradle index 3ad50900ea..74901f4662 100644 --- a/security-modules/cas/cas-server/settings.gradle +++ b/security-modules/cas/cas-server/settings.gradle @@ -1 +1 @@ -rootProject.name='cas' \ No newline at end of file +rootProject.name = 'cas' diff --git a/security-modules/cas/cas-server/src/main/java/org/apereo/cas/config/CasOverlayOverrideConfiguration.java b/security-modules/cas/cas-server/src/main/java/org/apereo/cas/config/CasOverlayOverrideConfiguration.java new file mode 100644 index 0000000000..eb7ec10bd9 --- /dev/null +++ b/security-modules/cas/cas-server/src/main/java/org/apereo/cas/config/CasOverlayOverrideConfiguration.java @@ -0,0 +1,23 @@ +package org.apereo.cas.config; + +//import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; + +//import org.apereo.cas.configuration.CasConfigurationProperties; + +@AutoConfiguration +//@EnableConfigurationProperties(CasConfigurationProperties.class) +public class CasOverlayOverrideConfiguration { + + /* + @Bean + public MyCustomBean myCustomBean() { + ... + } + */ +} diff --git a/security-modules/cas/cas-server/src/main/jib/docker/entrypoint.sh b/security-modules/cas/cas-server/src/main/jib/docker/entrypoint.sh index a3a0895b04..2747c3fa34 100644 --- a/security-modules/cas/cas-server/src/main/jib/docker/entrypoint.sh +++ b/security-modules/cas/cas-server/src/main/jib/docker/entrypoint.sh @@ -1,22 +1,30 @@ #!/bin/sh -#echo -e "\nChecking java..." -#java -version +ENTRYPOINT_DEBUG=${ENTRYPOINT_DEBUG:-false} +JVM_DEBUG=${JVM_DEBUG:-false} +JVM_DEBUG_PORT=${JVM_DEBUG_PORT:-5000} +JVM_DEBUG_SUSPEND=${JVM_DEBUG_SUSPEND:-n} +JVM_MEM_OPTS=${JVM_MEM_OPTS:--Xms512m -Xmx4096M} +JVM_EXTRA_OPTS=${JVM_EXTRA_OPTS:--server -noverify -XX:+TieredCompilation -XX:TieredStopAtLevel=1} -#echo -e "\nCreating CAS configuration directories..." -mkdir -p /etc/cas/config -mkdir -p /etc/cas/services +if [ $JVM_DEBUG = "true" ]; then + JVM_EXTRA_OPTS="${JVM_EXTRA_OPTS} -Xdebug -Xrunjdwp:transport=dt_socket,address=*:${JVM_DEBUG_PORT},server=y,suspend=${JVM_DEBUG_SUSPEND}" +fi -#echo "Listing provided CAS docker artifacts..." -#ls -R docker/cas +if [ $ENTRYPOINT_DEBUG = "true" ]; then + JVM_EXTRA_OPTS="${JVM_EXTRA_OPTS} -Ddebug=true" + + echo "\nChecking java..." + java -version -#echo -e "\nMoving CAS configuration artifacts..." -mv docker/cas/thekeystore /etc/cas 2>/dev/null -mv docker/cas/config/*.* /etc/cas/config 2>/dev/null -mv docker/cas/services/*.* /etc/cas/services 2>/dev/null + if [ -d /etc/cas ] ; then + echo "\nListing CAS configuration under /etc/cas..." + ls -R /etc/cas + fi + echo "\nRemote debugger configured on port ${JVM_DEBUG_PORT} with suspend=${JVM_DEBUG_SUSPEND}: ${JVM_DEBUG}" + echo "\nJava args: ${JVM_MEM_OPTS} ${JVM_EXTRA_OPTS}" +fi -#echo -e "\nListing CAS configuration under /etc/cas..." -#ls -R /etc/cas - -echo -e "\nRunning CAS..." -exec java -Xms512m -Xmx2048M -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -jar docker/cas/war/cas.war +echo "\nRunning CAS @ cas.war" +# shellcheck disable=SC2086 +exec java $JVM_EXTRA_OPTS $JVM_MEM_OPTS -jar cas.war "$@" diff --git a/security-modules/cas/cas-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/security-modules/cas/cas-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000000..3bc670c2c2 --- /dev/null +++ b/security-modules/cas/cas-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.apereo.cas.config.CasOverlayOverrideConfiguration diff --git a/security-modules/cas/cas-server/src/main/resources/application.properties b/security-modules/cas/cas-server/src/main/resources/application.properties deleted file mode 100644 index 185532f943..0000000000 --- a/security-modules/cas/cas-server/src/main/resources/application.properties +++ /dev/null @@ -1,4 +0,0 @@ -server.port=8443 -spring.main.allow-bean-definition-overriding=true -server.ssl.key-store=classpath:/etc/cas/thekeystore -server.ssl.key-store-password=changeit \ No newline at end of file diff --git a/security-modules/cas/cas-server/src/main/resources/application.yml b/security-modules/cas/cas-server/src/main/resources/application.yml new file mode 100644 index 0000000000..0c4e4ffde2 --- /dev/null +++ b/security-modules/cas/cas-server/src/main/resources/application.yml @@ -0,0 +1,10 @@ +# Application properties that need to be +# embedded within the web application can be included here +server: + port: 8443 + ssl: + key-store: classpath:/etc/cas/thekeystore + key-store-password: changeit +spring: + main: + allow-bean-definition-overriding: true diff --git a/security-modules/cas/cas-server/src/main/resources/etc/cas/config/cas.properties b/security-modules/cas/cas-server/src/main/resources/etc/cas/config/cas.properties index dda939bc1d..4d9207f013 100644 --- a/security-modules/cas/cas-server/src/main/resources/etc/cas/config/cas.properties +++ b/security-modules/cas/cas-server/src/main/resources/etc/cas/config/cas.properties @@ -1,15 +1,17 @@ -cas.serviceRegistry.initFromJson=true -cas.serviceRegistry.json.location=classpath:/etc/cas/services +cas.authn.accept.users=casuser::Mellon +cas.service-Registry.core.init-from-json=true +cas.service-Registry.json.location=classpath:/etc/cas/services + +# cas.authn.accept.users= + +# cas.authn.jdbc.query[0].sql=SELECT * FROM users WHERE email = ? +# cas.authn.jdbc.query[0].url=jdbc:mysql://127.0.0.1:3306/test?# useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC +# cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect +# cas.authn.jdbc.query[0].user=root +# cas.authn.jdbc.query[0].password=smattroot +# cas.authn.jdbc.query[0].ddlAuto=none +# cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver +# cas.authn.jdbc.query[0].fieldPassword=password +# cas.authn.jdbc.query[0].passwordEncoder.type=NONE -cas.authn.accept.users= - -cas.authn.jdbc.query[0].sql=SELECT * FROM users WHERE email = ? -cas.authn.jdbc.query[0].url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC -cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect -cas.authn.jdbc.query[0].user=root -cas.authn.jdbc.query[0].password=smattroot -cas.authn.jdbc.query[0].ddlAuto=none -cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver -cas.authn.jdbc.query[0].fieldPassword=password -cas.authn.jdbc.query[0].passwordEncoder.type=NONE \ No newline at end of file diff --git a/security-modules/cas/cas-server/src/main/resources/etc/cas/services/casSecuredApp-8900.json b/security-modules/cas/cas-server/src/main/resources/etc/cas/services/casSecuredApp-8900.json index 5d468945ff..107817818b 100644 --- a/security-modules/cas/cas-server/src/main/resources/etc/cas/services/casSecuredApp-8900.json +++ b/security-modules/cas/cas-server/src/main/resources/etc/cas/services/casSecuredApp-8900.json @@ -1,8 +1,8 @@ { "@class" : "org.apereo.cas.services.RegexRegisteredService", - "serviceId" : "http://cas-client:8900/login/cas", + "serviceId" : "http://localhost:8900/login/cas", "name" : "casSecuredApp", "id" : 8900, "logoutType" : "BACK_CHANNEL", - "logoutUrl" : "http://cas-client:8900/exit/cas" + "logoutUrl" : "http://localhost:8900/exit/cas" } \ No newline at end of file diff --git a/security-modules/cas/cas-server/src/main/resources/etc/cas/thekeystore b/security-modules/cas/cas-server/src/main/resources/etc/cas/thekeystore index a361bf03f9..0c2199c92d 100644 Binary files a/security-modules/cas/cas-server/src/main/resources/etc/cas/thekeystore and b/security-modules/cas/cas-server/src/main/resources/etc/cas/thekeystore differ diff --git a/security-modules/cas/cas-server/system.properties b/security-modules/cas/cas-server/system.properties new file mode 100644 index 0000000000..9146af5386 --- /dev/null +++ b/security-modules/cas/cas-server/system.properties @@ -0,0 +1 @@ +java.runtime.version=11