about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlukegb <lukegb@tvl.fyi>2020-07-05T19·21+0000
committerlukegb <lukegb@tvl.fyi>2020-07-05T19·21+0000
commit4b9fde0abe01bb09e0c30402ce89ff02bc0afb49 (patch)
treea43500d29b629314fc12f514c6b6714a214fb20b
parent8f4dea27a6561a30e4044d532a76e716c6cbf75b (diff)
parentc22796eb2a4c9e9f04dcc098c7705a6ddfce81a5 (diff)
Merge "merge(3p/apereo-cas-overlay): Merge commit '2e185f50c38db1a85f39a6bd5ad4c4e58462f003' as 'third_party/apereo-cas/overlay'" into canon r/1218
-rw-r--r--third_party/apereo-cas/overlay/.dockerignore7
-rw-r--r--third_party/apereo-cas/overlay/.gitattributes6
-rw-r--r--third_party/apereo-cas/overlay/.github/FUNDING.yml1
-rw-r--r--third_party/apereo-cas/overlay/.github/renovate.json11
-rwxr-xr-xthird_party/apereo-cas/overlay/.gitignore22
-rw-r--r--third_party/apereo-cas/overlay/.mergify.yml32
-rw-r--r--third_party/apereo-cas/overlay/.travis.yml62
-rw-r--r--third_party/apereo-cas/overlay/Dockerfile40
-rw-r--r--third_party/apereo-cas/overlay/LICENSE.txt202
-rw-r--r--third_party/apereo-cas/overlay/README.md146
-rw-r--r--third_party/apereo-cas/overlay/build.gradle111
-rwxr-xr-xthird_party/apereo-cas/overlay/docker-build.sh10
-rw-r--r--third_party/apereo-cas/overlay/docker-compose.yml7
-rwxr-xr-xthird_party/apereo-cas/overlay/docker-push.sh12
-rwxr-xr-xthird_party/apereo-cas/overlay/docker-run.sh7
-rw-r--r--third_party/apereo-cas/overlay/etc/cas/config/cas.properties6
-rw-r--r--third_party/apereo-cas/overlay/etc/cas/config/log4j2.xml119
-rw-r--r--third_party/apereo-cas/overlay/etc/cas/saml/.gitkeep1
-rw-r--r--third_party/apereo-cas/overlay/etc/cas/services/.donotdel0
-rw-r--r--third_party/apereo-cas/overlay/etc/cas/thekeystorebin0 -> 2266 bytes
-rw-r--r--third_party/apereo-cas/overlay/gradle.properties28
-rw-r--r--third_party/apereo-cas/overlay/gradle/dockerjib.gradle52
-rw-r--r--third_party/apereo-cas/overlay/gradle/springboot.gradle101
-rw-r--r--third_party/apereo-cas/overlay/gradle/tasks.gradle349
-rw-r--r--third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.jarbin0 -> 58910 bytes
-rw-r--r--third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xthird_party/apereo-cas/overlay/gradlew185
-rw-r--r--third_party/apereo-cas/overlay/gradlew.bat104
-rw-r--r--third_party/apereo-cas/overlay/lombok.config2
-rw-r--r--third_party/apereo-cas/overlay/settings.gradle1
-rwxr-xr-xthird_party/apereo-cas/overlay/src/main/jib/docker/entrypoint.sh22
-rw-r--r--third_party/apereo-cas/overlay/src/main/webapp/WEB-INF/web.xml7
32 files changed, 1658 insertions, 0 deletions
diff --git a/third_party/apereo-cas/overlay/.dockerignore b/third_party/apereo-cas/overlay/.dockerignore
new file mode 100644
index 0000000000..5e04587dd0
--- /dev/null
+++ b/third_party/apereo-cas/overlay/.dockerignore
@@ -0,0 +1,7 @@
+target/**
+build/**
+bin/**
+.idea/**
+.history/**
+.github/**
+.git/**
diff --git a/third_party/apereo-cas/overlay/.gitattributes b/third_party/apereo-cas/overlay/.gitattributes
new file mode 100644
index 0000000000..8fc5677e1d
--- /dev/null
+++ b/third_party/apereo-cas/overlay/.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/third_party/apereo-cas/overlay/.github/FUNDING.yml b/third_party/apereo-cas/overlay/.github/FUNDING.yml
new file mode 100644
index 0000000000..30b54fb734
--- /dev/null
+++ b/third_party/apereo-cas/overlay/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: ['https://www.apereo.org/content/apereo-membership']
diff --git a/third_party/apereo-cas/overlay/.github/renovate.json b/third_party/apereo-cas/overlay/.github/renovate.json
new file mode 100644
index 0000000000..58fa77f03c
--- /dev/null
+++ b/third_party/apereo-cas/overlay/.github/renovate.json
@@ -0,0 +1,11 @@
+{
+  "extends": [
+    "config:base",
+    ":preserveSemverRanges",
+    ":rebaseStalePrs",
+    ":disableRateLimiting",
+    ":semanticCommits",
+    ":semanticCommitTypeAll(renovatebot)"
+  ],
+  "labels": ["dependencies", "bot"]
+}
diff --git a/third_party/apereo-cas/overlay/.gitignore b/third_party/apereo-cas/overlay/.gitignore
new file mode 100755
index 0000000000..45840be342
--- /dev/null
+++ b/third_party/apereo-cas/overlay/.gitignore
@@ -0,0 +1,22 @@
+.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
diff --git a/third_party/apereo-cas/overlay/.mergify.yml b/third_party/apereo-cas/overlay/.mergify.yml
new file mode 100644
index 0000000000..4fcbdbe4ac
--- /dev/null
+++ b/third_party/apereo-cas/overlay/.mergify.yml
@@ -0,0 +1,32 @@
+#
+# 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/third_party/apereo-cas/overlay/.travis.yml b/third_party/apereo-cas/overlay/.travis.yml
new file mode 100644
index 0000000000..8347dd1719
--- /dev/null
+++ b/third_party/apereo-cas/overlay/.travis.yml
@@ -0,0 +1,62 @@
+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/third_party/apereo-cas/overlay/Dockerfile b/third_party/apereo-cas/overlay/Dockerfile
new file mode 100644
index 0000000000..17ab5648be
--- /dev/null
+++ b/third_party/apereo-cas/overlay/Dockerfile
@@ -0,0 +1,40 @@
+FROM adoptopenjdk/openjdk11:alpine-slim 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/
+
+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 --parallel --no-daemon;
+
+FROM adoptopenjdk/openjdk11:alpine-jre 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 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
+
+ENV PATH $PATH:$JAVA_HOME/bin:.
+
+WORKDIR cas-overlay
+ENTRYPOINT ["java", "-server", "-noverify", "-Xmx2048M", "-jar", "cas.war"]
diff --git a/third_party/apereo-cas/overlay/LICENSE.txt b/third_party/apereo-cas/overlay/LICENSE.txt
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/third_party/apereo-cas/overlay/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/third_party/apereo-cas/overlay/README.md b/third_party/apereo-cas/overlay/README.md
new file mode 100644
index 0000000000..ec8d964836
--- /dev/null
+++ b/third_party/apereo-cas/overlay/README.md
@@ -0,0 +1,146 @@
+CAS Overlay Template [![Build Status](https://travis-ci.org/apereo/cas-overlay-template.svg?branch=master)](https://travis-ci.org/apereo/cas-overlay-template)
+=======================
+
+Generic CAS WAR overlay to exercise the latest versions of CAS. This overlay could be freely used as a starting template for local CAS war overlays.
+
+# Versions
+
+- CAS `6.2.x`
+- JDK `11`
+
+# Overview
+
+To build the project, use:
+
+```bash
+# Use --refresh-dependencies to force-update SNAPSHOT versions
+./gradlew[.bat] clean build
+```
+
+To see what commands are available to the build script, run:
+
+```bash
+./gradlew[.bat] tasks
+```
+
+To launch into the CAS command-line shell:
+
+```bash
+./gradlew[.bat] downloadShell runShell
+```
+
+To fetch and overlay a CAS resource or view, use:
+
+```bash
+./gradlew[.bat] getResource -PresourceName=[resource-name]
+```
+
+To list all available CAS views and templates:
+
+```bash
+./gradlew[.bat] listTemplateViews
+```
+
+To unzip and explode the CAS web application file and the internal resources jar:
+
+```bash
+./gradlew[.bat] explodeWar
+```
+
+# Configuration
+
+- The `etc` directory contains the configuration files and directories that need to be copied to `/etc/cas/config`.
+
+```bash
+./gradlew[.bat] copyCasConfiguration
+```
+
+- The specifics of the build are controlled using the `gradle.properties` file.
+
+## Adding Modules
+
+CAS modules may be specified under the `dependencies` block of the [Gradle build script](build.gradle):
+
+```gradle
+dependencies {
+    compile "org.apereo.cas:cas-server-some-module:${project.casVersion}"
+    ...
+}
+```
+
+To collect the list of all project modules and dependencies:
+
+```bash
+./gradlew[.bat] allDependencies
+```
+
+### Clear Gradle Cache
+
+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.
+
+# Deployment
+
+- Create a keystore file `thekeystore` under `/etc/cas`. Use the password `changeit` for both the keystore and the key/certificate entries. This can either be done using the JDK's `keytool` utility or via the following command:
+
+```bash
+./gradlew[.bat] createKeystore
+```
+
+- Ensure the keystore is loaded up with keys and certificates of the server.
+
+On a successful deployment via the following methods, CAS will be available at:
+
+* `https://cas.server.name:8443/cas`
+
+## Executable WAR
+
+Run the CAS web application as an executable WAR:
+
+```bash
+./gradlew[.bat] run
+```
+
+Debug the CAS web application as an executable WAR:
+
+```bash
+./gradlew[.bat] debug
+```
+
+Run the CAS web application as a *standalone* executable WAR:
+
+```bash
+./gradlew[.bat] clean executable
+```
+
+## External
+
+Deploy the binary web application file `cas.war` 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
+./gradlew build jibDockerBuild
+```
+
+### Dockerfile
+
+You can also use the native Docker tooling and the provided `Dockerfile` to build and run CAS.
+
+```bash
+chmod +x *.sh
+./docker-build.sh
+./docker-run.sh
+```
diff --git a/third_party/apereo-cas/overlay/build.gradle b/third_party/apereo-cas/overlay/build.gradle
new file mode 100644
index 0000000000..79a7e7c1be
--- /dev/null
+++ b/third_party/apereo-cas/overlay/build.gradle
@@ -0,0 +1,111 @@
+buildscript {
+    repositories {
+        mavenLocal()
+        gradlePluginPortal()
+        mavenCentral()
+        jcenter()
+        maven { 
+            url "https://repo.spring.io/libs-milestone" 
+            mavenContent { releasesOnly() }
+        }
+        maven { 
+            url "https://repo.spring.io/libs-snapshot" 
+            mavenContent { snapshotsOnly() }
+        }
+        maven { 
+            url "https://plugins.gradle.org/m2/" 
+            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:lombok-plugin:${project.gradleLombokPluginVersion}"
+    }
+}
+
+repositories {
+    mavenLocal()
+    mavenCentral()
+    jcenter()
+    maven { 
+        url "https://oss.sonatype.org/content/repositories/snapshots" 
+        mavenContent { snapshotsOnly() }
+    }
+    maven { 
+        mavenContent { releasesOnly() }
+        url "https://build.shibboleth.net/nexus/content/repositories/releases/" 
+    }
+    maven { 
+        mavenContent { releasesOnly() }
+        url "https://repo.spring.io/milestone/" 
+    }
+    maven { 
+        url "https://repo.spring.io/snapshot/" 
+        mavenContent { snapshotsOnly() }
+    }
+    maven { 
+        mavenContent { snapshotsOnly() }
+        url "https://oss.jfrog.org/artifactory/oss-snapshot-local" 
+    }
+}
+
+def casServerVersion = project.'cas.version'
+def casWebApplicationBinaryName = "cas.war"
+
+project.ext."casServerVersion" = casServerVersion
+project.ext."casWebApplicationBinaryName" = casWebApplicationBinaryName
+
+apply plugin: "io.freefair.war-overlay"
+apply plugin: "io.freefair.lombok"
+apply from: rootProject.file("gradle/tasks.gradle")
+
+apply plugin: "war"
+apply plugin: "eclipse"
+apply plugin: "idea"
+
+apply from: rootProject.file("gradle/springboot.gradle")
+apply from: rootProject.file("gradle/dockerjib.gradle")
+
+dependencies {
+    // Other CAS dependencies/modules may be listed here...
+    // implementation "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}"
+}
+
+tasks.findByName("jibDockerBuild")
+    .dependsOn(copyWebAppIntoJib, copyConfigIntoJib)
+    .finalizedBy(deleteWebAppFromJib)
+
+tasks.findByName("jib")
+    .dependsOn(copyWebAppIntoJib, copyConfigIntoJib)
+    .finalizedBy(deleteWebAppFromJib)
+
+configurations.all {
+    resolutionStrategy {
+        cacheChangingModulesFor 0, "seconds"
+        cacheDynamicVersionsFor 0, "seconds"
+
+        preferProjectModules()
+
+        def failIfConflict = project.hasProperty("failOnVersionConflict") && Boolean.valueOf(project.getProperty("failOnVersionConflict"))
+        if (failIfConflict) {
+            failOnVersionConflict()
+        }
+    }
+}
+
+eclipse {
+    classpath {
+       downloadSources = true
+       downloadJavadoc = true
+    }
+}
+
+idea {
+    module {
+        downloadJavadoc = true
+        downloadSources = true
+    }
+}
diff --git a/third_party/apereo-cas/overlay/docker-build.sh b/third_party/apereo-cas/overlay/docker-build.sh
new file mode 100755
index 0000000000..dc098ade96
--- /dev/null
+++ b/third_party/apereo-cas/overlay/docker-build.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+image_tag=(`cat gradle.properties | grep "cas.version" | cut -d= -f2`)
+
+echo "Building CAS docker image tagged as [v$image_tag]"
+# read -p "Press [Enter] to continue..." any_key;
+
+docker build --tag="apereo/cas:v$image_tag" . \
+  && echo "Built CAS image successfully tagged as apereo/cas:v$image_tag" \
+  && docker images "apereo/cas:v$image_tag"
\ No newline at end of file
diff --git a/third_party/apereo-cas/overlay/docker-compose.yml b/third_party/apereo-cas/overlay/docker-compose.yml
new file mode 100644
index 0000000000..8f2e6ca7c9
--- /dev/null
+++ b/third_party/apereo-cas/overlay/docker-compose.yml
@@ -0,0 +1,7 @@
+version: '3'
+services:
+  cas:
+    build: .
+    ports:
+     - "8443:8443"
+     - "8080:8080"
\ No newline at end of file
diff --git a/third_party/apereo-cas/overlay/docker-push.sh b/third_party/apereo-cas/overlay/docker-push.sh
new file mode 100755
index 0000000000..ca7b784e6d
--- /dev/null
+++ b/third_party/apereo-cas/overlay/docker-push.sh
@@ -0,0 +1,12 @@
+#!/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 v$image_tag to apereo/cas..."
+docker push apereo/cas:"v$image_tag" \
+	&& echo "Pushed apereo/cas:v$image_tag successfully.";
\ No newline at end of file
diff --git a/third_party/apereo-cas/overlay/docker-run.sh b/third_party/apereo-cas/overlay/docker-run.sh
new file mode 100755
index 0000000000..6b4ce97559
--- /dev/null
+++ b/third_party/apereo-cas/overlay/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 "cas.version" | cut -d= -f2`)
+docker run -d -p 8080:8080 -p 8443:8443 --name="cas" apereo/cas:"v${image_tag}"
+docker logs -f cas
\ No newline at end of file
diff --git a/third_party/apereo-cas/overlay/etc/cas/config/cas.properties b/third_party/apereo-cas/overlay/etc/cas/config/cas.properties
new file mode 100644
index 0000000000..40cd89bbe9
--- /dev/null
+++ b/third_party/apereo-cas/overlay/etc/cas/config/cas.properties
@@ -0,0 +1,6 @@
+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/third_party/apereo-cas/overlay/etc/cas/config/log4j2.xml b/third_party/apereo-cas/overlay/etc/cas/config/log4j2.xml
new file mode 100644
index 0000000000..685dfab245
--- /dev/null
+++ b/third_party/apereo-cas/overlay/etc/cas/config/log4j2.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Specify the refresh internal in seconds. -->
+<Configuration monitorInterval="5" packages="org.apereo.cas.logging">
+    <Properties>
+        <Property name="baseDir">/var/log</Property>
+
+        <Property name="cas.log.level">info</Property>
+        <Property name="spring.webflow.log.level">warn</Property>
+        <Property name="spring.security.log.level">info</Property>
+        <Property name="spring.cloud.log.level">warn</Property>
+        <Property name="spring.boot.admin.log.level">debug</Property>
+        <Property name="spring.web.log.level">warn</Property>
+        <Property name="spring.boot.log.level">warn</Property>
+        <Property name="ldap.log.level">warn</Property>
+        <Property name="pac4j.log.level">warn</Property>
+        <Property name="opensaml.log.level">warn</Property>
+        <Property name="hazelcast.log.level">warn</Property>
+    </Properties>
+    <Appenders>
+        <Console name="console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%highlight{%d %p [%c] - &lt;%m&gt;}%n"/>
+        </Console>
+        <RollingFile name="file" fileName="${baseDir}/cas.log" append="true"
+                     filePattern="${baseDir}/cas-%d{yyyy-MM-dd-HH}-%i.log">
+            <PatternLayout pattern="%d %p [%c] - &lt;%m&gt;%n"/>
+            <Policies>
+                <OnStartupTriggeringPolicy />
+                <SizeBasedTriggeringPolicy size="10 MB"/>
+                <TimeBasedTriggeringPolicy />
+            </Policies>
+        </RollingFile>
+        <RollingFile name="auditlogfile" fileName="${baseDir}/cas_audit.log" append="true"
+                     filePattern="${baseDir}/cas_audit-%d{yyyy-MM-dd-HH}-%i.log">
+            <PatternLayout pattern="%d %p [%c] - %m%n"/>
+            <Policies>
+                <OnStartupTriggeringPolicy />
+                <SizeBasedTriggeringPolicy size="10 MB"/>
+                <TimeBasedTriggeringPolicy />
+            </Policies>
+        </RollingFile>
+
+        <CasAppender name="casAudit">
+            <AppenderRef ref="auditlogfile" />
+        </CasAppender>
+        <CasAppender name="casFile">
+            <AppenderRef ref="file" />
+        </CasAppender>
+        <CasAppender name="casConsole">
+            <AppenderRef ref="console" />
+        </CasAppender>
+    </Appenders>
+    <Loggers>
+        <!-- If adding a Logger with level set higher than warn, make category as selective as possible -->
+        <!-- Loggers inherit appenders from Root Logger unless additivity is false -->
+        <AsyncLogger name="org.apereo" level="${sys:cas.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.apereo.services.persondir" level="${sys:cas.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.apereo.cas.web.flow" level="${sys:cas.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.apereo.spring" level="${sys:cas.log.level}" includeLocation="true"/>
+
+        <AsyncLogger name="org.apache" level="warn" />
+        <AsyncLogger name="org.apache.http" level="error" />
+
+        <AsyncLogger name="org.springframework.boot" level="${sys:spring.boot.log.level" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.boot.context.embedded" level="info" includeLocation="true" />
+        <AsyncLogger name="org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration"
+        level="${sys:spring.security.log.level}" includeLocation="true" />
+        <AsyncLogger name="org.springframework.boot.autoconfigure.security" level="${sys:spring.security.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.boot.devtools" level="off" includeLocation="true"/>
+
+        <AsyncLogger name="org.springframework" level="warn" includeLocation="true" />
+        <AsyncLogger name="org.springframework.webflow" level="${sys:spring.webflow.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.aop" level="warn" includeLocation="true" />
+        <AsyncLogger name="org.springframework.web" level="warn" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.session" level="warn" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.scheduling" level="info" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.cloud.vault" level="warn" includeLocation="true" />
+        <AsyncLogger name="org.springframework.web.client" level="warn" includeLocation="true" />
+        <AsyncLogger name="org.springframework.security" level="${sys:spring.security.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.cloud" level="${sys:spring.cloud.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.amqp" level="error" />
+        <AsyncLogger name="org.springframework.integration" level="warn" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.messaging" level="warn" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.web" level="${sys:spring.web.log.level}" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.orm.jpa" level="warn" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.scheduling" level="warn" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.context.annotation" level="off" includeLocation="true"/>
+        <AsyncLogger name="org.springframework.web.socket" level="warn" includeLocation="true"/>
+
+        <AsyncLogger name="org.thymeleaf" level="warn" includeLocation="true"/>
+
+        <AsyncLogger name="org.pac4j" level="${sys:pac4j.log.level}" includeLocation="true"/>
+
+        <AsyncLogger name="org.opensaml" level="${sys:opensaml.log.level}" includeLocation="true"/>
+        <AsyncLogger name="PROTOCOL_MESSAGE" level="${sys:opensaml.log.level}" includeLocation="true" />
+
+        <AsyncLogger name="net.sf.ehcache" level="warn" includeLocation="true"/>
+        <AsyncLogger name="com.couchbase" level="warn" includeLocation="true"/>
+        <AsyncLogger name="de.codecentric" level="${sys:spring.boot.admin.log.level}" includeLocation="true"/>
+        <AsyncLogger name="net.jradius" level="warn" includeLocation="true" />
+        <AsyncLogger name="org.openid4java" level="warn" includeLocation="true" />
+        <AsyncLogger name="org.ldaptive" level="${sys:ldap.log.level}" includeLocation="true"/>
+        <AsyncLogger name="com.hazelcast" level="${sys:hazelcast.log.level}" includeLocation="true"/>
+
+        <!-- Log audit to all root appenders, and also to audit log (additivity is not false) -->
+        <AsyncLogger name="org.apereo.inspektr.audit.support" level="info" includeLocation="true" >
+            <AppenderRef ref="casAudit"/>
+        </AsyncLogger>
+
+        <!-- All Loggers inherit appenders specified here, unless additivity="false" on the Logger -->
+        <AsyncRoot level="warn">
+            <AppenderRef ref="casFile"/>
+            <!-- 
+                 For deployment to an application server running as service, 
+                 delete the casConsole appender below
+            -->
+            <AppenderRef ref="casConsole"/>
+        </AsyncRoot>
+    </Loggers>
+</Configuration>
diff --git a/third_party/apereo-cas/overlay/etc/cas/saml/.gitkeep b/third_party/apereo-cas/overlay/etc/cas/saml/.gitkeep
new file mode 100644
index 0000000000..882c99944d
--- /dev/null
+++ b/third_party/apereo-cas/overlay/etc/cas/saml/.gitkeep
@@ -0,0 +1 @@
+This directory is references in the Dockerfile so it needs to be here. 
\ No newline at end of file
diff --git a/third_party/apereo-cas/overlay/etc/cas/services/.donotdel b/third_party/apereo-cas/overlay/etc/cas/services/.donotdel
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/apereo-cas/overlay/etc/cas/services/.donotdel
diff --git a/third_party/apereo-cas/overlay/etc/cas/thekeystore b/third_party/apereo-cas/overlay/etc/cas/thekeystore
new file mode 100644
index 0000000000..78f49baf74
--- /dev/null
+++ b/third_party/apereo-cas/overlay/etc/cas/thekeystore
Binary files differdiff --git a/third_party/apereo-cas/overlay/gradle.properties b/third_party/apereo-cas/overlay/gradle.properties
new file mode 100644
index 0000000000..f49d921e1e
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradle.properties
@@ -0,0 +1,28 @@
+# Versions
+cas.version=6.2.0
+springBootVersion=2.2.8.RELEASE
+
+# Use -jetty, -undertow to other containers
+# Or blank if you want to deploy to an external container
+appServer=-tomcat
+executable=false
+
+tomcatVersion=9.0.36
+
+group=org.apereo.cas
+sourceCompatibility=11
+targetCompatibility=11
+
+jibVersion=2.4.0
+
+# Location of the downloaded CAS shell JAR
+shellDir=build/libs
+ivyVersion=2.4.0
+gradleDownloadTaskVersion=3.4.3
+gradleMavenPluginVersion=4.1.5
+gradleLombokPluginVersion=4.1.5
+
+# 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
+allowInsecureRegistries=false
diff --git a/third_party/apereo-cas/overlay/gradle/dockerjib.gradle b/third_party/apereo-cas/overlay/gradle/dockerjib.gradle
new file mode 100644
index 0000000000..dbadd5aa19
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradle/dockerjib.gradle
@@ -0,0 +1,52 @@
+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 = ["v" + casServerVersion]
+    }
+    container {
+        creationTime = "USE_CURRENT_TIMESTAMP"
+        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/third_party/apereo-cas/overlay/gradle/springboot.gradle b/third_party/apereo-cas/overlay/gradle/springboot.gradle
new file mode 100644
index 0000000000..79804b18e4
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradle/springboot.gradle
@@ -0,0 +1,101 @@
+apply plugin: "org.springframework.boot"
+
+configurations {
+    bootRunConfig.extendsFrom compileClasspath
+}
+
+dependencies {
+    bootRunConfig "org.apereo.cas:cas-server-webapp-init:${casServerVersion}"
+    bootRunConfig "org.apereo.cas:cas-server-webapp-tomcat:${casServerVersion}"
+    bootRunConfig "org.springframework.boot:spring-boot-devtools:${project.springBootVersion}"
+}
+
+sourceSets {
+    bootRunSources {
+        resources {
+            srcDirs new File("//etc/cas/templates/"),
+                    new File("${project.getProjectDir()}/src/main/resources/")
+        }
+    }
+}
+
+bootRun {
+    classpath = configurations.bootRunConfig + sourceSets.main.compileClasspath + sourceSets.main.runtimeClasspath
+    doFirst {
+        sourceResources sourceSets.bootRunSources
+        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("-XX:+UnlockExperimentalVMOptions")
+    list.add("-XX:+EnableJVMCI")
+    list.add("-XX:+UseJVMCICompiler")
+
+    list.add("-Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=n")
+
+    jvmArgs = list
+
+    def appArgList = []
+    args = appArgList
+}
+
+springBoot {
+    buildInfo()
+    mainClassName = "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"
+        launchScript()
+    } else {
+        logger.info "WAR artifact is not marked as an executable"
+    }
+    archiveName "${casWebApplicationBinaryName}"
+    baseName "cas"
+    excludeDevtools = false
+
+    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}:${casServerVersion}@war"
+            provided = false
+            /*
+            excludes = ["WEB-INF/lib/somejar-1.0*"]
+            enableCompilation = true
+            includes = ["*.xyz"]
+            targetPath = "sub-path/bar"
+            skip = false
+            */
+        }
+    }
+}
diff --git a/third_party/apereo-cas/overlay/gradle/tasks.gradle b/third_party/apereo-cas/overlay/gradle/tasks.gradle
new file mode 100644
index 0000000000..40fdac0b15
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradle/tasks.gradle
@@ -0,0 +1,349 @@
+import org.apache.ivy.util.url.ApacheURLLister
+import org.apache.tools.ant.taskdefs.condition.Os
+import org.gradle.internal.logging.text.StyledTextOutputFactory
+
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.nio.file.StandardCopyOption
+
+import static org.gradle.internal.logging.text.StyledTextOutput.Style
+
+buildscript {
+    repositories {
+        mavenLocal()
+        mavenCentral()
+        jcenter()
+        maven {
+            url "https://oss.sonatype.org/content/repositories/snapshots"
+            mavenContent { snapshotsOnly() }
+        }
+    }
+    
+    dependencies {
+        classpath "org.apache.ivy:ivy:${project.ivyVersion}"
+        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'}"
+    }
+}
+
+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 resourcesJarName = "cas-server-webapp-resources"
+def templateViewsJarName = "cas-server-support-thymeleaf"
+
+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()
+    }
+}
+
+task explodeWarOnly(type: Copy, group: "CAS", description: "Explodes the CAS web application archive") {
+    dependsOn 'build'
+    from zipTree("build/libs/${casWebApplicationBinaryName}")
+    into explodedDir
+    doLast {
+        println "Exploded WAR into ${explodedDir}"
+    }
+}
+
+task explodeWar(type: Copy, group: "CAS", description: "Explodes the CAS archive and resources jar from the CAS web application archive") {
+    dependsOn explodeWarOnly
+    from zipTree("${explodedDir}/WEB-INF/lib/${templateViewsJarName}-${casServerVersion}.jar")
+    into explodedResourcesDir
+
+    from zipTree("${explodedDir}/WEB-INF/lib/${resourcesJarName}-${casServerVersion}.jar")
+    into explodedResourcesDir
+    
+    doLast {
+        println "Exploded WAR resources into ${explodedResourcesDir}"
+    }
+}
+
+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(" "))
+        javaexec {
+            main = "-jar"
+            jvmArgs = casRunArgs
+            args = ["build/libs/${casWebApplicationBinaryName}"]
+            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(" "))
+        javaexec {
+            main = "-jar"
+            jvmArgs = casArgs
+            debug = true
+            args = ["build/libs/${casWebApplicationBinaryName}"]
+            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: "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 $casServerVersion"
+            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 casServerVersion
+        }
+    }
+}
+
+task createKeystore(group: "CAS", description: "Create CAS keystore") {
+    doFirst {
+        mkdir "/etc/cas"
+
+        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")
+        }
+        // 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}"
+        }
+        logger.info "Exporting cert from keystore..."
+        exec {
+            workingDir "."
+            commandLine "keytool", "-exportcert", "-alias", "cas",
+                    "-storepass", "changeit", "-keystore", keystorePath,
+                    "-file", "/etc/cas/cas.cer"
+        }
+        logger.info "Import /etc/cas/cas.cer into your Java truststore (JAVA_HOME/lib/security/cacerts)"
+    }
+}
+
+task listTemplateViews(group: "CAS", description: "List all CAS views") {
+    dependsOn explodeWar
+
+    doFirst {
+        fileTree(explodedResourcesDir).matching {
+            include "**/*.html"
+        }
+        .collect { 
+            return it.path.replace(explodedResourcesDir, "")
+        }
+        .toSorted()
+        .each { println it }
+    }
+}
+
+task exportConfigMetadata(group: "CAS", description: "Export collection of CAS properties") {
+    doLast {
+        def file = new File(project.rootDir, 'config-metadata.properties')
+        file.withWriter('utf-8') { writer ->
+            def metadataRepository = new org.apereo.cas.metadata.CasConfigurationMetadataRepository()
+            def repository = metadataRepository.repository;
+            repository.allGroups
+                    .values()
+                    .sort { o1, o2 -> o1.id <=> o2.id }
+                    .each({ group ->
+                        def groupProperties = group.properties
+                        if (!groupProperties.isEmpty()) {
+                            def groupId = group.id.equalsIgnoreCase("_ROOT_GROUP_") ? "" : group.id + "."
+
+                            writer.writeLine("# Group ${group.id}");
+                            writer.writeLine("# ====================")
+                            groupProperties
+                                    .values()
+                                    .sort { o1, o2 -> o1.id <=> o2.id }
+                                    .each({ property ->
+                                        def description = property.shortDescription
+                                        if (!property.shortDescription?.equalsIgnoreCase(property.description) && property.description != null) {
+                                            description = property.description.replace('\n', '#')
+                                        }
+                                        writer.writeLine("# ${description}");
+                                        writer.writeLine("# Type: ${property.type}");
+                                        if (property.deprecated) {
+                                            def deprecation = property.deprecation
+                                            writer.writeLine("# This setting is deprecated with a severity level of ${deprecation.level}.")
+                                            if (deprecation.shortReason != null) {
+                                                writer.writeLine("# because ${deprecation.shortReason}")
+                                            }
+                                            if (deprecation.replacement != null) {
+                                                writer.writeLine("# Replace with: ${deprecation.replacement}")
+                                            }
+                                        }
+                                        property.hints.valueHints.each {
+                                            if (it.value instanceof Object[]) {
+                                                if (it.value[0].toString().contains("RequiresModule")) {
+                                                    writer.writeLine("# Required module: org.apereo.cas:${it.description}")
+                                                    writer.writeLine("# Automatically included/available: ${it.value[1]}")
+                                                }
+                                            }
+                                            if (it.value.toString().contains("RequiredProperty")) {
+                                                writer.writeLine("# Note: This setting is required!")
+                                            }
+                                        }
+                                        writer.writeLine("${groupId}${property.name}=${property.defaultValue}")
+                                        writer.writeLine("")
+                                    });
+                        }
+                    });
+        }
+        println "Configuration metadata is available at ${file.absolutePath}"
+    }
+}
+
+task getResource(group: "CAS", description: "Fetch a CAS resource and move it into the overlay") {
+    dependsOn explodeWar
+
+    doFirst {
+        def resourceName = project.getProperty("resourceName")
+
+        def results = fileTree(explodedResourcesDir).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}:\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
+
+        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)
+        println "Copied file ${resourceFile} to ${toResourceFile}"
+    }
+}
+
+def isRunningCasServerSnapshot(casServerVersion) {
+    return "${casServerVersion}".contains("-SNAPSHOT")
+}
+
+task verifyRequiredJavaVersion {
+    logger.info "Checking current Java version ${JavaVersion.current()} for required Java version ${project.targetCompatibility}"
+    if (!JavaVersion.current().name.equalsIgnoreCase("${project.targetCompatibility}")) {
+        throw new GradleException("Current Java version ${JavaVersion.current()} does not match required Java version ${project.targetCompatibility}")
+    }
+}
diff --git a/third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.jar b/third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..62d4c05355
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.jar
Binary files differdiff --git a/third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.properties b/third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..4c5803d13c
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/third_party/apereo-cas/overlay/gradlew b/third_party/apereo-cas/overlay/gradlew
new file mode 100755
index 0000000000..fbd7c51583
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      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 UN*X
+##
+##############################################################################
+
+# 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"`
+
+# 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 "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# 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
+    ;;
+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" = "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=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# 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, 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"
+
+exec "$JAVACMD" "$@"
diff --git a/third_party/apereo-cas/overlay/gradlew.bat b/third_party/apereo-cas/overlay/gradlew.bat
new file mode 100644
index 0000000000..5093609d51
--- /dev/null
+++ b/third_party/apereo-cas/overlay/gradlew.bat
@@ -0,0 +1,104 @@
+@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=.
+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%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling 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%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/third_party/apereo-cas/overlay/lombok.config b/third_party/apereo-cas/overlay/lombok.config
new file mode 100644
index 0000000000..6aa51d71ec
--- /dev/null
+++ b/third_party/apereo-cas/overlay/lombok.config
@@ -0,0 +1,2 @@
+# This file is generated by the 'io.freefair.lombok' Gradle plugin
+config.stopBubbling = true
diff --git a/third_party/apereo-cas/overlay/settings.gradle b/third_party/apereo-cas/overlay/settings.gradle
new file mode 100644
index 0000000000..3ad50900ea
--- /dev/null
+++ b/third_party/apereo-cas/overlay/settings.gradle
@@ -0,0 +1 @@
+rootProject.name='cas'
\ No newline at end of file
diff --git a/third_party/apereo-cas/overlay/src/main/jib/docker/entrypoint.sh b/third_party/apereo-cas/overlay/src/main/jib/docker/entrypoint.sh
new file mode 100755
index 0000000000..a3a0895b04
--- /dev/null
+++ b/third_party/apereo-cas/overlay/src/main/jib/docker/entrypoint.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+#echo -e "\nChecking java..."
+#java -version
+
+#echo -e "\nCreating CAS configuration directories..."
+mkdir -p /etc/cas/config
+mkdir -p /etc/cas/services
+
+#echo "Listing provided CAS docker artifacts..."
+#ls -R docker/cas
+
+#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
+
+#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
diff --git a/third_party/apereo-cas/overlay/src/main/webapp/WEB-INF/web.xml b/third_party/apereo-cas/overlay/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..072a6a0fdb
--- /dev/null
+++ b/third_party/apereo-cas/overlay/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="3.1" 
+     metadata-complete="true"
+     xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
+     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+</web-app>
\ No newline at end of file