├── .java-version
├── .sdkmanrc
├── etc
└── cas
│ ├── .ignore
│ └── config
│ └── log4j2.xml
├── system.properties
├── .dockerignore
├── Procfile
├── helm
├── delete-cas-server.sh
├── install-cas-server.sh
├── install-cas-server-example.sh
├── cas-server
│ ├── templates
│ │ ├── casconfig-configmap.yaml
│ │ ├── serviceaccount.yaml
│ │ ├── role.yaml
│ │ ├── rolebinding.yaml
│ │ ├── service.yaml
│ │ ├── tests
│ │ │ └── test-cas-server.yaml
│ │ ├── script-configmap.yaml
│ │ ├── ingress.yaml
│ │ ├── NOTES.txt
│ │ ├── _helpers.tpl
│ │ └── statefulset.yaml
│ ├── .helmignore
│ ├── Chart.yaml
│ └── values.yaml
├── create-ingress-tls.sh
├── create-cas-server-keystore-secret.sh
├── create-truststore.sh
├── values-example1.yaml
└── README.md
├── docker-compose.yml
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── springboot.gradle
└── tasks.gradle
├── src
└── main
│ ├── resources
│ ├── application.yml
│ └── META-INF
│ │ └── spring
│ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
│ ├── java
│ └── org
│ │ └── apereo
│ │ └── cas
│ │ └── config
│ │ └── CasOverlayOverrideConfiguration.java
│ └── jib
│ └── docker
│ └── entrypoint.sh
├── puppeteer
├── package.json
├── scenarios
│ └── basic.js
└── run.sh
├── settings.gradle
├── .github
├── dependabot.yml
├── renovate.json
└── workflows
│ └── build.yml
├── .gitattributes
├── lombok.config
├── openrewrite.gradle
├── .gitignore
├── Dockerfile
├── gradlew.bat
├── gradle.properties
├── gradlew
├── LICENSE.txt
└── README.md
/.java-version:
--------------------------------------------------------------------------------
1 | 25.0
--------------------------------------------------------------------------------
/.sdkmanrc:
--------------------------------------------------------------------------------
1 | java=25
--------------------------------------------------------------------------------
/etc/cas/.ignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=25
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | target/**
2 | build/**
3 | bin/**
4 | .idea/**
5 | .history/**
6 | .github/**
7 | .git/**
8 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JAVA_OPTS -jar build/libs/cas.war --server.port=$PORT --server.ssl.enabled=false
2 |
--------------------------------------------------------------------------------
/helm/delete-cas-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | NAMESPACE=${1:-default}
3 | helm delete --namespace "${NAMESPACE}" cas-server
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | cas:
4 | build: .
5 | ports:
6 | - "8443:8443"
7 | - "8080:8080"
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apereo/cas-overlay-template/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/helm/install-cas-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | NAMESPACE=${1:-default}
3 |
4 | helm upgrade --install cas-server --namespace $NAMESPACE ./cas-server
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | # Application properties that need to be
2 | # embedded within the web application can be included here
3 |
4 |
--------------------------------------------------------------------------------
/puppeteer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "pino-pretty": "13.1.3",
4 | "pino": "10.1.0",
5 | "puppeteer": "24.33.1"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
--------------------------------------------------------------------------------
1 | org.apereo.cas.config.CasOverlayOverrideConfiguration
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "org.gradle.toolchains.foojay-resolver-convention" version "${gradleFoojayPluginVersion}"
3 | }
4 | rootProject.name = 'cas'
5 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gradle
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 |
--------------------------------------------------------------------------------
/helm/install-cas-server-example.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | NAMESPACE=${1:-default}
3 | EXAMPLE=${2:-example1}
4 |
5 | helm upgrade --install cas-server --values values-${EXAMPLE}.yaml --namespace ${NAMESPACE} ./cas-server
6 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set line endings to LF, even on Windows. Otherwise, execution within Docker fails.
2 | # See https://help.github.com/articles/dealing-with-line-endings/
3 | *.sh text eol=lf
4 | gradlew text eol=lf
5 | *.cmd text eol=crlf
6 | *.bat text eol=crlf
7 |
--------------------------------------------------------------------------------
/lombok.config:
--------------------------------------------------------------------------------
1 | lombok.log.fieldName = LOGGER
2 | lombok.log.fieldIsStatic=true
3 |
4 | lombok.toString.doNotUseGetters=true
5 | lombok.equalsAndHashCode.doNotUseGetters=true
6 |
7 | lombok.addLombokGeneratedAnnotation = true
8 |
9 | config.stopBubbling=true
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base",
4 | ":preserveSemverRanges",
5 | ":rebaseStalePrs",
6 | ":disableRateLimiting",
7 | ":semanticCommits",
8 | ":semanticCommitTypeAll(renovatebot)"
9 | ],
10 | "labels": ["dependencies", "bot"]
11 | }
12 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/casconfig-configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: {{ include "cas-server.fullname" . }}-casconfig
5 | labels: {{- include "cas-server.labels" . | nindent 4 }}
6 | data:
7 | {{- include "cas-server.tplvalues.render" (dict "value" .Values.casServerContainer.casConfig "context" $) | nindent 2 }}
8 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create -}}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: {{ include "cas-server.serviceAccountName" . }}
6 | labels:
7 | {{- include "cas-server.labels" . | nindent 4 }}
8 | {{- with .Values.serviceAccount.annotations }}
9 | annotations:
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | {{- end }}
13 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/role.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.rbac.create -}}
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | name: {{ include "cas-server.fullname" . }}
6 | labels:
7 | {{- include "cas-server.labels" . | nindent 4 }}
8 | rules:
9 | - apiGroups: ["", "extensions", "apps"]
10 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
11 | verbs: ["get", "list", "watch"]
12 | {{- end -}}
13 |
--------------------------------------------------------------------------------
/helm/cas-server/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *.orig
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 | .vscode/
24 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/rolebinding.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.rbac.create -}}
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: RoleBinding
4 | metadata:
5 | name: {{ include "cas-server.fullname" . }}
6 | labels:
7 | {{- include "cas-server.labels" . | nindent 4 }}
8 | roleRef:
9 | apiGroup: rbac.authorization.k8s.io
10 | kind: Role
11 | name: {{ include "cas-server.fullname" . }}
12 | subjects:
13 | - kind: ServiceAccount
14 | name: {{ template "cas-server.serviceAccountName" . }}
15 | namespace: {{ .Release.Namespace }}
16 | {{ end }}
17 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ include "cas-server.fullname" . }}
5 | labels:
6 | {{- include "cas-server.labels" . | nindent 4 }}
7 | spec:
8 | type: {{ .Values.cas.service.type }}
9 | publishNotReadyAddresses: {{ .Values.cas.service.publishNotReadyAddresses }}
10 | ports:
11 | - port: {{ .Values.cas.service.port }}
12 | targetPort: https
13 | protocol: TCP
14 | name: https
15 | selector:
16 | {{- include "cas-server.selectorLabels" . | nindent 4 }}
17 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/tests/test-cas-server.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: "{{ include "cas-server.fullname" . }}-test"
5 | labels:
6 | {{- include "cas-server.labels" . | nindent 4 }}
7 | annotations:
8 | "helm.sh/hook": test-success
9 | spec:
10 | containers:
11 | - name: wget
12 | image: alpine
13 | command: ['wget']
14 | args: [ '--no-check-certificate', 'https://{{ include "cas-server.fullname" . }}:{{ .Values.cas.service.port }}{{ .Values.casServerContainer.defaultStatusUrl }}' ]
15 | restartPolicy: Never
16 |
17 |
--------------------------------------------------------------------------------
/openrewrite.gradle:
--------------------------------------------------------------------------------
1 | initscript {
2 | repositories {
3 | gradlePluginPortal()
4 | }
5 | dependencies {
6 | classpath "org.openrewrite:plugin:7.20.0"
7 | }
8 | }
9 |
10 | rootProject {
11 | plugins.apply(org.openrewrite.gradle.RewritePlugin)
12 | dependencies {
13 | rewrite("org.apereo.cas:cas-server-support-openrewrite:${project.targetVersion}") {
14 | transitive = false
15 | }
16 | }
17 | afterEvaluate {
18 | if (repositories.isEmpty()) {
19 | repositories {
20 | mavenLocal()
21 | mavenCentral()
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | ### STS ###
3 | .apt_generated
4 | .classpath
5 | .factorypath
6 | .project
7 | .settings
8 | .springBeans
9 | .sts4-cache
10 |
11 | ### IntelliJ IDEA ###
12 | .idea
13 | *.iws
14 | *.iml
15 | *.ipr
16 |
17 | ### NetBeans ###
18 | /nbproject/private/
19 | /nbbuild/
20 | /dist/
21 | /nbdist/
22 | /.nb-gradle/
23 |
24 | ### VS Code ###
25 | .vscode/
26 | .classpath
27 | !/.project
28 | .project
29 | .settings
30 | .history
31 | .vscode
32 | target/
33 | .idea/
34 | .DS_Store
35 | .idea
36 | overlays/
37 | .gradle/
38 | build/
39 | log/
40 | bin/
41 | *.war
42 | *.iml
43 | *.log
44 | tmp/
45 | ./apache-tomcat
46 | apache-tomcat.zip
47 | config-metadata.properties
48 | node-modules
49 | package-lock.json
--------------------------------------------------------------------------------
/helm/create-ingress-tls.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | NAMESPACE=${1:-default}
3 | SUBJECT=/CN=cas.example.org/OU=Auth/O=example
4 | SAN=DNS:casadmin.example.org,DNS:cas.example.org
5 | SECRET_NAME=cas-server-ingress-tls
6 | KEY_FILE=cas-ingress.key
7 | CERT_FILE=cas-ingress.crt
8 |
9 | set -e
10 |
11 | # create certificate for external ingress
12 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
13 | -keyout "${KEY_FILE}" -out ${CERT_FILE} -subj "${SUBJECT}" \
14 | -addext "subjectAltName = $SAN"
15 |
16 | kubectl delete secret "${SECRET_NAME}" --namespace "${NAMESPACE}" || true
17 | # create tls secret with key and cert
18 | kubectl create secret tls "${SECRET_NAME}" --namespace "${NAMESPACE}" --key "${KEY_FILE}" --cert "${CERT_FILE}"
19 |
20 |
--------------------------------------------------------------------------------
/src/main/java/org/apereo/cas/config/CasOverlayOverrideConfiguration.java:
--------------------------------------------------------------------------------
1 | package org.apereo.cas.config;
2 |
3 | //import org.springframework.boot.context.properties.EnableConfigurationProperties;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Qualifier;
6 | import org.springframework.boot.autoconfigure.AutoConfiguration;
7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
8 | import org.springframework.context.ConfigurableApplicationContext;
9 | import org.springframework.context.annotation.Bean;
10 |
11 | //import org.apereo.cas.configuration.CasConfigurationProperties;
12 |
13 | @AutoConfiguration
14 | //@EnableConfigurationProperties(CasConfigurationProperties.class)
15 | public class CasOverlayOverrideConfiguration {
16 |
17 | /*
18 | @Bean
19 | public MyCustomBean myCustomBean() {
20 | ...
21 | }
22 | */
23 | }
24 |
--------------------------------------------------------------------------------
/helm/create-cas-server-keystore-secret.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script needs bash for pushd/popd
3 | set -e
4 | NAMESPACE=${1:-default}
5 | KEYSTORE=../etc/cas/thekeystore
6 |
7 | # it's important that the service names are supported in the cert used for tomcat in cas-server
8 | # keytool doesn't support wildcards which we really need to use here, e.g. *.cas-server.${NAMESPACE}.svc
9 | # java wasn't resolving using all available dns suffixes so had to use [namespace].svc
10 | SUBJECT=CN=cas.example.org,OU=Example,OU=Org,C=US
11 | SAN=dns:cas.example.org,dns:casadmin.example.org,dns:cas-server-0.cas-server.${NAMESPACE}.svc,dns:cas-server-1.cas-server.${NAMESPACE}.svc
12 |
13 | if [ ! -f "$KEYSTORE" ] ; then
14 | pushd ..
15 | ./gradlew --no-configuration-cache createKeyStore -PcertDir=./etc/cas -PcertificateDn="${SUBJECT}" -PcertificateSubAltName="${SAN}"
16 | popd
17 | fi
18 |
19 | kubectl delete secret cas-server-keystore --namespace "${NAMESPACE}" || true
20 | kubectl create secret generic cas-server-keystore --namespace "${NAMESPACE}" --from-file=thekeystore=$KEYSTORE
21 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | env:
4 | JAVA_OPTS: "-Xms512m -Xmx6048m -Xss128m -XX:ReservedCodeCacheSize=512m -XX:+UseG1GC"
5 | GRADLE_OPTS: "-Xms512m -Xmx6048m -Xss128m -XX:ReservedCodeCacheSize=512m -XX:+UseG1GC"
6 | TERM: xterm-256color
7 |
8 | on:
9 | push:
10 | branches: [ master ]
11 | pull_request:
12 | branches: [ master ]
13 |
14 | concurrency:
15 | group: "workflow = ${{ github.workflow }}, ref = ${{ github.event.ref }}"
16 | cancel-in-progress: ${{ github.event_name == 'push' }}
17 |
18 | jobs:
19 | build:
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | os: [ ubuntu-latest, macos-latest, windows-latest ]
24 | jdk: [ 25 ]
25 | runs-on: ${{ matrix.os }}
26 | steps:
27 | - uses: actions/checkout@v4
28 | - name: Set up JDK ${{ matrix.jdk }}
29 | uses: actions/setup-java@v4
30 | with:
31 | java-version: ${{ matrix.jdk }}
32 | distribution: 'corretto'
33 | - name: Build with JDK ${{ matrix.jdk }} on ${{ matrix.os }}
34 | run: ./gradlew build
35 |
36 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/script-configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: {{ include "cas-server.fullname" . }}-scripts
5 | labels: {{- include "cas-server.labels" . | nindent 4 }}
6 | data:
7 | entrypoint.sh: |-
8 | #!/bin/sh
9 | echo Working Directory: $(pwd)
10 | # Set debug options if required
11 | JAVA_DEBUG_ARGS=
12 | if [ "${JAVA_ENABLE_DEBUG}" == "true" ]; then
13 | JAVA_DEBUG_ARGS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=${JAVA_DEBUG_SUSPEND:-n},address=${JAVA_DEBUG_PORT:-5005}"
14 | echo "Run the following to forward local port to pod:"
15 | echo "kubectl port-forward $HOSTNAME ${JAVA_DEBUG_PORT:-5005}:${JAVA_DEBUG_PORT:-5005}"
16 | fi
17 | PROFILE_OPT=
18 | if [ ! -z $CAS_SPRING_PROFILES ]; then
19 | PROFILE_OPT="--spring.profiles.active=$CAS_SPRING_PROFILES"
20 | fi
21 | echo java -server -noverify $JAVA_DEBUG_ARGS $MAX_HEAP_OPT $NEW_HEAP_OPT $JVM_EXTRA_OPTS -jar $CAS_WAR $PROFILE_OPT $@
22 | exec java -server -noverify $JAVA_DEBUG_ARGS $MAX_HEAP_OPT $NEW_HEAP_OPT $JVM_EXTRA_OPTS -jar $CAS_WAR $PROFILE_OPT $@
23 |
--------------------------------------------------------------------------------
/src/main/jib/docker/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ENTRYPOINT_DEBUG=${ENTRYPOINT_DEBUG:-false}
4 | JVM_DEBUG=${JVM_DEBUG:-false}
5 | JVM_DEBUG_PORT=${JVM_DEBUG_PORT:-5000}
6 | JVM_DEBUG_SUSPEND=${JVM_DEBUG_SUSPEND:-n}
7 | JVM_MEM_OPTS=${JVM_MEM_OPTS:--Xms512m -Xmx4096M}
8 | JVM_EXTRA_OPTS=${JVM_EXTRA_OPTS:--server -noverify -XX:+TieredCompilation -XX:TieredStopAtLevel=1}
9 |
10 | if [ $JVM_DEBUG = "true" ]; then
11 | JVM_EXTRA_OPTS="${JVM_EXTRA_OPTS} -Xdebug -Xrunjdwp:transport=dt_socket,address=*:${JVM_DEBUG_PORT},server=y,suspend=${JVM_DEBUG_SUSPEND}"
12 | fi
13 |
14 | if [ $ENTRYPOINT_DEBUG = "true" ]; then
15 | JVM_EXTRA_OPTS="${JVM_EXTRA_OPTS} -Ddebug=true"
16 |
17 | echo "\nChecking java..."
18 | java -version
19 |
20 | if [ -d /etc/cas ] ; then
21 | echo "\nListing CAS configuration under /etc/cas..."
22 | ls -R /etc/cas
23 | fi
24 | echo "\nRemote debugger configured on port ${JVM_DEBUG_PORT} with suspend=${JVM_DEBUG_SUSPEND}: ${JVM_DEBUG}"
25 | echo "\nJava args: ${JVM_MEM_OPTS} ${JVM_EXTRA_OPTS}"
26 | fi
27 |
28 | echo "\nRunning CAS @ cas.war"
29 | # shellcheck disable=SC2086
30 | exec java $JVM_EXTRA_OPTS $JVM_MEM_OPTS -jar cas.war "$@"
31 |
--------------------------------------------------------------------------------
/helm/cas-server/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: cas-server
3 | description: A Helm chart for CAS SSO Server
4 | icon: "https://apereo.github.io/cas/images/cas_logo.png"
5 |
6 | # A chart can be either an 'application' or a 'library' chart.
7 | #
8 | # Application charts are a collection of templates that can be packaged into versioned archives
9 | # to be deployed.
10 | #
11 | # Library charts provide useful utilities or functions for the chart developer. They're included as
12 | # a dependency of application charts to inject those utilities and functions into the rendering
13 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
14 | type: application
15 |
16 | # This is the chart version. This version number should be incremented each time you make changes
17 | # to the chart and its templates, including the app version.
18 | # Versions are expected to follow Semantic Versioning (https://semver.org/)
19 | version: 0.1.0
20 |
21 | # This is the version number of the application being deployed. This version number should be
22 | # incremented each time you make changes to the application. Versions are not expected to
23 | # follow Semantic Versioning. They should reflect the version the application is using.
24 | appVersion: 'latest'
25 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG BASE_IMAGE="azul/zulu-openjdk:25"
2 |
3 | FROM $BASE_IMAGE AS overlay
4 |
5 | ARG EXT_BUILD_COMMANDS=""
6 | ARG EXT_BUILD_OPTIONS=""
7 |
8 | ARG JAVA_TOOL_OPTIONS="--enable-native-access=ALL-UNNAMED"
9 | ENV JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS}"
10 |
11 | WORKDIR /cas-overlay
12 | COPY ./src src/
13 | COPY ./gradle/ gradle/
14 | COPY ./gradlew ./settings.gradle ./build.gradle ./gradle.properties ./lombok.config ./
15 |
16 | RUN mkdir -p ~/.gradle \
17 | && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties \
18 | && echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties \
19 | && chmod 750 ./gradlew \
20 | && ./gradlew --version;
21 |
22 | RUN ./gradlew clean build $EXT_BUILD_COMMANDS --parallel --no-daemon -Pexecutable=false $EXT_BUILD_OPTIONS;
23 |
24 | RUN java -Djarmode=tools -jar build/libs/cas.war extract \
25 | && java -XX:ArchiveClassesAtExit=./cas/cas.jsa -Dspring.context.exit=onRefresh -jar cas/cas.war
26 |
27 | FROM $BASE_IMAGE AS cas
28 |
29 | LABEL "Organization"="Apereo"
30 | LABEL "Description"="Apereo CAS"
31 |
32 | RUN mkdir -p /etc/cas/config \
33 | && mkdir -p /etc/cas/services \
34 | && mkdir -p /etc/cas/saml;
35 |
36 | WORKDIR cas-overlay
37 | COPY --from=overlay /cas-overlay/cas cas/
38 |
39 | COPY etc/cas/ /etc/cas/
40 | COPY etc/cas/config/ /etc/cas/config/
41 | COPY etc/cas/services/ /etc/cas/services/
42 | COPY etc/cas/saml/ /etc/cas/saml/
43 |
44 | EXPOSE 8080 8443
45 |
46 | ENV PATH $PATH:$JAVA_HOME/bin:.
47 |
48 | ENTRYPOINT ["java", "-server", "-noverify", "-Xmx2048M", "-XX:SharedArchiveFile=cas/cas.jsa", "-jar", "cas/cas.war"]
49 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.cas.ingress.enabled -}}
2 | {{- $fullName := include "cas-server.fullname" . -}}
3 | {{- $svcPort := .Values.cas.service.port -}}
4 | {{- $kubeVersion := .Capabilities.KubeVersion.Version -}}
5 | {{- if semverCompare ">=1.19.0" $kubeVersion }}
6 | apiVersion: networking.k8s.io/v1
7 | {{- else -}}
8 | apiVersion: networking.k8s.io/v1beta1
9 | {{- end }}
10 | kind: Ingress
11 | metadata:
12 | name: {{ $fullName }}
13 | labels:
14 | {{- include "cas-server.labels" . | nindent 4 }}
15 | {{- with .Values.cas.ingress.annotations }}
16 | annotations:
17 | {{- toYaml . | nindent 4 }}
18 | {{- end }}
19 | spec:
20 | {{- if .Values.cas.ingress.tls }}
21 | tls:
22 | {{- range .Values.cas.ingress.tls }}
23 | - hosts:
24 | {{- range .hosts }}
25 | - {{ . | quote }}
26 | {{- end }}
27 | secretName: {{ .secretName }}
28 | {{- end }}
29 | {{- end }}
30 | rules:
31 | {{- range .Values.cas.ingress.hosts }}
32 | - host: {{ .host | quote }}
33 | http:
34 | paths:
35 | {{- range .paths }}
36 | - path: {{ . }}
37 | {{- if semverCompare ">=1.18.0" $kubeVersion }}
38 | pathType: Prefix
39 | {{- end }}
40 | {{- if semverCompare ">=1.19.0" $kubeVersion }}
41 | backend:
42 | service:
43 | name: {{ $fullName }}
44 | port:
45 | number: {{ $svcPort }}
46 | {{- else }}
47 | backend:
48 | serviceName: {{ $fullName }}
49 | servicePort: {{ $svcPort }}
50 | {{- end }}
51 | {{- end }}
52 | {{- end }}
53 | {{- end }}
54 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | 1. Get the application URL by running these commands:
2 | {{- if .Values.cas.ingress.enabled }}
3 | {{- range $host := .Values.cas.ingress.hosts }}
4 | {{- range .paths }}
5 | curl -k -v http{{ if $.Values.cas.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}/login
6 | {{- end }}
7 | {{- end }}
8 | {{- else if contains "NodePort" .Values.service.type }}
9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "cas-server.fullname" . }})
10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11 | echo http://$NODE_IP:$NODE_PORT
12 | {{- else if contains "LoadBalancer" .Values.service.type }}
13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "cas-server.fullname" . }}'
15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "cas-server.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16 | echo http://$SERVICE_IP:{{ .Values.service.port }}
17 | {{- else if contains "ClusterIP" .Values.service.type }}
18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "cas-server.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19 | echo "Visit http://127.0.0.1:8080 to use your application"
20 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80
21 | {{- end }}
22 |
23 | Kubernetes Version: {{ .Capabilities.KubeVersion.Version }}
24 |
--------------------------------------------------------------------------------
/puppeteer/scenarios/basic.js:
--------------------------------------------------------------------------------
1 | const puppeteer = require('puppeteer');
2 | const assert = require("assert");
3 | const pino = require('pino');
4 | const logger = pino({
5 | level: "info",
6 | transport: {
7 | target: 'pino-pretty'
8 | }
9 | });
10 |
11 | (async () => {
12 | const browser = await puppeteer.launch({
13 | headless: (process.env.CI === "true" || process.env.HEADLESS === "true") ? "new" : false,
14 | ignoreHTTPSErrors: true,
15 | devtools: false,
16 | defaultViewport: null,
17 | slowMo: 5,
18 | args: ['--start-maximized', "--window-size=1920,1080"]
19 | });
20 |
21 | try {
22 | const page = await browser.newPage();
23 |
24 | const casHost = process.env.PUPPETEER_CAS_HOST || "https://localhost:8443";
25 | await page.goto(`${casHost}/cas/login`);
26 |
27 | await page.waitForSelector("#username", {visible: true});
28 | await page.$eval("#username", el => el.value = '');
29 | await page.type("#username", "casuser");
30 |
31 | await page.waitForSelector("#password", {visible: true});
32 | await page.$eval("#password", el => el.value = '');
33 | await page.type("#password", "Mellon");
34 |
35 | await page.keyboard.press('Enter');
36 | await page.waitForNavigation();
37 |
38 | const cookies = (await page.cookies()).filter(c => {
39 | logger.debug(`Checking cookie ${c.name}:${c.value}`);
40 | return c.name === "TGC";
41 | });
42 | assert(cookies.length !== 0);
43 | logger.info(`Cookie:\n${JSON.stringify(cookies, undefined, 2)}`);
44 | await process.exit(0)
45 | } catch (e) {
46 | logger.error(e);
47 | await process.exit(1)
48 | } finally {
49 | await browser.close();
50 | }
51 | })();
52 |
--------------------------------------------------------------------------------
/helm/create-truststore.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | NAMESPACE=${1:-default}
3 | INGRESS_CERT_FILE=cas-ingress.crt
4 | CAS_CERT_FILE=cas.crt
5 | CAS_KEYSTORE=../etc/cas/thekeystore
6 | TRUST_STORE=../etc/cas/truststore
7 | JAVA_CACERTS=${2:-/etc/ssl/certs/java/cacerts}
8 |
9 | STORE_PASS=changeit
10 |
11 | set -e
12 |
13 | if [ -f ${TRUST_STORE} ]; then
14 | rm ${TRUST_STORE}
15 | fi
16 |
17 | if [ -f "${JAVA_CACERTS}" ]; then
18 | keytool -importkeystore -noprompt -srckeystore "${JAVA_CACERTS}" -srcstorepass "${STORE_PASS}" -destkeystore "${TRUST_STORE}" -deststoretype PKCS12 -deststorepass "${STORE_PASS}"
19 | else
20 | echo "Missing ${JAVA_CACERTS} JAVA_HOME is ${JAVA_HOME}"
21 | if [ -d "${JAVA_HOME}" ]; then
22 | find ${JAVA_HOME} -name cacerts -print
23 | find ${JAVA_HOME} -name cacerts -exec keytool -importkeystore -noprompt -srckeystore {} -srcstorepass "${STORE_PASS}" -destkeystore "${TRUST_STORE}" -deststoretype PKCS12 -deststorepass "${STORE_PASS}" \;
24 | fi
25 | fi
26 |
27 | # create truststore that trusts ingress cert
28 | if [ -f "${INGRESS_CERT_FILE}" ] ; then
29 | keytool -importcert -noprompt -keystore "${TRUST_STORE}" -storepass "${STORE_PASS}" -alias cas-ingress -file "${INGRESS_CERT_FILE}" -storetype PKCS12
30 | else
31 | echo "Missing ingress cert file to put in trust bundle: ${INGRESS_CERT_FILE}"
32 | fi
33 |
34 | # add cas server cert to trust store
35 | if [ -f "${CAS_KEYSTORE}" ] ; then
36 | keytool -exportcert -keystore "${CAS_KEYSTORE}" -storepass "${STORE_PASS}" -alias cas -file "${CAS_CERT_FILE}" -rfc
37 | keytool -importcert -noprompt -storepass "${STORE_PASS}" -keystore "${TRUST_STORE}" -alias cas -file "${CAS_CERT_FILE}" -storetype PKCS12
38 | else
39 | echo "Missing keystore ${CAS_KEYSTORE} to put cas cert in trust bundle"
40 | fi
41 | kubectl delete configmap cas-truststore --namespace "${NAMESPACE}" || true
42 | kubectl create configmap cas-truststore --namespace "${NAMESPACE}" --from-file=truststore=${TRUST_STORE}
--------------------------------------------------------------------------------
/puppeteer/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CAS_ARGS="${CAS_ARGS:-}"
4 |
5 | RED="\e[31m"
6 | GREEN="\e[32m"
7 | YELLOW="\e[33m"
8 | ENDCOLOR="\e[0m"
9 |
10 | function printgreen() {
11 | printf "${GREEN}$1${ENDCOLOR}\n"
12 | }
13 | function printyellow() {
14 | printf "${YELLOW}$1${ENDCOLOR}\n"
15 | }
16 | function printred() {
17 | printf "${RED}$1${ENDCOLOR}\n"
18 | }
19 |
20 | casWebApplicationFile="${PWD}/build/libs/cas.war"
21 | if [[ ! -f "$casWebApplicationFile" ]]; then
22 | echo "Building CAS"
23 | ./gradlew clean build -x test -x javadoc --no-configuration-cache --offline
24 | if [ $? -ne 0 ]; then
25 | printred "Failed to build CAS"
26 | exit 1
27 | fi
28 | fi
29 |
30 | if [[ ! -d "${PWD}/puppeteer/node_modules/puppeteer" ]]; then
31 | echo "Installing Puppeteer"
32 | (cd "${PWD}/puppeteer" && npm install puppeteer)
33 | else
34 | echo "Using existing Puppeteer modules..."
35 | fi
36 |
37 | echo -n "NPM version: " && npm --version
38 | echo -n "Node version: " && node --version
39 |
40 | echo "Launching CAS at $casWebApplicationFile with options $CAS_ARGS"
41 | java -jar "$casWebApplicationFile" $CAS_ARGS &
42 | pid=$!
43 | echo "Waiting for CAS under process id ${pid}"
44 | sleep 45
45 | casLogin="${PUPPETEER_CAS_HOST:-https://localhost:8443}/cas/login"
46 | echo "Checking CAS status at ${casLogin}"
47 | curl -k -L --output /dev/null --silent --fail "$casLogin"
48 | if [[ $? -ne 0 ]]; then
49 | printred "Unable to launch CAS instance under process id ${pid}."
50 | printred "Killing process id $pid and exiting"
51 | kill -9 "$pid"
52 | exit 1
53 | fi
54 |
55 | export NODE_TLS_REJECT_UNAUTHORIZED=0
56 | echo "Executing puppeteer scenarios..."
57 | for scenario in "${PWD}"/puppeteer/scenarios/*; do
58 | scenarioName=$(basename "$scenario")
59 | echo "=========================="
60 | echo "- Scenario $scenarioName "
61 | echo -e "==========================\n"
62 | node "$scenario"
63 | rc=$?
64 | echo -e "\n"
65 | if [[ $rc -ne 0 ]]; then
66 | printred "🔥 Scenario $scenarioName FAILED"
67 | else
68 | printgreen "✅ Scenario $scenarioName PASSED"
69 | fi
70 | echo -e "\n"
71 | sleep 1
72 | done;
73 |
74 | kill -9 "$pid"
75 | exit 0
76 |
--------------------------------------------------------------------------------
/helm/values-example1.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | # This is example of a values file that can override and add to the default values.yaml
4 | # Deployers might have one or more values files of their own per deployment environment.
5 |
6 | # CAS Server container properties
7 | casServerContainer:
8 |
9 | # override profiles to include gitsvc
10 | profiles: 'standalone,gitsvc'
11 |
12 | ## Override list of config files from casConfig to mount, include some from default values file
13 | casConfigMounts:
14 | - 'cas.properties'
15 | - 'cas.yaml'
16 | - 'application-gitsvc.yaml'
17 | casConfig:
18 | application-gitsvc.yaml: |-
19 | ---
20 | cas:
21 | service-registry:
22 | git:
23 | repository-url: "{{- .Values.gitsvcRepoUrl -}}"
24 | branches-to-clone: "{{- .Values.gitsvcBranchesToClone -}}"
25 | active-branch: "{{- .Values.gitsvcActiveBranch -}}"
26 | clone-directory: "{{- .Values.gitsvcCloneDirectory -}}"
27 | root-directory: "{{- .Values.gitsvcRootDirectory -}}"
28 | #eof
29 | application-redis.yaml: |-
30 | ---
31 | #helm repo add bitnami https://charts.bitnami.com/bitnami
32 | #helm install cas-server-redis bitnami/redis --set usePassword=false --set sentinel.enabled=true --set sentinel.usePassword=false
33 | cas:
34 | ticket:
35 | registry:
36 | redis:
37 | enabled: true
38 | database: 0
39 | host: 'cas-server-redis'
40 | pool:
41 | test-on-borrow: true
42 | read-from: 'UPSTREAMPREFERRED'
43 | crypto:
44 | enabled: false
45 | timeout: 5000
46 | port: 6379
47 | password: ' '
48 | cluster:
49 | nodes:
50 | - host: 'cas-server-redis-headless'
51 | port: 6379
52 | password: ' '
53 | sentinel:
54 | master: 'mymaster'
55 | node: 'cas-server-redis-headless:26379'
56 | # eof
57 |
58 |
59 | gitsvcRepoUrl: 'https://github.com/apereo/cas.git' # need smaller repo with services
60 | gitsvcBranchesToClone: 'master'
61 | gitsvcActiveBranch: 'master'
62 | gitsvcCloneDirectory: '/tmp/cas/services'
63 | gitsvcRootDirectory: 'etc' # only supports one level
64 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Expand the name of the chart.
3 | */}}
4 | {{- define "cas-server.name" -}}
5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6 | {{- end }}
7 |
8 | {{/*
9 | Create a default fully qualified app name.
10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11 | If release name contains chart name it will be used as a full name.
12 | */}}
13 | {{- define "cas-server.fullname" -}}
14 | {{- if .Values.fullnameOverride }}
15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16 | {{- else }}
17 | {{- $name := default .Chart.Name .Values.nameOverride }}
18 | {{- if contains $name .Release.Name }}
19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }}
20 | {{- else }}
21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22 | {{- end }}
23 | {{- end }}
24 | {{- end }}
25 |
26 | {{/*
27 | Create chart name and version as used by the chart label.
28 | */}}
29 | {{- define "cas-server.chart" -}}
30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31 | {{- end }}
32 |
33 | {{/*
34 | Common labels
35 | */}}
36 | {{- define "cas-server.labels" -}}
37 | helm.sh/chart: {{ include "cas-server.chart" . }}
38 | {{ include "cas-server.selectorLabels" . }}
39 | {{- if .Chart.AppVersion }}
40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41 | {{- end }}
42 | app.kubernetes.io/managed-by: {{ .Release.Service }}
43 | {{- end }}
44 |
45 | {{/*
46 | Selector labels
47 | */}}
48 | {{- define "cas-server.selectorLabels" -}}
49 | app.kubernetes.io/name: {{ include "cas-server.name" . }}
50 | app.kubernetes.io/instance: {{ .Release.Name }}
51 | {{- end }}
52 |
53 | {{/*
54 | Create the name of the service account to use
55 | */}}
56 | {{- define "cas-server.serviceAccountName" -}}
57 | {{- if .Values.serviceAccount.create }}
58 | {{- default (include "cas-server.fullname" .) .Values.serviceAccount.name }}
59 | {{- else }}
60 | {{- default "default" .Values.serviceAccount.name }}
61 | {{- end }}
62 | {{- end }}
63 |
64 | {{/*
65 | Return the proper cas-server image name
66 | */}}
67 | {{- define "cas-server.imageName" -}}
68 | {{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }}
69 | {{- end -}}
70 |
71 |
72 | {{/*
73 | Return the proper image name (for the init container volume-permissions image)
74 | */}}
75 | {{- define "cas-server.volumePermissions.image" -}}
76 | {{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }}
77 | {{- end -}}
78 |
79 | {{/*
80 | Return the proper image name
81 | {{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }}
82 | */}}
83 | {{- define "common.images.image" -}}
84 | {{- $registryName := .imageRoot.registry -}}
85 | {{- $repositoryName := .imageRoot.repository -}}
86 | {{- $tag := default "latest" .imageRoot.tag | toString -}}
87 | {{- if .global }}
88 | {{- if .global.imageRegistry }}
89 | {{- $registryName = .global.imageRegistry -}}
90 | {{- end -}}
91 | {{- end -}}
92 | {{- if ne $registryName "" }}
93 | {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
94 | {{- else -}}
95 | {{- printf "%s:%s" $repositoryName $tag -}}
96 | {{- end -}}
97 | {{- end -}}
98 |
99 |
100 | {{/*
101 | Return log directory volume
102 | */}}
103 | {{- define "cas-server.logdir" -}}
104 | {{- if .Values.logdir.hostPath -}}
105 | hostPath:
106 | path: {{ .Values.logdir.hostPath }}
107 | type: Directory
108 | {{- else if .Values.logdir.claimName -}}
109 | persistentVolumeClaim:
110 | claimName: {{ .Values.logdir.claimName }}
111 | {{- else -}}
112 | emptyDir: {}
113 | {{- end }}
114 | {{- end -}}
115 |
116 |
117 | {{/*
118 | Renders a value that contains template.
119 | Usage:
120 | {{ include "cas-server.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }}
121 | */}}
122 | {{- define "cas-server.tplvalues.render" -}}
123 | {{- if typeIs "string" .value }}
124 | {{- tpl .value .context }}
125 | {{- else }}
126 | {{- tpl (.value | toYaml) .context }}
127 | {{- end }}
128 | {{- end -}}
129 |
--------------------------------------------------------------------------------
/helm/README.md:
--------------------------------------------------------------------------------
1 | ## Helm Chart for CAS
2 |
3 | The current helm chart for cas-server demonstrates standing up CAS.
4 | The chart functionality will grow over time, hopefully with contributions from real world deployments.
5 | Eventually it might be nice to support a config-server.
6 | The chart supports mapping in arbitrary volumes and cas config can be specified in values files.
7 | The config could be in cloud config rather than kubernetes config maps, the service registry
8 | could be in a database, git, or a simple json registry in a kubernetes persistent volume. The ticket registry could use a standard helm chart for redis,
9 | postgresql, or mongo, etc.
10 | Currently the chart is attempting to use SSL between ingress controller and the CAS servers.
11 | This is probably overkill and involves all the pain that comes with SSL (e.g. trust & hostname verification).
12 | This chart uses stateful set for CAS rather than a deployment and this may change in the future.
13 |
14 | #### Warning: semver versioning will not be employed until published to a repository.
15 |
16 | ### Install Kubernetes (Docker for Windows/Mac, Minikube, K3S, Rancher, etc)
17 |
18 | - [Docker Desktop](https://www.docker.com/products/docker-desktop)
19 |
20 | - [Minikube](https://minikube.sigs.k8s.io/docs/start/)
21 |
22 | - [k3s](https://k3s.io/) - Works on linux, very light-weight and easy to install for development
23 | ```shell script
24 | curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable traefik" sh
25 | # the following export is for helm
26 | export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
27 | ./gradlew clean build jibBuildTar --refresh-dependencies
28 | k3s ctr images import build/jib-image.tar
29 | k3s ctr images ls | grep cas
30 | ./gradlew createKeystore
31 | cd helm
32 | # create secret for tomcat
33 | kubectl create secret generic cas-server-keystore --from-file=thekeystore=/etc/cas/thekeystore
34 | # create secret for ingress controller to use with CAS ingress (nginx-ingress will use default if you don't create)
35 | ./create-ingress-tls.sh
36 | # install cas-server helm chart
37 | helm upgrade --install cas-server ./cas-server
38 | ```
39 |
40 | ### Install Helm and Kubectl
41 |
42 | Helm v3 and Kubectl are just single binary programs. Kubectl may come with your kubernetes
43 | installation, but you can download both of programs and put them in your path.
44 | - Install [Helm](https://helm.sh/docs/intro/install/)
45 | - Install [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
46 |
47 | ### Install ingress controller
48 |
49 | CAS helm chart only tested with Kubernetes ingress-nginx, feel free to add support for other ingress controllers.
50 |
51 | [Kubernetes Nginx Ingress Installation Guide](https://kubernetes.github.io/ingress-nginx/deploy/)
52 |
53 | ### Create secret containing keystore
54 |
55 | Assuming you have run `./gradlew createKeystore` or put you server keystore in `/etc/cas/thekeystore`,
56 | run the following to create a secret containing the keystore:
57 | ```shell script
58 | kubectl create secret generic cas-server-keystore --from-file=thekeystore=/etc/cas/thekeystore
59 | ```
60 |
61 | ### Install CAS Server helm chart
62 |
63 | Helm charts consist of templates which are combined with values from one or more values files
64 | (and command line set arguments) to produce kubernetes yaml. The templates folder contains a default
65 | values.yaml that is used by default but additional values files can be specified on the command line.
66 | The following examples use the `default` namespace but `--namespace cas` can be added to any resources
67 | created by the helm command to use the specified kubernetes namespace.
68 | ```
69 | # delete cas-server helm chart install
70 | helm delete cas-server
71 | # install cas-server chart
72 | helm install cas-server ./cas-server
73 | # install or update cas-server
74 | helm upgrade --install cas-server ./cas-server
75 | # use local values file to override defaults
76 | helm upgrade --install cas-server --values values-local.yaml ./cas-server
77 | # see kubernetes yaml without installing
78 | helm upgrade --install cas-server --values values-local.yaml ./cas-server --dry-run --debug
79 | # sometimes dry-run fails b/c yaml can't convert to json so use template instead to see problem
80 | helm template cas-server --values values-local.yaml ./cas-server --debug
81 | ```
82 |
83 | ### Useful `kubectl` Commands
84 |
85 | ```
86 | # tail the console logs
87 | kubectl logs cas-server-0 -f
88 | # exec into container
89 | kubectl exec -it cas-server-0 sh
90 | # bounce CAS pod
91 | kubectl delete pod cas-server-0
92 | ```
93 |
94 | ### Browse to CAS
95 |
96 | Make sure you have host entries for whatever host is listed in values file for this entry:
97 | ```
98 | ingress:
99 | hosts:
100 | - host: cas.example.org
101 | paths:
102 | - "/cas"
103 | tls:
104 | - secretName: cas-server-ingress-tls
105 | hosts:
106 | - cas.example.org
107 | ```
108 |
109 | ```
110 | # host entry
111 | 127.0.0.1 cas.example.org
112 | ```
113 | Browse to `https://cas.example.org/cas/login`
114 |
--------------------------------------------------------------------------------
/gradle/springboot.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "java"
2 |
3 | sourceSets {
4 | bootRunSources {
5 | resources {
6 | srcDirs new File("//etc/cas/templates/"), new File("${project.getProjectDir()}/src/main/resources/")
7 | }
8 | }
9 | }
10 |
11 | configurations {
12 | bootRunConfig {
13 | extendsFrom compileClasspath
14 |
15 | exclude(group: "org.springframework.boot", module: "spring-boot-starter-logging")
16 | exclude(group: "ch.qos.logback", module: "logback-core")
17 | exclude(group: "ch.qos.logback", module: "logback-classic")
18 | }
19 | }
20 |
21 | dependencies {
22 | bootRunConfig "org.apereo.cas:cas-server-core"
23 | bootRunConfig "org.apereo.cas:cas-server-core-logging"
24 | bootRunConfig "org.apereo.cas:cas-server-core-web"
25 | bootRunConfig "org.apereo.cas:cas-server-core-webflow"
26 | bootRunConfig "org.apereo.cas:cas-server-core-cookie"
27 | bootRunConfig "org.apereo.cas:cas-server-core-logout"
28 | bootRunConfig "org.apereo.cas:cas-server-core-authentication"
29 | bootRunConfig "org.apereo.cas:cas-server-core-validation"
30 | bootRunConfig "org.apereo.cas:cas-server-core-audit"
31 | bootRunConfig "org.apereo.cas:cas-server-core-tickets"
32 | bootRunConfig "org.apereo.cas:cas-server-core-services"
33 | bootRunConfig "org.apereo.cas:cas-server-core-util"
34 |
35 | bootRunConfig "org.apereo.cas:cas-server-support-webconfig"
36 | bootRunConfig "org.apereo.cas:cas-server-support-thymeleaf"
37 | bootRunConfig "org.apereo.cas:cas-server-support-validation"
38 | bootRunConfig "org.apereo.cas:cas-server-support-person-directory"
39 | bootRunConfig "org.apereo.cas:cas-server-webapp-resources"
40 | bootRunConfig "org.apereo.cas:cas-server-webapp-init"
41 | bootRunConfig "org.apereo.cas:cas-server-webapp-tomcat"
42 | bootRunConfig "org.apereo.cas:cas-server-webapp-init-tomcat"
43 |
44 | bootRunConfig "org.springframework.cloud:spring-cloud-starter-bootstrap"
45 | bootRunConfig "org.springframework.boot:spring-boot-devtools"
46 | }
47 |
48 | bootRun {
49 | classpath = configurations.bootRunConfig + sourceSets.main.compileClasspath + sourceSets.main.runtimeClasspath
50 | sourceResources sourceSets.bootRunSources
51 | doFirst {
52 | systemProperties = System.properties
53 | }
54 |
55 | def list = []
56 | list.add("-XX:TieredStopAtLevel=1")
57 | list.add("-Xverify:none")
58 | list.add("--add-modules")
59 | list.add("java.se")
60 | list.add("--add-exports")
61 | list.add("java.base/jdk.internal.ref=ALL-UNNAMED")
62 | list.add("--add-opens")
63 | list.add("java.base/java.lang=ALL-UNNAMED")
64 | list.add("--add-opens")
65 | list.add("java.base/java.nio=ALL-UNNAMED")
66 | list.add("--add-opens")
67 | list.add("java.base/sun.nio.ch=ALL-UNNAMED")
68 | list.add("--add-opens")
69 | list.add("java.management/sun.management=ALL-UNNAMED")
70 | list.add("--add-opens")
71 | list.add("jdk.management/com.sun.management.internal=ALL-UNNAMED")
72 | list.add("-Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=n")
73 |
74 | jvmArgs = list
75 |
76 | def appArgList = ["--spring.thymeleaf.cache=false"]
77 | args = appArgList
78 | }
79 |
80 | springBoot {
81 | mainClass = "org.apereo.cas.web.CasWebApplication"
82 |
83 | }
84 |
85 | tasks.cyclonedxDirectBom {
86 | xmlOutput.unsetConvention()
87 | jsonOutput.set(file("$buildDir/META-INF/sbom/application.cdx"))
88 | }
89 | tasks.cyclonedxBom {
90 | xmlOutput.unsetConvention()
91 | jsonOutput.set(file("$buildDir/META-INF/sbom/application.cdx"))
92 | }
93 |
94 | bootWar {
95 | dependsOn cyclonedxBom
96 |
97 | archiveFileName = "cas.war"
98 | archiveBaseName = "cas"
99 |
100 | entryCompression = ZipEntryCompression.STORED
101 |
102 | /*
103 | attachClasses = true
104 | classesClassifier = 'classes'
105 | archiveClasses = true
106 | */
107 |
108 | from("$buildDir/META-INF/sbom") {
109 | into "META-INF/sbom"
110 | }
111 |
112 | overlays {
113 | /*
114 | https://docs.freefair.io/gradle-plugins/current/reference/#_io_freefair_war_overlay
115 | Note: The "excludes" property is only for files in the war dependency.
116 | If a jar is excluded from the war, it could be brought back into the final war as a dependency
117 | of non-war dependencies. Those should be excluded via normal gradle dependency exclusions.
118 | */
119 | cas {
120 | from "org.apereo.cas:cas-server-webapp${project.findProperty('appServer') ?: ''}:${project.'cas.version'}@war"
121 |
122 |
123 | provided = false
124 |
125 | def excludeArtifacts = ["WEB-INF/lib/servlet-api-2*.jar"]
126 | if (project.hasProperty("tomcatVersion")) {
127 | excludeArtifacts += "WEB-INF/lib/tomcat-*.jar"
128 | }
129 | excludes = excludeArtifacts
130 |
131 | /*
132 | excludes = ["WEB-INF/lib/somejar-1.0*"]
133 | enableCompilation = true
134 | includes = ["*.xyz"]
135 | targetPath = "sub-path/bar"
136 | skip = false
137 | */
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # This overlay project's version
2 | # For consistency and with no other effect, this is set to the CAS version itself.
3 | version=8.0.0-SNAPSHOT
4 |
5 | # This is the CAS server version that will be deployed.
6 | # Versions typically are in the format of:
7 | # [Major].[Minor].[Patch].[Security]
8 | # For patch and security releases and unless explicitly stated otherwise, the below property
9 | # and NOTHING ELSE is the only change you usually need to make to upgrade your CAS deployment.
10 | cas.version=8.0.0-SNAPSHOT
11 |
12 | # The Spring Boot version is very much tied to the CAS release 8.0.0-SNAPSHOT
13 | # and must not be modified or upgraded out of band, as doing so would most likely
14 | # jeopardize the stability of your CAS deployment leading to unpredictable behavior.
15 | springBootVersion=4.0.0
16 |
17 | # The coordinates of this overlay project
18 | group=org.apereo.cas
19 | artifactId=cas-overlay
20 |
21 | # Before changing the JDK versions here, you must account for the following:
22 | # - Dependency Compatibility: Ensure that all libraries and frameworks you use are compatible with Java 25 and above.
23 | # - Environment Compatibility: Check that your deployment environments (e.g., servers, CI/CD pipelines, cloud services) support Java 25 and above.
24 | # Remember that this CAS build does and will only officially support Java 25. Do NOT change platform requirements unless
25 | # you really know what you are doing and are comfortable managing the deployment and its risks completely on your own.
26 |
27 | # This property defines the version of Java that your source code is written in.
28 | # It ensures that your code is compatible with the specified version of the Java language.
29 | # Gradle will expect your source code to be compatible with JDK 25.
30 | sourceCompatibility=25
31 |
32 | # This property specifies the version of the Java runtime that the compiled bytecode should be compatible with.
33 | # It ensures that the bytecode generated by the compiler will run on JDK 25.
34 | targetCompatibility=25
35 |
36 | # This property controls the JVM vendor that is used by Gradle toolchains.
37 | # You may want to build CAS using a Java version that is not supported for running Gradle
38 | # by setting this property to the vendor of the JDK you want to use.
39 | # If Gradle can’t find a locally available toolchain that matches the requirements
40 | # of the build, it can automatically download one.
41 | # Options include: AMAZON, ADOPTIUM, JETBRAINS, MICROSOFT, ORACLE, SAP, BELLSOFT, etc.
42 | # Setting this to a blank or invalid value will force Gradle to use the JDK installation on the build machine.
43 | jvmVendor=AMAZON
44 |
45 | # This plugin controls how JDK distributions required by the Grtadle toolchain
46 | # are discovered, and downloaded when necessary.
47 | # Note that auto-provisioning of a JDK distribution only kicks in when auto-detection fails
48 | # to find a matching JDK, and auto-provisioning can only download new JDKs and is in no way
49 | # involved in updating any of the already installed ones.
50 | gradleFoojayPluginVersion=1.0.0
51 |
52 | gradleFreeFairPluginVersion=9.1.0
53 | lombokVersion=1.18.42
54 |
55 | # Used to build Docker images
56 | jibVersion=3.5.2
57 | gradleDockerPluginVersion=9.4.0
58 |
59 | # Specify the coordinates of the container image to build
60 | containerImageOrg=apereo
61 | containerImageName=cas
62 |
63 | baseDockerImage=azul/zulu-openjdk:25
64 | allowInsecureRegistries=false
65 |
66 | # Multiple platforms may be specified, separated by a comma i.e amd64:linux,arm64:linux
67 | dockerImagePlatform=amd64:linux
68 |
69 |
70 | # The plugin creates an aggregate of all dependencies of the CAS project
71 | # and creates a valid SBOM, designed for use in application security contexts
72 | # and supply chain component analysis.
73 | gradleCyclonePluginVersion=3.0.2
74 |
75 | # Use -tomcat, -jetty, -undertow for deployment to control the embedded server container
76 | # that will be used to deploy and manage your CAS deployment.
77 | # You should set this to blank if you want to deploy to an external container.
78 | # and want to set up, download and manage the container (i.e. Apache Tomcat) yourself.
79 | appServer=-tomcat
80 |
81 | # If you are using an embedded Apache Tomcat container to deploy and run CAS,
82 | # and need to override the Apache Tomcat version, uncomment the property below
83 | # and specify the the Apache Tomcat version, i.e. 11.0.15.
84 | # While enabled, this will override any and all upstream changes to
85 | # Apache Tomcat dependency management and you will be directly responsible to make
86 | # adjustments and upgrades as necessary. Use with caution, favor less work.
87 | # tomcatVersion=11.0.15
88 |
89 | # Settings to generate a keystore
90 | # used by the build to assist with creating
91 | # self-signed certificates for TLS
92 | certDir=/etc/cas
93 | serverKeystore=thekeystore
94 | exportedServerCert=cas.crt
95 | storeType=PKCS12
96 |
97 | # Location of the downloaded CAS Shell JAR
98 | shellDir=build/libs
99 | ivyVersion=2.5.2
100 | gradleDownloadTaskVersion=5.6.0
101 |
102 | # Include private repository
103 | # override these in user properties or pass in values from env on command line
104 | privateRepoUrl=
105 | privateRepoUsername=
106 |
107 | # Gradle build settings
108 | # Do NOT modify unless you know what you're doing!
109 | org.gradle.configureondemand=true
110 | org.gradle.caching=true
111 | org.gradle.parallel=true
112 | org.gradle.jvmargs=-Xms1024m -Xmx4048m -XX:TieredStopAtLevel=1
113 | org.gradle.unsafe.configuration-cache=false
114 | org.gradle.unsafe.configuration-cache-problems=warn
115 | systemProp.org.gradle.internal.http.connectionTimeout=60000
116 | systemProp.org.gradle.internal.http.requestTimeout=120000
117 |
--------------------------------------------------------------------------------
/etc/cas/config/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 | /var/log
10 | info
11 | warn
12 | info
13 | warn
14 | warn
15 | warn
16 | warn
17 | warn
18 | warn
19 | warn
20 | warn
21 | true
22 | false
23 |
24 | casStackTraceFile
25 | false
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 | # Collect all arguments for the java command;
201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202 | # shell script including quotes and variable substitutions, so put them in
203 | # double quotes to make sure that they get re-expanded; and
204 | # * put everything else in single quotes, so that it's not re-expanded.
205 |
206 | set -- \
207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
208 | -classpath "$CLASSPATH" \
209 | org.gradle.wrapper.GradleWrapperMain \
210 | "$@"
211 |
212 | # Stop when "xargs" is not available.
213 | if ! command -v xargs >/dev/null 2>&1
214 | then
215 | die "xargs is not available"
216 | fi
217 |
218 | # Use "xargs" to parse quoted args.
219 | #
220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
221 | #
222 | # In Bash we could simply go:
223 | #
224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
225 | # set -- "${ARGS[@]}" "$@"
226 | #
227 | # but POSIX shell has neither arrays nor command substitution, so instead we
228 | # post-process each arg (as a line of input to sed) to backslash-escape any
229 | # character that might be a shell metacharacter, then use eval to reverse
230 | # that process (while maintaining the separation between arguments), and wrap
231 | # the whole thing up as a single "set" statement.
232 | #
233 | # This will of course break if any of these variables contains a newline or
234 | # an unmatched quote.
235 | #
236 |
237 | eval "set -- $(
238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
239 | xargs -n1 |
240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
241 | tr '\n' ' '
242 | )" '"$@"'
243 |
244 | exec "$JAVACMD" "$@"
245 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/helm/cas-server/templates/statefulset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: {{ include "cas-server.fullname" . }}
5 | labels: {{- include "cas-server.labels" . | nindent 4 }}
6 | spec:
7 | replicas: {{ .Values.replicaCount }}
8 | updateStrategy:
9 | type: {{ .Values.updateStrategy | quote}}
10 | serviceName: {{ include "cas-server.fullname" . }}
11 | podManagementPolicy: {{ .Values.podManagementPolicy | quote}}
12 | selector:
13 | matchLabels:
14 | {{- include "cas-server.selectorLabels" . | nindent 6 }}
15 | template:
16 | metadata:
17 | annotations:
18 | {{- with .Values.podAnnotations }}
19 | {{- toYaml . | nindent 8 }}
20 | {{- end }}
21 | {{ if .Values.casServerContainer.alwaysRoll }}
22 | rollme: {{ randAlphaNum 5 | quote }}
23 | {{- else }}
24 | rollme: "rolldisabled"
25 | {{- end }}
26 | labels:
27 | {{- include "cas-server.selectorLabels" . | nindent 8 }}
28 | spec:
29 | {{- with .Values.imagePullSecrets }}
30 | imagePullSecrets:
31 | {{- toYaml . | nindent 8 }}
32 | {{- end }}
33 | serviceAccountName: {{ include "cas-server.serviceAccountName" . }}
34 | {{- if .Values.podSecurityContext.enabled }}
35 | securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }}
36 | {{- end }}
37 | volumes:
38 | {{- range $.Values.casServerContainer.casConfigMounts }}
39 | {{- $configMount := printf "%s-%s" "cas-config" . | replace "." "-" | replace "_" "-" | lower }}
40 | - name: {{ $configMount | quote }}
41 | configMap:
42 | name: {{ include "cas-server.fullname" $ }}-casconfig
43 | defaultMode: 0644
44 | {{- end }}
45 | - name: scripts
46 | configMap:
47 | name: {{ include "cas-server.fullname" . }}-scripts
48 | defaultMode: 0755
49 | - name: logdir
50 | {{- include "cas-server.logdir" . | nindent 10 }}
51 | {{- if .Values.casServerContainer.serverKeystoreExistingSecret }}
52 | - name: cas-server-keystore
53 | secret:
54 | secretName: {{ .Values.casServerContainer.serverKeystoreExistingSecret }}
55 | defaultMode: 0444
56 | items:
57 | - key: {{ .Values.casServerContainer.serverKeystoreSubPath }}
58 | path: {{ .Values.casServerContainer.serverKeystoreSubPath }}
59 | {{- end }}
60 | {{- if .Values.casServerContainer.extraVolumes }}
61 | {{- include "cas-server.tplvalues.render" ( dict "value" .Values.casServerContainer.extraVolumes "context" $ ) | nindent 8 }}
62 | {{- end }}
63 | {{- if or .Values.casServerContainer.initContainers (and .Values.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.persistence.enabled) }}
64 | initContainers:
65 | {{- if and .Values.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.persistence.enabled }}
66 | - name: volume-permissions
67 | image: {{ include "cas-server.volumePermissions.image" . }}
68 | imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }}
69 | command:
70 | - /bin/sh
71 | - -cx
72 | - |
73 | {{- if .Values.persistence.enabled }}
74 | {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }}
75 | chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }}
76 | {{- else }}
77 | chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} {{ .Values.persistence.mountPath }}
78 | {{- end }}
79 | mkdir -p {{ .Values.persistence.mountPath }}/data
80 | chmod 700 {{ .Values.persistence.mountPath }}/data
81 | find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | \
82 | {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }}
83 | xargs chown -R `id -u`:`id -G | cut -d " " -f2`
84 | {{- else }}
85 | xargs chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }}
86 | {{- end }}
87 | {{- end }}
88 | {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }}
89 | securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }}
90 | {{- else }}
91 | securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }}
92 | {{- end }}
93 | {{- if .Values.volumePermissions.resources }}
94 | resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }}
95 | {{- end }}
96 | volumeMounts:
97 | - name: data
98 | mountPath: {{ .Values.persistence.mountPath }}
99 | {{- end }}
100 | {{- if .Values.casServerContainer.initContainers }}
101 | {{- include "cas-server.tplvalues.render" (dict "value" .Values.casServerContainer.initContainers "context" $) | nindent 8 }}
102 | {{- end }}
103 | {{- end }}
104 | containers:
105 | - name: {{ .Chart.Name }}
106 | {{- if .Values.containerSecurityContext.enabled }}
107 | securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }}
108 | {{- end }}
109 | image: {{ include "cas-server.imageName" . }}
110 | imagePullPolicy: {{ .Values.image.pullPolicy }}
111 | env:
112 | {{- if .Values.casServerContainer.warPath }}
113 | - name: CAS_WAR
114 | value: {{ .Values.casServerContainer.warPath | quote }}
115 | {{- end }}
116 | {{- if .Values.casServerContainer.profiles }}
117 | - name: CAS_SPRING_PROFILES
118 | value: {{ .Values.casServerContainer.profiles | quote }}
119 | {{- end }}
120 | {{- if .Values.casServerContainer.jvm.maxHeapOpt }}
121 | - name: MAX_HEAP_OPT
122 | value: {{ .Values.casServerContainer.jvm.maxHeapOpt | quote }}
123 | {{- end }}
124 | {{- if .Values.casServerContainer.jvm.minHeapOpt }}
125 | - name: MIN_HEAP_OPT
126 | value: {{ .Values.casServerContainer.jvm.minHeapOpt | quote }}
127 | {{- end }}
128 | {{- if .Values.casServerContainer.jvm.extraOpts }}
129 | - name: JVM_EXTRA_OPTS
130 | value: {{ .Values.casServerContainer.jvm.extraOpts | quote }}
131 | {{- end }}
132 | - name: JAVA_ENABLE_DEBUG
133 | value: {{ .Values.casServerContainer.jvm.debugEnabled | quote }}
134 | - name: JAVA_DEBUG_SUSPEND
135 | value: {{ .Values.casServerContainer.jvm.debugSuspend | quote }}
136 | - name: 'KUBERNETES_NAMESPACE' # used by org.apache.catalina.tribes.membership.cloud.CloudMembershipProvider
137 | value: {{ .Release.Namespace }}
138 | - name: 'POD_IP'
139 | valueFrom:
140 | fieldRef:
141 | fieldPath: status.podIP
142 | {{- if .Values.casServerContainer.extraEnvVars }}
143 | {{- include "cas-server.tplvalues.render" (dict "value" .Values.casServerContainer.extraEnvVars "context" $) | nindent 12 }}
144 | {{- end }}
145 | envFrom:
146 | {{- if .Values.casServerContainer.extraEnvVarsConfigMap }}
147 | - configMapRef:
148 | name: {{ .Values.casServerContainer.extraEnvVarsConfigMap }}
149 | {{- end }}
150 | {{- if .Values.casServerContainer.extraEnvVarsSecret }}
151 | - secretRef:
152 | name: {{ .Values.casServerContainer.extraEnvVarsSecret }}
153 | {{- end }}
154 | {{- if .Values.casServerContainer.command }}
155 | command: {{- include "cas-server.tplvalues.render" (dict "value" .Values.casServerContainer.command "context" $) | nindent 12 }}
156 | {{- else }}
157 | command:
158 | - '/entrypoint.sh'
159 | {{- end }}
160 | {{- if .Values.casServerContainer.args }}
161 | args: {{- include "cas-server.tplvalues.render" (dict "value" .Values.casServerContainer.args "context" $) | nindent 12 }}
162 | {{- end }}
163 | ports:
164 | - name: https
165 | containerPort: {{ .Values.cas.listenPortHttps }}
166 | protocol: TCP
167 | - name: jvm-debug
168 | containerPort: {{ .Values.cas.listenPortJvmDebug }}
169 | protocol: TCP
170 | volumeMounts:
171 | {{- if .Values.persistence.enabled }}
172 | - name: data
173 | mountPath: {{ .Values.persistence.mountPath }}
174 | {{- end }}
175 | {{- range $.Values.casServerContainer.casConfigMounts }}
176 | {{- $configMount := printf "%s-%s" "cas-config" . | replace "." "-" | replace "_" "-" | lower }}
177 | {{- $configMountPath := printf "%s/%s" "/etc/cas/config" . }}
178 | - name: {{ $configMount | quote }}
179 | mountPath: {{ $configMountPath }}
180 | subPath: {{ . | quote }}
181 | {{- end }}
182 | - name: scripts
183 | mountPath: /entrypoint.sh
184 | subPath: entrypoint.sh
185 | - name: logdir
186 | mountPath: {{ .Values.logdir.mountPath }}
187 | {{- if .Values.casServerContainer.serverKeystoreExistingSecret }}
188 | - name: cas-server-keystore
189 | mountPath: {{ .Values.casServerContainer.serverKeystoreMountPath }}
190 | subPath: {{ .Values.casServerContainer.serverKeystoreSubPath }}
191 | {{- end }}
192 | {{- if .Values.casServerContainer.extraVolumeMounts }}
193 | {{- include "cas-server.tplvalues.render" ( dict "value" .Values.casServerContainer.extraVolumeMounts "context" $ ) | nindent 12 }}
194 | {{- end }}
195 | startupProbe:
196 | httpGet:
197 | path: {{ .Values.casServerContainer.defaultStatusUrl }}
198 | port: https
199 | scheme: HTTPS
200 | {{- if .Values.casServerContainer.defaultStatusHeaders }}
201 | {{- include "cas-server.tplvalues.render" ( dict "value" .Values.casServerContainer.defaultStatusHeaders "context" $ ) | nindent 14 }}
202 | {{- end }}
203 | failureThreshold: {{ .Values.casServerContainer.startupFailureThreshold }}
204 | periodSeconds: 20
205 | readinessProbe:
206 | httpGet:
207 | path: {{ .Values.casServerContainer.defaultStatusUrl }}
208 | port: https
209 | scheme: HTTPS
210 | {{- if .Values.casServerContainer.defaultStatusHeaders }}
211 | {{- include "cas-server.tplvalues.render" ( dict "value" .Values.casServerContainer.defaultStatusHeaders "context" $ ) | nindent 14 }}
212 | {{- end }}
213 | initialDelaySeconds: {{ .Values.casServerContainer.readinessInitialDelaySeconds }}
214 | periodSeconds: 5
215 | failureThreshold: {{ .Values.casServerContainer.readinessFailureThreshold }}
216 | livenessProbe:
217 | httpGet:
218 | path: {{ .Values.casServerContainer.defaultStatusUrl }}
219 | port: https
220 | scheme: HTTPS
221 | {{- if .Values.casServerContainer.defaultStatusHeaders }}
222 | {{- include "cas-server.tplvalues.render" ( dict "value" .Values.casServerContainer.defaultStatusHeaders "context" $ ) | nindent 14 }}
223 | {{- end }}
224 | initialDelaySeconds: {{ .Values.casServerContainer.livenessInitialDelaySeconds }}
225 | periodSeconds: 15
226 | failureThreshold: {{ .Values.casServerContainer.livenessFailureThreshold }}
227 | resources:
228 | {{- toYaml .Values.resources | nindent 12 }}
229 | {{- with .Values.nodeSelector }}
230 | nodeSelector:
231 | {{- toYaml . | nindent 8 }}
232 | {{- end }}
233 | {{- with .Values.affinity }}
234 | affinity:
235 | {{- toYaml . | nindent 8 }}
236 | {{- end }}
237 | {{- with .Values.tolerations }}
238 | tolerations:
239 | {{- toYaml . | nindent 8 }}
240 | {{- end }}
241 | {{- if .Values.persistence.enabled }}
242 | volumeClaimTemplates:
243 | - metadata:
244 | name: data
245 | {{- with .Values.persistence.annotations }}
246 | annotations:
247 | {{- range $key, $value := . }}
248 | {{ $key }}: {{ $value }}
249 | {{- end }}
250 | {{- end }}
251 | spec:
252 | accessModes:
253 | {{- range .Values.persistence.accessModes }}
254 | - {{ . | quote }}
255 | {{- end }}
256 | resources:
257 | requests:
258 | storage: {{ .Values.persistence.size | quote }}
259 | storageClassName: {{ .Values.persistence.storageClassName | quote }}
260 | {{- if .Values.persistence.selector }}
261 | selector: {{- include "cas-server.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }}
262 | {{- end -}}
263 | {{- end }}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IMPORTANT NOTE
******************************************************
This repository is always automatically generated from the [CAS Initializr](https://github.com/apereo/cas-initializr). Do NOT submit pull requests here as the change-set will be overwritten on the next sync. To learn more, please visit the [CAS documentation](https://apereo.github.io/cas).
******************************************************
2 | Apereo CAS WAR Overlay Template
3 | =====================================
4 |
5 | WAR Overlay Type: `cas-overlay`
6 |
7 | # Versions
8 |
9 | - CAS Server `8.0.0-SNAPSHOT`
10 | - JDK `25`
11 |
12 | # Build
13 |
14 | To build the project, use:
15 |
16 | ```bash
17 | # Use --refresh-dependencies to force-update SNAPSHOT versions
18 | ./gradlew[.bat] clean build
19 | ```
20 |
21 | To see what commands/tasks are available to the build script, run:
22 |
23 | ```bash
24 | ./gradlew[.bat] tasks
25 | ```
26 |
27 | If you need to, on Linux/Unix systems, you can delete all the existing artifacts
28 | (artifacts and metadata) Gradle has downloaded using:
29 |
30 | ```bash
31 | # Only do this when absolutely necessary
32 | rm -rf $HOME/.gradle/caches/
33 | ```
34 |
35 | Same strategy applies to Windows too, provided you switch `$HOME` to its equivalent in the above command.
36 |
37 | # Keystore
38 |
39 | For the server to run successfully, you might need to create a keystore file.
40 | This can either be done using the JDK's `keytool` utility or via the following command:
41 |
42 | ```bash
43 | ./gradlew[.bat] createKeystore
44 | ```
45 |
46 | Use the password `changeit` for both the keystore and the key/certificate entries.
47 | Ensure the keystore is loaded up with keys and certificates of the server.
48 |
49 | # Extension Modules
50 |
51 | Extension modules may be specified under the `dependencies` block of the [Gradle build script](build.gradle):
52 |
53 | ```gradle
54 | dependencies {
55 | implementation "org.apereo.cas:cas-server-some-module"
56 | ...
57 | }
58 | ```
59 |
60 | To collect the list of all project modules and dependencies in the overlay:
61 |
62 | ```bash
63 | ./gradlew[.bat] dependencies
64 | ```
65 |
66 | # Deployment
67 |
68 | On a successful deployment via the following methods, the server will be available at:
69 |
70 | * `https://localhost:8443/cas`
71 |
72 |
73 | ## Executable WAR
74 |
75 | 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.
76 |
77 | The current servlet container is specified as `-tomcat`.
78 |
79 | ```bash
80 | java -jar build/libs/cas.war
81 | ```
82 |
83 | Or via:
84 |
85 | ```bash
86 | ./gradlew[.bat] run
87 | ```
88 |
89 | It is often an advantage to explode the generated web application and run it in unpacked mode.
90 | One way to run an unpacked archive is by starting the appropriate launcher, as follows:
91 |
92 | ```bash
93 | jar -xf build/libs/cas.war
94 | cd build/libs
95 | java org.springframework.boot.loader.launch.JarLauncher
96 | ```
97 |
98 | This is slightly faster on startup (depending on the size of the WAR file) than
99 | running from an unexploded archive. After startup, you should not expect any differences.
100 |
101 | Debug the CAS web application as an executable WAR:
102 |
103 | ```bash
104 | ./gradlew[.bat] debug
105 | ```
106 |
107 | Or via:
108 |
109 | ```bash
110 | java -Xdebug -Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=y -jar build/libs/cas.war
111 | ```
112 |
113 |
114 | ### CDS Support
115 |
116 | CDS is a JVM feature that can help reduce the startup time and memory footprint of Java applications. CAS via Spring Boot
117 | now has support for easy creation of a CDS friendly layout. This layout can be created by extracting the CAS web application file
118 | with the help of the `tools` jarmode:
119 |
120 | ```bash
121 |
122 | java -Djarmode=tools -jar build/libs/cas.war extract
123 |
124 | # Perform a training run once
125 | java -XX:ArchiveClassesAtExit=cas.jsa -Dspring.context.exit=onRefresh -jar cas/cas.war
126 |
127 | # Run the CAS web application via CDS
128 | java XX:SharedArchiveFile=cas.jsa -jar cas/cas.war
129 | ```
130 |
131 | ## External
132 |
133 | Deploy the binary web application file in `build/libs` after a successful build to a servlet container of choice.
134 |
135 | # Docker
136 |
137 | The following strategies outline how to build and deploy CAS Docker images.
138 |
139 | ## Jib
140 |
141 | 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.
142 |
143 | ```bash
144 | # Running this task requires that you have Docker installed and running.
145 | ./gradlew build jibDockerBuild
146 | ```
147 |
148 | ## Dockerfile
149 |
150 | You can also use the Docker tooling and the provided `Dockerfile` to build and run.
151 | There are dedicated Gradle tasks available to build and push Docker images using the supplied `DockerFile`:
152 |
153 | ```bash
154 | ./gradlew build casBuildDockerImage
155 | ```
156 |
157 | Once ready, you may also push the images:
158 |
159 | ```bash
160 | ./gradlew casPushDockerImage
161 | ```
162 |
163 | If credentials (username+password) are required for pull and push operations, they may be specified
164 | using system properties via `-DdockerUsername=...` and `-DdockerPassword=...`.
165 |
166 | A `docker-compose.yml` is also provided to orchestrate the build:
167 |
168 | ```bash
169 | docker-compose build
170 | ```
171 |
172 | ## Spring Boot
173 |
174 | You can use the Spring Boot build plugin for Gradle to create CAS container images.
175 | The plugins create an OCI image (the same format as one created by docker build)
176 | by using [Cloud Native Buildpacks](https://buildpacks.io/). You do not need a Dockerfile, but you do need a Docker daemon,
177 | either locally (which is what you use when you build with docker) or remotely
178 | through the `DOCKER_HOST` environment variable. The default builder is optimized for
179 | Spring Boot applications such as CAS, and the image is layered efficiently.
180 |
181 | ```bash
182 | ./gradlew bootBuildImage
183 | ```
184 |
185 | The first build might take a long time because it has to download some container
186 | images and the JDK, but subsequent builds should be fast.
187 |
188 |
189 | # Software Bill of Materials (SBOM)
190 |
191 | The build supports generating a Software Bill of Materials (SBOM) for the CAS web application. This is done using
192 | the Gradle CycloneDX plugin, which creates an aggregate of all direct and transitive dependencies of a project
193 | and creates a valid CycloneDX SBOM. CycloneDX is a lightweight software bill of materials (SBOM)
194 | specification designed for use in application security contexts and supply chain component analysis.
195 |
196 | You may run the following Gradle task to generate the SBOM:
197 |
198 | ```bash
199 | ./gradlew cyclonedxBom
200 | ```
201 |
202 | The generated SBOM file will be located in the `build/META-INF/sbom/application.cdx` file.
203 | # CAS Command-line Shell
204 |
205 | To launch into the CAS command-line shell:
206 |
207 | ```bash
208 | ./gradlew[.bat] downloadShell runShell
209 | ```
210 |
211 | # Retrieve Overlay Resources
212 |
213 | To fetch and overlay a CAS resource or view, use:
214 |
215 | ```bash
216 | ./gradlew[.bat] getResource -PresourceName=[resource-name]
217 | ```
218 |
219 | # Create User Interface Themes Structure
220 |
221 | You can use the overlay to construct the correct directory structure for custom user interface themes:
222 |
223 | ```bash
224 | ./gradlew[.bat] createTheme -Ptheme=redbeard
225 | ```
226 |
227 | The generated directory structure should match the following:
228 |
229 | ```
230 | ├── redbeard.properties
231 | ├── static
232 | │ └── themes
233 | │ └── redbeard
234 | │ ├── css
235 | │ │ └── cas.css
236 | │ └── js
237 | │ └── cas.js
238 | └── templates
239 | └── redbeard
240 | └── fragments
241 | ```
242 |
243 | HTML templates and fragments can be moved into the above directory structure,
244 | and the theme may be assigned to applications for use.
245 |
246 | # List Overlay Resources
247 |
248 | To list all available CAS views and templates:
249 |
250 | ```bash
251 | ./gradlew[.bat] listTemplateViews
252 | ```
253 |
254 | To unzip and explode the CAS web application file and the internal resources jar:
255 |
256 | ```bash
257 | ./gradlew[.bat] explodeWar
258 | ```
259 |
260 | # Configuration
261 |
262 | - The `etc` directory contains the configuration files and directories that need to be copied to `/etc/cas/config`.
263 |
264 | ```bash
265 | ./gradlew[.bat] copyCasConfiguration
266 | ```
267 |
268 | - The specifics of the build are controlled using the `gradle.properties` file.
269 |
270 | ## Configuration Metadata
271 |
272 | Configuration metadata allows you to export collection of CAS properties as a report into a file
273 | that can later be examined. You will find a full list of CAS settings along with notes, types, default and accepted values:
274 |
275 | ```bash
276 | ./gradlew exportConfigMetadata
277 | ```
278 |
279 | # Puppeteer
280 |
281 | > [Puppeteer](https://pptr.dev/) is a Node.js library which provides a high-level API to control Chrome/Chromium over the DevTools Protocol.
282 | > Puppeteer runs in headless mode by default, but can be configured to run in full (non-headless) Chrome/Chromium.
283 |
284 | Puppeteer scenarios, used here as a form of acceptance testing, allow you to verify CAS functionality to address a particular authentication flow. The scenarios, which may be
285 | found inside the `./puppeteer/scenarios` directory are designed as small Node.js scripts that spin up a headless browser and walk through a test scenario. You may
286 | design your own test scenarios that verify functionality specific to your CAS deployment or feature.
287 |
288 | To execute Puppeteer scenarios, run:
289 |
290 | ```bash
291 | ./puppeteer/run.sh
292 | ```
293 |
294 | This will first attempt to build your CAS deployment, will install Puppeteer and all other needed libraries. It will then launch the CAS server,
295 | and upon its availability, will iterate through defined scenarios and will execute them one at a time.
296 |
297 | The following defaults are assumed:
298 |
299 | - CAS will be available at `https://localhost:8443/cas/login`.
300 | - The CAS overlay is prepped with an embedded server container, such as Apache Tomcat.
301 |
302 | You may of course need to make adjustments to account for your specific environment and deployment settings, URLs, etc.
303 |
304 |
305 | # Duct
306 |
307 | `duct` is a Gradle task to do quick smoke tests of multi-node CAS high-availability deployments. In particular, it tests correctness of ticket
308 | sharing between multiple individual CAS server nodes backed by distributed ticket registries such as Hazelcast, Redis, etc.
309 |
310 | This task requires CAS server nodes to **enable the CAS REST module**. It will **NOT** work without it.
311 |
312 | The task accepts the following properties:
313 |
314 | - Arbitrary number of CAS server nodes specified via the `duct.cas.X` properties.
315 | - URL of the service application registered with CAS specified via `duct.service`, for which tickets will be requested.
316 | - `duct.username` and `duct.password` to use for authentication, when requesting ticket-granting tickets.
317 |
318 | It automates the following scenario:
319 |
320 | - Authenticate and issue a service ticket on one CAS node
321 | - Validate this service ticket on the another node
322 | - Repeat (You may cancel and stop the task at any time with `Ctrl+C`)
323 |
324 | If the task succeeds, then we effectively have proven that the distributed ticket registry has been set up and deployed
325 | correctly and that there are no connectivity issues between CAS nodes.
326 |
327 | To run the task, you may use:
328 |
329 | ```bash
330 | ./gradlew duct
331 | -Pduct.cas.1=https://node1.example.org/cas \
332 | -Pduct.cas.2=https://node2.example.org/cas \
333 | -Pduct.cas.3=https://node3.example.org/cas \
334 | -Pduct.cas.4=https://node4.example.org/cas \
335 | -Pduct.service=https://apereo.github.io \
336 | -Pduct.username=casuser \
337 | -Pduct.password=Mellon
338 | ```
339 |
340 | You may also supply the following options:
341 |
342 | - `duct.debug`: Boolean flag to output debug and verbose logging.
343 | - `duct.duration`: Number of seconds, i.e. `30` to execute the scenario.
344 | - `duct.count`: Number of iterations, i.e. `5` to execute the scenario.
345 |
346 |
347 | # OpenRewrite
348 |
349 | [OpenRewrite](https://docs.openrewrite.org/) is a tool used by the CAS in form of a Gradle plugin
350 | that allows the project to upgrade in place. It works by making changes to the project structure representing
351 | your CAS build and printing the modified files back. Modifications are packaged together in form of upgrade
352 | scripts called `Recipes` that are automatically packaged and presented to the build and may be discovered via:
353 |
354 | ```bash
355 | ./gradlew --init-script openrewrite.gradle rewriteDiscover -PtargetVersion=X.Y.Z --no-configuration-cache | grep "org.apereo.cas"
356 | ```
357 |
358 | **NOTE:** All CAS specific recipes begin with `org.apereo.cas`. The `targetVersion` must be the CAS version to which you want to upgrade.
359 |
360 | OpenRewrite recipes make minimally invasive changes to your CAS build allowing you to upgrade from one version
361 | to the next with minimal effort. The recipe contains *almost* everything that is required for a CAS build system to navigate
362 | from one version to other and automated tedious aspects of the upgrade such as finding the correct versions of CAS,
363 | relevant libraries and plugins as well as any possible structural changes to one's CAS build.
364 |
365 | To run, you will need to find and select the name of the recipe first. Then, you can dry-run the selected recipes and see which files would be changed in the build log.
366 | This does not alter your source files on disk at all. This goal can be used to preview the changes that would be made by the active recipes.
367 |
368 | ```bash
369 | ./gradlew --init-script openrewrite.gradle rewriteDryRun -PtargetVersion=X.Y.Z -DactiveRecipe=[recipe name] --no-configuration-cache
370 | ```
371 |
372 | When you are ready, you can run the actual recipe:
373 |
374 | ```bash
375 | ./gradlew --init-script openrewrite.gradle rewriteRun -PtargetVersion=X.Y.Z -DactiveRecipe=[recipe name] --no-configuration-cache
376 | ```
377 |
378 | This will run the selected recipes and apply the changes. This will write changes locally to your source files on disk.
379 | Afterward, review the changes, and when you are comfortable with the changes, commit them.
380 | The run goal generates warnings in the build log wherever it makes changes to source files.
381 |
--------------------------------------------------------------------------------
/helm/cas-server/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for cas-server.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 |
5 | casServerName: cas.example.org
6 |
7 | replicaCount: 1
8 |
9 | image:
10 | registry: ""
11 | repository: "apereo/cas"
12 | pullPolicy: IfNotPresent
13 | # Overrides the image tag whose default is the chart appVersion.
14 | tag: "latest"
15 |
16 |
17 | imagePullSecrets: []
18 | nameOverride: ""
19 | fullnameOverride: ""
20 |
21 | # There are two valid stateful set update strategies, RollingUpdate and the (legacy) OnDelete
22 | updateStrategy: RollingUpdate
23 |
24 | # OrderedReady: Pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing.
25 | # When scaling down, the pods are removed in the opposite order.
26 | # Parallel: Creates pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.
27 | podManagementPolicy: OrderedReady
28 |
29 | # Map folder for logs directory from host or pvc, or leave both blank to use emptyDir volume
30 | # In docker for windows hostPath could be '/host_mnt/c/opt/cas/logs'
31 | # Windows: Give full access local Users group to the to ~/.docker folder if getting permission denied)
32 | logdir:
33 | # hostPath: '/host_mnt/c/opt/cas/logs'
34 | hostPath: ''
35 | claimName: ''
36 | mountPath: '/var/log'
37 |
38 | # CAS Server container properties
39 | casServerContainer:
40 | ## Roll on upgrade changes deployment when helm upgrade runs, forcing pod to restart
41 | alwaysRoll: false
42 | ## JVM Settings
43 | ## JVM settings only used if command not set, use args to set app arguments
44 | jvm:
45 | ## Extra JVM options
46 | ##
47 | extraOpts: '-Djavax.net.ssl.trustStore=/etc/cas/truststore -Djavax.net.ssl.trustStoreType=PKCS12 -Djavax.net.ssl.trustStorePassword=changeit'
48 |
49 | ## Memory settings: If these aren't defined, java will calc values automatically, but requires setting limits on pod
50 | ## so it doesn't base heap size on host memory
51 | maxHeapOpt: '-Xmx2G'
52 | newHeapOpt: '-Xms600M'
53 | debugEnabled: true
54 | debugSuspend: "n" # could be n or y, must quote or yaml changes to boolean
55 | warPath: 'cas.war'
56 | ## Override cmd
57 | ##
58 | command:
59 | ## Override args
60 | ##
61 | args:
62 | ## extraVolumes and extraVolumeMounts allows you to mount other volumes
63 | ## Examples:
64 | ## extraVolumeMounts:
65 | ## - name: extras
66 | ## mountPath: /usr/share/extras
67 | ## readOnly: true
68 | ## extraVolumes:
69 | ## - name: extras
70 | ## emptyDir: {}
71 | ##
72 | profiles: 'standalone'
73 |
74 | extraVolumeMounts:
75 | - name: truststore
76 | mountPath: /etc/cas/truststore
77 | subPath: truststore
78 |
79 | extraVolumes:
80 | - name: truststore
81 | configMap:
82 | name: cas-truststore
83 | defaultMode: 0444
84 |
85 | ## Url to use for readiness, startupprobe, and liveliness check, change to health actuator if the module is available
86 | ## Naming it "default" in case in future template supports individual urls for the different checks, with this as default if they aren't specified
87 | defaultStatusUrl: '/cas/actuator/health'
88 |
89 | # number of startup probe failures before it will be killed, set high if trying to debug startup issues
90 | # liveness and readiness failure threshold might be 1 but startup failure threshold accounts for
91 | # failures while server is starting up
92 | startupFailureThreshold: 30
93 | livenessFailureThreshold: 1
94 | readinessFailureThreshold: 1
95 | readinessInitialDelaySeconds: 45
96 | livenessInitialDelaySeconds: 120
97 |
98 | ## Extra init containers to add to the statefulset
99 | ##
100 | initContainers: []
101 |
102 | ## An array to add extra env vars
103 | ## For example:
104 | ## extraEnvVars:
105 | ## - name: MY_ENV_VAR
106 | ## value: env_var_value
107 | ##
108 | extraEnvVars: []
109 |
110 | ## Name of a ConfigMap containing extra env vars
111 | ##
112 | extraEnvVarsConfigMap: ''
113 |
114 | # name of secret containing server keystore
115 | serverKeystoreExistingSecret: cas-server-keystore
116 | # folder that should container the keystore
117 | serverKeystoreMountPath: '/etc/cas/thekeystore'
118 | # name of keystore file in container and in secret
119 | serverKeystoreSubPath: 'thekeystore'
120 |
121 | ## Name of a Secret containing extra env vars
122 | ##
123 | extraEnvVarsSecret: ''
124 | ## Choose which config files from casConfig to mount
125 | casConfigMounts:
126 | - 'cas.properties'
127 | - 'cas.yaml'
128 | ## Create various config files from casConfig that may or may not be mounted
129 | casConfig:
130 | # issue with line breaks? means can't use {{}} variables after first line
131 | # workaround is to use {{}} variables in yaml version of properties file
132 | cas.properties: |-
133 | cas.server.name=https://{{ .Values.casServerName }}
134 | context.path=/cas
135 | cas.server.prefix=${cas.server.name}${context.path}
136 |
137 | cas.http-client.truststore.psw=changeit
138 | cas.http-client.truststore.file=/etc/cas/truststore
139 |
140 | # put web access logs in same directory as cas logs
141 | cas.server.tomcat.ext-access-log.directory=/var/log
142 | cas.server.tomcat.ext-access-log.enabled=true
143 |
144 | # uncomment the folowing to not allow login of built-in users
145 | # cas.authn.accept.users=
146 |
147 | # since we are behind ingress controller, need to use x-forwarded-for to get client ip
148 | # if nginx ingress controller is behind another proxy, it needs to be configured globally with the following settings in the ingress controller configmap
149 | # use-forwarded-headers: "true" # very important for CAS or any app that compares IP being used against IP that initiated sessions (session fixation)
150 | # enable-underscores-in-headers: "true" # while you are at it, allow underscores in headers, can't recall if important for cas but no need to have nginx dropping your headers with underscores
151 | cas.audit.engine.alternate-client-addr-header-name=X-Forwarded-For
152 | server.tomcat.remoteip.remote-ip-header=X-FORWARDED-FOR
153 |
154 | server.ssl.key-store=file:/etc/cas/thekeystore
155 | server.ssl.key-store-type=PKCS12
156 | server.ssl.key-store-password=changeit
157 | server.ssl.trust-store=file:/etc/cas/truststore
158 | server.ssl.trust-store-type=PKCS12
159 | server.ssl.trust-store-password=changeit
160 |
161 | # expose endpoints via http
162 | management.endpoints.web.exposure.include=health,info,prometheus,metrics,env,loggers,statistics,status,loggingConfig,events,configurationMetadata,caches
163 | management.endpoints.web.base-path=/actuator
164 | management.endpoints.web.cors.allowed-origins=https://${cas-host}
165 | management.endpoints.web.cors.allowed-methods=GET,POST
166 |
167 | # enable endpoints
168 | management.endpoint.metrics.enabled=true
169 | management.endpoint.health.enabled=true
170 | management.endpoint.info.enabled=true
171 | management.endpoint.env.enabled=true
172 | management.endpoint.loggers.enabled=true
173 | management.endpoint.status.enabled=true
174 | management.endpoint.statistics.enabled=true
175 | management.endpoint.prometheus.enabled=true
176 | management.endpoint.events.enabled=true
177 | management.endpoint.loggingConfig.enabled=true
178 | management.endpoint.configurationMetadata.enabled=true
179 | # configure health endpoint
180 | management.health.defaults.enabled=false
181 | management.health.ping.enabled=true
182 | management.health.caches.enabled=true
183 |
184 | # secure endpoints to localhost
185 |
186 | cas.monitor.endpoints.endpoint.defaults.access[0]=AUTHENTICATED
187 | cas.monitor.endpoints.endpoint.health.access[0]=IP_ADDRESS
188 | cas.monitor.endpoints.endpoint.health.requiredIpAddresses[0]=127.0.0.1
189 | cas.monitor.endpoints.endpoint.health.requiredIpAddresses[1]=0:0:0:0:0:0:0:1
190 | cas.monitor.endpoints.endpoint.health.requiredIpAddresses[2]=10\\..*
191 | cas.monitor.endpoints.endpoint.health.requiredIpAddresses[3]=172\\.16\\..*
192 | cas.monitor.endpoints.endpoint.health.requiredIpAddresses[4]=192\\.168\\..*
193 | #eof
194 |
195 | cas.yaml: |-
196 | ---
197 | logging:
198 | config: 'file:/etc/cas/config/log4j2.xml'
199 | cas:
200 | server:
201 | tomcat:
202 | clustering:
203 | enabled: true
204 | clustering-type: 'CLOUD'
205 | cloud-membership-provider: 'kubernetes'
206 | spring:
207 | security:
208 | user:
209 | name: "{{ .Values.casAdminUser }}"
210 | password: "{{ .Values.casAdminPassword }}"
211 | #eof
212 |
213 | podAnnotations: {}
214 |
215 | ## Pod security context
216 | ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod
217 | ##
218 | podSecurityContext:
219 | enabled: true
220 | fsGroup: 1000
221 |
222 | containerSecurityContext:
223 | enabled: false
224 | # capabilities:
225 | # drop:
226 | # - ALL
227 | # readOnlyRootFilesystem: true
228 | # runAsNonRoot: true
229 | runAsUser: 1000
230 |
231 | ## Override parts of this ingress in your own values file with appropriate host names
232 | ## This currently is only set up to work with Nginx Ingress Controller from Kubernetes project
233 | cas:
234 | service:
235 | type: ClusterIP
236 | publishNotReadyAddresses: true
237 | port: 8443
238 | listenPortHttps: 8443
239 | listenPortJvmDebug: 5005
240 | ingress:
241 | enabled: true
242 | annotations:
243 | kubernetes.io/ingress.class: nginx
244 | nginx.ingress.kubernetes.io/session-cookie-samesite: "None"
245 | nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none: "true"
246 | nginx.ingress.kubernetes.io/affinity: "cookie"
247 | nginx.ingress.kubernetes.io/session-cookie-name: "sticky-session-route"
248 | nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
249 | nginx.ingress.kubernetes.io/secure-backends: "true"
250 | nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
251 | hosts:
252 | - host: cas.example.org
253 | paths:
254 | - "/cas"
255 | - host: kubernetes.docker.internal
256 | paths:
257 | - "/cas"
258 | tls:
259 | - secretName: cas-server-ingress-tls
260 | hosts:
261 | - cas.example.org
262 | - kubernetes.docker.internal
263 |
264 | # Request some resources for main cas server so kubernetes will schedule somewhere with enough resources
265 | # Limits can also be set if desired
266 | resources:
267 | requests:
268 | cpu: 100m
269 | memory: 512Mi
270 | # limits:
271 | # cpu: 100m
272 | # memory: 128Mi
273 |
274 | # node selector for CAS server
275 | nodeSelector: {}
276 | # tolerations for CAS server (i.e taints on nodes that it can tolerate)
277 | tolerations: []
278 | # affinity config for CAS server
279 | affinity: {}
280 |
281 | casAdminUser: 'casuser'
282 | casAdminPassword: 'Mellon'
283 |
284 | # rbac may or may not be necessary, but it can allow for certain types of discovery (e.g. tomcat cloud session replication)
285 | rbac:
286 | # specified whether RBAC resources should be created
287 | create: true
288 |
289 | serviceAccount:
290 | # Specifies whether a service account should be created
291 | create: true
292 | # Annotations to add to the service account
293 | annotations: {}
294 | # The name of the service account to use.
295 | # If not set and create is true, a name is generated using the fullname template
296 | name: ""
297 |
298 |
299 | ## CAS can use a persistent volume to store config such as services and saml IDP/SP metadata that it pulls from git
300 | ## Enable persistence using Persistent Volume Claims
301 | ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
302 | ##
303 | persistence:
304 | ## If true, use a Persistent Volume Claim for data folder mounted where you specify using mountPath
305 | ##
306 | enabled: true
307 | ## Persistent Volume Storage Class
308 | ## If defined, storageClassName:
309 | ## If set to "-", storageClassName: "", which disables dynamic provisioning
310 | ## If undefined (the default) or set to null, no storageClassName spec is
311 | ## set, choosing the default provisioner. (gp2 on AWS, standard on
312 | ## GKE, AWS & OpenStack)
313 | ##
314 | # storageClass: "-"
315 | ## Persistent Volume Claim annotations
316 | ##
317 | annotations:
318 | ## Persistent Volume Access Mode
319 | ##
320 | accessModes:
321 | - ReadWriteOnce
322 | ## Persistent Volume size
323 | ##
324 | size: 2Gi
325 | ## The path the volume will be mounted at, will contain writable folder called "data" under mountPath,
326 | ## if volumePermissions init container creates it
327 | ##
328 | mountPath: /var/cas
329 |
330 | ## Init containers parameters:
331 | ## volumePermissions: Change the owner and group of the persistent volume mountpoint to runAsUser:fsGroup values from
332 | ## the securityContext section.
333 | ##
334 | volumePermissions:
335 | enabled: false
336 | image:
337 | registry: docker.io
338 | repository: alpine
339 | tag: latest
340 | pullPolicy: Always
341 | ## Optionally specify an array of imagePullSecrets.
342 | ## Secrets must be manually created in the namespace.
343 | ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
344 | ##
345 | # pullSecrets:
346 | # - myRegistryKeySecretName
347 | ## Init container' resource requests and limits
348 | ## ref: http://kubernetes.io/docs/user-guide/compute-resources/
349 | ##
350 | resources:
351 | # We usually recommend not to specify default resources and to leave this as a conscious
352 | # choice for the user. This also increases chances charts run on environments with little
353 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
354 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
355 | limits: {}
356 | # cpu: 100m
357 | # memory: 128Mi
358 | requests: {}
359 | # cpu: 100m
360 | # memory: 128Mi
361 | ## Init container Security Context
362 | ## Note: the chown of the data folder is done to securityContext.runAsUser
363 | ## and not the below volumePermissions.securityContext.runAsUser
364 | ## When runAsUser is set to special value "auto", init container will try to chown the
365 | ## data folder to autodetermined user&group, using commands: `id -u`:`id -G | cut -d" " -f2`
366 | ## "auto" is especially useful for OpenShift which has scc with dynamic userids (and 0 is not allowed).
367 | ## You may want to use this volumePermissions.securityContext.runAsUser="auto" in combination with
368 | ## pod securityContext.enabled=false and shmVolume.chmod.enabled=false
369 | ##
370 | securityContext:
371 | runAsUser: 0
372 |
--------------------------------------------------------------------------------
/gradle/tasks.gradle:
--------------------------------------------------------------------------------
1 | import static org.gradle.internal.logging.text.StyledTextOutput.Style
2 |
3 | import org.apereo.cas.metadata.*
4 | import org.gradle.internal.logging.text.*
5 |
6 | import groovy.json.*
7 | import groovy.time.*
8 | import groovy.xml.*
9 |
10 | import java.nio.file.*
11 | import java.util.*
12 | import java.security.*
13 |
14 | buildscript {
15 | repositories {
16 | mavenLocal()
17 | mavenCentral()
18 | gradlePluginPortal()
19 | maven {
20 | url = 'https://central.sonatype.com/repository/maven-snapshots/'
21 | mavenContent { snapshotsOnly() }
22 | }
23 | maven {
24 | url = "https://repo.spring.io/milestone"
25 | mavenContent { releasesOnly() }
26 | }
27 | }
28 | dependencies {
29 | classpath "org.apache.ivy:ivy:${project.ivyVersion}"
30 | classpath "org.apereo.cas:cas-server-support-configuration-metadata-repository:${project.'cas.version'}"
31 | }
32 | }
33 | apply plugin: "de.undercouch.download"
34 |
35 | tasks.register('debug', JavaExec) {
36 | group = "CAS"
37 | description = "Debug the CAS web application in embedded container mode on port 5005"
38 | jvmArgs = [
39 | "-server",
40 | "-Xmx2048M",
41 | "-XX:+TieredCompilation",
42 | "-XX:TieredStopAtLevel=1"
43 | ]
44 | debug = true
45 | systemProperties = System.properties
46 | classpath = files("$buildDir/libs/cas.war")
47 | }
48 | tasks.register('run', JavaExec) {
49 | group = "CAS"
50 | description = "Run the CAS web application in embedded container mode"
51 | jvmArgs = [
52 | "-server",
53 | "-Xmx2048M",
54 | "-XX:+TieredCompilation",
55 | "-XX:TieredStopAtLevel=1"
56 | ]
57 | debug = false
58 | systemProperties = System.properties
59 | classpath = files("$buildDir/libs/cas.war")
60 | }
61 |
62 |
63 |
64 | task showConfiguration(group: "CAS", description: "Show configurations for each dependency, etc") {
65 | doLast() {
66 | def cfg = project.hasProperty("configuration") ? project.property("configuration") : "compile"
67 | configurations.getByName(cfg).each { println it }
68 | }
69 | }
70 |
71 | task allDependenciesInsight(group: "build", type: DependencyInsightReportTask, description: "Produce insight information for all dependencies") {}
72 |
73 | task allDependencies(group: "build", type: DependencyReportTask, description: "Display a graph of all project dependencies") {}
74 |
75 | task casVersion(group: "CAS", description: "Display the current CAS version") {
76 | doFirst {
77 | def verbose = project.hasProperty("verbose") && Boolean.valueOf(project.getProperty("verbose"))
78 | if (verbose) {
79 | def out = services.get(StyledTextOutputFactory).create("CAS")
80 | println "******************************************************************"
81 | out.withStyle(Style.Info).println "Apereo CAS ${project.version}"
82 | out.withStyle(Style.Description).println "Enterprise Single SignOn for all earthlings and beyond"
83 | out.withStyle(Style.SuccessHeader).println "- GitHub: "
84 | out.withStyle(Style.Success).println "https://github.com/apereo/cas"
85 | out.withStyle(Style.SuccessHeader).println "- Docs: "
86 | out.withStyle(Style.Success).println "https://apereo.github.io/cas"
87 | out.withStyle(Style.SuccessHeader).println "- Blog: "
88 | out.withStyle(Style.Success).println "https://apereo.github.io"
89 | println "******************************************************************"
90 | } else {
91 | println project.version
92 | }
93 | }
94 | }
95 |
96 | task springBootVersion(description: "Display current Spring Boot version") {
97 | doLast {
98 | println rootProject.springBootVersion
99 | }
100 | }
101 |
102 | task zip(type: Zip, description: "Package the CAS overlay into a zip archive", group: "CAS") {
103 | from projectDir
104 | exclude '**/.idea/**', '.gradle', 'tmp', '.git', '**/build/**', '**/bin/**', '**/out/**', '**/.settings/**'
105 | destinationDirectory = buildDir
106 | archiveFileName = "${project.name}.zip"
107 | def zipFile = new File("${buildDir}/${archiveFileName.get()}")
108 | doLast {
109 | if (zipFile.exists()) {
110 | println "Zip archive is available at ${zipFile.absolutePath}"
111 | }
112 | }
113 | }
114 |
115 | task createKeystore(group: "CAS", description: "Create CAS keystore") {
116 | def dn = "CN=cas.example.org,OU=Example,OU=Org,C=US"
117 | if (project.hasProperty("certificateDn")) {
118 | dn = project.getProperty("certificateDn")
119 | }
120 | def subjectAltName = "dns:example.org,dns:localhost,ip:127.0.0.1"
121 | if (project.hasProperty("certificateSubAltName")) {
122 | subjectAltName = project.getProperty("certificateSubAltName")
123 | }
124 |
125 | doFirst {
126 | def certDir = project.getProperty("certDir")
127 | def serverKeyStore = project.getProperty("serverKeystore")
128 | def exportedServerCert = project.getProperty("exportedServerCert")
129 | def storeType = project.getProperty("storeType")
130 | def keystorePath = "$certDir/$serverKeyStore"
131 | def serverCert = "$certDir/$exportedServerCert"
132 |
133 | mkdir certDir
134 | // this will fail if thekeystore exists and has cert with cas alias already (so delete if you want to recreate)
135 | logger.info "Generating keystore for CAS with DN ${dn}"
136 | exec {
137 | workingDir "."
138 | commandLine "keytool", "-genkeypair", "-alias", "cas",
139 | "-keyalg", "RSA",
140 | "-keypass", "changeit", "-storepass", "changeit",
141 | "-keystore", keystorePath,
142 | "-dname", dn, "-ext", "SAN=${subjectAltName}",
143 | "-storetype", storeType
144 | }
145 | logger.info "Exporting cert from keystore..."
146 | exec {
147 | workingDir "."
148 | commandLine "keytool", "-exportcert", "-alias", "cas",
149 | "-storepass", "changeit", "-keystore", keystorePath,
150 | "-file", serverCert
151 | }
152 | logger.info "Import $serverCert into your Java truststore (\$JAVA_HOME/lib/security/cacerts)"
153 | }
154 | }
155 |
156 | task unzipWAR(type: Copy, group: "CAS", description: "Explodes the CAS web application archive") {
157 | dependsOn 'build'
158 | def destination = "${buildDir}/app"
159 |
160 | from zipTree("build/libs/cas.war")
161 | into "${destination}"
162 | doLast {
163 | println "Unzipped WAR into ${destination}"
164 | }
165 | }
166 |
167 | task verifyRequiredJavaVersion {
168 | def currentVersion = org.gradle.api.JavaVersion.current()
169 | logger.info "Checking current Java version ${currentVersion} for required Java version ${project.targetCompatibility}"
170 | def targetVersion = JavaVersion.toVersion(project.targetCompatibility)
171 | if (!currentVersion.isCompatibleWith(targetVersion)) {
172 | logger.warn("Careful: Current Java version ${currentVersion} does not match required Java version ${project.targetCompatibility}")
173 | }
174 | }
175 |
176 | task copyCasConfiguration(type: Copy, group: "CAS",
177 | description: "Copy the CAS configuration from this project to /etc/cas/config") {
178 | from "etc/cas/config"
179 | into new File('/etc/cas/config').absolutePath
180 | doFirst {
181 | new File('/etc/cas/config').mkdirs()
182 | }
183 | }
184 |
185 |
186 | def explodedDir = "${buildDir}/app"
187 | def explodedResourcesDir = "${buildDir}/cas-resources"
188 |
189 | def resourcesJarName = "cas-server-webapp-resources"
190 | def templateViewsJarName = "cas-server-support-thymeleaf"
191 |
192 | task unzip(type: Copy, group: "CAS", description: "Explodes the CAS archive and resources jar from the CAS web application archive") {
193 | dependsOn unzipWAR
194 | from zipTree("${explodedDir}/WEB-INF/lib/${templateViewsJarName}-${project.'cas.version'}.jar")
195 | into explodedResourcesDir
196 |
197 | from zipTree("${explodedDir}/WEB-INF/lib/${resourcesJarName}-${project.'cas.version'}.jar")
198 | into explodedResourcesDir
199 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE
200 | doLast {
201 | println "Exploded WAR resources into ${explodedResourcesDir}"
202 | }
203 | }
204 |
205 | task downloadShell(group: "Shell", description: "Download CAS shell jar from snapshot or release maven repo", type: Download) {
206 | def shellDir = project.providers.gradleProperty("shellDir").get()
207 | def casVersion = project.providers.gradleProperty("cas.version").get()
208 | def downloadFile
209 | if (casVersion.contains("-SNAPSHOT")) {
210 | def snapshotDir = "https://central.sonatype.com/repository/maven-snapshots/org/apereo/cas/cas-server-support-shell/${casVersion}"
211 | def metaUrl = "${snapshotDir}/maven-metadata.xml"
212 | def xml = new XmlSlurper().parse(metaUrl.toURL().openStream())
213 | def sv = xml.versioning.snapshotVersions.snapshotVersion.findAll {
214 | it.extension.text() == "jar" && it.classifier.text() == ""
215 | }.sort { a, b -> a.updated.text() <=> b.updated.text() }.last()
216 | def stamped = sv.value.text()
217 | def jarName = "cas-server-support-shell-${stamped}.jar"
218 | def jarUrl = "${snapshotDir}/${jarName}"
219 | downloadFile = jarUrl
220 | } else {
221 | downloadFile = "https://github.com/apereo/cas/releases/download/v${casVersion}/cas-server-support-shell-${casVersion}.jar"
222 | }
223 | new File("${shellDir}").mkdir()
224 | logger.info "Downloading file: ${downloadFile} into ${shellDir}"
225 | src downloadFile
226 | dest new File("${shellDir}", "cas-server-support-shell-${casVersion}.jar")
227 | overwrite false
228 | }
229 |
230 | task runShell(group: "Shell", description: "Run the CAS shell") {
231 | dependsOn downloadShell
232 | def shellDir = project.providers.gradleProperty("shellDir").get()
233 | def casVersion = project.providers.gradleProperty("cas.version").get()
234 | doLast {
235 | println "Run the following command to launch the shell:\n\tjava -jar ${shellDir}/cas-server-support-shell-${casVersion}.jar"
236 | }
237 | }
238 |
239 | task debugShell(group: "Shell", description: "Run the CAS shell with debug options, wait for debugger on port 5005") {
240 | dependsOn downloadShell
241 | def casVersion = project.providers.gradleProperty("cas.version").get()
242 | def shellDir = project.providers.gradleProperty("shellDir").get()
243 | doLast {
244 | println """
245 | Run the following command to launch the shell:\n\t
246 | java -Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=y -jar ${shellDir}/cas-server-support-shell-${casVersion}.jar
247 | """
248 | }
249 | }
250 |
251 | task listTemplateViews(group: "CAS", description: "List all CAS views") {
252 | dependsOn unzip
253 |
254 | def templateViews = fileTree(explodedResourcesDir).matching {
255 | include "**/*.html"
256 | }
257 | .collect {
258 | return it.path.replace(explodedResourcesDir, "")
259 | }
260 | .toSorted()
261 |
262 | doFirst {
263 | templateViews.each { println it }
264 | }
265 | }
266 |
267 | task getResource(group: "CAS", description: "Fetch a CAS resource and move it into the overlay") {
268 | dependsOn unzip
269 |
270 | def resourceName = project.providers.gradleProperty("resourceName").getOrNull()
271 | def resourcesDirectory = fileTree(explodedResourcesDir)
272 | def projectDirectory = projectDir
273 |
274 | doFirst {
275 | def results = resourcesDirectory.matching {
276 | include "**/${resourceName}.*"
277 | include "**/${resourceName}"
278 | }
279 | if (results.isEmpty()) {
280 | println "No resources could be found matching ${resourceName}"
281 | return
282 | }
283 | if (results.size() > 1) {
284 | println "Multiple resources found matching ${resourceName}:\n"
285 | results.each {
286 | println "\t-" + it.path.replace(explodedResourcesDir, "")
287 | }
288 | println "\nNarrow down your search criteria and try again."
289 | return
290 | }
291 |
292 | def fromFile = explodedResourcesDir
293 | def resourcesDir = "src/main/resources"
294 | new File(resourcesDir).mkdir()
295 |
296 | def resourceFile = results[0].canonicalPath
297 | def toResourceFile = new File("${projectDirectory}", resourceFile.replace(fromFile, resourcesDir))
298 | toResourceFile.getParentFile().mkdirs()
299 |
300 | Files.copy(Paths.get(resourceFile), Paths.get(toResourceFile.absolutePath), StandardCopyOption.REPLACE_EXISTING)
301 | println "Copied file ${resourceFile} to ${toResourceFile}"
302 | }
303 | }
304 |
305 | task createTheme(group: "CAS", description: "Create theme directory structure in the overlay") {
306 | def theme = project.providers.gradleProperty("theme").getOrNull()
307 |
308 | doFirst {
309 | def builder = new FileTreeBuilder()
310 | new File("src/main/resources/${theme}.properties").delete()
311 |
312 | builder.src {
313 | main {
314 | resources {
315 | "static" {
316 | themes {
317 | "${theme}" {
318 | css {
319 | 'cas.css'('')
320 | }
321 | js {
322 | 'cas.js'('')
323 | }
324 | images {
325 | '.ignore'('')
326 | }
327 | }
328 | }
329 | }
330 |
331 | templates {
332 | "${theme}" {
333 | fragments {
334 |
335 | }
336 | }
337 | }
338 |
339 | "${theme}.properties"("""cas.standard.css.file=/themes/${theme}/css/cas.css
340 | cas.standard.js.file=/themes/${theme}/js/cas.js
341 | """)
342 | }
343 | }
344 | }
345 | }
346 | }
347 |
348 | def skipValidation = project.hasProperty("validate") && project.property("validate").equals("false")
349 | if (!skipValidation) {
350 | task validateConfiguration(type: Copy, group: "CAS",
351 | description: "Validate CAS configuration") {
352 | def file = new File("${projectDir}/src/main/resources/application.properties")
353 | if (file.exists()) {
354 | throw new GradleException("This overlay project is overriding a CAS-supplied configuration file at ${file.path}. "
355 | + "Overriding this file will disable all default CAS settings that are provided to the overlay, and "
356 | + "generally has unintended side-effects. It's best to move your configuration inside an application.yml "
357 | + "file, if you intend to keep the configuration bundled with the CAS web application. \n\nTo disable this "
358 | + "validation step, run the build with -Pvalidate=false.");
359 | }
360 | }
361 | processResources.dependsOn(validateConfiguration)
362 | }
363 |
364 | task duct(group: "CAS", description: "Test ticket registry functionality via the CAS REST API") {
365 | def service = project.findProperty("duct.service") ?: "https://apereo.github.io"
366 | def casServerNodes = providers.gradlePropertiesPrefixedBy("duct.cas").get()
367 | def username = project.findProperty("duct.username") ?: "casuser"
368 | def password = project.findProperty("duct.password") ?: "Mellon"
369 | def debug = Boolean.parseBoolean(project.findProperty("duct.debug") ?: "false")
370 | def duration = Long.parseLong(project.findProperty("duct.duration") ?: "-1")
371 | def count = Long.parseLong(project.findProperty("duct.count") ?: "-1")
372 |
373 | doLast {
374 | def out = services.get(StyledTextOutputFactory).create("cas")
375 |
376 | def getCasServerNode = {
377 | def casServerNodesArray = casServerNodes.values().toArray()
378 | return casServerNodesArray[new SecureRandom().nextInt(casServerNodesArray.length)] as String
379 | }
380 |
381 | def startTime = new Date()
382 | def keepGoing = true
383 | def executionCount = 0
384 |
385 | while(keepGoing) {
386 | executionCount++
387 |
388 | def casServerPrefix1 = getCasServerNode()
389 | def casServerPrefix2 = getCasServerNode()
390 |
391 | if (casServerNodes.size() >= 2) {
392 | while (casServerPrefix1.equals(casServerPrefix2)) {
393 | casServerPrefix2 = getCasServerNode()
394 | }
395 | }
396 |
397 | if (debug) {
398 | out.withStyle(Style.Normal).println("CAS Server 1: ${casServerPrefix1}")
399 | out.withStyle(Style.Normal).println("CAS Server 2: ${casServerPrefix2}")
400 | out.withStyle(Style.Normal).println("Fetching ticket-granting ticket @ ${casServerPrefix1} for ${username}...")
401 | }
402 | def connection = new URL("${casServerPrefix1}/v1/tickets").openConnection()
403 | connection.setRequestMethod("POST")
404 | connection.setDoOutput(true)
405 | connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
406 | connection.getOutputStream().write("username=${username}&password=${password}".getBytes("UTF-8"))
407 | def rc = connection.getResponseCode()
408 |
409 | if (rc == 201) {
410 | def tgt = connection.getHeaderFields().get("Location").get(0).find('TGT-.*')
411 |
412 | if (debug) {
413 | out.withStyle(Style.Normal).println("Received ticket-granting ticket ${tgt} @ ${casServerPrefix2} for ${username}...")
414 | out.withStyle(Style.Normal).println("Fetching service ticket @ ${casServerPrefix2} for ${tgt} and service ${service}...")
415 | }
416 | connection = new URL("${casServerPrefix2}/v1/tickets/${tgt}").openConnection()
417 | connection.setRequestMethod("POST")
418 | connection.setDoOutput(true)
419 | connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
420 | connection.getOutputStream().write("service=${service}".getBytes("UTF-8"))
421 | rc = connection.getResponseCode()
422 |
423 | if (rc == 200) {
424 | def st = connection.getInputStream().getText()
425 | if (debug) {
426 | out.withStyle(Style.Normal).println("Received service ticket ${st} @ ${casServerPrefix1} for ${service}...")
427 | out.withStyle(Style.Normal).println("Validating service ticket ${st} @ ${casServerPrefix1} for service ${service}...")
428 | }
429 | connection = new URL("${casServerPrefix1}/p3/serviceValidate?service=${service}&ticket=${st}&format=json").openConnection()
430 | connection.setRequestMethod("GET")
431 | connection.setDoOutput(true)
432 | connection.setRequestProperty("Content-Type", "application/json")
433 | rc = connection.getResponseCode()
434 |
435 | if (rc == 200) {
436 | def serverResponse = connection.getInputStream().getText()
437 | def response = new JsonSlurper().parseText(serverResponse)
438 |
439 | if (response.serviceResponse["authenticationSuccess"] != null) {
440 | out.withStyle(Style.Success).println("Service ticket ${st} is successfully validated @ ${casServerPrefix1}")
441 | } else {
442 | out.withStyle(Style.Failure).println("Service ticket ${st} cannot be validated @ ${casServerPrefix1} for ${tgt}")
443 | if (debug) {
444 | out.withStyle(Style.Failure).println(serverResponse)
445 | }
446 | }
447 | } else {
448 | out.withStyle(Style.Failure).println("${rc}: Unable to validate service ticket ${st} @ ${casServerPrefix1} for ${tgt}")
449 | }
450 | } else {
451 | out.withStyle(Style.Failure).println("${rc}: Unable to fetch service ticket @ ${casServerPrefix2} for ${tgt}")
452 | }
453 | } else {
454 | out.withStyle(Style.Failure).println("${rc}: Unable to fetch ticket-granting ticket @ ${casServerPrefix1}")
455 | }
456 |
457 | if (keepGoing && duration > 0) {
458 | def executionDuration = TimeCategory.minus(new Date(), startTime)
459 | keepGoing = executionDuration.getSeconds() < duration
460 | }
461 | if (keepGoing) {
462 | keepGoing = executionCount < count
463 | }
464 | Thread.sleep(250)
465 | }
466 | }
467 | }
468 |
469 | task exportConfigMetadata(group: "CAS", description: "Export collection of CAS properties") {
470 | def file = new File(project.rootDir, 'config-metadata.properties')
471 | def queryType = ConfigurationMetadataCatalogQuery.QueryTypes.CAS
472 | if (project.hasProperty("queryType")) {
473 | queryType = ConfigurationMetadataCatalogQuery.QueryTypes.valueOf(project.findProperty("queryType"))
474 | }
475 | doLast {
476 | file.withWriter('utf-8') { writer ->
477 | def props = CasConfigurationMetadataCatalog.query(
478 | ConfigurationMetadataCatalogQuery.builder()
479 | .queryType(queryType)
480 | .build())
481 | .properties()
482 | props.each { property ->
483 | writer.writeLine("# Type: ${property.type}");
484 | writer.writeLine("# Module: ${property.module}")
485 | writer.writeLine("# Owner: ${property.owner}")
486 | if (property.deprecationLevel != null) {
487 | writer.writeLine("# This setting is deprecated with a severity level of ${property.deprecationLevel}.")
488 | if (property.deprecationReason != null) {
489 | writer.writeLine("# because ${property.deprecationReason}")
490 | }
491 | if (property.deprecationReason != null) {
492 | writer.writeLine("# Replace with: ${property.deprecationReason}")
493 | }
494 | }
495 | writer.writeLine("#")
496 | def description = property.description.replace("\n", "\n# ").replace("\r", "")
497 | description = org.apache.commons.text.WordUtils.wrap(description, 70, "\n# ", true)
498 | writer.writeLine("# ${description}")
499 | writer.writeLine("#")
500 | writer.writeLine("# ${property.name}: ${property.defaultValue}")
501 | writer.writeLine("")
502 | }
503 | }
504 | println "Configuration metadata is available at ${file.absolutePath}"
505 | }
506 | }
507 |
--------------------------------------------------------------------------------