diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5e04587 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +target/** +build/** +bin/** +.idea/** +.history/** +.github/** +.git/** diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8fc5677 --- /dev/null +++ b/.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/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c8398bd --- /dev/null +++ b/.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/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..58fa77f --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,11 @@ +{ + "extends": [ + "config:base", + ":preserveSemverRanges", + ":rebaseStalePrs", + ":disableRateLimiting", + ":semanticCommits", + ":semanticCommitTypeAll(renovatebot)" + ], + "labels": ["dependencies", "bot"] +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f450bdd --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,39 @@ +name: Build + +env: + JAVA_OPTS: "-Xms512m -Xmx6048m -Xss128m -XX:ReservedCodeCacheSize=512m -XX:+UseG1GC" + GRADLE_OPTS: "-Xms512m -Xmx6048m -Xss128m -XX:ReservedCodeCacheSize=512m -XX:+UseG1GC" + TERM: xterm-256color + JDK_CURRENT: 21 + +on: + push: + branches: [ 7.0 ] + pull_request: + branches: [ 7.0 ] + +jobs: + cancel-previous-runs: + runs-on: ubuntu-latest + timeout-minutes: 1 + steps: + - uses: styfle/cancel-workflow-action@0.12.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@v4 + - name: Set up JDK + uses: actions/setup-java@v4.0.0 + with: + java-version: ${{ env.JDK_CURRENT }} + distribution: 'corretto' + - name: Build + run: ./gradlew clean build + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d6df4a --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ + +### 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 +.settings +.history +.vscode +target/ +.idea/ +.DS_Store +.idea +overlays/ +.gradle/ +build/ +log/ +bin/ +*.war +*.iml +*.log +tmp/ +./apache-tomcat +apache-tomcat.zip +config-metadata.properties +node-modules +package-lock.json \ No newline at end of file diff --git a/.java-version b/.java-version new file mode 100644 index 0000000..74623ac --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +21.0 \ No newline at end of file diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 0000000..6b6985e --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1 @@ +java=21 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..36a0b39 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,84 @@ +ARG BASE_IMAGE="azul/zulu-openjdk:21" + +FROM $BASE_IMAGE AS overlay + +ARG EXT_BUILD_COMMANDS="" +ARG EXT_BUILD_OPTIONS="" + +RUN mkdir -p cas-overlay +COPY ./src cas-overlay/src/ +COPY ./gradle/ cas-overlay/gradle/ +COPY ./gradlew ./settings.gradle ./build.gradle ./gradle.properties ./lombok.config /cas-overlay/ + +RUN mkdir -p ~/.gradle \ + && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties \ + && echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties \ + && cd cas-overlay \ + && chmod 750 ./gradlew \ + && ./gradlew --version; + +RUN cd cas-overlay \ + && ./gradlew clean build $EXT_BUILD_COMMANDS --parallel --no-daemon $EXT_BUILD_OPTIONS; + +FROM $BASE_IMAGE AS cas + +LABEL "Organization"="Apereo" +LABEL "Description"="Apereo CAS" + +RUN cd / \ + && mkdir -p /etc/cas/config \ + && mkdir -p /etc/cas/services \ + && 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/ + +EXPOSE 8080 8443 + +ENV PATH $PATH:$JAVA_HOME/bin:. + +WORKDIR cas-overlay +ENTRYPOINT ["java", "-server", "-noverify", "-Xmx2048M", "-jar", "cas.war"] +FROM azul/zulu-openjdk:21 AS management-overlay + +RUN mkdir -p cas-management-overlay +#COPY ./src cas-management-overlay/src/ +COPY ./gradle/ cas-management-overlay/gradle/ +COPY ./gradlew ./settings.gradle ./build.gradle ./gradle.properties /cas-management-overlay/ + +RUN mkdir -p ~/.gradle \ + && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties \ + && echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties \ + && cd cas-management-overlay \ + && chmod 750 ./gradlew \ + && ./gradlew --version; + +RUN cd cas-management-overlay \ + && ./gradlew clean build --parallel --no-daemon; + + +FROM azul/zulu-openjdk:21 AS cas-management + +LABEL "Organization"="Apereo" +LABEL "Description"="Apereo CAS Management" + +RUN cd / \ + && mkdir -p /etc/cas/config \ + && mkdir -p /etc/cas/services \ + && mkdir -p cas-management-overlay; + +COPY --from=management-overlay cas-management-overlay/build/libs/cas-management.war cas-management-overlay/ +COPY etc/cas/ /etc/cas/ +COPY etc/cas/config/ /etc/cas/config/ +#COPY etc/cas/services/ /etc/cas/services/ + +EXPOSE 8080 8443 + +ENV PATH $PATH:$JAVA_HOME/bin:. + +WORKDIR cas-management-overlay +ENTRYPOINT ["java", "-server", "-noverify", "-Xmx2048M", "-jar", "cas-management.war"] diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..36eafb4 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: java $JAVA_OPTS -jar build/libs/cas-management.war --server.port=$PORT --server.ssl.enabled=false diff --git a/README.md b/README.md new file mode 100644 index 0000000..95cabf2 --- /dev/null +++ b/README.md @@ -0,0 +1,160 @@ +Apereo CAS WAR Overlay Template +===================================== + +WAR Overlay Type: `cas-management-overlay` + +# Versions + +- CAS Management `7.0.0-SNAPSHOT` +- CAS Server `7.0.0` + + +# Build + +To build the project, use: + +```bash +# Use --refresh-dependencies to force-update SNAPSHOT versions +./gradlew[.bat] clean build +``` + +To see what commands/tasks are available to the build script, run: + +```bash +./gradlew[.bat] tasks +``` + +If you need to, on Linux/Unix systems, you can delete all the existing artifacts +(artifacts and metadata) Gradle has downloaded using: + +```bash +# Only do this when absolutely necessary +rm -rf $HOME/.gradle/caches/ +``` + +Same strategy applies to Windows too, provided you switch `$HOME` to its equivalent in the above command. + +# Keystore + +For the server to run successfully, you might need to create a keystore file. +This can either be done using the JDK's `keytool` utility or via the following command: + +```bash +./gradlew[.bat] createKeystore +``` + +Use the password `changeit` for both the keystore and the key/certificate entries. +Ensure the keystore is loaded up with keys and certificates of the server. + +## Extension Modules + +Extension modules may be specified under the `dependencies` block of the [Gradle build script](build.gradle): + +```gradle +dependencies { + implementation "org.apereo.cas:cas-server-some-module" + ... +} +``` + +To collect the list of all project modules and dependencies in the overlay: + +```bash +./gradlew[.bat] dependencies +``` + +# Deployment + +On a successful deployment via the following methods, the server will be available at: + +* `https://localhost:8443/cas-management` + + + +## Executable WAR + +Run the server web application as an executable WAR. Note that running an executable WAR requires CAS to use an embedded container such as Apache Tomcat, Jetty, etc. + +The current servlet container is specified as `-tomcat`. + +```bash +java -jar build/libs/cas-management.war +``` + +Or via: + +```bash +./gradlew[.bat] run +``` + +It is often an advantage to explode the generated web application and run it in unpacked mode. +One way to run an unpacked archive is by starting the appropriate launcher, as follows: + +```bash +jar -xf build/libs/cas-management.war +cd build/libs +java org.springframework.boot.loader.launch.JarLauncher +``` + +This is slightly faster on startup (depending on the size of the WAR file) than +running from an unexploded archive. After startup, you should not expect any differences. + +Debug the CAS web application as an executable WAR: + +```bash +./gradlew[.bat] debug +``` + +Or via: + +```bash +java -Xdebug -Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=y -jar build/libs/cas-management.war +``` + +Run the CAS web application as a *standalone* executable WAR: + +```bash +./gradlew[.bat] clean executable +``` + +## External + +Deploy the binary web application file in `build/libs` after a successful build to a servlet container of choice. + +# Docker + +The following strategies outline how to build and deploy CAS Docker images. + +## Jib + +The overlay embraces the [Jib Gradle Plugin](https://github.com/GoogleContainerTools/jib) to provide easy-to-use out-of-the-box tooling for building CAS docker images. Jib is an open-source Java containerizer from Google that lets Java developers build containers using the tools they know. It is a container image builder that handles all the steps of packaging your application into a container image. It does not require you to write a Dockerfile or have Docker installed, and it is directly integrated into the overlay. + +```bash +# Running this task requires that you have Docker installed and running. +./gradlew build jibDockerBuild +``` + +## Dockerfile + +You can also use the Docker tooling and the provided `Dockerfile` to build and run. +There are dedicated Gradle tasks available to build and push Docker images using the supplied `DockerFile`: + +```bash +./gradlew build casBuildDockerImage +``` + +Once ready, you may also push the images: + +```bash +./gradlew casPushDockerImage +``` + +If credentials (username+password) are required for pull and push operations, they may be specified +using system properties via `-DdockerUsername=...` and `-DdockerPassword=...`. + +A `docker-compose.yml` is also provided to orchestrate the build: + +```bash +docker-compose build +``` + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..0645336 --- /dev/null +++ b/build.gradle @@ -0,0 +1,264 @@ +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() + gradlePluginPortal() + maven { + url "https://repo.spring.io/milestone" + mavenContent { releasesOnly() } + } + } + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.springBootVersion}" + 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}" + } +} + +repositories { + if (project.privateRepoUrl) { + maven { + url project.privateRepoUrl + credentials { + username = project.privateRepoUsername + password = System.env.PRIVATE_REPO_TOKEN + } + } + } + mavenLocal() + mavenCentral() + 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() } + } +} + + +apply plugin: "io.freefair.war-overlay" +apply plugin: "war" + +apply plugin: "org.springframework.boot" +apply plugin: "io.freefair.lombok" + + +apply from: rootProject.file("gradle/springboot.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() + } + + if (project.hasProperty("tomcatVersion")) { + eachDependency { DependencyResolveDetails dependency -> + def requested = dependency.requested + if (requested.group.startsWith("org.apache.tomcat") && requested.name != "jakartaee-migration") { + dependency.useVersion("${tomcatVersion}") + } + } + } + } + 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 { + /** + * Do NOT modify the lines below or else you will risk breaking dependency management. + **/ + implementation enforcedPlatform("org.apereo.cas:cas-mgmt-support-bom:${project.'casmgmt.version'}") + implementation enforcedPlatform("org.apereo.cas:cas-server-support-bom:${project.'cas.version'}") + implementation platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) + + /** + * 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. + **/ + + compileOnly "org.apereo.cas:cas-mgmt-webapp-init" + + testImplementation "org.springframework.boot:spring-boot-starter-test" +} + diff --git a/docker-build.sh b/docker-build.sh new file mode 100644 index 0000000..8f79e46 --- /dev/null +++ b/docker-build.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +imageTag="$1" + +if [ -z "$imageTag" ]; then + version=(`cat gradle.properties | grep "casmgmt.version" | cut -d= -f2`) + imageTag="v$version" +fi + +echo "Building CAS Management docker image tagged as [$imageTag]" +# read -p "Press [Enter] to continue..." any_key; + +docker build --tag="apereo/cas-management:$imageTag" . \ + && echo "Built CAS Management image successfully tagged as apereo/cas-management:$imageTag" \ + && docker images "apereo/cas-management:$imageTag" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b6a66b5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +version: '3' +services: + mgmt: + build: . + ports: + - "8443:8443" + - "8080:8080" diff --git a/docker-push.sh b/docker-push.sh new file mode 100644 index 0000000..b068863 --- /dev/null +++ b/docker-push.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +docker_user="$1" +docker_psw="$2" +echo "$docker_psw" | docker login --username "$docker_user" --password-stdin + +imageTag="$3" +if [ -z "$imageTag" ]; then + version=(`cat gradle.properties | grep "casmgmt.version" | cut -d= -f2`) + imageTag="v$version" +fi + +echo "Pushing CAS Management docker image tagged as $imageTag to apereo/cas-management..." +docker push apereo/cas-management:"$imageTag" \ + && echo "Pushed apereo/cas-management:$imageTag successfully."; diff --git a/docker-run.sh b/docker-run.sh new file mode 100644 index 0000000..5a55e2f --- /dev/null +++ b/docker-run.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +docker stop cas > /dev/null 2>&1 +docker rm cas > /dev/null 2>&1 +image_tag=(`cat gradle.properties | grep "casmgmt.version" | cut -d= -f2`) +docker run -d -p 8080:8080 -p 8443:8443 --name="cas-management" apereo/cas-management:"v${image_tag}" +docker logs -f cas-management diff --git a/etc/cas/.ignore b/etc/cas/.ignore new file mode 100644 index 0000000..e69de29 diff --git a/etc/cas/config/log4j2-management.xml b/etc/cas/config/log4j2-management.xml new file mode 100644 index 0000000..cc44a93 --- /dev/null +++ b/etc/cas/config/log4j2-management.xml @@ -0,0 +1,43 @@ + + + + + + /etc/cas/logs + + info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/etc/cas/config/management.properties b/etc/cas/config/management.properties new file mode 100644 index 0000000..9aaefe9 --- /dev/null +++ b/etc/cas/config/management.properties @@ -0,0 +1,7 @@ +cas.server.name=https://casserver.herokuapp.com +cas.server.prefix=${cas.server.name}/cas + +mgmt.server-name=https://localhost:8443 +mgmt.user-properties-file=file:/etc/cas/config/users.json + +logging.config=file:/etc/cas/config/log4j2-management.xml diff --git a/etc/cas/config/users.json b/etc/cas/config/users.json new file mode 100644 index 0000000..e57ace9 --- /dev/null +++ b/etc/cas/config/users.json @@ -0,0 +1,6 @@ +{ + "casuser" : { + "@class" : "org.apereo.cas.mgmt.authz.json.UserAuthorizationDefinition", + "roles" : [ "ROLE_ADMIN" ] + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..a831b90 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,72 @@ +version=7.0.0-SNAPSHOT +# CAS Management version +casmgmt.version=7.0.0-SNAPSHOT + +# This is the CAS server version that is compatible +# with the build/version of the CAS management web application. +cas.version=7.0.0 + + +springBootVersion=3.2.1 + +# The version of this overlay project +group=org.apereo.cas +artifactId=cas-management-overlay +sourceCompatibility=21 +targetCompatibility=21 + +gradleFreeFairPluginVersion=8.6 +gradleDependencyManagementPluginVersion=1.1.5 + +# Used to build docker images +jibVersion=3.4.0 +gradleDockerPluginVersion=9.3.1 + +# Specify the coordinates of the container image to build via jib +containerImageOrg=apereo +containerImageName=cas-management + +baseDockerImage=azul/zulu-openjdk:21 +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 + +# If you are using an embedded Apache Tomcat container to deploy and run CAS, +# and need to override the Apache Tomcat version, uncomment the property below +# and specify the the Apache Tomcat version, i.e. 10.1.24. +# While enabled, this will override any and all upstream changes to +# Apache Tomcat dependency management and you will be directly responsible to make +# adjustments and upgrades as necessary. Use with caution, favor less work. +# tomcatVersion=10.1.24 + +# 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 + + +# 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/gradle/springboot.gradle b/gradle/springboot.gradle new file mode 100644 index 0000000..e992004 --- /dev/null +++ b/gradle/springboot.gradle @@ -0,0 +1,56 @@ + +springBoot { + + mainClass = "org.apereo.cas.mgmt.web.CasManagementWebApplication" +} + + bootWar { + def executable = project.hasProperty("executable") && Boolean.valueOf(project.getProperty("executable")) + if (executable) { + logger.info "Including launch script for executable WAR artifact" + launchScript() + } else { + logger.info "WAR artifact is not marked as an executable" + } + + archiveFileName = "cas-management.war" + archiveBaseName = "cas-management" + + 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-mgmt-webapp${project.appServer}:${project.'casmgmt.version'}@war" + + + provided = false + + def excludeArtifacts = ["WEB-INF/lib/servlet-api-2*.jar"] + if (project.hasProperty("tomcatVersion")) { + excludes += ["WEB-INF/lib/tomcat-*.jar"] + } + excludes = excludeArtifacts + + /* + excludes = ["WEB-INF/lib/somejar-1.0*"] + enableCompilation = true + includes = ["*.xyz"] + targetPath = "sub-path/bar" + skip = false + */ + } + } + } diff --git a/gradle/tasks.gradle b/gradle/tasks.gradle new file mode 100644 index 0000000..488a1ac --- /dev/null +++ b/gradle/tasks.gradle @@ -0,0 +1,186 @@ + +task run(group: "build", description: "Run the CAS web application in embedded container mode") { + dependsOn 'build' + doLast { + def casRunArgs = Arrays.asList("-server -noverify -Xmx2048M -XX:+TieredCompilation -XX:TieredStopAtLevel=1".split(" ")) + project.javaexec { + jvmArgs = casRunArgs + classpath = project.files("build/libs/cas-management.war") + systemProperties = System.properties + logger.info "Started ${commandLine}" + } + } +} + +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: "CAS", description: "Run the CAS web application in standalone executable mode") { + dependsOn setExecutable, 'build' + doFirst { + workingDir "." + if (!Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine "chmod", "+x", bootWar.archivePath + } + logger.info "Running ${bootWar.archivePath}" + commandLine bootWar.archivePath + } +} + + +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(" ")) + project.javaexec { + jvmArgs = casArgs + debug = true + classpath = project.files("build/libs/cas-management.war") + systemProperties = System.properties + logger.info "Started ${commandLine}" + } + } +} + +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 } + } +} + +task allDependenciesInsight(group: "build", type: DependencyInsightReportTask, description: "Produce insight information for all dependencies") {} + +task allDependencies(group: "build", type: DependencyReportTask, description: "Display a graph of all project dependencies") {} + +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 ${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" + out.withStyle(Style.SuccessHeader).println "- Docs: " + out.withStyle(Style.Success).println "https://apereo.github.io/cas" + out.withStyle(Style.SuccessHeader).println "- Blog: " + out.withStyle(Style.Success).println "https://apereo.github.io" + println "******************************************************************" + } else { + println project.version + } + } +} + +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 { + 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" + + 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 { + workingDir "." + commandLine "keytool", "-genkeypair", "-alias", "cas", + "-keyalg", "RSA", + "-keypass", "changeit", "-storepass", "changeit", + "-keystore", keystorePath, + "-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", serverCert + } + logger.info "Import $serverCert into your Java truststore (\$JAVA_HOME/lib/security/cacerts)" + } +} + +task unzipWAR(type: Copy, group: "CAS", description: "Explodes the CAS web application archive") { + dependsOn 'build' + def destination = "${buildDir}/app" + + from zipTree("build/libs/cas-management.war") + into "${destination}" + doLast { + println "Unzipped WAR into ${destination}" + } +} + +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 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) +} + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3499ded --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# 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. +# You may obtain a copy of the License at +# +# https://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. +# + +############################################################################## +# +# 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 +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 + +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$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 + +# 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. + +# 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" ) + + 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/gradlew.bat b/gradlew.bat new file mode 100755 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..f562841 --- /dev/null +++ b/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/settings.gradle b/settings.gradle new file mode 100644 index 0000000..74901f4 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'cas' diff --git a/src/main/java/org/apereo/cas/config/CasOverlayOverrideConfiguration.java b/src/main/java/org/apereo/cas/config/CasOverlayOverrideConfiguration.java new file mode 100644 index 0000000..eb7ec10 --- /dev/null +++ b/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/src/main/jib/docker/entrypoint.sh b/src/main/jib/docker/entrypoint.sh new file mode 100755 index 0000000..0a10563 --- /dev/null +++ b/src/main/jib/docker/entrypoint.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +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} + +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 + +if [ $ENTRYPOINT_DEBUG = "true" ]; then + JVM_EXTRA_OPTS="${JVM_EXTRA_OPTS} -Ddebug=true" + + echo "\nChecking java..." + java -version + + 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 "\nRunning CAS @ cas-management.war" +# shellcheck disable=SC2086 +exec java $JVM_EXTRA_OPTS $JVM_MEM_OPTS -jar cas-management.war "$@" diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..3bc670c --- /dev/null +++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.apereo.cas.config.CasOverlayOverrideConfiguration diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..cebe337 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ +# Application properties that need to be +# embedded within the web application can be included here + diff --git a/system.properties b/system.properties new file mode 100644 index 0000000..5a9b50d --- /dev/null +++ b/system.properties @@ -0,0 +1 @@ +java.runtime.version=21