From 2469deece018ef55d6d60ca01fd7cae42223dd29 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Sun, 2 Aug 2020 20:36:39 -0400 Subject: [PATCH 01/89] Upgrade to 6.2.x See: https://github.com/apereo/cas-overlay-template/tree/6.2 --- Dockerfile | 2 +- README.md | 2 +- build.gradle | 68 ++++++----- docker-build.sh | 8 +- docker-push.sh | 6 +- docker-run.sh | 2 +- etc/cas/config/cas.properties | 2 +- gradle.properties | 12 +- gradle/dockerjib.gradle | 4 +- gradle/springboot.gradle | 105 +++++++++++++--- gradle/tasks.gradle | 148 ++++++++++++++++++----- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 58910 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 31 +++-- gradlew.bat | 4 + lombok.config | 2 + src/main/webapp/WEB-INF/web.xml | 7 ++ 17 files changed, 296 insertions(+), 109 deletions(-) create mode 100644 lombok.config create mode 100644 src/main/webapp/WEB-INF/web.xml diff --git a/Dockerfile b/Dockerfile index b2f15ef..17ab564 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN mkdir -p ~/.gradle \ && ./gradlew --version; RUN cd cas-overlay \ - && ./gradlew clean build --parallel; + && ./gradlew clean build --parallel --no-daemon; FROM adoptopenjdk/openjdk11:alpine-jre AS cas diff --git a/README.md b/README.md index b224738..ec8d964 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Generic CAS WAR overlay to exercise the latest versions of CAS. This overlay cou # Versions -- CAS `6.1.x` +- CAS `6.2.x` - JDK `11` # Overview diff --git a/build.gradle b/build.gradle index 9ea1ee4..79a7e7c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,28 @@ buildscript { repositories { mavenLocal() + gradlePluginPortal() mavenCentral() jcenter() - maven { url "https://repo.spring.io/libs-milestone" } - maven { url "https://repo.spring.io/libs-snapshot" } - maven { url "https://plugins.gradle.org/m2/" } + maven { + url "https://repo.spring.io/libs-milestone" + mavenContent { releasesOnly() } + } + maven { + url "https://repo.spring.io/libs-snapshot" + mavenContent { snapshotsOnly() } + } + maven { + url "https://plugins.gradle.org/m2/" + mavenContent { releasesOnly() } + } } dependencies { classpath "de.undercouch:gradle-download-task:${project.gradleDownloadTaskVersion}" classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.springBootVersion}" classpath "gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:${project.jibVersion}" classpath "io.freefair.gradle:maven-plugin:${project.gradleMavenPluginVersion}" + classpath "io.freefair.gradle:lombok-plugin:${project.gradleLombokPluginVersion}" } } @@ -19,11 +30,26 @@ repositories { mavenLocal() mavenCentral() jcenter() - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } - maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } - maven { url "https://repo.spring.io/milestone/" } - maven { url "https://repo.spring.io/snapshot/" } - maven { url "https://oss.jfrog.org/artifactory/oss-snapshot-local" } + maven { + url "https://oss.sonatype.org/content/repositories/snapshots" + mavenContent { snapshotsOnly() } + } + maven { + mavenContent { releasesOnly() } + url "https://build.shibboleth.net/nexus/content/repositories/releases/" + } + maven { + mavenContent { releasesOnly() } + url "https://repo.spring.io/milestone/" + } + maven { + url "https://repo.spring.io/snapshot/" + mavenContent { snapshotsOnly() } + } + maven { + mavenContent { snapshotsOnly() } + url "https://oss.jfrog.org/artifactory/oss-snapshot-local" + } } def casServerVersion = project.'cas.version' @@ -33,6 +59,7 @@ project.ext."casServerVersion" = casServerVersion project.ext."casWebApplicationBinaryName" = casWebApplicationBinaryName apply plugin: "io.freefair.war-overlay" +apply plugin: "io.freefair.lombok" apply from: rootProject.file("gradle/tasks.gradle") apply plugin: "war" @@ -44,7 +71,7 @@ apply from: rootProject.file("gradle/dockerjib.gradle") dependencies { // Other CAS dependencies/modules may be listed here... - // compile "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}" + // implementation "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}" } tasks.findByName("jibDockerBuild") @@ -54,7 +81,7 @@ tasks.findByName("jibDockerBuild") tasks.findByName("jib") .dependsOn(copyWebAppIntoJib, copyConfigIntoJib) .finalizedBy(deleteWebAppFromJib) - + configurations.all { resolutionStrategy { cacheChangingModulesFor 0, "seconds" @@ -82,24 +109,3 @@ idea { downloadSources = true } } - -bootWar { - entryCompression = ZipEntryCompression.STORED - overlays { - // https://docs.freefair.io/gradle-plugins/current/reference/#_io_freefair_war_overlay - // Note: The "excludes" property is only for files in the war dependency. - // If a jar is excluded from the war, it could be brought back into the final war as a dependency - // of non-war dependencies. Those should be excluded via normal gradle dependency exclusions. - cas { - from "org.apereo.cas:cas-server-webapp${project.appServer}:${casServerVersion}@war" - provided = false - //excludes = ["WEB-INF/lib/somejar-1.0*"] - } - } -} - - -wrapper { - distributionType = Wrapper.DistributionType.BIN - gradleVersion = "${project.gradleVersion}" -} diff --git a/docker-build.sh b/docker-build.sh index 8f2c277..dc098ad 100755 --- a/docker-build.sh +++ b/docker-build.sh @@ -2,9 +2,9 @@ image_tag=(`cat gradle.properties | grep "cas.version" | cut -d= -f2`) -echo "Building CAS docker image tagged as [$image_tag]" +echo "Building CAS docker image tagged as [v$image_tag]" # read -p "Press [Enter] to continue..." any_key; -docker build --tag="org.apereo.cas/cas:$image_tag" . \ - && echo "Built CAS image successfully tagged as org.apereo.cas/cas:$image_tag" \ - && docker images "org.apereo.cas/cas:$image_tag" \ No newline at end of file +docker build --tag="apereo/cas:v$image_tag" . \ + && echo "Built CAS image successfully tagged as apereo/cas:v$image_tag" \ + && docker images "apereo/cas:v$image_tag" \ No newline at end of file diff --git a/docker-push.sh b/docker-push.sh index e04b107..ca7b784 100755 --- a/docker-push.sh +++ b/docker-push.sh @@ -7,6 +7,6 @@ echo "$docker_psw" | docker login --username "$docker_user" --password-stdin image_tag=(`cat gradle.properties | grep "cas.version" | cut -d= -f2`) -echo "Pushing CAS docker image tagged as $image_tag to org.apereo.cas/cas..." -docker push org.apereo.cas/cas:"$image_tag" \ - && echo "Pushed org.apereo.cas/cas:$image_tag successfully."; \ No newline at end of file +echo "Pushing CAS docker image tagged as v$image_tag to apereo/cas..." +docker push apereo/cas:"v$image_tag" \ + && echo "Pushed apereo/cas:v$image_tag successfully."; \ No newline at end of file diff --git a/docker-run.sh b/docker-run.sh index f862785..6b4ce97 100755 --- a/docker-run.sh +++ b/docker-run.sh @@ -3,5 +3,5 @@ docker stop cas > /dev/null 2>&1 docker rm cas > /dev/null 2>&1 image_tag=(`cat gradle.properties | grep "cas.version" | cut -d= -f2`) -docker run -d -p 8080:8080 -p 8443:8443 --name="cas" org.apereo.cas/cas:"${image_tag}" +docker run -d -p 8080:8080 -p 8443:8443 --name="cas" apereo/cas:"v${image_tag}" docker logs -f cas \ No newline at end of file diff --git a/etc/cas/config/cas.properties b/etc/cas/config/cas.properties index a3be0e1..40cd89b 100644 --- a/etc/cas/config/cas.properties +++ b/etc/cas/config/cas.properties @@ -1,6 +1,6 @@ cas.server.name=https://cas.example.org:8443 cas.server.prefix=${cas.server.name}/cas -logging.config: file:/etc/cas/config/log4j2.xml +logging.config=file:/etc/cas/config/log4j2.xml # cas.authn.accept.users= diff --git a/gradle.properties b/gradle.properties index c66cce4..cb578cd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,26 +1,26 @@ # Versions -cas.version=6.1.5 -springBootVersion=2.2.0.RELEASE +cas.version=6.2.1 +springBootVersion=2.2.8.RELEASE # Use -jetty, -undertow to other containers # Or blank if you want to deploy to an external container appServer=-tomcat executable=false -gradleVersion=5.6.3 -tomcatVersion=9.0.33 +tomcatVersion=9.0.36 group=org.apereo.cas sourceCompatibility=11 targetCompatibility=11 -jibVersion=1.7.0 +jibVersion=2.4.0 # Location of the downloaded CAS shell JAR shellDir=build/libs ivyVersion=2.4.0 gradleDownloadTaskVersion=3.4.3 -gradleMavenPluginVersion=3.8.4 +gradleMavenPluginVersion=4.1.5 +gradleLombokPluginVersion=4.1.5 # use without "-slim" in tag name if you want tools like jstack, adds about 100MB to image size # (https://hub.docker.com/r/adoptopenjdk/openjdk11/tags/) diff --git a/gradle/dockerjib.gradle b/gradle/dockerjib.gradle index dcd70e3..dbadd5a 100644 --- a/gradle/dockerjib.gradle +++ b/gradle/dockerjib.gradle @@ -17,11 +17,11 @@ jib { username = "*******" password = "*******" } - tags = [casServerVersion] */ + tags = ["v" + casServerVersion] } container { - useCurrentTimestamp = true + creationTime = "USE_CURRENT_TIMESTAMP" entrypoint = ['docker/entrypoint.sh'] ports = ['80', '443', '8080', '8443'] labels = [version:casServerVersion, name:project.name, group:project.group] diff --git a/gradle/springboot.gradle b/gradle/springboot.gradle index b6a46b8..79804b1 100644 --- a/gradle/springboot.gradle +++ b/gradle/springboot.gradle @@ -1,24 +1,101 @@ apply plugin: "org.springframework.boot" -bootRun.enabled = false -bootRun.onlyIf { return false } -tasks.remove(tasks['bootRun']) +configurations { + bootRunConfig.extendsFrom compileClasspath +} + +dependencies { + bootRunConfig "org.apereo.cas:cas-server-webapp-init:${casServerVersion}" + bootRunConfig "org.apereo.cas:cas-server-webapp-tomcat:${casServerVersion}" + bootRunConfig "org.springframework.boot:spring-boot-devtools:${project.springBootVersion}" +} + +sourceSets { + bootRunSources { + resources { + srcDirs new File("//etc/cas/templates/"), + new File("${project.getProjectDir()}/src/main/resources/") + } + } +} + +bootRun { + classpath = configurations.bootRunConfig + sourceSets.main.compileClasspath + sourceSets.main.runtimeClasspath + doFirst { + sourceResources sourceSets.bootRunSources + systemProperties = System.properties + } + + def list = [] + list.add("-XX:TieredStopAtLevel=1") + list.add("-Xverify:none") + list.add("--add-modules") + list.add("java.se") + list.add("--add-exports") + list.add("java.base/jdk.internal.ref=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.base/java.lang=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.base/java.nio=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.base/sun.nio.ch=ALL-UNNAMED") + list.add("--add-opens") + list.add("java.management/sun.management=ALL-UNNAMED") + list.add("--add-opens") + list.add("jdk.management/com.sun.management.internal=ALL-UNNAMED") + + list.add("-XX:+UnlockExperimentalVMOptions") + list.add("-XX:+EnableJVMCI") + list.add("-XX:+UseJVMCICompiler") + + list.add("-Xrunjdwp:transport=dt_socket,address=5000,server=y,suspend=n") + + jvmArgs = list + + def appArgList = [] + args = appArgList +} springBoot { + buildInfo() mainClassName = "org.apereo.cas.web.CasWebApplication" } bootWar { - doFirst { - def executable = project.hasProperty("executable") && Boolean.valueOf(project.getProperty("executable")) - if (executable) { - logger.info "Including launch script for executable WAR artifact" - launchScript() - } else { - logger.info "WAR artifact is not marked as an executable" + def executable = project.hasProperty("executable") && Boolean.valueOf(project.getProperty("executable")) + if (executable) { + logger.info "Including launch script for executable WAR artifact" + launchScript() + } else { + logger.info "WAR artifact is not marked as an executable" + } + archiveName "${casWebApplicationBinaryName}" + baseName "cas" + excludeDevtools = false + + entryCompression = ZipEntryCompression.STORED + /* + attachClasses = true + classesClassifier = 'classes' + archiveClasses = true + */ + overlays { + /* + https://docs.freefair.io/gradle-plugins/current/reference/#_io_freefair_war_overlay + Note: The "excludes" property is only for files in the war dependency. + If a jar is excluded from the war, it could be brought back into the final war as a dependency + of non-war dependencies. Those should be excluded via normal gradle dependency exclusions. + */ + cas { + from "org.apereo.cas:cas-server-webapp${project.appServer}:${casServerVersion}@war" + provided = false + /* + excludes = ["WEB-INF/lib/somejar-1.0*"] + enableCompilation = true + includes = ["*.xyz"] + targetPath = "sub-path/bar" + skip = false + */ } - archiveName "${casWebApplicationBinaryName}" - baseName "cas" - excludeDevtools = true } -} \ No newline at end of file +} diff --git a/gradle/tasks.gradle b/gradle/tasks.gradle index eea4bc5..40fdac0 100644 --- a/gradle/tasks.gradle +++ b/gradle/tasks.gradle @@ -1,19 +1,28 @@ -import org.apache.ivy.util.url.* +import org.apache.ivy.util.url.ApacheURLLister import org.apache.tools.ant.taskdefs.condition.Os -import org.gradle.api.tasks.Copy +import org.gradle.internal.logging.text.StyledTextOutputFactory -import java.nio.file.* -import org.gradle.internal.logging.text.StyledTextOutputFactory; -import static org.gradle.internal.logging.text.StyledTextOutput.Style; +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardCopyOption + +import static org.gradle.internal.logging.text.StyledTextOutput.Style buildscript { repositories { mavenLocal() mavenCentral() jcenter() + maven { + url "https://oss.sonatype.org/content/repositories/snapshots" + mavenContent { snapshotsOnly() } + } } + dependencies { classpath "org.apache.ivy:ivy:${project.ivyVersion}" + classpath "org.apereo.cas:cas-server-core-api-configuration-model:${project.'cas.version'}" + classpath "org.apereo.cas:cas-server-core-configuration-metadata-repository:${project.'cas.version'}" } } @@ -22,11 +31,13 @@ apply plugin: "de.undercouch.download" def tomcatDirectory = "${buildDir}/apache-tomcat-${tomcatVersion}" project.ext."tomcatDirectory" = tomcatDirectory -def explodedDir="${buildDir}/cas" -def explodedResourcesDir="${buildDir}/cas-resources" -def resourceJarName = "cas-server-webapp-resources" +def explodedDir = "${buildDir}/cas" +def explodedResourcesDir = "${buildDir}/cas-resources" + +def resourcesJarName = "cas-server-webapp-resources" +def templateViewsJarName = "cas-server-support-thymeleaf" -task copyCasConfiguration(type: Copy, group: "build", description: "Copy the CAS configuration from this project to /etc/cas/config") { +task copyCasConfiguration(type: Copy, group: "CAS", description: "Copy the CAS configuration from this project to /etc/cas/config") { from "etc/cas/config" into new File('/etc/cas/config').absolutePath doFirst { @@ -34,16 +45,26 @@ task copyCasConfiguration(type: Copy, group: "build", description: "Copy the CAS } } -task explodeWarOnly(type: Copy, group: "build", description: "Explodes the CAS web application archive") { +task explodeWarOnly(type: Copy, group: "CAS", description: "Explodes the CAS web application archive") { dependsOn 'build' from zipTree("build/libs/${casWebApplicationBinaryName}") into explodedDir + doLast { + println "Exploded WAR into ${explodedDir}" + } } -task explodeWar(type: Copy, group: "build", description: "Explodes the CAS archive and resources jar from the CAS web application archive") { +task explodeWar(type: Copy, group: "CAS", description: "Explodes the CAS archive and resources jar from the CAS web application archive") { dependsOn explodeWarOnly - from zipTree("${explodedDir}/WEB-INF/lib/${resourceJarName}-${casServerVersion}.jar") + from zipTree("${explodedDir}/WEB-INF/lib/${templateViewsJarName}-${casServerVersion}.jar") into explodedResourcesDir + + from zipTree("${explodedDir}/WEB-INF/lib/${resourcesJarName}-${casServerVersion}.jar") + into explodedResourcesDir + + doLast { + println "Exploded WAR resources into ${explodedResourcesDir}" + } } task run(group: "build", description: "Run the CAS web application in embedded container mode") { @@ -54,19 +75,20 @@ task run(group: "build", description: "Run the CAS web application in embedded c main = "-jar" jvmArgs = casRunArgs args = ["build/libs/${casWebApplicationBinaryName}"] + systemProperties = System.properties logger.info "Started ${commandLine}" } } } -task setExecutable(group: "build", description: "Configure the project to run in executable mode") { +task setExecutable(group: "CAS", description: "Configure the project to run in executable mode") { doFirst { project.setProperty("executable", "true") logger.info "Configuring the project as executable" } } -task executable(type:Exec, group: "build", description: "Run the CAS web application in standalone executable mode") { +task executable(type: Exec, group: "CAS", description: "Run the CAS web application in standalone executable mode") { dependsOn setExecutable, 'build' doFirst { workingDir "." @@ -78,7 +100,7 @@ task executable(type:Exec, group: "build", description: "Run the CAS web applica } } -task debug(group: "build", description: "Debug the CAS web application in embedded mode on port 5005") { +task debug(group: "CAS", description: "Debug the CAS web application in embedded mode on port 5005") { dependsOn 'build' doLast { logger.info "Debugging process is started in a suspended state, listening on port 5005." @@ -88,12 +110,13 @@ task debug(group: "build", description: "Debug the CAS web application in embedd jvmArgs = casArgs debug = true args = ["build/libs/${casWebApplicationBinaryName}"] + systemProperties = System.properties logger.info "Started ${commandLine}" } } } -task downloadShell(group: "shell", description: "Download CAS shell jar from snapshot or release maven repo") { +task downloadShell(group: "Shell", description: "Download CAS shell jar from snapshot or release maven repo") { doFirst { mkdir "${project.shellDir}" } @@ -102,7 +125,7 @@ task downloadShell(group: "shell", description: "Download CAS shell jar from sna if (isRunningCasServerSnapshot(casServerVersion)) { def snapshotDir = "https://oss.sonatype.org/content/repositories/snapshots/org/apereo/cas/cas-server-support-shell/${casServerVersion}/" def files = new ApacheURLLister().listFiles(new URL(snapshotDir)) - files = files.sort{it.path} + files = files.sort { it.path } files.each { if (it.path.endsWith(".jar")) { downloadFile = it @@ -120,14 +143,14 @@ task downloadShell(group: "shell", description: "Download CAS shell jar from sna } } -task runShell(group: "shell", description: "Run the CAS shell") { +task runShell(group: "Shell", description: "Run the CAS shell") { dependsOn downloadShell doLast { println "Run the following command to launch the shell:\n\tjava -jar ${project.shellDir}/cas-server-support-shell-${casServerVersion}.jar" } } -task debugShell(group: "shell", description: "Run the CAS shell with debug options, wait for debugger on port 5005") { +task debugShell(group: "Shell", description: "Run the CAS shell with debug options, wait for debugger on port 5005") { dependsOn downloadShell doLast { println """ @@ -137,7 +160,7 @@ task debugShell(group: "shell", description: "Run the CAS shell with debug optio } } -task showConfiguration(group: "build", description: "Show configurations for each dependency, etc") { +task showConfiguration(group: "CAS", description: "Show configurations for each dependency, etc") { doLast() { def cfg = project.hasProperty("configuration") ? project.property("configuration") : "compile" configurations.getByName(cfg).each { println it } @@ -148,7 +171,7 @@ task allDependenciesInsight(group: "build", type: DependencyInsightReportTask, d task allDependencies(group: "build", type: DependencyReportTask, description: "Display a graph of all project dependencies") {} -task casVersion (group: "build", description: "Display the current CAS version") { +task casVersion(group: "CAS", description: "Display the current CAS version") { doFirst { def verbose = project.hasProperty("verbose") && Boolean.valueOf(project.getProperty("verbose")) if (verbose) { @@ -169,7 +192,7 @@ task casVersion (group: "build", description: "Display the current CAS version") } } -task createKeystore(group: "build", description: "Create CAS keystore") { +task createKeystore(group: "CAS", description: "Create CAS keystore") { doFirst { mkdir "/etc/cas" @@ -204,20 +227,79 @@ task createKeystore(group: "build", description: "Create CAS keystore") { } } -task listTemplateViews (group: "build", description: "List all CAS views") { +task listTemplateViews(group: "CAS", description: "List all CAS views") { dependsOn explodeWar doFirst { fileTree(explodedResourcesDir).matching { include "**/*.html" } - .collect { it.name } + .collect { + return it.path.replace(explodedResourcesDir, "") + } .toSorted() .each { println it } } } -task getResource(group: "build", description: "Fetch a CAS resource and move it into the overlay") { +task exportConfigMetadata(group: "CAS", description: "Export collection of CAS properties") { + doLast { + def file = new File(project.rootDir, 'config-metadata.properties') + file.withWriter('utf-8') { writer -> + def metadataRepository = new org.apereo.cas.metadata.CasConfigurationMetadataRepository() + def repository = metadataRepository.repository; + repository.allGroups + .values() + .sort { o1, o2 -> o1.id <=> o2.id } + .each({ group -> + def groupProperties = group.properties + if (!groupProperties.isEmpty()) { + def groupId = group.id.equalsIgnoreCase("_ROOT_GROUP_") ? "" : group.id + "." + + writer.writeLine("# Group ${group.id}"); + writer.writeLine("# ====================") + groupProperties + .values() + .sort { o1, o2 -> o1.id <=> o2.id } + .each({ property -> + def description = property.shortDescription + if (!property.shortDescription?.equalsIgnoreCase(property.description) && property.description != null) { + description = property.description.replace('\n', '#') + } + writer.writeLine("# ${description}"); + writer.writeLine("# Type: ${property.type}"); + if (property.deprecated) { + def deprecation = property.deprecation + writer.writeLine("# This setting is deprecated with a severity level of ${deprecation.level}.") + if (deprecation.shortReason != null) { + writer.writeLine("# because ${deprecation.shortReason}") + } + if (deprecation.replacement != null) { + writer.writeLine("# Replace with: ${deprecation.replacement}") + } + } + property.hints.valueHints.each { + if (it.value instanceof Object[]) { + if (it.value[0].toString().contains("RequiresModule")) { + writer.writeLine("# Required module: org.apereo.cas:${it.description}") + writer.writeLine("# Automatically included/available: ${it.value[1]}") + } + } + if (it.value.toString().contains("RequiredProperty")) { + writer.writeLine("# Note: This setting is required!") + } + } + writer.writeLine("${groupId}${property.name}=${property.defaultValue}") + writer.writeLine("") + }); + } + }); + } + println "Configuration metadata is available at ${file.absolutePath}" + } +} + +task getResource(group: "CAS", description: "Fetch a CAS resource and move it into the overlay") { dependsOn explodeWar doFirst { @@ -225,13 +307,18 @@ task getResource(group: "build", description: "Fetch a CAS resource and move it def results = fileTree(explodedResourcesDir).matching { include "**/${resourceName}.*" + include "**/${resourceName}" } if (results.isEmpty()) { println "No resources could be found matching ${resourceName}" return } if (results.size() > 1) { - println "Multiple resources found matching ${resourceName}: ${results}" + println "Multiple resources found matching ${resourceName}:\n" + results.each { + println "\t-" + it.path.replace(explodedResourcesDir, "") + } + println "\nNarrow down your search criteria and try again." return } @@ -252,4 +339,11 @@ task getResource(group: "build", description: "Fetch a CAS resource and move it def isRunningCasServerSnapshot(casServerVersion) { return "${casServerVersion}".contains("-SNAPSHOT") -} \ No newline at end of file +} + +task verifyRequiredJavaVersion { + logger.info "Checking current Java version ${JavaVersion.current()} for required Java version ${project.targetCompatibility}" + if (!JavaVersion.current().name.equalsIgnoreCase("${project.targetCompatibility}")) { + throw new GradleException("Current Java version ${JavaVersion.current()} does not match required Java version ${project.targetCompatibility}") + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..62d4c053550b91381bbd28b1afc82d634bf73a8a 100644 GIT binary patch delta 23020 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 delta 19839 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 + + \ No newline at end of file From 843dad4b37154633d8e72a9e7f240faceb26721e Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 3 Aug 2020 02:26:29 -0400 Subject: [PATCH 02/89] Configure HTTP and a static user for local development --- etc/cas/config/cas.properties | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/etc/cas/config/cas.properties b/etc/cas/config/cas.properties index 40cd89b..7241cec 100644 --- a/etc/cas/config/cas.properties +++ b/etc/cas/config/cas.properties @@ -1,6 +1,15 @@ -cas.server.name=https://cas.example.org:8443 -cas.server.prefix=${cas.server.name}/cas +# CAS Server +cas.server.name=http://192.168.168.167:8080 +cas.server.prefix=${cas.server.name} +# Logging logging.config=file:/etc/cas/config/log4j2.xml -# cas.authn.accept.users= +# Authentication: Accept Users Authentication - Demo purpose only, must set to empty for production +cas.authn.accept.users=longze::rearwindow + +# Enable HTTP connections for the embedded Tomcat container +cas.server.tomcat.http.port=8080 +cas.server.tomcat.http.protocol=org.apache.coyote.http11.Http11NioProtocol +cas.server.tomcat.http.enabled=true +cas.server.tomcat.http.attributes.attribute-name=attributeValue From 230bfd556d494043a15abf655ecb316724c5b5ff Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 3 Aug 2020 02:37:41 -0400 Subject: [PATCH 03/89] Configure JSON registered service with attribute release policy --- build.gradle | 3 ++- etc/cas/config/cas.properties | 10 ++++++-- etc/cas/services/osf-203948234207230.json | 25 +++++++++++++++++++ .../osfpreprints-203948234207240.json | 25 +++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 etc/cas/services/osf-203948234207230.json create mode 100644 etc/cas/services/osfpreprints-203948234207240.json diff --git a/build.gradle b/build.gradle index 79a7e7c..23f2b8f 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,8 @@ apply from: rootProject.file("gradle/dockerjib.gradle") dependencies { // Other CAS dependencies/modules may be listed here... - // implementation "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}" + implementation "org.apereo.cas:cas-server-support-generic:${project.'cas.version'}" + implementation "org.apereo.cas:cas-server-support-json-service-registry:${project.'cas.version'}" } tasks.findByName("jibDockerBuild") diff --git a/etc/cas/config/cas.properties b/etc/cas/config/cas.properties index 7241cec..e78669e 100644 --- a/etc/cas/config/cas.properties +++ b/etc/cas/config/cas.properties @@ -5,11 +5,17 @@ cas.server.prefix=${cas.server.name} # Logging logging.config=file:/etc/cas/config/log4j2.xml -# Authentication: Accept Users Authentication - Demo purpose only, must set to empty for production -cas.authn.accept.users=longze::rearwindow +# Authentication: Accept Users Authentication - Demo purpose only, must set to empty on production +cas.authn.accept.users= # Enable HTTP connections for the embedded Tomcat container cas.server.tomcat.http.port=8080 cas.server.tomcat.http.protocol=org.apache.coyote.http11.Http11NioProtocol cas.server.tomcat.http.enabled=true cas.server.tomcat.http.attributes.attribute-name=attributeValue + +# Service Registry: JSON Service Registry +cas.serviceRegistry.json.location=file:/etc/cas/services + +# Authentication: JSON Authentication - DevOps purpose only, set to empty unless necessary on production +cas.authn.json.location=file:/etc/cas/users/osf.json diff --git a/etc/cas/services/osf-203948234207230.json b/etc/cas/services/osf-203948234207230.json new file mode 100644 index 0000000..827c04c --- /dev/null +++ b/etc/cas/services/osf-203948234207230.json @@ -0,0 +1,25 @@ +{ + "@class": "org.apereo.cas.services.RegexRegisteredService", + "serviceId": "^https?://(localhost|127\\.0\\.0\\.1|192\\.168\\.168\\.167):5000/(login|logout)/?\\?next=.*", + "name": "OSF", + "id": 203948234207230, + "evaluationOrder": 20, + "logo": "", + "attributeReleasePolicy": { + "@class": "org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy", + "allowedAttributes": [ + "java.util.ArrayList", + [ + "guid", + "mail", + "givenName", + "sn" + ] + ] + }, + "usernameAttributeProvider" : { + "@class" : "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider", + "usernameAttribute" : "guid", + "canonicalizationMode" : "LOWER" + } +} \ No newline at end of file diff --git a/etc/cas/services/osfpreprints-203948234207240.json b/etc/cas/services/osfpreprints-203948234207240.json new file mode 100644 index 0000000..071486b --- /dev/null +++ b/etc/cas/services/osfpreprints-203948234207240.json @@ -0,0 +1,25 @@ +{ + "@class": "org.apereo.cas.services.RegexRegisteredService", + "serviceId": "^https?://(localhost|127\\.0\\.0\\.1):5000/(login|logout)/?\\?next=https?(%3A|:)(%2F|/)(%2F|/)(localhost|127\\.0\\.0\\.1)(%3A|:)5000(%2F|/)preprints($|%2F|/).*", + "name": "OSF Preprints", + "id": 203948234207240, + "evaluationOrder": 15, + "logo": "", + "attributeReleasePolicy": { + "@class": "org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy", + "allowedAttributes": [ + "java.util.ArrayList", + [ + "guid", + "mail", + "givenName", + "sn" + ] + ] + }, + "usernameAttributeProvider" : { + "@class" : "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider", + "usernameAttribute" : "guid", + "canonicalizationMode" : "LOWER" + } +} \ No newline at end of file From 8b8b02caa672ebdf87d63fa7a149640e4dc7918c Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 3 Aug 2020 02:42:14 -0400 Subject: [PATCH 04/89] Configure logout service redirect --- etc/cas/config/cas.properties | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/etc/cas/config/cas.properties b/etc/cas/config/cas.properties index e78669e..dd72747 100644 --- a/etc/cas/config/cas.properties +++ b/etc/cas/config/cas.properties @@ -18,4 +18,10 @@ cas.server.tomcat.http.attributes.attribute-name=attributeValue cas.serviceRegistry.json.location=file:/etc/cas/services # Authentication: JSON Authentication - DevOps purpose only, set to empty unless necessary on production -cas.authn.json.location=file:/etc/cas/users/osf.json +cas.authn.json.location= + +# Logout +cas.logout.follow-service-redirects=true +cas.logout.redirect-parameter=service +cas.logout.confirm-logout=false +cas.logout.remove-descendant-tickets=false From e34e315dfa052777c144b47c25c3b1f718a668fa Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 5 Aug 2020 18:41:08 -0400 Subject: [PATCH 05/89] Update gradle lombok lombok.log needs to be configured properly for class overylay to work properly since he default variable name is 'LOG' while Apereo libraries uses "LOGGER". --- build.gradle | 10 ++++++++++ lombok.config | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/build.gradle b/build.gradle index 23f2b8f..f673e9d 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,17 @@ project.ext."casServerVersion" = casServerVersion project.ext."casWebApplicationBinaryName" = casWebApplicationBinaryName apply plugin: "io.freefair.war-overlay" + apply plugin: "io.freefair.lombok" +lombok { + config["config.stopBubbling"] = "true" + config["lombok.addLombokGeneratedAnnotation"] = "true" + config["lombok.equalsAndHashCode.doNotUseGetters"] = "true" + config["lombok.log.fieldName"] = "LOGGER" + config["lombok.log.fieldIsStatic"] = "true" + config["lombok.toString.doNotUseGetters"] = "true" +} + apply from: rootProject.file("gradle/tasks.gradle") apply plugin: "war" diff --git a/lombok.config b/lombok.config index 6aa51d7..82988bc 100644 --- a/lombok.config +++ b/lombok.config @@ -1,2 +1,7 @@ # This file is generated by the 'io.freefair.lombok' Gradle plugin config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true +lombok.equalsAndHashCode.doNotUseGetters = true +lombok.log.fieldIsStatic = true +lombok.log.fieldName = LOGGER +lombok.toString.doNotUseGetters = true From 986fd200c0fc1313d86fbac9872f4c77bc938d29 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 5 Aug 2020 19:23:00 -0400 Subject: [PATCH 06/89] Set web server servlet path to / for OSF compatibility --- etc/cas/config/cas.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etc/cas/config/cas.properties b/etc/cas/config/cas.properties index dd72747..51807ce 100644 --- a/etc/cas/config/cas.properties +++ b/etc/cas/config/cas.properties @@ -1,3 +1,6 @@ +# Web Server +server.servlet.contextPath=/ + # CAS Server cas.server.name=http://192.168.168.167:8080 cas.server.prefix=${cas.server.name} From 9c81444f4af9e11643b1326e089f9b626beea76f Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 5 Aug 2020 19:36:12 -0400 Subject: [PATCH 07/89] Enable pac4j authn delegation to test web flow customiation * Added oauth-orcid client and cas-oldcas client * Updated registered service to allow the above two providers --- build.gradle | 1 + etc/cas/config/cas.properties | 17 +++++++++ etc/cas/services/osf-203948234207230.json | 23 ++++++++--- .../services/osfgeneric-203948234207220.json | 38 +++++++++++++++++++ .../osfpreprints-203948234207240.json | 23 ++++++++--- 5 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 etc/cas/services/osfgeneric-203948234207220.json diff --git a/build.gradle b/build.gradle index f673e9d..079c74a 100644 --- a/build.gradle +++ b/build.gradle @@ -83,6 +83,7 @@ dependencies { // Other CAS dependencies/modules may be listed here... implementation "org.apereo.cas:cas-server-support-generic:${project.'cas.version'}" implementation "org.apereo.cas:cas-server-support-json-service-registry:${project.'cas.version'}" + implementation "org.apereo.cas:cas-server-support-pac4j-webflow:${project.'cas.version'}" } tasks.findByName("jibDockerBuild") diff --git a/etc/cas/config/cas.properties b/etc/cas/config/cas.properties index 51807ce..83cb53e 100644 --- a/etc/cas/config/cas.properties +++ b/etc/cas/config/cas.properties @@ -28,3 +28,20 @@ cas.logout.follow-service-redirects=true cas.logout.redirect-parameter=service cas.logout.confirm-logout=false cas.logout.remove-descendant-tickets=false + +# Pac4j Delegated Authentication +cas.authn.pac4j.typed-id-used=true +cas.authn.pac4j.lazy-init=true +cas.authn.pac4j.replicate-sessions=false + +# Pac4j Delegated Authentication Clients: ORCiD +cas.authn.pac4j.orcid.id=changeme +cas.authn.pac4j.orcid.secret=changme +cas.authn.pac4j.orcid.client-name=orcid +cas.authn.pac4j.orcid.enabled=true +cas.authn.pac4j.orcid.callback-url-type=QUERY_PARAMETER + +# Pac4j Delegated Authentication Clients: CAS +cas.authn.pac4j.cas[0].login-url=http://localhost:8081/login +cas.authn.pac4j.cas[0].client-name=oldcas +cas.authn.pac4j.cas[0].protocol=SAML diff --git a/etc/cas/services/osf-203948234207230.json b/etc/cas/services/osf-203948234207230.json index 827c04c..b89198d 100644 --- a/etc/cas/services/osf-203948234207230.json +++ b/etc/cas/services/osf-203948234207230.json @@ -17,9 +17,22 @@ ] ] }, - "usernameAttributeProvider" : { - "@class" : "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider", - "usernameAttribute" : "guid", - "canonicalizationMode" : "LOWER" + "usernameAttributeProvider": { + "@class": "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider", + "usernameAttribute": "guid", + "canonicalizationMode": "LOWER" + }, + "accessStrategy": { + "@class": "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy", + "delegatedAuthenticationPolicy": { + "@class": "org.apereo.cas.services.DefaultRegisteredServiceDelegatedAuthenticationPolicy", + "allowedProviders": [ + "java.util.ArrayList", + [ + "orcid", + "oldcas" + ] + ] + } } -} \ No newline at end of file +} diff --git a/etc/cas/services/osfgeneric-203948234207220.json b/etc/cas/services/osfgeneric-203948234207220.json new file mode 100644 index 0000000..4e143da --- /dev/null +++ b/etc/cas/services/osfgeneric-203948234207220.json @@ -0,0 +1,38 @@ +{ + "@class": "org.apereo.cas.services.RegexRegisteredService", + "serviceId": "^https?://(localhost|127\\.0\\.0\\.1|192\\.168\\.168\\.167):5000/.*", + "name": "OSF Generic", + "id": 203948234207220, + "evaluationOrder": 25, + "logo": "", + "attributeReleasePolicy": { + "@class": "org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy", + "allowedAttributes": [ + "java.util.ArrayList", + [ + "guid", + "mail", + "givenName", + "sn" + ] + ] + }, + "usernameAttributeProvider": { + "@class": "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider", + "usernameAttribute": "guid", + "canonicalizationMode": "LOWER" + }, + "accessStrategy": { + "@class": "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy", + "delegatedAuthenticationPolicy": { + "@class": "org.apereo.cas.services.DefaultRegisteredServiceDelegatedAuthenticationPolicy", + "allowedProviders": [ + "java.util.ArrayList", + [ + "orcid", + "oldcas" + ] + ] + } + } +} diff --git a/etc/cas/services/osfpreprints-203948234207240.json b/etc/cas/services/osfpreprints-203948234207240.json index 071486b..ad86616 100644 --- a/etc/cas/services/osfpreprints-203948234207240.json +++ b/etc/cas/services/osfpreprints-203948234207240.json @@ -17,9 +17,22 @@ ] ] }, - "usernameAttributeProvider" : { - "@class" : "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider", - "usernameAttribute" : "guid", - "canonicalizationMode" : "LOWER" + "usernameAttributeProvider": { + "@class": "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider", + "usernameAttribute": "guid", + "canonicalizationMode": "LOWER" + }, + "accessStrategy": { + "@class": "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy", + "delegatedAuthenticationPolicy": { + "@class": "org.apereo.cas.services.DefaultRegisteredServiceDelegatedAuthenticationPolicy", + "allowedProviders": [ + "java.util.ArrayList", + [ + "orcid", + "oldcas" + ] + ] + } } -} \ No newline at end of file +} From e27f0f6c29469bc1b34600818e8a126f09c8fdf7 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 5 Aug 2020 21:59:36 -0400 Subject: [PATCH 08/89] Overlay minimal frontend - copy from exploded build war --- src/main/resources/messages.properties | 477 ++++++++++++++++++ src/main/resources/static/favicon.ico | Bin 0 -> 29582 bytes .../resources/templates/casLoginView.html | 40 ++ .../resources/templates/casLogoutView.html | 24 + .../resources/templates/fragments/footer.html | 5 + .../resources/templates/fragments/header.html | 231 +++++++++ .../templates/fragments/pmlinks.html | 39 ++ .../templates/fragments/serviceui.html | 48 ++ src/main/resources/templates/layout.html | 43 ++ 9 files changed, 907 insertions(+) create mode 100644 src/main/resources/messages.properties create mode 100644 src/main/resources/static/favicon.ico create mode 100644 src/main/resources/templates/casLoginView.html create mode 100644 src/main/resources/templates/casLogoutView.html create mode 100644 src/main/resources/templates/fragments/footer.html create mode 100644 src/main/resources/templates/fragments/header.html create mode 100644 src/main/resources/templates/fragments/pmlinks.html create mode 100644 src/main/resources/templates/fragments/serviceui.html create mode 100644 src/main/resources/templates/layout.html diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 0000000..9659627 --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1,477 @@ +screen.welcome.welcome=Congratulations on bringing CAS online! To learn how to authenticate, please review the default authentication handler configuration. +screen.welcome.security=For security reasons, please log out and exit your web browser when you are done accessing \ + services that require authentication! +screen.welcome.instructions=Enter Username & Password +screen.welcome.forcedsso=Welcome back, {0}. We have detected an existing single sign-on session for you. However, \ + you are being asked to re-authenticate again as CAS cannot successfully accept your previous single sign-on participation status which may be \ + related to the policy assigned to {1}. Please enter your Username and Password and proceed. +screen.welcome.label.source=Source: +screen.welcome.label.netid=Username: +screen.welcome.label.netid.accesskey=u +screen.welcome.label.password=Password: +screen.welcome.label.password.accesskey=p +screen.welcome.label.publicstation=I am at a public workstation. +screen.welcome.label.warn=Warn me! +screen.welcome.label.warn.accesskey=w +screen.welcome.label.warnremove=Do not warn me again +screen.welcome.button.login=LOGIN +screen.welcome.button.logout=LOGOUT +screen.welcome.button.loginwip=One moment please... +screen.welcome.button.register=Register +screen.welcome.button.print=Print +screen.welcome.button.clear=CLEAR +screen.welcome.label.loginwith=External Identity Providers +screen.welcome.label.navto=Navigating to authentication provider. Please wait... + +screen.pm.button.submit=SUBMIT +screen.pm.button.cancel=CANCEL +screen.pm.button.forgotpwd=Forgot your password? +screen.pm.button.resetPassword=Reset your password +screen.pm.button.forgotUsername=Forgot your username? +screen.pm.reset.username=Username: +screen.pm.reset.email=Email: +screen.pm.reset.heading=Password Reset Failed +screen.pm.reset.message=We were unable to process your password reset request at this time. +screen.pm.reset.qstitle=Answer Security Questions +screen.pm.reset.qsmsg=Welcome {0}. Before you can reset your password, you must answer the following security questions. +screen.pm.reset.sentInstructions=You should shortly receive an email with follow-instructions to reset your password. Please do not take \ + long as the password reset instructions may expire. +screen.pm.reset.sent=Password Reset Instructions Sent Successfully. +screen.pm.reset.title=Reset your password +screen.pm.reset.instructions=Please provide your username. You will receive a notification with follow-up instructions on how to reset your password. +screen.pm.reset.answer=Answer {0} +screen.pm.password.policyViolation=Password does not match the password policy requirement. +screen.pm.password.confirmMismatch=Passwords do not match. +screen.pm.password.strength=Strength: +screen.pm.password.strength.0=Worst +screen.pm.password.strength.1=Bad +screen.pm.password.strength.2=Weak +screen.pm.password.strength.3=Good +screen.pm.password.strength.4=Strong + +screen.pm.reset.contact.failed=Unable to send email/SMS as no email/SMS server is defined in the CAS configuration. +screen.pm.reset.username.required=No email is provided. +screen.pm.reset.contact.invalid=Provided contact information is missing or invalid. +screen.pm.reset.email.invalid=Provided email address is invalid. +screen.pm.reset.username.failed=Failed to send the username to the given email address. + +screen.pm.forgotusername.title=Forgot your username? +screen.pm.forgotusername.instructions=Please provide your email address. You will receive an email with your username. +screen.pm.forgotusername.email.failed=Unable to send email as no email server is defined in the CAS configuration. +screen.pm.forgotusername.email.required=No email is provided. +screen.pm.forgotusername.contact.invalid=Provided email address or phone number is invalid. +screen.pm.forgotusername.username.missing=No username could be located for the given email address. +screen.pm.forgotusername.username.failed=Failed to send the username to the given email address. +screen.pm.forgotusername.sent=Instructions Sent Successfully. +screen.pm.forgotusername.sentInstructions=You should shortly receive an message with follow-instructions to how to retrieve your username. + +screen.aup.heading=Acceptable Usage Policy +screen.aup.policyterms=

The purpose of this policy is to establish acceptable and unacceptable use of electronic devices \ +and network resources in conjunction with the established culture of ethical and lawful behavior, openness, trust, and integrity.

\ +

By using these resources, you agree to abide by the Acceptable Usage Policy.

+ +screen.aup.button.accept=ACCEPT +screen.aup.button.cancel=CANCEL + +screen.saml.idp.discovery=SAML Identity Provider Discovery + +screen.consent.confirm=CONFIRM +screen.consent.cancel=CANCEL +screen.consent.title=Attribute Consent +screen.consent.attributes=Attributes +screen.consent.options=Options +screen.consent.attributes.header=The following attributes will be released to [{0}]: +screen.consent.attributes.attribute=Attribute +screen.consent.attributes.values=Value(s) +\ +screen.consent.options.header=How should I be prompted for consent again? +screen.consent.options.always=Every Time +screen.consent.options.desc.always=Show the consent screen every time I attempt to log into {0}. +screen.consent.options.attributename=Attribute Name +screen.consent.options.desc.attributename=Show the consent screen, if an attribute is added or removed from the collection of attributes released to {0}. +screen.consent.options.attributevalue=Attribute Value +screen.consent.options.desc.attributevalue.intro=Show the consent screen, if: +screen.consent.options.desc.attributevalue.first=A new attribute is authorized for release to {0}. +screen.consent.options.desc.attributevalue.second=An attribute is removed from the attribute bundle previously released to {0}. +screen.consent.options.desc.attributevalue.third=The value of an attribute authorized for release to {0} has changed. +screen.consent.options.reminder.header=How often should I be reminded to consent again? +screen.consent.options.reminder.expl=Show the consent screen, as a reminder, in the event that there is no change to the collection of attributes released to {0}. +screen.consent.options.timeunit.seconds=Seconds +screen.consent.options.timeunit.minutes=Minutes +screen.consent.options.timeunit.hours=Hours +screen.consent.options.timeunit.days=Days +screen.consent.options.timeunit.weeks=Weeks +screen.consent.options.timeunit.months=Months +screen.consent.options.timeunit.years=Years + +screen.consent.review.header=Review Attribute Consent +screen.consent.review.loading=Loading consent decisions... +screen.consent.review.noconsentdecisions=There are no consent decisions registered for you. +screen.consent.review.success=Consent decision was deleted successfully. +screen.consent.review.error=There was an error! +screen.consent.review.confirm=Delete consent decision for [{}]? +screen.consent.review.yes=Yes +screen.consent.review.no=No +screen.consent.review.date=Date +screen.consent.review.service=Service +screen.consent.review.delete=DELETE +screen.consent.review.createddate=Created Date: +screen.consent.review.reminder=Reminder: +screen.consent.review.option=Option: +screen.consent.review.options.attributename=Attribute Name +screen.consent.review.options.attributevalue=Attribute Value +screen.consent.review.options.always=Always +screen.consent.review.options.desc.always=Show the consent screen every time I attempt to log in. +screen.consent.review.options.desc.attributename=Show the consent screen, if an attribute is added or removed from the collection of attributes released. +screen.consent.review.options.desc.attributevalue=Show the consent screen, if 1) a new attribute is authorized for release, 2) an attribute is removed from the attribute bundle previously released, 3) the value of an attribute authorized for release has changed. +screen.consent.review.attributes=Attributes: +screen.consent.review.data.search=Search +screen.consent.review.data.zerorecords=No matching decisions found +screen.consent.review.data.info=Showing _START_ to _END_ of _TOTAL_ entries +screen.consent.review.data.infofiltered=(filtered from _MAX_ total entries) +screen.consent.review.data.infoempty=No decisions to show +screen.consent.review.logout.success=You have successfully logged out of the Consent Review page. You may completely log out of the Central Authentication Service and end your single sign-on session. + +screen.nonsecure.title=Non-secure Connection +screen.nonsecure.message=You are currently accessing CAS over a non-secure connection. Single Sign On WILL NOT WORK. In order to have single sign on work, you MUST log in over HTTPS. + +screen.defaultauthn.title=Static Authentication +screen.defaultauthn.heading=CAS is configured to accept a static list of users for primary authentication. Please be advised that this is ONLY useful for \ + demo purposes. It is recommended that you connect CAS to LDAP, JDBC, etc instead. +logo.title=go to Apereo home page +copyright=Copyright © 2005–2020 Apereo, Inc. +screen.capslock.on = CAPSLOCK key is turned on! +screen.button.continue=Continue +screen.post.response.message=You are being redirected to {0}. + +screen.interrupt.title=Authentication Interrupt +screen.interrupt.message=The authentication flow has been interrupted. CAS has not yet established a single sign-on session for {0}. + +screen.mdui.infolink.text=More information about this application. +screen.mdui.privacylink.text=Privacy statement for application. + +screen.interrupt.btn.proceed=Proceed +screen.interrupt.btn.cancel=Cancel + +# Generic Error Pages 401, 404, 500, etc +######################################## +screen.error.page.heading=Error +screen.error.page.title.accessdenied=Error - 401 +screen.error.page.title.permissiondenied=Error - Permission Denied +screen.error.page.title.pagenotfound=Error - Page Not Found +screen.error.page.title.requestunsupported=Error - Unsupported Request +screen.error.page.accessdenied=Access Denied +screen.error.page.permissiondenied=You do not have permission to view this page. +screen.error.page.requestunsupported=The request type or syntax is not supported. +screen.error.page.loginagain=Login Again +screen.error.page.notfound=Page Not Found +screen.error.page.doesnotexist=The page you are attempting to access does not exist at the moment. +screen.error.page.authdenied=Authorization Denied + +# Remember-Me Authentication +screen.rememberme.checkbox.title=Remember Me + +# Gua +screen.gua.confirm.message=If you do not recognize this image as yours, do NOT continue. + +# Blocked Errors Page +screen.error.page.title.blocked=Error - Permission Denied +screen.blocked.header=Access Denied +screen.blocked.message=You've entered the wrong password for the user too many times. You've been throttled. +AbstractAccessDecisionManager.accessDenied=You are not authorized to access this resource. Contact your CAS administrator for more info. + +# Confirmation Screen Messages +screen.confirmation.message=You asked to be warned before logging into applications. Please proceed. +screen.authentication.warning=Authentication Succeeded with Warnings + +# Generic Success Screen Messages +screen.success.header=Log In Successful +screen.success.success=You, {0}, have successfully logged into the Central Authentication Service. However, you are seeing \ + this page because CAS does not know about your target destination and how to get you there. Examine the authentication request again and \ + make sure a target service/application that is authorized and registered with CAS is specified. +screen.success.security=When you are finished, for security reasons, please log out and exit your web browser. + +# Logout Screen Messages +screen.logout.confirm.header=Do you want to log out completely? +screen.logout.confirm.text=

An application may have redirected you to the Central Authentication Service \ + to completely log you out and destroy your single sign-on session. If you choose to log out, you will be asked again \ + to provide your credentials and re-autheticate once you attempt to access an application.

Do you want to proceed?

+ +screen.logout.header=Logout successful +screen.logout.success=You have successfully logged out of the Central Authentication Service. You may log in again. +screen.logout.fc.success=You have successfully logged out of the Central Authentication Service. Given single logout is enabled with CAS, \ + the following list of applications are only notified to log you out and destroy your user session. Remember that this \ + is just a notification, not a guarantee. It is up the application itself to honor these notifications and properly take action to log you \ + out. +screen.logout.security=For security reasons, exit your web browser. + +screen.service.sso.error.header=Re-Authentication Required to Access this Service +screen.service.sso.error.message=You attempted to access a service that requires authentication without re-authenticating. Please try authenticating again. +screen.service.required.message=You attempted authentication without specifying the target application. Please re-examine the request and try again. +screen.service.initial.message=Attempting to access CAS or the indicated target application is disallowed at this time. \ + The authentication policy requires that you change your starting application \ + and then move onto other applications and services. + +captchaError=reCAPTCHA validation failed. +username.required=Username is a required field. +password.required=Password is a required field. +source.required=Authentication source is a required field. + +# Password Management +confirmedPassword.required=Password must be confirmed. +pm.passwordsMustMatch=Provided passwords do not match. +pm.passwordFailedCriteria=Provided password does not satisfy the password security policy. Please try again. +pm.updateFailure=Account password could not be modified. Please try again. + +# Authentication failure messages +authenticationFailure.AccountDisabledException=This account has been disabled. +authenticationFailure.AccountLockedException=This account has been locked. +authenticationFailure.AccountExpiredException=This account has expired and is forbidden to login at this time. +authenticationFailure.CredentialExpiredException=Your password has expired. +authenticationFailure.InvalidLoginLocationException=You cannot login from this workstation. +authenticationFailure.UniquePrincipalRequiredException=You cannot login at this time, since you have another active single sign-on session in progress \ + and CAS is configured with an authentication policy the prevents multiple concurrent single sign-on sessions. +authenticationFailure.InvalidLoginTimeException=Your account is forbidden to login at this time. +authenticationFailure.AccountNotFoundException=Your account is not recognized and cannot login at this time. +authenticationFailure.FailedLoginException=Authentication attempt has failed, likely due to invalid credentials. Please verify and try again. +authenticationFailure.MultifactorAuthenticationProviderAbsentException=Unable to satisfy multifactor authentication requirements. \ + Your account is configured for a multifactor authentication strategy, yet CAS is unable to locate and execute that strategy \ + most likely due to misconfiguration of the server. Contact the service administrators for assistance. +authenticationFailure.SurrogateAuthenticationException=You are not authorized to impersonate the indicated user at this time. +authenticationFailure.AccountPasswordMustChangeException=Your account password has expired and must be changed. +authenticationFailure.UnauthorizedServiceForPrincipalException=Service access denied due to missing privileges. +authenticationFailure.UNKNOWN=Authentication attempt has failed. +authenticationFailure.AuthenticationException=Credentials are rejected/invalid and authentication attempt has failed. + +INVALID_REQUEST_PROXY=The request is incorrectly formatted. Ensure all required parameters are properly encoded and included. +INVALID_TICKET_SPEC=Ticket failed validation specification. Possible errors could include attempting to validate a Proxy Ticket via a Service Ticket validator, or not complying with the renew true request. +INVALID_REQUEST=Unable to identify, authorize or complete this request, likely due to malformed or missing required parameters. +INVALID_AUTHENTICATION_CONTEXT=The validation request for [''{0}''] cannot be satisfied. The request is either unrecognized or unfulfilled. +INVALID_TICKET=Ticket ''{0}'' not recognized +INVALID_PROXY_GRANTING_TICKET=PGT already generated for this ST. Cannot grant more than one PGT for ST +INVALID_SERVICE=Ticket ''{0}'' does not match supplied service. The original service was ''{1}'' and the supplied service was ''{2}''. +INVALID_PROXY_CALLBACK=The supplied proxy callback url ''{0}'' could not be authenticated. Either ''{0}'' cannot be reached, it is not \ + allowed to exercise proxy authentication. +UNAUTHORIZED_SERVICE_PROXY=The supplied service ''{0}'' is not authorized to use CAS proxy authentication. +UNSATISFIED_AUTHN_POLICY=Service access denied due to an unsatisfied authentication policy. +INVALID_AUTHN_REQUEST=Authentication attempt has failed, likely due to invalid credentials. + +screen.service.error.header=Application Not Authorized to Use CAS +service.principal.resolution.error=CAS is unable to determine the correct authentication principal. \ + Either the principal could not be resolved correctly as a single unique entity or CAS has found \ + mixed/multiple candidate principals and is unable to decide which should be used. \ + This error may also be caused if the authenticated principal is not allowed to access the target application \ + due to missing privileges set by the CAS server authorization policies. +service.not.authorized.missing.attr=You are not authorized to access the application as your account \ +is missing privileges required by the CAS server to authenticate into this service. Please notify your support desk. +screen.service.error.message=The application you attempted to authenticate to is not authorized to use CAS. \ + This usually indicates that the application is not registered with CAS, or its authorization policy defined in its registration record \ + prevents it from leveraging CAS functionality, or it's malformed and unrecognized by CAS. \ + Contact your CAS administrator to learn how you might register and integrate your application with CAS. +screen.service.empty.error.message=The services registry of CAS is empty and has no service definitions. \ +Applications that wish to authenticate with CAS must explicitly be defined in the services registry. +screen.service.expired.message=The application you attempted to authenticate to has been expired in the CAS Service Registry. \ + If this service should still be considered in use, please contact the service administrators to have the application renewed. +# Surrogate Account Selection +screen.surrogates.account.selection.header=Surrogate Account Selection +screen.surrogates.choose.account=Choose Account +screen.surrogates.message=

You are provided with a list of accounts on behalf of which you are allowed to authenticate.

\ +

Select one and continue.

+screen.surrogates.button.cancel=Cancel +screen.surrogates.account.selection.error=You are not authorized to impersonate the indicated user at this time. + +# Password policy +password.expiration.warning=Your password expires in {0} day(s). Please change your password now. +password.expiration.loginsRemaining=You have {0} login(s) remaining before you MUST change your password. +screen.accountdisabled.heading=This account has been disabled. +screen.accountdisabled.message=Please contact the system administrator to regain access. +screen.accountlocked.heading=This account has been locked. +screen.accountlocked.message=Please contact the system administrator to regain access. +screen.expiredpass.heading=Your password has expired. +screen.expiredpass.message=Please change your password. +screen.mustchangepass.heading=You must change your password. +screen.mustchangepass.message=Please change your password. +screen.badhours.heading=Your account is forbidden to login at this time. +screen.badhours.message=Please try again later. +screen.authnblocked.heading=Authentication attempt is blocked. +screen.authnblocked.message=Your authentication attempt is untrusted and unauthorized from your current workstation. +screen.risk.authnblocked.heading=Authentication attempt is blocked. +screen.risk.authnblocked.message=Your authentication attempt is untrusted and unauthorized from your current workstation. +screen.badworkstation.heading=You cannot login from this workstation. +screen.badworkstation.message=Please contact the system administrator to regain access. +screen.button.changePassword=Change Password + +screen.pm.success.header=Password Change Successful +screen.pm.success.message=Your account password is successfully updated. + +screen.pm.confirmpsw=Confirm Password: +screen.pm.enterpsw=Enter Password: + +screen.pac4j.unauthz.pagetitle=Unauthorized Access +screen.pac4j.unauthz.gotoapp=Goto Application +screen.pac4j.unauthz.login=Back to CAS +screen.pac4j.unauthz.heading=Unauthorized Access +screen.pac4j.unauthz.message=Either the authentication request was rejected/cancelled, or the authentication provider denied access due \ + to permissions, etc. Review logs to find the root cause of the issue. + +screen.delauthn.error.header=Delegated Authentication Failure +screen.delauthn.error.message=CAS is unable to complete the delegated authentication scenario, \ +or redirect to the selected identity provider. Please examine the original authentication request and try again. \ +You may need to close your browser and start again. + +# GAuth +screen.authentication.gauth.register=Your account is not registered. Use the below settings to register your device with CAS. +screen.authentication.gauth.key=Secret key to register is:
{0}
+ +# OAuth +screen.oauth.confirm.header=Authorization +screen.oauth.confirm.message=Do you want to grant access to "{0}" ? +screen.oauth.confirm.allow=Allow +screen.oauth.confirm.deny=Deny +cas.oauth.confirm.pagetitle=Approve Access + +cas.oauth.device.confirm.header=Connect Device +cas.oauth.device.confirm.message=Enter the code displayed on your device to proceed. + +cas.oauth.device.confirmed.header=Code Approved +cas.oauth.device.confirmed.message=Return to your device to continue. + +cas.oauth.error.pagetitle=OAuth Error +cas.oauth.error.header=OAuth Authorization Error +OAUTH_BAD_SESSION_REQUEST=The OAuth request cannot be completed, as CAS is unable to locate or determine OAuth redirect URL. \ + This is usually an indication of a stale or mismatched request where the processed request session id is different from the \ + active session and may be caused by an complete logout operation. Clear cookies and history, restart your browser and try again please. + +# OIDC +screen.oidc.confirm.infourl=Learn more about {0}. +screen.oidc.confirm.privacyurl=Learn about {0} privacy rules. + +screen.oidc.confirm.scope.profile=This requests access to the profile claims excluding the address and email claims. +screen.oidc.confirm.scope.email=This requests access to the email claims. +screen.oidc.confirm.scope.address=This requests access to the address claims. +screen.oidc.confirm.scope.openid=This indicates an OpenID Connect authorization request. +screen.oidc.confirm.scope.phone=This requests access to the phone claims. +screen.oidc.confirm.scope.offline_access=This requests access for a refresh token used for offline access. + + +screen.oidc.confirm.claim.name=End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences. +screen.oidc.confirm.claim.given_name=Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters. +screen.oidc.confirm.claim.middle_name=Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used. +screen.oidc.confirm.claim.family_name=Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters. +screen.oidc.confirm.claim.email=End-User's preferred e-mail address. +screen.oidc.confirm.claim.preferred_username=MAY be any valid JSON string including special characters such as @, /, or whitespace. The RP MUST NOT rely upon this value being unique. +screen.oidc.confirm.claim.profile=URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User. +screen.oidc.confirm.claim.picture=URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User. +screen.oidc.confirm.claim.phone_number=End-User's preferred telephone number. It is RECOMMENDED as the format of this claim. \ + For example, +1 (425) 555-1212 or +56 (2) 687 2400. + +screen.oidc.confirm.asksinfo=The client is asking for the following information: +screen.oidc.confirm.dynamic=This client was dynamically registered at {0}. + +# Unavailable +screen.unavailable.header=CAS error +screen.unavailable.heading=CAS is unable to process this request: "{0}:{1}" +screen.unavailable.message=There was an error trying to complete your request. \ +Please notify your support desk or try again. \ +
Apereo is a non-profit open source software governance foundation. The CAS software is an Apereo sponsored project \ +and is freely downloadable and usable by anyone. However, Apereo does not operate the systems of anyone using the \ +software and in most cases doesn't even know who is using it or how to contact them unless they are an active part \ +of the Apereo community.

If you are having problems logging in using CAS, \ +you will need to contact the IT staff or Help Desk of your organization for assistance. \ +

We wish we could be more directly helpful to you.
+ +screen.mfaDenied.header=MFA Denied +screen.mfaDenied.heading=MFA attempt has been denied by provider +screen.mfaDenied.message=Your MFA provider has denied your attempt at second factor \ +authentication. Contact your system administrator for help in restoring your account. + +screen.mfaUnavailable.header=MFA Provider Unavailable +screen.mfaUnavailable.heading=MFA Provider Unavailable +screen.mfaUnavailable.message=CAS was unable to reach your configured MFA provider at this time. \ + Due to failure policies configured for the service you are attempting to access, authentication can not \ + be granted at this time. + +##################################################################### +# Login View +##################################################################### +#Resources Labels +cas.login.pagetitle=Login +cas.login.resources.header=Resources +cas.login.resources.wiki=Documentation +cas.login.resources.endpoints=Actuator Endpoints +cas.login.resources.pulls=Pull Requests +cas.login.resources.mailinglist=Mailing Lists +cas.login.resources.chat=Chatroom +cas.login.resources.blog=Blog +cas.login.resources.support=Support +cas.login.resources.contribguide=Contributor Guidelines + +#### +# Acceptable Usage Policy View +# +cas.acceptableusagepolicyview.pagetitle=Acceptable Usage Policy View + +## +# MFA +## +cas.mfa.providerselection.pagetitle=Multifactor Provider Selection + +cas.mfa.providerselection.mfa-duo=Duo Security +cas.mfa.providerselection.mfa-duo.notes=Duo's wide variety of authentication methods enable every user to \ + securely and quickly log in. Duo Push, sent by the Duo Mobile authentication app, allows users to approve \ + push notifications to verify their identity. +cas.mfa.providerselection.mfa-gauth=Google Authenticator +cas.mfa.providerselection.mfa-gauth.notes=Google Authenticator is a software-based authenticator that \ + implements two-step verification services for authenticating users of mobile applications by Google. +cas.mfa.providerselection.mfa-simple=CAS Multifactor Authentication +cas.mfa.providerselection.mfa-simple.notes=Allow CAS to act as a multifactor authentication provider on its own, \ + issuing tokens and sending them to end-users via pre-defined communication channels such as email or text messages. +cas.mfa.providerselection.mfa-yubikey=YubiKey Multifactor Authentication +cas.mfa.providerselection.mfa-yubikey.notes=Yubico is a cloud-based service that enables strong, easy-to-use \ + and affordable two-factor authentication with one-time passwords through their flagship product, YubiKey. +cas.mfa.providerselection.mfa-u2f=YubiKey Multifactor Authentication +cas.mfa.providerselection.mfa-u2f.notes=U2F is an open authentication standard that enables internet users \ + to securely access any number of online services, with one single device, instantly and with no drivers, or client software needed. + +cas.mfa.duologin.pagetitle=Duo Security Login + +cas.mfa.simple.pagetitle=CAS Multifactor Authentication Login +cas.mfa.simple.label.token=Token: +cas.mfa.simple.label.resend=Resend +cas.mfa.simple.label.tokensent=A multifactor authentication token is sent to you via an email or text message. \ + Please locate the token and submit it here to continue. Note that the token will expire after a short period of time \ + and will no longer be recognized by CAS, in which case, you may ask CAS to send you a new token. + +cas.mfa.googleauth.pagetitle=Google Authenticator +cas.mfa.googleauth.label.token=Token: + +cas.mfa.radius.pagetitle=Radius Authentication + +cas.mfa.yubikey.pagetitle=YubiKey Authentication +cas.mfa.yubikey.authenticate=Use your registered YubiKey device to authenticate. +cas.mfa.yubikey.register=Your device is not yet registered. Use the below form to register your device with CAS. +cas.mfa.yubikey.label.token=Token: + +cas.mfa.u2f.pagetitle=U2F Authentication +cas.mfa.u2f.authentication.device=Authentication Device +cas.mfa.u2f.authentication.message=

Please touch the flashing U2F device now.

You may be prompted to allow \ + the site permission to access your security keys. After granting permission, the device will start to blink.

+cas.mfa.u2f.register.device=Register Device +cas.mfa.u2f.register.message=

Please touch the flashing U2F device now.

You may be prompted to allow \ + the site permission to access your security keys. After granting permission, the device will start to blink.

+ +cas.mfa.authy.pagetitle=Authy Login + +cas.mfa.swivel.pagetitle=Swivel Authentication +cas.mfa.swivel.label.token=Token: +cas.mfa.swivel.label.header=Swivel Authentication + +cas.mfa.registerdevice.label.title=Register Device +cas.mfa.registerdevice.label.intro=Please name the current device. +cas.mfa.registerdevice.pagetitle=Register Device +cas.mfa.registerdevice.label.name=Name +cas.mfa.registerdevice.button.register=Register +cas.mfa.registerdevice.button.skip=Skip + +passwordless.error.unknown.user=Provided username cannot be recognized and located by CAS. +passwordless.error.invalid.user=Provided username does not carry enough contact information. diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..19228a402c5b93d1f2e9c29d60631a360da3a46a GIT binary patch literal 29582 zcmeHQL5n0y5zcnNc?a2^lgFJpdh8z{JLNC1e?ncn39muq?P;g*q6cvl5k!zWc<~@K zc<>}-9~2gR*b47~iqNfl5J8y+Q3o8R{J!eSCbv4PDkCead!Lv~OjTq?e(^;_Ms;;x zR;da8eeijOzlnPJ6{S9_l=>Xx1ISAV=Q+Opes%f(pA%)EEX%Z(1+KY6I8U3|i6{+n zQ*+H6aWrFF&=Kse>OG9nS4PbRcq9TY73Vq@-Y+2=NXXFGbO)IaDng{qGf3;s}O|b2)p-B@n(p(ZGA1lNF zJ|>POiH$HcFq5(3B`)zH1tjK30rpPyF8CC}0GD-sv~Zu){BDf;f<#Df`EWO9J_J7> zDOT-6+_I^7Kp*-Xvmje<*H}qn#7!Is@x~mXy<2_G#fa0!K%iO}ta&dKZVNW&85;CH z$^DR1oW&u%o7r`VaUT#f&n9zq$`M>&kUUjgO+Mb4-Zg{-SLTZRPdzqpDEhtt`vh`| zeubOEcxQU|z{vB4&kC4kp60R5s`ohG=M|iQO`MK2`Ou-=DF540{rI0#6F&hg!iri9-X;2dw{`n5i(kT*8o@& z0@&EYb0<2lCZ^_he|cU0{{9bd$xpXGE5G&I@7^Pa6jM`k<(zMB zc`hX*Z*p(y@XfR4<)3@u{7yokw%s)S8XfCJ0G_=iH(0T*0C(oO_}!08%_LW^`ogC) zuW47jsA(ptE3v?V=Q?CK`wBouJt6>aeI9hh-M0q+&fX=jvFZzGO?<&f+u}e?@1%7# zSn#g}9E|A^7c8V^G2q(!j%N6VT*1fLyW|;IU-o*|#s#pN*n)A?E379(ht`$c;9m z`gxl-eG6h~n$DV7W9W6Pz3&5jSAPE&Uk$Bsfx66^0e0!H|N1M|lKXf@cuiu&51-Qa zIYTqI{bMrpjI_RU+xW5W!Lze>O>q2Btz0H$De{{{ayD(fL{7 z;tSiQaUOVO?=#2VK3zOQ=I9eL#J=PN%MHu9* zEn(h#hS#|_ya5=yzCIkgY`lNx1W4Wm6tI?>ytG|M@F}6`b?$BM0Dsr&>vcZCJ9h6T zc3xcfwNKwZlhn<}_TI&*Zw|n$@3}ZPs;}l@?4J9yWT#TQ}AUkoTI)=Nllx!;Ji2!Z@4`Sujg`|=NP@Kn|bKd!Ibsg92lSO zCh6nWwGNsIM4yiCktfM>IY#dW^#fSb_o<(Ci>})Fu7UR)vVoj>NS~2IdpGyZiOUYj zIc1(ixpDEe=fFLKtWR8E$#Z#V@5btdvGc@dL~7S2);(}%kODIHdxVA$@7gjx*bnL5 zG**skF3i&%Bt}1I;4C0XOpaYx@J0U2r-sBB8X&-8!0l~SqEFQ9YT=#wY)1T9gwgu!@HkJZ)auN3F6z~lia zO6_$8eOKu()DE-nscY(=QR;=-Ds`>Y^V^;J0$e|bJ}Dp7dpA4v#mx@-N?k%%*E@Av zJXHEMqEb`nHxKF=))}?B+^JW$kI-{)1LupKdWn4p9e)2*>qu9w)J%OH9J#*N0a@wH z6?3rX{H)5|UN4cVTHkV=FwghvTmH9+q*3cTb+fH9ZRwK|p}*MMKd9^Ngs5-nIrCNd ze^gVf!^cxqD-ehNgtc0KQkUBavV!{LUp7wda{igoygUSjCqpO$a%7ZtzQJ*&>=Bl199sZXowe-yhw?k?ej z`U)53(&*bt&(#z@)Tb1wVI4X!sB@aTrp!O?IK9{N>aKEt1WX|qj9sZ)>JNLJm)jk5 W?1X=wM%Er_SfReq_bI{+^YLGB4R@yi literal 0 HcmV?d00001 diff --git a/src/main/resources/templates/casLoginView.html b/src/main/resources/templates/casLoginView.html new file mode 100644 index 0000000..805839b --- /dev/null +++ b/src/main/resources/templates/casLoginView.html @@ -0,0 +1,40 @@ + + + + + + + + CAS Acceptable Use Policy View + + + + + +
+ +
+ + + + +
+
+ + + diff --git a/src/main/resources/templates/casLogoutView.html b/src/main/resources/templates/casLogoutView.html new file mode 100644 index 0000000..837cb79 --- /dev/null +++ b/src/main/resources/templates/casLogoutView.html @@ -0,0 +1,24 @@ + + + + + + + + CAS Logout + + + + +
+
+
+ + + +
+
+
+ + diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html new file mode 100644 index 0000000..1e7a299 --- /dev/null +++ b/src/main/resources/templates/fragments/footer.html @@ -0,0 +1,5 @@ +
+ Copyright Date Apereo, Inc + Powered by Apereo CAS + +
diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html new file mode 100644 index 0000000..d8ad74b --- /dev/null +++ b/src/main/resources/templates/fragments/header.html @@ -0,0 +1,231 @@ + + + + + + + + + Header Fragment + + + + +
+
+ + +
+ + + +
+
+
+

+ Notifications +

+
+
+ +
+

+ + Static AuthN is ONLY useful for demo purposes. It is recommended that you connect CAS to + LDAP, + JDBC, etc + instead. +

+
+
+
+ +

+ Unsure Connection +

+
+
+
+ +
+
+
+
+
+ +
+ + + + + diff --git a/src/main/resources/templates/fragments/pmlinks.html b/src/main/resources/templates/fragments/pmlinks.html new file mode 100644 index 0000000..6d2bb4d --- /dev/null +++ b/src/main/resources/templates/fragments/pmlinks.html @@ -0,0 +1,39 @@ + + + + + + + + + Password Management Links Fragment + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/serviceui.html b/src/main/resources/templates/fragments/serviceui.html new file mode 100644 index 0000000..1dc8fb6 --- /dev/null +++ b/src/main/resources/templates/fragments/serviceui.html @@ -0,0 +1,48 @@ + + + + + + + + + Service UI Fragment + + + + +
+
+
+ + + +
+

serviceUIMetadata.displayName

+

serviceUIMetadata.description

+ +

+ screen.mdui.infolink.text

+

+ serviceUIMetadata.privacyStatementURL

+
+
+ +
+ +
+
Registered Service Name
+

Registered Service Description

+
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html new file mode 100644 index 0000000..dcf329f --- /dev/null +++ b/src/main/resources/templates/layout.html @@ -0,0 +1,43 @@ + + + + + + + + + CAS – Central Authentication Service + + + + + + + + + + + + +

- Unsure Connection

diff --git a/src/main/resources/templates/fragments/pmlinks.html b/src/main/resources/templates/fragments/pmlinks.html index 6d2bb4d..5325f85 100644 --- a/src/main/resources/templates/fragments/pmlinks.html +++ b/src/main/resources/templates/fragments/pmlinks.html @@ -6,34 +6,18 @@ - Password Management Links Fragment +
+
+
+ + + + From dc19eb760672d889f347317be1a6b737bf5e3f9c Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 31 Aug 2020 17:35:11 -0400 Subject: [PATCH 62/89] Fully implement two-factor authn flow * Reconfigured OSF CAS login authn and exc web flow * Customized 2FA HTML templates and updated messages.properties --- .../OsfCasLoginWebflowConfigurer.java | 53 +++++++++-- src/main/resources/messages.properties | 15 ++- .../templates/casTwoFactorLoginView.html | 19 +--- .../templates/fragments/totploginform.html | 95 ++++++------------- 4 files changed, 90 insertions(+), 92 deletions(-) diff --git a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/configurer/OsfCasLoginWebflowConfigurer.java b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/configurer/OsfCasLoginWebflowConfigurer.java index 584bd02..d0e0a6a 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/configurer/OsfCasLoginWebflowConfigurer.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/configurer/OsfCasLoginWebflowConfigurer.java @@ -3,6 +3,7 @@ import org.apereo.cas.adaptors.osf.authentication.credential.OsfPostgresCredential; import org.apereo.cas.adaptors.osf.authentication.exceptions.AccountNotConfirmedIdpException; import org.apereo.cas.adaptors.osf.authentication.exceptions.AccountNotConfirmedOsfException; +import org.apereo.cas.adaptors.osf.authentication.exceptions.InvalidOneTimePasswordException; import org.apereo.cas.adaptors.osf.authentication.exceptions.InvalidUserStatusException; import org.apereo.cas.adaptors.osf.authentication.exceptions.InvalidVerificationKeyException; import org.apereo.cas.adaptors.osf.authentication.exceptions.OneTimePasswordRequiredException; @@ -20,8 +21,6 @@ import org.apereo.cas.web.flow.CasWebflowConstants; import org.apereo.cas.web.flow.configurer.DefaultLoginWebflowConfigurer; -import lombok.extern.slf4j.Slf4j; - import org.springframework.context.ConfigurableApplicationContext; import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.definition.registry.FlowDefinitionRegistry; @@ -45,7 +44,6 @@ * @author Longze Chen * @since 6.2.1 */ -@Slf4j public class OsfCasLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer { /** @@ -70,12 +68,15 @@ public OsfCasLoginWebflowConfigurer( protected void createDefaultViewStates(final Flow flow) { super.createDefaultViewStates(flow); // Create OSF customized view states - createOsfCasViewStates(flow); + createTwoFactorLoginFormView(flow); + createOsfCasAuthenticationExceptionViewStates(flow); } @Override protected void createLoginFormView(final Flow flow) { - List propertiesToBind = CollectionUtils.wrapList("username", "password", "source"); + + List propertiesToBind = CollectionUtils + .wrapList("username", "password", "verificationKey", "oneTimePassword", "source"); BinderConfiguration binder = createStateBinderConfiguration(propertiesToBind); casProperties.getView().getCustomLoginFormFields() .forEach((field, props) -> { @@ -225,6 +226,11 @@ protected void createHandleAuthenticationFailureAction(final Flow flow) { OneTimePasswordRequiredException.class.getSimpleName(), OsfCasWebflowConstants.VIEW_ID_ONE_TIME_PASSWORD_REQUIRED ); + createTransitionForState( + handler, + InvalidOneTimePasswordException.class.getSimpleName(), + OsfCasWebflowConstants.VIEW_ID_ONE_TIME_PASSWORD_REQUIRED + ); // The default transition createStateDefaultTransition(handler, CasWebflowConstants.STATE_ID_INIT_LOGIN_FORM); @@ -274,11 +280,11 @@ private void createOsfNonInteractiveAuthenticationCheckAction(final Flow flow) { } /** - * Create extra view states for OSF CAS. + * Create extra authentication exception view states for OSF CAS. * * @param flow the flow */ - private void createOsfCasViewStates(final Flow flow) { + private void createOsfCasAuthenticationExceptionViewStates(final Flow flow) { createViewState( flow, OsfCasWebflowConstants.VIEW_ID_ACCOUNT_NOT_CONFIRMED_IDP, @@ -299,10 +305,39 @@ private void createOsfCasViewStates(final Flow flow) { OsfCasWebflowConstants.VIEW_ID_INVALID_VERIFICATION_KEY, OsfCasWebflowConstants.VIEW_ID_INVALID_VERIFICATION_KEY ); - createViewState( + } + + /** + * Create the customized two-factor authentication form submission view state for OSF CAS. + * + * @param flow the flow + */ + private void createTwoFactorLoginFormView(final Flow flow) { + List propertiesToBind = CollectionUtils.wrapList("oneTimePassword", "source"); + BinderConfiguration binder = createStateBinderConfiguration(propertiesToBind); + casProperties.getView().getCustomLoginFormFields() + .forEach((field, props) -> { + String fieldName = String.format("customFields[%s]", field); + binder.addBinding( + new BinderConfiguration.Binding(fieldName, props.getConverter(), props.isRequired()) + ); + }); + ViewState state = createViewState( flow, OsfCasWebflowConstants.VIEW_ID_ONE_TIME_PASSWORD_REQUIRED, - OsfCasWebflowConstants.VIEW_ID_ONE_TIME_PASSWORD_REQUIRED + OsfCasWebflowConstants.VIEW_ID_ONE_TIME_PASSWORD_REQUIRED, + binder + ); + state.getRenderActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_RENDER_LOGIN_FORM)); + createStateModelBinding(state, CasWebflowConstants.VAR_ID_CREDENTIAL, OsfPostgresCredential.class); + Transition transition = createTransitionForState( + state, + CasWebflowConstants.TRANSITION_ID_SUBMIT, + CasWebflowConstants.STATE_ID_REAL_SUBMIT ); + MutableAttributeMap attributes = transition.getAttributes(); + attributes.put("bind", Boolean.TRUE); + attributes.put("validate", Boolean.TRUE); + attributes.put("history", History.INVALIDATE); } } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 4b5e246..2f3efdb 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -527,6 +527,15 @@ screen.welcome.label.email.accesskey=e screen.welcome.label.password=Password screen.welcome.label.password.accesskey=p # +# Two factor and login form submission +# +cas.twofactor.pagetitle=Sign in +screen.twofactor.instructions=Enter your one-time password to finish login +screen.twofactor.label.onetimepassword=One-time password (6-digit) +screen.twofactor.label.onetimepassword.accesskey=o +screen.twofactor.button.verify=VERIFY +screen.twofactor.button.verifywip=One moment please... +# # Authentication exception messages on the login form submission page (inline / pop-up) # authenticationFailure.AccountDisabledException=This account has been disabled. @@ -564,8 +573,10 @@ screen.invaliduserstatus.message=Cannot log in to an invalid account. If you bel screen.invalidverificationkey.heading=Invalid Verification key screen.invalidverificationkey.message=Automatic login has failed due to invalid one-time verification key. This key \ may have expired or have already been used. -screen.onetimepasswordrequired.heading=Two-factor authentication required -screen.onetimepasswordrequired.message=(page under construction / feature not implemented) +screen.onetimepasswordrequired.heading=OSF Two-factor Authentication +screen.onetimepasswordrequired.message=Two-factor authentication has been enabled for this OSF account. Please enter \ + the one-time password generated by the authentication app. If you believe this should not happen, please contact OSF Support. # # Enf of OSF CAS customized messages.properties # diff --git a/src/main/resources/templates/casTwoFactorLoginView.html b/src/main/resources/templates/casTwoFactorLoginView.html index ee1e7a6..e311c9d 100644 --- a/src/main/resources/templates/casTwoFactorLoginView.html +++ b/src/main/resources/templates/casTwoFactorLoginView.html @@ -5,7 +5,7 @@ - CAS Acceptable Use Policy View + @@ -15,22 +15,11 @@
- - - -
diff --git a/src/main/resources/templates/fragments/totploginform.html b/src/main/resources/templates/fragments/totploginform.html index e33b366..427640b 100644 --- a/src/main/resources/templates/fragments/totploginform.html +++ b/src/main/resources/templates/fragments/totploginform.html @@ -6,48 +6,43 @@ - Login Form Fragment +
- -
+ +
-
-   - -
-

+

- Enter your Username and Password: +

- +
- +
@@ -66,70 +61,41 @@

id="username" size="25" type="text" - th:readonly="!${@casThymeleafLoginFormDirector.isLoginFormUsernameInputVisible(#vars)}" + readonly th:disabled="${@casThymeleafLoginFormDirector.isLoginFormUsernameInputDisabled(#vars)}" th:field="*{username}" - th:accesskey="#{screen.welcome.label.netid.accesskey}" + th:accesskey="#{screen.welcome.label.email.accesskey}" th:value="${@casThymeleafLoginFormDirector.getLoginFormUsername(#vars)}" autocomplete="off" /> - +

- - -
-
-

- -

+ +
-
-
-
- Label - -

-

-
-
- -
-

- - -

-
- -
-

- - -

-
-
- -
- -

@@ -138,19 +104,17 @@

- -
+ +

-
- -
From f8f285b2a400360e877df435f43263f864f027f0 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 31 Aug 2020 18:57:41 -0400 Subject: [PATCH 63/89] Fix duplicate error messages for non-interactive flow In addititon, disabled two-factor one-time password support in the non-interactive action and moved all OSF customized exceptions to the top fo the handled error list. --- .../OsfCasCoreWebflowConfiguration.java | 8 ++++- ...alFromNonInteractiveCredentialsAction.java | 33 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java index 6476e17..a94a7c1 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java @@ -17,6 +17,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -34,10 +35,11 @@ public class OsfCasCoreWebflowConfiguration extends CasCoreWebflowConfiguration @Override @Bean public Set> handledAuthenticationExceptions() { - Set> errors = super.handledAuthenticationExceptions(); + // OSF CAS Customization: Add newly created OSF-specific authentication exceptions to the handled error list, // which is required to enable all authentication exception handlers and respective // handling actions to handle these exceptions. + Set> errors = new LinkedHashSet<>(); errors.add(AccountNotConfirmedIdpException.class); errors.add(AccountNotConfirmedOsfException.class); errors.add(InvalidOneTimePasswordException.class); @@ -45,6 +47,10 @@ public Set> handledAuthenticationExceptions() { errors.add(InvalidUserStatusException.class); errors.add(InvalidVerificationKeyException.class); errors.add(OneTimePasswordRequiredException.class); + + // Add built-in exceptions after OSF-specific exceptions since order matters + errors.addAll(super.handledAuthenticationExceptions()); + return errors; } } diff --git a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/login/OsfPrincipalFromNonInteractiveCredentialsAction.java b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/login/OsfPrincipalFromNonInteractiveCredentialsAction.java index 810960b..1841ca8 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/login/OsfPrincipalFromNonInteractiveCredentialsAction.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/login/OsfPrincipalFromNonInteractiveCredentialsAction.java @@ -43,6 +43,10 @@ public class OsfPrincipalFromNonInteractiveCredentialsAction extends AbstractNon private static final List AUTHN_DELEGATION_CLIENT_LIST = new ArrayList<>(List.of("oldcas", "orcid")); + private static final String USERNAME_PARAMETER_NAME = "username"; + + private static final String VERIFICATION_KEY_PARAMETER_NAME = "verification_key"; + @NotNull private CentralAuthenticationService centralAuthenticationService; @@ -87,20 +91,18 @@ protected Credential constructCredentialsFromRequest(final RequestContext contex return null; } - LOGGER.debug("No credential found in context, check username, verification key and one-time password"); + LOGGER.debug("No valid credential found in the request context."); final OsfPostgresCredential osfPostgresCredential = new OsfPostgresCredential(); - final String username = request.getParameter("username"); - final String verification_key = request.getParameter("verification_key"); - final String oneTimePassword = request.getParameter("2fa_passcode"); - if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(verification_key)) { + final String username = request.getParameter(USERNAME_PARAMETER_NAME); + final String verificationKey = request.getParameter(VERIFICATION_KEY_PARAMETER_NAME); + if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(verificationKey)) { osfPostgresCredential.setUsername(username); - osfPostgresCredential.setVerificationKey(verification_key); - osfPostgresCredential.setOneTimePassword(oneTimePassword); + osfPostgresCredential.setVerificationKey(verificationKey); osfPostgresCredential.setRememberMe(true); - LOGGER.debug("User [{}] found in request w/ one-time verification key [{}]", username, verification_key); + LOGGER.debug("User [{}] found in request w/ verificationKey", username); return osfPostgresCredential; } - LOGGER.debug("No user with username and verification key found in request"); + LOGGER.debug("No username or verification key found in the request parameters."); return null; } @@ -113,4 +115,17 @@ protected Event doPreExecute(final RequestContext context) throws Exception { protected Event doExecute(final RequestContext requestContext) { return super.doExecute(requestContext); } + + /** + * On error. + * + * Super class {@link AbstractNonInteractiveCredentialsAction} always appends an error message to the message + * context when returning error, of which the default one is{@link javax.security.auth.login.FailedLoginException}. + * This leads to an extra error message being displayed along with the actual error message when exception happens. + * Thus, must override the {@link AbstractNonInteractiveCredentialsAction#onError(RequestContext)} with no-op. + * + * @param context the context + */ + @Override + protected void onError(final RequestContext context) {} } From f0d6f3bf6e0fa6c753f3a9c54a55bb2689d1fad5 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 31 Aug 2020 22:22:50 -0400 Subject: [PATCH 64/89] Improve import order and remove unnecessary lombok decorators * Import order * org.apereo.cas (or org.pac4j in some cases) packages come fisrt * javax and java packages come last * other packages sit in between the above two * all sorted by alphabetical order * lombok optimization * Removed Slf4j, NoArgsConstructor and AllArgsConstructor decorator for classes that don't need it, which turned out to be a lot * Removed all val definition (a "final " replacement) since it affects code readability due to no typing information * Added trivial style and JavaDoc updates --- .../credential/OsfPostgresCredential.java | 4 - .../support/OsfPasswordUtils.java | 2 - .../authentication/support/OsfUserUtils.java | 2 - .../osf/authentication/support/TotpUtils.java | 3 - .../osf/config/JpaOsfDaoConfiguration.java | 3 - ...cationEventExecutionPlanConfiguration.java | 3 - .../cas/adaptors/osf/daos/AbstractOsfDao.java | 5 - .../cas/adaptors/osf/daos/JpaOsfDao.java | 2 - .../dialects/OsfPostgresDialect.java | 1 + .../adaptors/osf/models/AbstractOsfModel.java | 11 +- .../osf/models/DjangoContentType.java | 2 - .../cas/adaptors/osf/models/OsfEmail.java | 2 - .../cas/adaptors/osf/models/OsfGuid.java | 2 - .../cas/adaptors/osf/models/OsfTotp.java | 2 - .../cas/adaptors/osf/models/OsfUser.java | 2 - .../OsfCasSupportActionsConfiguration.java | 3 - .../osf/web/flow/OsfCasWebflowConstants.java | 2 +- .../OsfCasCoreWebflowConfiguration.java | 3 - .../OsfCasWebflowContextConfiguration.java | 3 - .../OsfPostgresAuthenticationProperties.java | 2 - .../support/osf/OsfPostgresJpaProperties.java | 4 +- ...egatedAuthenticationWebflowConfigurer.java | 104 +++++++++++++----- .../profile/orcid/OrcidProfileDefinition.java | 6 +- .../pac4j/scribe/builder/api/OrcidApi20.java | 4 +- 24 files changed, 86 insertions(+), 91 deletions(-) diff --git a/src/main/java/org/apereo/cas/adaptors/osf/authentication/credential/OsfPostgresCredential.java b/src/main/java/org/apereo/cas/adaptors/osf/authentication/credential/OsfPostgresCredential.java index cbcf833..e5a58a7 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/authentication/credential/OsfPostgresCredential.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/authentication/credential/OsfPostgresCredential.java @@ -2,13 +2,11 @@ import org.apereo.cas.authentication.credential.RememberMeUsernamePasswordCredential; -import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; /** * This is {@link OsfPostgresCredential}. @@ -16,13 +14,11 @@ * @author Longze Chen * @since 6.2.1 */ -@AllArgsConstructor @NoArgsConstructor @Getter @Setter @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -@Slf4j public class OsfPostgresCredential extends RememberMeUsernamePasswordCredential { private static final long serialVersionUID = 4705325561237083442L; diff --git a/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfPasswordUtils.java b/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfPasswordUtils.java index 7c55ed0..9c48c5a 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfPasswordUtils.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfPasswordUtils.java @@ -1,6 +1,5 @@ package org.apereo.cas.adaptors.osf.authentication.support; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.bcrypt.BCrypt; @@ -14,7 +13,6 @@ * @author Longze Chen * @since 6.2.1 */ -@NoArgsConstructor @Slf4j public final class OsfPasswordUtils { diff --git a/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfUserUtils.java b/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfUserUtils.java index 96edd44..54d111b 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfUserUtils.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/OsfUserUtils.java @@ -5,7 +5,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import javax.security.auth.login.FailedLoginException; @@ -17,7 +16,6 @@ * @author Longze Chen * @since 6.2.1 */ -@NoArgsConstructor @Slf4j public final class OsfUserUtils { diff --git a/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/TotpUtils.java b/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/TotpUtils.java index 4433e1f..d002de3 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/TotpUtils.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/authentication/support/TotpUtils.java @@ -1,7 +1,5 @@ package org.apereo.cas.adaptors.osf.authentication.support; -import lombok.NoArgsConstructor; - import org.apache.commons.codec.binary.Base32; import javax.crypto.Mac; @@ -20,7 +18,6 @@ * @author Longze Chen * @since 6.2.1 */ -@NoArgsConstructor public final class TotpUtils { private static final int[] DIGITS_POWER = { diff --git a/src/main/java/org/apereo/cas/adaptors/osf/config/JpaOsfDaoConfiguration.java b/src/main/java/org/apereo/cas/adaptors/osf/config/JpaOsfDaoConfiguration.java index c3e9d41..7f3c6e6 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/config/JpaOsfDaoConfiguration.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/config/JpaOsfDaoConfiguration.java @@ -9,8 +9,6 @@ import org.apereo.cas.jpa.JpaBeanFactory; import org.apereo.cas.util.spring.ApplicationContextProvider; -import lombok.extern.slf4j.Slf4j; - import org.reflections.Reflections; import org.reflections.scanners.SubTypesScanner; import org.reflections.util.ClasspathHelper; @@ -43,7 +41,6 @@ */ @Configuration("jpaOsfDaoConfiguration") @EnableConfigurationProperties(CasConfigurationProperties.class) -@Slf4j public class JpaOsfDaoConfiguration { @Autowired diff --git a/src/main/java/org/apereo/cas/adaptors/osf/config/OsfPostgresAuthenticationEventExecutionPlanConfiguration.java b/src/main/java/org/apereo/cas/adaptors/osf/config/OsfPostgresAuthenticationEventExecutionPlanConfiguration.java index 0873315..c3cd307 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/config/OsfPostgresAuthenticationEventExecutionPlanConfiguration.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/config/OsfPostgresAuthenticationEventExecutionPlanConfiguration.java @@ -11,8 +11,6 @@ import org.apereo.cas.configuration.model.support.osf.OsfPostgresAuthenticationProperties; import org.apereo.cas.services.ServicesManager; -import lombok.extern.slf4j.Slf4j; - import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -30,7 +28,6 @@ */ @Configuration("osfPostgresAuthenticationEventExecutionPlanConfiguration") @EnableConfigurationProperties(CasConfigurationProperties.class) -@Slf4j public class OsfPostgresAuthenticationEventExecutionPlanConfiguration { @Autowired diff --git a/src/main/java/org/apereo/cas/adaptors/osf/daos/AbstractOsfDao.java b/src/main/java/org/apereo/cas/adaptors/osf/daos/AbstractOsfDao.java index 754a215..4e132c2 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/daos/AbstractOsfDao.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/daos/AbstractOsfDao.java @@ -1,21 +1,16 @@ package org.apereo.cas.adaptors.osf.daos; -import lombok.NoArgsConstructor; - import org.apereo.cas.adaptors.osf.models.OsfTotp; import org.apereo.cas.adaptors.osf.models.OsfEmail; import org.apereo.cas.adaptors.osf.models.OsfGuid; import org.apereo.cas.adaptors.osf.models.OsfUser; -import lombok.extern.slf4j.Slf4j; /** * This is {@link AbstractOsfDao}. * * @author Longze Chen * @since 6.2.1 */ -@NoArgsConstructor -@Slf4j public abstract class AbstractOsfDao { public OsfUser findOneUserByEmail(final String address) { diff --git a/src/main/java/org/apereo/cas/adaptors/osf/daos/JpaOsfDao.java b/src/main/java/org/apereo/cas/adaptors/osf/daos/JpaOsfDao.java index a59bbb6..08cf089 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/daos/JpaOsfDao.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/daos/JpaOsfDao.java @@ -6,7 +6,6 @@ import org.apereo.cas.adaptors.osf.models.OsfUser; import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +23,6 @@ */ @NoArgsConstructor @Transactional(transactionManager = "jpaOsfDaoTransactionManager") -@Slf4j public class JpaOsfDao extends AbstractOsfDao { @NotNull diff --git a/src/main/java/org/apereo/cas/adaptors/osf/hibernate/dialects/OsfPostgresDialect.java b/src/main/java/org/apereo/cas/adaptors/osf/hibernate/dialects/OsfPostgresDialect.java index e385f15..5d83e10 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/hibernate/dialects/OsfPostgresDialect.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/hibernate/dialects/OsfPostgresDialect.java @@ -3,6 +3,7 @@ import org.hibernate.dialect.PostgreSQL95Dialect; import java.sql.Types; + /** * This is {@link OsfPostgresDialect}. * diff --git a/src/main/java/org/apereo/cas/adaptors/osf/models/AbstractOsfModel.java b/src/main/java/org/apereo/cas/adaptors/osf/models/AbstractOsfModel.java index 6d707e5..cacaac5 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/models/AbstractOsfModel.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/models/AbstractOsfModel.java @@ -1,15 +1,12 @@ package org.apereo.cas.adaptors.osf.models; -import javax.persistence.Column; -import javax.persistence.Id; -import javax.persistence.MappedSuperclass; - import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; import java.io.Serializable; /** @@ -19,10 +16,8 @@ * @since 6.2.1 */ @MappedSuperclass -@NoArgsConstructor @Setter @ToString -@Slf4j public abstract class AbstractOsfModel implements Serializable { private static final long serialVersionUID = -5372017847228934320L; diff --git a/src/main/java/org/apereo/cas/adaptors/osf/models/DjangoContentType.java b/src/main/java/org/apereo/cas/adaptors/osf/models/DjangoContentType.java index c9f8982..f1fdaa2 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/models/DjangoContentType.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/models/DjangoContentType.java @@ -3,7 +3,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; import javax.persistence.Column; import javax.persistence.Entity; @@ -20,7 +19,6 @@ @NoArgsConstructor @Getter @ToString -@Slf4j public class DjangoContentType extends AbstractOsfModel { private static final long serialVersionUID = 7532814264322554678L; diff --git a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfEmail.java b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfEmail.java index c40acbf..a965990 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfEmail.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfEmail.java @@ -3,7 +3,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; import javax.persistence.Column; import javax.persistence.Entity; @@ -22,7 +21,6 @@ @NoArgsConstructor @Getter @ToString -@Slf4j public final class OsfEmail extends AbstractOsfModel { private static final long serialVersionUID = -1608581129054050883L; diff --git a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfGuid.java b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfGuid.java index ccebecb..7f67d31 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfGuid.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfGuid.java @@ -3,7 +3,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; import javax.persistence.Column; import javax.persistence.Entity; @@ -19,7 +18,6 @@ @NoArgsConstructor @Getter @ToString -@Slf4j public class OsfGuid extends AbstractOsfModel { private static final long serialVersionUID = 5782669366012776490L; diff --git a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfTotp.java b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfTotp.java index e922f02..976e32e 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfTotp.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfTotp.java @@ -4,7 +4,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base32; @@ -29,7 +28,6 @@ @NoArgsConstructor @Getter @ToString -@Slf4j public class OsfTotp extends AbstractOsfModel { @OneToOne diff --git a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfUser.java b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfUser.java index 3bc6326..2f9db27 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/models/OsfUser.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/models/OsfUser.java @@ -8,7 +8,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; @@ -34,7 +33,6 @@ @NoArgsConstructor @Getter @ToString -@Slf4j public final class OsfUser extends AbstractOsfModel { private static final long serialVersionUID = 5634562720139150055L; diff --git a/src/main/java/org/apereo/cas/adaptors/osf/web/config/OsfCasSupportActionsConfiguration.java b/src/main/java/org/apereo/cas/adaptors/osf/web/config/OsfCasSupportActionsConfiguration.java index c573c30..37c6899 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/web/config/OsfCasSupportActionsConfiguration.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/web/config/OsfCasSupportActionsConfiguration.java @@ -8,8 +8,6 @@ import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver; import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver; -import lombok.extern.slf4j.Slf4j; - import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -28,7 +26,6 @@ @Configuration(value = "osfCasSupportActionsConfiguration", proxyBeanMethods = false) @AutoConfigureBefore(CasSupportActionsConfiguration.class) @EnableConfigurationProperties(CasConfigurationProperties.class) -@Slf4j public class OsfCasSupportActionsConfiguration extends CasSupportActionsConfiguration { @Autowired diff --git a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/OsfCasWebflowConstants.java b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/OsfCasWebflowConstants.java index 7a94bc6..38631e8 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/OsfCasWebflowConstants.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/OsfCasWebflowConstants.java @@ -2,7 +2,7 @@ /** * This is {@link OsfCasWebflowConstants}, which expands the default {@link org.apereo.cas.web.flow.CasWebflowConstants} - * interface by adding OSF CAS customized action and state IDs. + * interface by adding OSF CAS customized action, state and view IDs. * * @author Longze Chen * @since 6.2.1 diff --git a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java index a94a7c1..e024ce7 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasCoreWebflowConfiguration.java @@ -10,8 +10,6 @@ import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.web.flow.config.CasCoreWebflowConfiguration; -import lombok.extern.slf4j.Slf4j; - import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -29,7 +27,6 @@ @Configuration("osfCasCoreWebflowConfiguration") @AutoConfigureBefore(CasCoreWebflowConfiguration.class) @EnableConfigurationProperties(CasConfigurationProperties.class) -@Slf4j public class OsfCasCoreWebflowConfiguration extends CasCoreWebflowConfiguration { @Override diff --git a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasWebflowContextConfiguration.java b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasWebflowContextConfiguration.java index 4c8b95c..eb7b0d3 100644 --- a/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasWebflowContextConfiguration.java +++ b/src/main/java/org/apereo/cas/adaptors/osf/web/flow/config/OsfCasWebflowContextConfiguration.java @@ -5,8 +5,6 @@ import org.apereo.cas.web.flow.CasWebflowConfigurer; import org.apereo.cas.web.flow.config.CasWebflowContextConfiguration; -import lombok.extern.slf4j.Slf4j; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -26,7 +24,6 @@ @Configuration("osfCasWebflowContextConfiguration") @AutoConfigureBefore(CasWebflowContextConfiguration.class) @EnableConfigurationProperties(CasConfigurationProperties.class) -@Slf4j public class OsfCasWebflowContextConfiguration extends CasWebflowContextConfiguration { private static final int DEFAULT_WEB_FLOW_CONFIGURER_ORDER = 0; diff --git a/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresAuthenticationProperties.java b/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresAuthenticationProperties.java index 0c88386..c43e58f 100644 --- a/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresAuthenticationProperties.java +++ b/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresAuthenticationProperties.java @@ -5,7 +5,6 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; -import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.NestedConfigurationProperty; @@ -20,7 +19,6 @@ @Getter @Setter @Accessors(chain = true) -@Slf4j public class OsfPostgresAuthenticationProperties implements Serializable { private static final long serialVersionUID = -6126944686676618138L; diff --git a/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresJpaProperties.java b/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresJpaProperties.java index 7081f24..845cc7e 100644 --- a/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresJpaProperties.java +++ b/src/main/java/org/apereo/cas/configuration/model/support/osf/OsfPostgresJpaProperties.java @@ -1,11 +1,10 @@ package org.apereo.cas.configuration.model.support.osf; -import lombok.experimental.Accessors; import org.apereo.cas.configuration.model.support.jpa.AbstractJpaProperties; import lombok.Getter; import lombok.Setter; -import lombok.extern.slf4j.Slf4j; +import lombok.experimental.Accessors; /** * This is {@link OsfPostgresJpaProperties}. @@ -16,7 +15,6 @@ @Getter @Setter @Accessors(chain = true) -@Slf4j public class OsfPostgresJpaProperties extends AbstractJpaProperties { private static final long serialVersionUID = 2593648298993956294L; diff --git a/src/main/java/org/apereo/cas/web/flow/DelegatedAuthenticationWebflowConfigurer.java b/src/main/java/org/apereo/cas/web/flow/DelegatedAuthenticationWebflowConfigurer.java index cf38a9d..dcd8bcb 100644 --- a/src/main/java/org/apereo/cas/web/flow/DelegatedAuthenticationWebflowConfigurer.java +++ b/src/main/java/org/apereo/cas/web/flow/DelegatedAuthenticationWebflowConfigurer.java @@ -1,45 +1,58 @@ package org.apereo.cas.web.flow; -import lombok.extern.slf4j.Slf4j; -import lombok.val; - import org.apereo.cas.adaptors.osf.web.flow.OsfCasWebflowConstants; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer; import org.apereo.cas.web.support.WebUtils; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.web.servlet.ModelAndView; import org.springframework.webflow.action.AbstractAction; import org.springframework.webflow.definition.registry.FlowDefinitionRegistry; +import org.springframework.webflow.engine.ActionState; import org.springframework.webflow.engine.Flow; +import org.springframework.webflow.engine.TransitionSet; +import org.springframework.webflow.engine.TransitionableState; +import org.springframework.webflow.engine.ViewState; import org.springframework.webflow.engine.builder.support.FlowBuilderServices; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Optional; + /** - * The {@link DelegatedAuthenticationWebflowConfigurer} is responsible for - * adjusting the CAS webflow context for pac4j integration. + * This is {@link DelegatedAuthenticationWebflowConfigurer}. + * + * The configurer is responsible for adjusting the CAS webflow context for pac4j integration. OSF CAS customizes it by + * intercepting transition of the success event: it redirects the web flow to the non-interactive authentication check + * state with action {@link org.apereo.cas.adaptors.osf.web.flow.login.OsfPrincipalFromNonInteractiveCredentialsAction}. * * @author Misagh Moayyed * @author Longze Chen * @since 4.2 */ -@Slf4j public class DelegatedAuthenticationWebflowConfigurer extends AbstractCasWebflowConfigurer { + private static final String DECISION_STATE_CHECK_DELEGATED_AUTHN_FAILURE = "checkDelegatedAuthnFailureDecision"; - public DelegatedAuthenticationWebflowConfigurer(final FlowBuilderServices flowBuilderServices, - final FlowDefinitionRegistry loginFlowDefinitionRegistry, - final FlowDefinitionRegistry logoutFlowDefinitionRegistry, - final ConfigurableApplicationContext applicationContext, - final CasConfigurationProperties casProperties) { + private static final String ACTION_ID_DELEGATED_AUTHN_CLIENT_LOGOUT = "delegatedAuthenticationClientLogoutAction"; + + public DelegatedAuthenticationWebflowConfigurer( + final FlowBuilderServices flowBuilderServices, + final FlowDefinitionRegistry loginFlowDefinitionRegistry, + final FlowDefinitionRegistry logoutFlowDefinitionRegistry, + final ConfigurableApplicationContext applicationContext, + final CasConfigurationProperties casProperties + ) { super(flowBuilderServices, loginFlowDefinitionRegistry, applicationContext, casProperties); setLogoutFlowDefinitionRegistry(logoutFlowDefinitionRegistry); } @Override protected void doInitialize() { - val flow = getLoginFlow(); + final Flow flow = getLoginFlow(); if (flow != null) { createClientActionActionState(flow); createStopWebflowViewState(flow); @@ -48,40 +61,71 @@ protected void doInitialize() { } protected void createSaml2ClientLogoutAction() { - val logoutFlow = getLogoutFlow(); - val state = getState(logoutFlow, CasWebflowConstants.STATE_ID_TERMINATE_SESSION); - state.getEntryActionList().add(createEvaluateAction("delegatedAuthenticationClientLogoutAction")); + final Flow logoutFlow = getLogoutFlow(); + final TransitionableState state = getState(logoutFlow, CasWebflowConstants.STATE_ID_TERMINATE_SESSION); + state.getEntryActionList().add(createEvaluateAction(ACTION_ID_DELEGATED_AUTHN_CLIENT_LOGOUT)); } protected void createClientActionActionState(final Flow flow) { - val actionState = createActionState(flow, CasWebflowConstants.STATE_ID_DELEGATED_AUTHENTICATION, - createEvaluateAction(CasWebflowConstants.ACTION_ID_DELEGATED_AUTHENTICATION)); + final ActionState actionState = createActionState( + flow, + CasWebflowConstants.STATE_ID_DELEGATED_AUTHENTICATION, + createEvaluateAction(CasWebflowConstants.ACTION_ID_DELEGATED_AUTHENTICATION) + ); - val transitionSet = actionState.getTransitionSet(); - transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS, OsfCasWebflowConstants.STATE_ID_OSF_NON_INTERACTIVE_AUTHENTICATION_CHECK)); + final TransitionSet transitionSet = actionState.getTransitionSet(); + // OSF CAS customization: transit to the non-interactive authentication check action upon success + transitionSet.add(createTransition( + CasWebflowConstants.TRANSITION_ID_SUCCESS, + OsfCasWebflowConstants.STATE_ID_OSF_NON_INTERACTIVE_AUTHENTICATION_CHECK + )); - val currentStartState = getStartState(flow).getId(); + final String currentStartState = getStartState(flow).getId(); transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_ERROR, currentStartState)); - transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_RESUME, CasWebflowConstants.STATE_ID_CREATE_TICKET_GRANTING_TICKET)); - transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE, DECISION_STATE_CHECK_DELEGATED_AUTHN_FAILURE)); - transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_STOP, CasWebflowConstants.STATE_ID_STOP_WEBFLOW)); + transitionSet.add(createTransition( + CasWebflowConstants.TRANSITION_ID_RESUME, + CasWebflowConstants.STATE_ID_CREATE_TICKET_GRANTING_TICKET + )); + transitionSet.add(createTransition( + CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE, + DECISION_STATE_CHECK_DELEGATED_AUTHN_FAILURE + )); + transitionSet.add( + createTransition(CasWebflowConstants.TRANSITION_ID_STOP, + CasWebflowConstants.STATE_ID_STOP_WEBFLOW + )); transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_WARN, CasWebflowConstants.STATE_ID_WARN)); setStartState(flow, actionState); } protected void createStopWebflowViewState(final Flow flow) { - createDecisionState(flow, DECISION_STATE_CHECK_DELEGATED_AUTHN_FAILURE, "flowScope.unauthorizedRedirectUrl != null", - CasWebflowConstants.STATE_ID_SERVICE_UNAUTHZ_CHECK, CasWebflowConstants.STATE_ID_STOP_WEBFLOW); + createDecisionState( + flow, + DECISION_STATE_CHECK_DELEGATED_AUTHN_FAILURE, + "flowScope.unauthorizedRedirectUrl != null", + CasWebflowConstants.STATE_ID_SERVICE_UNAUTHZ_CHECK, + CasWebflowConstants.STATE_ID_STOP_WEBFLOW + ); - val state = createViewState(flow, CasWebflowConstants.STATE_ID_STOP_WEBFLOW, CasWebflowConstants.STATE_ID_PAC4J_STOP_WEBFLOW); + final ViewState state = createViewState( + flow, + CasWebflowConstants.STATE_ID_STOP_WEBFLOW, + CasWebflowConstants.STATE_ID_PAC4J_STOP_WEBFLOW + ); state.getEntryActionList().add(new AbstractAction() { @Override protected Event doExecute(final RequestContext requestContext) { - val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext); - val response = WebUtils.getHttpServletResponseFromExternalWebflowContext(requestContext); - val mv = DelegatedClientAuthenticationAction.hasDelegationRequestFailed(request, response.getStatus()); - mv.ifPresent(modelAndView -> modelAndView.getModel().forEach((k, v) -> requestContext.getFlowScope().put(k, v))); + final HttpServletRequest request + = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext); + final HttpServletResponse response + = WebUtils.getHttpServletResponseFromExternalWebflowContext(requestContext); + Optional mv + = DelegatedClientAuthenticationAction.hasDelegationRequestFailed(request, response.getStatus()); + mv.ifPresent( + modelAndView -> + modelAndView.getModel().forEach((k, v) -> requestContext.getFlowScope().put(k, v)) + ); return null; } }); diff --git a/src/main/java/org/pac4j/oauth/profile/orcid/OrcidProfileDefinition.java b/src/main/java/org/pac4j/oauth/profile/orcid/OrcidProfileDefinition.java index 94af8e8..5e88509 100644 --- a/src/main/java/org/pac4j/oauth/profile/orcid/OrcidProfileDefinition.java +++ b/src/main/java/org/pac4j/oauth/profile/orcid/OrcidProfileDefinition.java @@ -1,8 +1,5 @@ package org.pac4j.oauth.profile.orcid; -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.model.OAuth2AccessToken; - import static org.pac4j.core.profile.AttributeLocation.PROFILE_ATTRIBUTE; import org.pac4j.core.profile.converter.Converters; @@ -11,6 +8,9 @@ import org.pac4j.oauth.profile.definition.OAuth20ProfileDefinition; import org.pac4j.scribe.model.OrcidToken; +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuth2AccessToken; + /** * This class is the Orcid profile definition. * diff --git a/src/main/java/org/pac4j/scribe/builder/api/OrcidApi20.java b/src/main/java/org/pac4j/scribe/builder/api/OrcidApi20.java index 5b0cb66..ecb7a6b 100644 --- a/src/main/java/org/pac4j/scribe/builder/api/OrcidApi20.java +++ b/src/main/java/org/pac4j/scribe/builder/api/OrcidApi20.java @@ -1,11 +1,13 @@ package org.pac4j.scribe.builder.api; +import org.pac4j.scribe.extractors.OrcidJsonExtractor; + import com.github.scribejava.core.builder.api.DefaultApi20; import com.github.scribejava.core.extractors.TokenExtractor; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.Verb; + import java.util.Map; -import org.pac4j.scribe.extractors.OrcidJsonExtractor; /** * This class represents the OAuth API implementation for ORCiD using OAuth protocol version 2. From d76a32dac14f60d5004c6b07c71b4e2e261c0871 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 1 Sep 2020 12:09:30 -0400 Subject: [PATCH 65/89] Overlay a few views and view fragments - copy as of 6.2.1 * casGenericSuccessView.html: successful authn w/o a service * casServiceErrorView.html: unauthorized service error page * error.html: CAS unavailable page for unexpected exceptions * loginform.html: username-password login form in casLoginView.html * loginproviders.html: authn delegation clients in casLoginView.html * loginsidebar.html - shared bottom "side" bar in a couple of login fragments including loginform.html and totploginform.html --- .../templates/casGenericSuccessView.html | 60 ++++++ .../templates/casServiceErrorView.html | 21 +++ src/main/resources/templates/error.html | 40 ++++ .../templates/fragments/loginform.html | 174 ++++++++++++++++++ .../templates/fragments/loginproviders.html | 118 ++++++++++++ .../templates/fragments/loginsidebar.html | 25 +++ 6 files changed, 438 insertions(+) create mode 100644 src/main/resources/templates/casGenericSuccessView.html create mode 100644 src/main/resources/templates/casServiceErrorView.html create mode 100644 src/main/resources/templates/error.html create mode 100644 src/main/resources/templates/fragments/loginform.html create mode 100644 src/main/resources/templates/fragments/loginproviders.html create mode 100644 src/main/resources/templates/fragments/loginsidebar.html diff --git a/src/main/resources/templates/casGenericSuccessView.html b/src/main/resources/templates/casGenericSuccessView.html new file mode 100644 index 0000000..3b25ef5 --- /dev/null +++ b/src/main/resources/templates/casGenericSuccessView.html @@ -0,0 +1,60 @@ + + + + + + + + Generic Success View + + + + +
+
+
+ +

Log In Successful

+

You, username, have successfully logged into the Central Authentication Service. This is what CAS knows about you:However, you are seeing this page because CAS does not know about your target destination and how to get you there. Examine the authentication request again and make sure a target service/application that is authorized and registered with CAS is specified.

+

+ + The following attributes are resolved for : + +

+ + + + + + + + + + + + + + + + + + + + +
AttributeValue(s)Type
Principal
Authentication
+
+ +

+

+ When you are finished, for security reasons, please log out and exit your web browser. +

+
+ + +
+
+ + diff --git a/src/main/resources/templates/casServiceErrorView.html b/src/main/resources/templates/casServiceErrorView.html new file mode 100644 index 0000000..63a6df1 --- /dev/null +++ b/src/main/resources/templates/casServiceErrorView.html @@ -0,0 +1,21 @@ + + + + + + + + Service Error View + + + + +
+ +
+ + diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html new file mode 100644 index 0000000..10a045f --- /dev/null +++ b/src/main/resources/templates/error.html @@ -0,0 +1,40 @@ + + + + + + + + CAS Error View + + + + + + +
+
+ +
+
+ + diff --git a/src/main/resources/templates/fragments/loginform.html b/src/main/resources/templates/fragments/loginform.html new file mode 100644 index 0000000..707ed0e --- /dev/null +++ b/src/main/resources/templates/fragments/loginform.html @@ -0,0 +1,174 @@ + + + + + + + + + Login Form Fragment + + + + +
+ +
+ +
+
+ +
+   + +
+

+ + Enter your Username and Password: +

+ + + +
+ + + +
+ +
+
+ + + +
+
+ +
+
+ + +
+
+ +
+
+
+ + +
+
+

+ +

+
+
+ +
+ +
+
+
+ Label + +

+

+
+
+ +
+

+ + +

+
+ +
+

+ + +

+
+ +
+ +
+ + + + + +

+ + + + + +

+
+ +
+ + + +
+ +
+ + + +
+
+
+
+
+ + diff --git a/src/main/resources/templates/fragments/loginproviders.html b/src/main/resources/templates/fragments/loginproviders.html new file mode 100644 index 0000000..022f7a2 --- /dev/null +++ b/src/main/resources/templates/fragments/loginproviders.html @@ -0,0 +1,118 @@ + + + + + + + + + loginProviders Fragment + + + + +
+ +
+ + diff --git a/src/main/resources/templates/fragments/loginsidebar.html b/src/main/resources/templates/fragments/loginsidebar.html new file mode 100644 index 0000000..5c0addd --- /dev/null +++ b/src/main/resources/templates/fragments/loginsidebar.html @@ -0,0 +1,25 @@ + + + + + + + + + Login Sidebar Fragment + + + + +
+ +
+ + From 995a23afaab16e5890168501c4ba21746c78deed Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 1 Sep 2020 17:53:57 -0400 Subject: [PATCH 66/89] Further customize messages.properties --- src/main/resources/messages.properties | 58 +++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 2f3efdb..69a6685 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -22,18 +22,18 @@ screen.welcome.label.publicstation=I am at a public workstation. screen.welcome.label.warn=Warn me! screen.welcome.label.warn.accesskey=w screen.welcome.label.warnremove=Do not warn me again -screen.welcome.button.login=LOGIN +# screen.welcome.button.login=LOGIN screen.welcome.button.logout=LOGOUT screen.welcome.button.loginwip=One moment please... screen.welcome.button.register=Register screen.welcome.button.print=Print screen.welcome.button.clear=CLEAR -screen.welcome.label.loginwith=External Identity Providers +# screen.welcome.label.loginwith=External Identity Providers screen.welcome.label.navto=Navigating to authentication provider. Please wait... screen.pm.button.submit=SUBMIT screen.pm.button.cancel=CANCEL -screen.pm.button.forgotpwd=Forgot your password? +# screen.pm.button.forgotpwd=Forgot your password? screen.pm.button.resetPassword=Reset your password screen.pm.button.forgotUsername=Forgot your username? screen.pm.reset.username=Username: @@ -177,7 +177,7 @@ screen.error.page.doesnotexist=The page you are attempting to access does not ex screen.error.page.authdenied=Authorization Denied # Remember-Me Authentication -screen.rememberme.checkbox.title=Remember Me +# screen.rememberme.checkbox.title=Remember Me # Gua screen.gua.confirm.message=If you do not recognize this image as yours, do NOT continue. @@ -502,6 +502,9 @@ passwordless.error.invalid.user=Provided username does not carry enough contact # # App drawer (left) # +cas.drawer.title=OSF CAS +cas.drawer.subtitle=The central authentication and authorization service for OSF +cas.login.resources.cas.home=CAS Home cas.login.resources.osf.home=OSF Home cas.login.resources.osf.preprints=OSF Preprints cas.login.resources.osf.registries=OSF Registries @@ -509,6 +512,7 @@ cas.login.resources.osf.meetings=OSF Meetings cas.login.resources.osf.institutions=OSF Institutions cas.login.resources.osf.support=OSF Support cas.login.resources.cos.donate=Donate +cas.login.resources.cos.home=COS Home # # App notifications (right) # @@ -516,7 +520,16 @@ cas.login.resources.cos.donate=Donate # copyright=Copyright © 2011 – 2020 Center for Open Science | \ Terms of Use | \ - Privacy Policy + Privacy Policy | \ + Status | \ + API | \ + TOP Guidelines | \ + Reproducibility Project: Psychology | \ + Reproducibility Project: Cancer Biology +# +# IdP SSO +# +screen.welcome.label.loginwith=Sign in through external identity providers # # Login page and login form submission # @@ -526,6 +539,12 @@ screen.welcome.label.email=Email screen.welcome.label.email.accesskey=e screen.welcome.label.password=Password screen.welcome.label.password.accesskey=p +screen.welcome.button.login=Sign in +screen.rememberme.checkbox.title=Stay signed in +screen.pm.button.forgotpwd=Forgot your password? +screen.pm.button.createaccount=Create an OSF account +screen.pm.button.backtoosf=Back to OSF +screen.pm.button.resendosfconfirmation=Resend confirmation email # # Two factor and login form submission # @@ -533,7 +552,7 @@ cas.twofactor.pagetitle=Sign in screen.twofactor.instructions=Enter your one-time password to finish login screen.twofactor.label.onetimepassword=One-time password (6-digit) screen.twofactor.label.onetimepassword.accesskey=o -screen.twofactor.button.verify=VERIFY +screen.twofactor.button.verify=Verify screen.twofactor.button.verifywip=One moment please... # # Authentication exception messages on the login form submission page (inline / pop-up) @@ -582,3 +601,30 @@ screen.onetimepasswordrequired.message=Two-factor authentication has been enable # ######################################################################################################################## ######################################################################################################################## + + + +######################################################################################################################## +# OSF URLs +######################################################################################################################## +# +# OSF +# +osf.home.url=http://192.168.168.167:5000/ +osf.resend.osf.confirmation.url=http://192.168.168.167:5000/resend/ +osf.forgot.password.url=http://192.168.168.167:5000/forgotpassword/ +osf.create.account.url=http://192.168.168.167:5000/register/ +# +# API +# +# Other +# +osf.preprints.home.url=http://192.168.168.167:5000/preprints/ +osf.registries.home.url=http://192.168.168.167:5000/registries/ +osf.institutions.home.url=http://192.168.168.167:5000/registries/ +osf.meetings.home.url=http://192.168.168.167:5000/meetings/ +osf.support.url=http://192.168.168.167:5000/support/ +osf.cos.donation.url=https://www.cos.io/about/support-cos/ +osf.cos.home.url=https://www.cos.io/ +# +######################################################################################################################## From 3b5ecb609a5a1e7c7c9301fb9bfa9c05d431a16e Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 1 Sep 2020 18:10:50 -0400 Subject: [PATCH 67/89] Customize casLoginView with related fragments --- .../resources/templates/casLoginView.html | 11 +--- .../templates/fragments/loginform.html | 60 ++++++------------- .../templates/fragments/loginproviders.html | 6 +- .../templates/fragments/loginsidebar.html | 7 +-- .../templates/fragments/pmlinks.html | 16 ++++- 5 files changed, 40 insertions(+), 60 deletions(-) diff --git a/src/main/resources/templates/casLoginView.html b/src/main/resources/templates/casLoginView.html index 4d998e3..f3fe4a3 100644 --- a/src/main/resources/templates/casLoginView.html +++ b/src/main/resources/templates/casLoginView.html @@ -23,18 +23,11 @@
- -
- diff --git a/src/main/resources/templates/fragments/loginform.html b/src/main/resources/templates/fragments/loginform.html index 707ed0e..b49bc52 100644 --- a/src/main/resources/templates/fragments/loginform.html +++ b/src/main/resources/templates/fragments/loginform.html @@ -6,7 +6,7 @@ - Login Form Fragment + @@ -22,32 +22,26 @@
-
-   - -
-

+

- Enter your Username and Password: +

- +
@@ -55,7 +49,7 @@

+ th:value="${availableAuthenticationHandlerNames.get(0)}" />

@@ -69,19 +63,24 @@

th:readonly="!${@casThymeleafLoginFormDirector.isLoginFormUsernameInputVisible(#vars)}" th:disabled="${@casThymeleafLoginFormDirector.isLoginFormUsernameInputDisabled(#vars)}" th:field="*{username}" - th:accesskey="#{screen.welcome.label.netid.accesskey}" + th:accesskey="#{screen.welcome.label.email.accesskey}" th:value="${@casThymeleafLoginFormDirector.getLoginFormUsername(#vars)}" autocomplete="off" /> - +

- - + +

-
-
-
- Label - -

-

-
-
- -
-

- - -

-
-

- - +   +

-
- diff --git a/src/main/resources/templates/fragments/loginproviders.html b/src/main/resources/templates/fragments/loginproviders.html index 022f7a2..65bcc42 100644 --- a/src/main/resources/templates/fragments/loginproviders.html +++ b/src/main/resources/templates/fragments/loginproviders.html @@ -6,18 +6,18 @@ - loginProviders Fragment +
-
- From 3abd587c2c4ce00d83e433634b6ba9fd5c34b510 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 1 Sep 2020 18:17:08 -0400 Subject: [PATCH 70/89] Slightly improve a few views and fragments --- .../resources/templates/casGenericSuccessView.html | 10 ++++------ src/main/resources/templates/casLogoutView.html | 2 +- src/main/resources/templates/casServiceErrorView.html | 5 ++--- src/main/resources/templates/error.html | 6 +++--- src/main/resources/templates/fragments/serviceui.html | 2 +- .../resources/templates/fragments/totploginform.html | 1 - 6 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/resources/templates/casGenericSuccessView.html b/src/main/resources/templates/casGenericSuccessView.html index 3b25ef5..bf1beca 100644 --- a/src/main/resources/templates/casGenericSuccessView.html +++ b/src/main/resources/templates/casGenericSuccessView.html @@ -5,7 +5,7 @@ - Generic Success View + @@ -14,8 +14,8 @@
-

Log In Successful

-

You, username, have successfully logged into the Central Authentication Service. This is what CAS knows about you:However, you are seeing this page because CAS does not know about your target destination and how to get you there. Examine the authentication request again and make sure a target service/application that is authorized and registered with CAS is specified.

+

+

The following attributes are resolved for : @@ -45,9 +45,7 @@

Log In Successful

-

- When you are finished, for security reasons, please log out and exit your web browser. -

+