diff --git a/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatAjpProperties.java b/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatAjpProperties.java index e37edb4ce58f..4d7682455f17 100644 --- a/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatAjpProperties.java +++ b/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatAjpProperties.java @@ -42,6 +42,11 @@ public class CasEmbeddedApacheTomcatAjpProperties implements Serializable { */ private boolean secure; + /** + * Set the secret that must be included with every request. + */ + private String secret; + /** * A boolean value which can be used to enable or disable * the TRACE HTTP method. If not specified, this attribute is set to false. diff --git a/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatHttpProxyProperties.java b/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatHttpProxyProperties.java index f96b8204b785..d34dcba46d0c 100644 --- a/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatHttpProxyProperties.java +++ b/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/core/web/tomcat/CasEmbeddedApacheTomcatHttpProxyProperties.java @@ -52,6 +52,11 @@ public class CasEmbeddedApacheTomcatHttpProxyProperties implements Serializable */ private String protocol = "AJP/1.3"; + /** + * Set the secret that must be included with every request. + */ + private String secret; + /** * Custom attributes to set on the proxy connector. */ diff --git a/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/webapp/WebflowProperties.java b/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/webapp/WebflowProperties.java index ad4836473e5b..4bc3a9fd7beb 100644 --- a/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/webapp/WebflowProperties.java +++ b/api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/model/webapp/WebflowProperties.java @@ -70,6 +70,18 @@ public class WebflowProperties implements Serializable { */ private Groovy groovy = new Groovy(); + /** + * With a base path defined, the algorithm that assigns flow identifiers changes slightly. + * Flows will now be assigned registry identifiers equal to the the path segment between + * their base path and file name. For example, if a flow definition is located + * at {@code /WEB-INF/hotels/booking/booking-flow.xml} and the base path is {@code /WEB-INF} the remaining path + * to this flow is {@code hotels/booking} which becomes the flow id. + * If no base path is not specified or if the flow definition is directly on the base + * path, flow id assignment from the filename (minus the extension) is used. For example, + * if a flow definition file is {@code booking.xml}, the flow identifier is simply {@code booking}. + */ + private String basePath; + @RequiresModule(name = "cas-server-core-webflow", automated = true) @Getter @Setter diff --git a/api/cas-server-core-api-webflow/src/main/java/org/apereo/cas/web/flow/CasWebflowConstants.java b/api/cas-server-core-api-webflow/src/main/java/org/apereo/cas/web/flow/CasWebflowConstants.java index 083d49870c88..ba3acc65314e 100644 --- a/api/cas-server-core-api-webflow/src/main/java/org/apereo/cas/web/flow/CasWebflowConstants.java +++ b/api/cas-server-core-api-webflow/src/main/java/org/apereo/cas/web/flow/CasWebflowConstants.java @@ -27,6 +27,7 @@ public interface CasWebflowConstants { * The transition state 'yes'. */ String TRANSITION_ID_YES = "yes"; + /** * The transition state 'prompt'. */ @@ -240,7 +241,7 @@ public interface CasWebflowConstants { * The state 'realSubmit'. */ String STATE_ID_REAL_SUBMIT = "realSubmit"; - + /** * 'finishMfaTrustedAuth' state id. */ @@ -677,7 +678,7 @@ public interface CasWebflowConstants { * Action id 'initialFlowSetupAction'. */ String ACTION_ID_INITIAL_FLOW_SETUP = "initialFlowSetupAction"; - + /** * Action id 'verifyRequiredServiceAction'. */ diff --git a/core/cas-server-core-cookie/build.gradle b/core/cas-server-core-cookie/build.gradle index d7e8b0c60ece..6fe94789b093 100644 --- a/core/cas-server-core-cookie/build.gradle +++ b/core/cas-server-core-cookie/build.gradle @@ -1,6 +1,6 @@ description = "Apereo CAS Cookie Management Core" dependencies { - implementation libraries.thymeleaf + implementation project(":core:cas-server-core-util-api") implementation project(":core:cas-server-core-cookie-api") implementation project(":core:cas-server-core-configuration-api") diff --git a/core/cas-server-core-validation-api/src/main/java/org/apereo/cas/validation/CasProtocolViewFactory.java b/core/cas-server-core-validation-api/src/main/java/org/apereo/cas/validation/CasProtocolViewFactory.java new file mode 100644 index 000000000000..d3b7205b794f --- /dev/null +++ b/core/cas-server-core-validation-api/src/main/java/org/apereo/cas/validation/CasProtocolViewFactory.java @@ -0,0 +1,37 @@ +package org.apereo.cas.validation; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.web.servlet.View; + +/** + * This is {@link CasProtocolViewFactory}. + * + * @author Misagh Moayyed + * @since 6.2.0 + */ +@FunctionalInterface +public interface CasProtocolViewFactory { + /** + * Create view. + * + * @param applicationContext the application context + * @param viewName the view name + * @param contentType the content type + * @return the view + */ + View create(ConfigurableApplicationContext applicationContext, + String viewName, + String contentType); + + /** + * Create view. + * + * @param applicationContext the application context + * @param viewName the view name + * @return the view + */ + default View create(final ConfigurableApplicationContext applicationContext, final String viewName) { + return create(applicationContext, viewName, StringUtils.EMPTY); + } +} diff --git a/core/cas-server-core-web-api/build.gradle b/core/cas-server-core-web-api/build.gradle index 9a67bb0befaf..de43c0f48e19 100644 --- a/core/cas-server-core-web-api/build.gradle +++ b/core/cas-server-core-web-api/build.gradle @@ -12,11 +12,9 @@ dependencies { implementation project(":core:cas-server-core-authentication-api") implementation project(":core:cas-server-core-configuration-api") implementation project(":core:cas-server-core-util-api") - - implementation libraries.thymeleaf + implementation libraries.oshi - testImplementation project(path: ":core:cas-server-core-util-api", configuration: "tests") testImplementation project(path: ":core:cas-server-core-authentication-api", configuration: "tests") } diff --git a/core/cas-server-core-web-api/src/test/java/org/apereo/cas/AllTestsSuite.java b/core/cas-server-core-web-api/src/test/java/org/apereo/cas/AllTestsSuite.java index cc5b54ff0182..305273589013 100644 --- a/core/cas-server-core-web-api/src/test/java/org/apereo/cas/AllTestsSuite.java +++ b/core/cas-server-core-web-api/src/test/java/org/apereo/cas/AllTestsSuite.java @@ -1,10 +1,7 @@ - package org.apereo.cas; import org.apereo.cas.web.support.filters.AddResponseHeadersFilterTests; import org.apereo.cas.web.support.filters.RequestParameterPolicyEnforcementFilterTests; -import org.apereo.cas.web.view.ChainingTemplateViewResolverTests; -import org.apereo.cas.web.view.RestfulUrlTemplateResolverTests; import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.suite.api.SelectClasses; @@ -17,8 +14,6 @@ * @since 6.0.0-RC3 */ @SelectClasses({ - ChainingTemplateViewResolverTests.class, - RestfulUrlTemplateResolverTests.class, RequestParameterPolicyEnforcementFilterTests.class, AddResponseHeadersFilterTests.class }) diff --git a/core/cas-server-core-web/build.gradle b/core/cas-server-core-web/build.gradle index 4792640e88ad..37ff23468419 100644 --- a/core/cas-server-core-web/build.gradle +++ b/core/cas-server-core-web/build.gradle @@ -23,7 +23,6 @@ dependencies { implementation libraries.pac4jcore implementation libraries.nimbus - implementation libraries.thymeleaf implementation libraries.snakeyaml } diff --git a/core/cas-server-core-web/src/main/java/org/apereo/cas/config/CasCoreViewsConfiguration.java b/core/cas-server-core-web/src/main/java/org/apereo/cas/config/CasCoreViewsConfiguration.java index b376ce17430a..e4e1c38e20e9 100644 --- a/core/cas-server-core-web/src/main/java/org/apereo/cas/config/CasCoreViewsConfiguration.java +++ b/core/cas-server-core-web/src/main/java/org/apereo/cas/config/CasCoreViewsConfiguration.java @@ -1,23 +1,9 @@ package org.apereo.cas.config; import org.apereo.cas.configuration.CasConfigurationProperties; -import org.apereo.cas.web.view.ChainingTemplateViewResolver; -import org.apereo.cas.web.view.RestfulUrlTemplateResolver; -import org.apereo.cas.web.view.ThemeFileTemplateResolver; -import lombok.val; -import org.apache.commons.lang3.StringUtils; -import org.jooq.lambda.Unchecked; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.util.ResourceUtils; -import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver; -import org.thymeleaf.templateresolver.AbstractTemplateResolver; -import org.thymeleaf.templateresolver.FileTemplateResolver; /** * This is {@link CasCoreViewsConfiguration}. @@ -28,53 +14,4 @@ @Configuration(value = "casCoreWebViewsConfiguration", proxyBeanMethods = false) @EnableConfigurationProperties(CasConfigurationProperties.class) public class CasCoreViewsConfiguration { - - @Autowired - private CasConfigurationProperties casProperties; - - @Autowired - private ObjectProvider thymeleafProperties; - - @Bean - public AbstractTemplateResolver chainingTemplateViewResolver() { - val chain = new ChainingTemplateViewResolver(); - - val templatePrefixes = casProperties.getView().getTemplatePrefixes(); - templatePrefixes.forEach(Unchecked.consumer(prefix -> { - val prefixPath = ResourceUtils.getFile(prefix).getCanonicalPath(); - val viewPath = StringUtils.appendIfMissing(prefixPath, "/"); - - val rest = casProperties.getView().getRest(); - if (StringUtils.isNotBlank(rest.getUrl())) { - val url = new RestfulUrlTemplateResolver(casProperties); - configureTemplateViewResolver(url); - chain.addResolver(url); - } - - val theme = new ThemeFileTemplateResolver(casProperties); - configureTemplateViewResolver(theme); - theme.setPrefix(viewPath + "themes/%s/"); - chain.addResolver(theme); - - - val file = new FileTemplateResolver(); - configureTemplateViewResolver(file); - file.setPrefix(viewPath); - chain.addResolver(file); - })); - - chain.initialize(); - return chain; - } - - private void configureTemplateViewResolver(final AbstractConfigurableTemplateResolver resolver) { - val props = thymeleafProperties.getObject(); - resolver.setCacheable(props.isCache()); - resolver.setCharacterEncoding(props.getEncoding().name()); - resolver.setCheckExistence(props.isCheckTemplateLocation()); - resolver.setForceTemplateMode(true); - resolver.setOrder(0); - resolver.setSuffix(".html"); - resolver.setTemplateMode(props.getMode()); - } } diff --git a/core/cas-server-core-web/src/test/java/org/apereo/cas/AllTestsSuite.java b/core/cas-server-core-web/src/test/java/org/apereo/cas/AllTestsSuite.java index d781d22ab6e1..a53ca30a2edd 100644 --- a/core/cas-server-core-web/src/test/java/org/apereo/cas/AllTestsSuite.java +++ b/core/cas-server-core-web/src/test/java/org/apereo/cas/AllTestsSuite.java @@ -1,7 +1,5 @@ - package org.apereo.cas; -import org.apereo.cas.web.CasCoreViewsConfigurationTests; import org.apereo.cas.web.RegisteredServiceResponseHeadersEnforcementFilterTests; import org.apereo.cas.web.SimpleUrlValidatorFactoryBeanTests; import org.apereo.cas.web.WebjarValidationTests; @@ -19,8 +17,7 @@ @SelectClasses({ RegisteredServiceResponseHeadersEnforcementFilterTests.class, SimpleUrlValidatorFactoryBeanTests.class, - WebjarValidationTests.class, - CasCoreViewsConfigurationTests.class + WebjarValidationTests.class }) @RunWith(JUnitPlatform.class) public class AllTestsSuite { diff --git a/core/cas-server-core-web/src/test/java/org/apereo/cas/web/CasCoreViewsConfigurationTests.java b/core/cas-server-core-web/src/test/java/org/apereo/cas/web/CasCoreViewsConfigurationTests.java deleted file mode 100644 index b53cb871ef6f..000000000000 --- a/core/cas-server-core-web/src/test/java/org/apereo/cas/web/CasCoreViewsConfigurationTests.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.apereo.cas.web; - -import org.apereo.cas.config.CasCoreViewsConfiguration; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; -import org.thymeleaf.templateresolver.AbstractTemplateResolver; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * This is {@link CasCoreViewsConfigurationTests}. - * - * @author Misagh Moayyed - * @since 6.2.0 - */ -@SpringBootTest(classes = { - RefreshAutoConfiguration.class, - ThymeleafAutoConfiguration.class, - CasCoreViewsConfiguration.class -}, - properties = { - "cas.view.rest.url=http://localhost:8182", - "cas.view.template-prefixes=file:/templates" - }) -public class CasCoreViewsConfigurationTests { - @Autowired - @Qualifier("chainingTemplateViewResolver") - private AbstractTemplateResolver chainingTemplateViewResolver; - - @Test - public void verifyOperation() { - assertNotNull(chainingTemplateViewResolver); - } -} diff --git a/core/cas-server-core-webflow-api/src/main/java/org/apereo/cas/web/flow/executor/EncryptedTranscoder.java b/core/cas-server-core-webflow-api/src/main/java/org/apereo/cas/web/flow/executor/EncryptedTranscoder.java index ea8d2c69b8fb..a84908c780da 100644 --- a/core/cas-server-core-webflow-api/src/main/java/org/apereo/cas/web/flow/executor/EncryptedTranscoder.java +++ b/core/cas-server-core-webflow-api/src/main/java/org/apereo/cas/web/flow/executor/EncryptedTranscoder.java @@ -43,8 +43,7 @@ public class EncryptedTranscoder implements Transcoder { * Flag to indicate whether to Gzip compression before encryption. */ private boolean compression = true; - - + public EncryptedTranscoder(final CipherBean cipherBean) { setCipherBean(cipherBean); } diff --git a/core/cas-server-core-webflow/src/main/java/org/apereo/cas/web/flow/config/CasWebflowContextConfiguration.java b/core/cas-server-core-webflow/src/main/java/org/apereo/cas/web/flow/config/CasWebflowContextConfiguration.java index 565c7cdbf06b..232c341e6c9c 100644 --- a/core/cas-server-core-webflow/src/main/java/org/apereo/cas/web/flow/config/CasWebflowContextConfiguration.java +++ b/core/cas-server-core-webflow/src/main/java/org/apereo/cas/web/flow/config/CasWebflowContextConfiguration.java @@ -18,6 +18,7 @@ import org.apereo.cas.web.flow.executor.WebflowExecutorFactory; import lombok.val; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -210,8 +211,9 @@ public HandlerMapping loginFlowHandlerMapping() { @Lazy(false) @Bean public FlowDefinitionRegistry logoutFlowRegistry() { + val basePath = StringUtils.defaultIfBlank(casProperties.getWebflow().getBasePath(), CasWebflowConstants.BASE_CLASSPATH_WEBFLOW); val builder = new FlowDefinitionRegistryBuilder(this.applicationContext, builder()); - builder.setBasePath(CasWebflowConstants.BASE_CLASSPATH_WEBFLOW); + builder.setBasePath(basePath); builder.addFlowLocationPattern("/logout/*-webflow.xml"); return builder.build(); } @@ -219,8 +221,9 @@ public FlowDefinitionRegistry logoutFlowRegistry() { @Lazy(false) @Bean public FlowDefinitionRegistry loginFlowRegistry() { + val basePath = StringUtils.defaultIfBlank(casProperties.getWebflow().getBasePath(), CasWebflowConstants.BASE_CLASSPATH_WEBFLOW); val builder = new FlowDefinitionRegistryBuilder(this.applicationContext, builder()); - builder.setBasePath(CasWebflowConstants.BASE_CLASSPATH_WEBFLOW); + builder.setBasePath(basePath); builder.addFlowLocationPattern("/login/*-webflow.xml"); return builder.build(); } diff --git a/docs/cas-server-documentation/configuration/Configuration-Properties.md b/docs/cas-server-documentation/configuration/Configuration-Properties.md index 8178af0f32d6..7b3e654f069b 100644 --- a/docs/cas-server-documentation/configuration/Configuration-Properties.md +++ b/docs/cas-server-documentation/configuration/Configuration-Properties.md @@ -4789,6 +4789,8 @@ To learn more about this topic, [please review this guide](../webflow/Webflow-Cu # cas.webflow.alwaysPauseRedirect=false # cas.webflow.refresh=true # cas.webflow.redirectSameState=false +# cas.webflow.autoconfigure=true +# cas.webflow.basePath= ``` ### Spring Webflow Login Decorations diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 91a4a32ec609..b95c6e88c5d6 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -1398,15 +1398,6 @@ ext.libraries = [ dependencies.create("javax.servlet:jstl:$javaxJstlVersion"), dependencies.create("javax.transaction:jta:$jtaVersion") ], - thymeleafdialect : [ - dependencies.create("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:$thymeleafDialectVersion") { - exclude(group: "org.javassist", module: "javassist") - exclude(group: "org.slf4j", module: "slf4j-api") - exclude(group: "org.codehaus.groovy", module: "groovy") - exclude(group: "org.thymeleaf", module: "thymeleaf") - force = true - } - ], activemq : [ dependencies.create("org.springframework.boot:spring-boot-starter-activemq:$springBootVersion") { exclude(group: "org.slf4j", module: "slf4j-api") @@ -1444,6 +1435,13 @@ ext.libraries = [ exclude(group: "org.springframework", module: "spring-aop") force = true }, + dependencies.create("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:$thymeleafDialectVersion") { + exclude(group: "org.javassist", module: "javassist") + exclude(group: "org.slf4j", module: "slf4j-api") + exclude(group: "org.codehaus.groovy", module: "groovy") + exclude(group: "org.thymeleaf", module: "thymeleaf") + force = true + }, dependencies.create("org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion") { exclude(group: "com.fasterxml", module: "classmate") exclude(group: "org.thymeleaf", module: "thymeleaf") diff --git a/gradle/webapp.gradle b/gradle/webapp.gradle index 85bf4c7c060d..2434f56444cf 100644 --- a/gradle/webapp.gradle +++ b/gradle/webapp.gradle @@ -44,6 +44,7 @@ dependencies { implementation project(":support:cas-server-support-person-directory") implementation project(":support:cas-server-support-themes") implementation project(":support:cas-server-support-validation") + implementation project(":support:cas-server-support-thymeleaf") implementation project(":support:cas-server-support-pm-webflow") implementation project(":webapp:cas-server-webapp-config") diff --git a/settings.gradle b/settings.gradle index 64681edda002..3e0746866827 100644 --- a/settings.gradle +++ b/settings.gradle @@ -321,6 +321,7 @@ include "support:cas-server-support-throttle-hazelcast" include "support:cas-server-support-throttle-jdbc" include "support:cas-server-support-throttle-mongo" include "support:cas-server-support-throttle-redis" +include "support:cas-server-support-thymeleaf" include "support:cas-server-support-token-authentication" include "support:cas-server-support-token-core-api" include "support:cas-server-support-token-core" diff --git a/support/cas-server-support-openid-webflow/build.gradle b/support/cas-server-support-openid-webflow/build.gradle index 8c0837a35736..637f641282d3 100644 --- a/support/cas-server-support-openid-webflow/build.gradle +++ b/support/cas-server-support-openid-webflow/build.gradle @@ -4,10 +4,12 @@ dependencies { implementation project(":core:cas-server-core-services-api") implementation project(":core:cas-server-core-services-registry") + implementation project(":core:cas-server-core-web-api") implementation project(":core:cas-server-core-webflow") implementation project(":core:cas-server-core-webflow-mfa") implementation project(":core:cas-server-core-webflow-api") implementation project(":core:cas-server-core-configuration-api") + implementation project(":core:cas-server-core-validation-api") implementation project(":support:cas-server-support-openid") implementation project(":support:cas-server-support-validation") @@ -23,6 +25,11 @@ dependencies { testImplementation project(":core:cas-server-core-validation") testImplementation project(":core:cas-server-core") + testImplementation project(":support:cas-server-support-thymeleaf") + testImplementation project(":support:cas-server-support-themes") + testImplementation project(":support:cas-server-support-person-directory") + testImplementation project(":support:cas-server-support-validation") + testImplementation project(path: ":core:cas-server-core-services", configuration: "tests") testImplementation project(path: ":core:cas-server-core-web", configuration: "tests") testImplementation project(path: ":core:cas-server-core-webflow", configuration: "tests") @@ -31,4 +38,5 @@ dependencies { testImplementation project(path: ":core:cas-server-core-util", configuration: "tests") testImplementation project(path: ":core:cas-server-core-util-api", configuration: "tests") testImplementation project(path: ":core:cas-server-core", configuration: "tests") + testImplementation project(path: ":support:cas-server-support-openid", configuration: "tests") } diff --git a/support/cas-server-support-openid-webflow/src/main/java/org/apereo/cas/web/flow/config/OpenIdWebflowConfiguration.java b/support/cas-server-support-openid-webflow/src/main/java/org/apereo/cas/web/flow/config/OpenIdWebflowConfiguration.java index 99009b981de0..7e41fc58448e 100644 --- a/support/cas-server-support-openid-webflow/src/main/java/org/apereo/cas/web/flow/config/OpenIdWebflowConfiguration.java +++ b/support/cas-server-support-openid-webflow/src/main/java/org/apereo/cas/web/flow/config/OpenIdWebflowConfiguration.java @@ -1,9 +1,16 @@ package org.apereo.cas.web.flow.config; +import org.apereo.cas.authentication.adaptive.AdaptiveAuthenticationPolicy; import org.apereo.cas.configuration.CasConfigurationProperties; +import org.apereo.cas.support.openid.web.flow.OpenIdSingleSignOnAction; +import org.apereo.cas.support.openid.web.support.DefaultOpenIdUserNameExtractor; +import org.apereo.cas.support.openid.web.support.OpenIdUserNameExtractor; +import org.apereo.cas.ticket.registry.TicketRegistrySupport; import org.apereo.cas.web.flow.CasWebflowConfigurer; import org.apereo.cas.web.flow.CasWebflowExecutionPlanConfigurer; import org.apereo.cas.web.flow.OpenIdWebflowConfigurer; +import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver; +import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -16,13 +23,14 @@ import org.springframework.context.annotation.DependsOn; import org.springframework.webflow.definition.registry.FlowDefinitionRegistry; import org.springframework.webflow.engine.builder.support.FlowBuilderServices; +import org.springframework.webflow.execution.Action; /** * This is {@link OpenIdWebflowConfiguration}. * * @author Misagh Moayyed - * @deprecated 6.2 * @since 5.0.0 + * @deprecated 6.2 */ @Configuration("openIdWebflowConfiguration") @EnableConfigurationProperties(CasConfigurationProperties.class) @@ -35,6 +43,18 @@ public class OpenIdWebflowConfiguration { @Autowired private CasConfigurationProperties casProperties; + @Autowired + @Qualifier("adaptiveAuthenticationPolicy") + private ObjectProvider adaptiveAuthenticationPolicy; + + @Autowired + @Qualifier("serviceTicketRequestWebflowEventResolver") + private ObjectProvider serviceTicketRequestWebflowEventResolver; + + @Autowired + @Qualifier("initialAuthenticationAttemptWebflowEventResolver") + private ObjectProvider initialAuthenticationAttemptWebflowEventResolver; + @Autowired @Qualifier("loginFlowRegistry") private ObjectProvider loginFlowDefinitionRegistry; @@ -42,6 +62,15 @@ public class OpenIdWebflowConfiguration { @Autowired private ObjectProvider flowBuilderServices; + @Autowired + @Qualifier("defaultTicketRegistrySupport") + private ObjectProvider ticketRegistrySupport; + + @Bean + public OpenIdUserNameExtractor defaultOpenIdUserNameExtractor() { + return new DefaultOpenIdUserNameExtractor(); + } + @ConditionalOnMissingBean(name = "openidWebflowConfigurer") @Bean @DependsOn("defaultWebflowConfigurer") @@ -50,6 +79,15 @@ public CasWebflowConfigurer openidWebflowConfigurer() { loginFlowDefinitionRegistry.getObject(), applicationContext, casProperties); } + @Bean + public Action openIdSingleSignOnAction() { + return new OpenIdSingleSignOnAction(initialAuthenticationAttemptWebflowEventResolver.getObject(), + serviceTicketRequestWebflowEventResolver.getObject(), + adaptiveAuthenticationPolicy.getObject(), + defaultOpenIdUserNameExtractor(), + ticketRegistrySupport.getObject()); + } + @Bean @ConditionalOnMissingBean(name = "openidCasWebflowExecutionPlanConfigurer") public CasWebflowExecutionPlanConfigurer openidCasWebflowExecutionPlanConfigurer() { diff --git a/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/AllTestsSuite.java b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/AllTestsSuite.java new file mode 100644 index 000000000000..038094477faf --- /dev/null +++ b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/AllTestsSuite.java @@ -0,0 +1,22 @@ +package org.apereo.cas.web.flow; + +import org.junit.platform.runner.JUnitPlatform; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.runner.RunWith; + +/** + * The {@link AllTestsSuite} is responsible for + * running all openid test cases. + * + * @author Misagh Moayyed + * @since 4.2.0 + */ + +@SelectClasses({ + OpenIdSingleSignOnActionTests.class, + OpenIdWebflowConfigurerTests.class, + DefaultOpenIdUserNameExtractorTests.class +}) +@RunWith(JUnitPlatform.class) +public class AllTestsSuite { +} diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/support/DefaultOpenIdUserNameExtractorTests.java b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/DefaultOpenIdUserNameExtractorTests.java similarity index 59% rename from support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/support/DefaultOpenIdUserNameExtractorTests.java rename to support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/DefaultOpenIdUserNameExtractorTests.java index 3c2fbe26918f..2c9d3d5b39b8 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/support/DefaultOpenIdUserNameExtractorTests.java +++ b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/DefaultOpenIdUserNameExtractorTests.java @@ -1,18 +1,33 @@ -package org.apereo.cas.support.openid.web.support; +package org.apereo.cas.web.flow; import org.apereo.cas.support.openid.AbstractOpenIdTests; +import org.apereo.cas.support.openid.web.support.OpenIdUserNameExtractor; +import org.apereo.cas.web.flow.config.OpenIdWebflowConfiguration; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.*; /** * @author Scott Battaglia * @since 3.1 + * @deprecated 6.2 */ -public class DefaultOpenIdUserNameExtractorTests extends AbstractOpenIdTests { +@Tag("Webflow") +@SpringBootTest(classes = { + AbstractOpenIdTests.SharedTestConfiguration.class, + OpenIdWebflowConfiguration.class +}, + properties = { + "spring.mail.host=localhost", + "spring.mail.port=25000" + }) +@Deprecated +public class DefaultOpenIdUserNameExtractorTests { @Autowired @Qualifier("defaultOpenIdUserNameExtractor") diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/flow/OpenIdSingleSignOnActionTests.java b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/OpenIdSingleSignOnActionTests.java similarity index 90% rename from support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/flow/OpenIdSingleSignOnActionTests.java rename to support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/OpenIdSingleSignOnActionTests.java index cc115bca44ae..c45b2f407bb3 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/flow/OpenIdSingleSignOnActionTests.java +++ b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/OpenIdSingleSignOnActionTests.java @@ -1,4 +1,4 @@ -package org.apereo.cas.support.openid.web.flow; +package org.apereo.cas.web.flow; import org.apereo.cas.authentication.CoreAuthenticationTestUtils; import org.apereo.cas.support.openid.AbstractOpenIdTests; @@ -7,6 +7,7 @@ import org.apereo.cas.ticket.TicketGrantingTicketImpl; import org.apereo.cas.ticket.expiration.NeverExpiresExpirationPolicy; import org.apereo.cas.ticket.registry.TicketRegistry; +import org.apereo.cas.web.flow.config.OpenIdWebflowConfiguration; import org.apereo.cas.web.support.WebUtils; import lombok.val; @@ -15,6 +16,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; @@ -27,9 +29,19 @@ /** * @author Scott Battaglia * @since 3.1 + * @deprecated 6.2 */ @Tag("Webflow") -public class OpenIdSingleSignOnActionTests extends AbstractOpenIdTests { +@Deprecated +@SpringBootTest(classes = { + AbstractOpenIdTests.SharedTestConfiguration.class, + OpenIdWebflowConfiguration.class +}, + properties = { + "spring.mail.host=localhost", + "spring.mail.port=25000" + }) +public class OpenIdSingleSignOnActionTests { @Autowired @Qualifier("openIdSingleSignOnAction") diff --git a/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/OpenIdWebflowConfigurerTests.java b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/OpenIdWebflowConfigurerTests.java index e01dca32d9ee..f6c933055064 100644 --- a/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/OpenIdWebflowConfigurerTests.java +++ b/support/cas-server-support-openid-webflow/src/test/java/org/apereo/cas/web/flow/OpenIdWebflowConfigurerTests.java @@ -1,7 +1,9 @@ package org.apereo.cas.web.flow; import org.apereo.cas.config.CasCoreMultifactorAuthenticationConfiguration; +import org.apereo.cas.config.CasThymeleafConfiguration; import org.apereo.cas.config.OpenIdConfiguration; +import org.apereo.cas.services.web.config.CasThemesConfiguration; import org.apereo.cas.validation.config.CasCoreValidationConfiguration; import org.apereo.cas.web.config.CasProtocolViewsConfiguration; import org.apereo.cas.web.config.CasValidationConfiguration; @@ -22,6 +24,7 @@ * * @author Misagh Moayyed * @since 6.2.0 + * @deprecated 6.2 */ @Import({ BaseWebflowConfigurerTests.SharedTestConfiguration.class, @@ -29,12 +32,15 @@ CasCoreMultifactorAuthenticationConfiguration.class, CasCoreValidationConfiguration.class, CasValidationConfiguration.class, + CasThemesConfiguration.class, + CasThymeleafConfiguration.class, + CasProtocolViewsConfiguration.class, ThymeleafAutoConfiguration.class, OpenIdConfiguration.class, - OpenIdWebflowConfiguration.class, - CasProtocolViewsConfiguration.class + OpenIdWebflowConfiguration.class }) @Tag("Webflow") +@Deprecated public class OpenIdWebflowConfigurerTests extends BaseWebflowConfigurerTests { @Test public void verifyOperation() { diff --git a/support/cas-server-support-openid/build.gradle b/support/cas-server-support-openid/build.gradle index e6071e2e3f22..7fbaa634a29d 100644 --- a/support/cas-server-support-openid/build.gradle +++ b/support/cas-server-support-openid/build.gradle @@ -15,15 +15,14 @@ dependencies { implementation project(":core:cas-server-core-util-api") implementation project(":core:cas-server-core-configuration-api") implementation project(":core:cas-server-core-authentication-api") + implementation project(":core:cas-server-core-validation-api") implementation project(":support:cas-server-support-validation-core") - testImplementation libraries.thymeleaf - testImplementation libraries.thymeleafdialect - + testImplementation project(":support:cas-server-support-thymeleaf") + testImplementation project(":support:cas-server-support-themes") testImplementation project(":support:cas-server-support-person-directory") testImplementation project(":support:cas-server-support-validation") - testImplementation project(":core:cas-server-core-validation") testImplementation project(":core:cas-server-core-validation-api") testImplementation project(":core:cas-server-core") diff --git a/support/cas-server-support-openid/src/main/java/org/apereo/cas/config/OpenIdConfiguration.java b/support/cas-server-support-openid/src/main/java/org/apereo/cas/config/OpenIdConfiguration.java index 891c1b79d9f0..89dd54be602a 100644 --- a/support/cas-server-support-openid/src/main/java/org/apereo/cas/config/OpenIdConfiguration.java +++ b/support/cas-server-support-openid/src/main/java/org/apereo/cas/config/OpenIdConfiguration.java @@ -2,23 +2,19 @@ import org.apereo.cas.CentralAuthenticationService; import org.apereo.cas.authentication.AuthenticationSystemSupport; -import org.apereo.cas.authentication.adaptive.AdaptiveAuthenticationPolicy; import org.apereo.cas.authentication.principal.ResponseBuilder; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.support.openid.authentication.principal.OpenIdServiceResponseBuilder; import org.apereo.cas.support.openid.web.OpenIdProviderController; -import org.apereo.cas.support.openid.web.flow.OpenIdSingleSignOnAction; import org.apereo.cas.support.openid.web.mvc.OpenIdValidateController; import org.apereo.cas.support.openid.web.mvc.SmartOpenIdController; import org.apereo.cas.support.openid.web.mvc.YadisController; -import org.apereo.cas.support.openid.web.support.DefaultOpenIdUserNameExtractor; import org.apereo.cas.support.openid.web.support.OpenIdPostUrlHandlerMapping; -import org.apereo.cas.support.openid.web.support.OpenIdUserNameExtractor; import org.apereo.cas.ticket.proxy.ProxyHandler; -import org.apereo.cas.ticket.registry.TicketRegistrySupport; import org.apereo.cas.util.CollectionUtils; import org.apereo.cas.validation.CasProtocolValidationSpecification; +import org.apereo.cas.validation.CasProtocolViewFactory; import org.apereo.cas.validation.RequestedAuthenticationContextValidator; import org.apereo.cas.validation.ServiceTicketValidationAuthorizersExecutionPlan; import org.apereo.cas.web.AbstractDelegateController; @@ -26,8 +22,6 @@ import org.apereo.cas.web.ServiceValidateConfigurationContext; import org.apereo.cas.web.ServiceValidationViewFactory; import org.apereo.cas.web.ServiceValidationViewFactoryConfigurer; -import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver; -import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver; import org.apereo.cas.web.support.ArgumentExtractor; import lombok.extern.slf4j.Slf4j; @@ -38,13 +32,15 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; import org.springframework.web.servlet.View; -import org.springframework.webflow.execution.Action; import java.util.Properties; @@ -52,8 +48,8 @@ * This is {@link OpenIdConfiguration}. * * @author Misagh Moayyed - * @deprecated 6.2 * @since 5.0.0 + * @deprecated 6.2 */ @Configuration("openidConfiguration") @EnableConfigurationProperties(CasConfigurationProperties.class) @@ -64,18 +60,6 @@ public class OpenIdConfiguration { @Qualifier("serviceValidationViewFactory") private ObjectProvider serviceValidationViewFactory; - @Autowired - @Qualifier("adaptiveAuthenticationPolicy") - private ObjectProvider adaptiveAuthenticationPolicy; - - @Autowired - @Qualifier("serviceTicketRequestWebflowEventResolver") - private ObjectProvider serviceTicketRequestWebflowEventResolver; - - @Autowired - @Qualifier("initialAuthenticationAttemptWebflowEventResolver") - private ObjectProvider initialAuthenticationAttemptWebflowEventResolver; - @Autowired @Qualifier("casOpenIdServiceSuccessView") private ObjectProvider casOpenIdServiceSuccessView; @@ -99,6 +83,9 @@ public class OpenIdConfiguration { @Autowired private CasConfigurationProperties casProperties; + @Autowired + private ConfigurableApplicationContext applicationContext; + @Autowired @Qualifier("centralAuthenticationService") private ObjectProvider centralAuthenticationService; @@ -119,10 +106,6 @@ public class OpenIdConfiguration { @Qualifier("servicesManager") private ObjectProvider servicesManager; - @Autowired - @Qualifier("defaultTicketRegistrySupport") - private ObjectProvider ticketRegistrySupport; - @Autowired @Qualifier("serviceValidationAuthorizers") private ObjectProvider validationAuthorizers; @@ -163,20 +146,6 @@ public OpenIdProviderController openIdProviderController() { return new OpenIdProviderController(); } - @Bean - public Action openIdSingleSignOnAction() { - return new OpenIdSingleSignOnAction(initialAuthenticationAttemptWebflowEventResolver.getObject(), - serviceTicketRequestWebflowEventResolver.getObject(), - adaptiveAuthenticationPolicy.getObject(), - defaultOpenIdUserNameExtractor(), - ticketRegistrySupport.getObject()); - } - - @Bean - public OpenIdUserNameExtractor defaultOpenIdUserNameExtractor() { - return new DefaultOpenIdUserNameExtractor(); - } - @Bean public OpenIdPostUrlHandlerMapping openIdPostUrlHandlerMapping() { val context = ServiceValidateConfigurationContext.builder() @@ -211,4 +180,41 @@ public ServiceValidationViewFactoryConfigurer openIdServiceValidationViewFactory factory.registerView(OpenIdValidateController.class, Pair.of(casOpenIdServiceSuccessView.getObject(), casOpenIdServiceFailureView.getObject())); } + + + /** + * The openid protocol views. + */ + @Configuration("OpenIdProtocolViews") + public class OpenIdProtocolViews { + + @Autowired + @Qualifier("casProtocolViewFactory") + private ObjectProvider casProtocolViewFactory; + + @Bean + @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public View casOpenIdServiceFailureView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/openid/casOpenIdServiceFailureView"); + } + + @Bean + @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public View casOpenIdServiceSuccessView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/openid/casOpenIdServiceSuccessView"); + } + + @Bean + @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public View casOpenIdAssociationSuccessView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/openid/casOpenIdAssociationSuccessView"); + } + + @Bean + @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public View openIdProviderView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/openid/user"); + } + + } } diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/AllTestsSuite.java b/support/cas-server-support-openid/src/test/java/org/apereo/cas/AllTestsSuite.java index 8eb2509b2fa6..72d729c45886 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/AllTestsSuite.java +++ b/support/cas-server-support-openid/src/test/java/org/apereo/cas/AllTestsSuite.java @@ -3,9 +3,7 @@ import org.apereo.cas.support.openid.authentication.handler.support.OpenIdCredentialsAuthenticationHandlerTests; import org.apereo.cas.support.openid.authentication.principal.OpenIdServiceFactoryTests; import org.apereo.cas.support.openid.authentication.principal.OpenIdServiceTests; -import org.apereo.cas.support.openid.web.flow.OpenIdSingleSignOnActionTests; import org.apereo.cas.support.openid.web.mvc.SmartOpenIdControllerTests; -import org.apereo.cas.support.openid.web.support.DefaultOpenIdUserNameExtractorTests; import org.apereo.cas.support.openid.web.support.OpenIdPostUrlHandlerMappingTests; import org.junit.platform.runner.JUnitPlatform; @@ -22,9 +20,7 @@ @SelectClasses({ OpenIdPostUrlHandlerMappingTests.class, - DefaultOpenIdUserNameExtractorTests.class, SmartOpenIdControllerTests.class, - OpenIdSingleSignOnActionTests.class, OpenIdCredentialsAuthenticationHandlerTests.class, OpenIdServiceFactoryTests.class, OpenIdServiceTests.class diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/AbstractOpenIdTests.java b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/AbstractOpenIdTests.java index 9da917c82004..42525c3ef44f 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/AbstractOpenIdTests.java +++ b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/AbstractOpenIdTests.java @@ -1,7 +1,5 @@ package org.apereo.cas.support.openid; -import org.apereo.cas.CentralAuthenticationService; -import org.apereo.cas.authentication.AuthenticationSystemSupport; import org.apereo.cas.config.CasCoreAuthenticationConfiguration; import org.apereo.cas.config.CasCoreAuthenticationHandlersConfiguration; import org.apereo.cas.config.CasCoreAuthenticationMetadataConfiguration; @@ -22,13 +20,14 @@ import org.apereo.cas.config.CasDefaultServiceTicketIdGeneratorsConfiguration; import org.apereo.cas.config.CasPersonDirectoryConfiguration; import org.apereo.cas.config.CasRegisteredServicesTestConfiguration; +import org.apereo.cas.config.CasThymeleafConfiguration; import org.apereo.cas.config.OpenIdConfiguration; import org.apereo.cas.config.support.CasWebApplicationServiceFactoryConfiguration; import org.apereo.cas.config.support.authentication.OpenIdAuthenticationEventExecutionPlanConfiguration; import org.apereo.cas.config.support.authentication.OpenIdServiceFactoryConfiguration; import org.apereo.cas.config.support.authentication.OpenIdUniqueTicketIdGeneratorConfiguration; import org.apereo.cas.logout.config.CasCoreLogoutConfiguration; -import org.apereo.cas.support.openid.authentication.principal.OpenIdServiceFactory; +import org.apereo.cas.services.web.config.CasThemesConfiguration; import org.apereo.cas.validation.config.CasCoreValidationConfiguration; import org.apereo.cas.web.config.CasCookieConfiguration; import org.apereo.cas.web.config.CasProtocolViewsConfiguration; @@ -37,87 +36,72 @@ import org.apereo.cas.web.flow.config.CasMultifactorAuthenticationWebflowConfiguration; import org.apereo.cas.web.flow.config.CasWebflowContextConfiguration; -import org.openid4java.server.ServerManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.context.annotation.Import; /** * Bootstrap context for openid tests. * * @author Misagh Moayyed * @since 4.2 + * @deprecated 6.2 */ -@SpringBootTest(classes = { - CasCoreServicesConfiguration.class, - CasRegisteredServicesTestConfiguration.class, - CasCoreMultifactorAuthenticationConfiguration.class, - CasCoreAuthenticationConfiguration.class, - CasCoreServicesAuthenticationConfiguration.class, - CasCoreAuthenticationPolicyConfiguration.class, - CasCoreAuthenticationPrincipalConfiguration.class, - CasCoreAuthenticationMetadataConfiguration.class, - CasCoreAuthenticationSupportConfiguration.class, - CasCoreAuthenticationHandlersConfiguration.class, - CasProtocolViewsConfiguration.class, - CasCookieConfiguration.class, - CasValidationConfiguration.class, - CasCoreLogoutConfiguration.class, - CasPersonDirectoryConfiguration.class, - CasCoreConfiguration.class, - CasMultifactorAuthenticationWebflowConfiguration.class, - CasCoreAuthenticationServiceSelectionStrategyConfiguration.class, - RefreshAutoConfiguration.class, - CasCoreWebConfiguration.class, - CasDefaultServiceTicketIdGeneratorsConfiguration.class, - CasCoreTicketIdGeneratorsConfiguration.class, - CasWebApplicationServiceFactoryConfiguration.class, - CasCoreHttpConfiguration.class, - CasCoreValidationConfiguration.class, - CasCoreTicketsConfiguration.class, - CasCoreTicketCatalogConfiguration.class, - CasWebflowContextConfiguration.class, - CasCoreWebflowConfiguration.class, - CasCoreUtilConfiguration.class, - OpenIdConfiguration.class, - MailSenderAutoConfiguration.class, - OpenIdUniqueTicketIdGeneratorConfiguration.class, - OpenIdServiceFactoryConfiguration.class, - OpenIdAuthenticationEventExecutionPlanConfiguration.class, - ThymeleafAutoConfiguration.class -}, properties = { - "spring.mail.host=localhost", - "spring.mail.port=25000" -}) +@SpringBootTest(classes = AbstractOpenIdTests.SharedTestConfiguration.class, + properties = { + "spring.mail.host=localhost", + "spring.mail.port=25000" + }) +@Deprecated public class AbstractOpenIdTests { - @Autowired - @Qualifier("serverManager") - protected ServerManager serverManager; - @Autowired - @Qualifier("openIdServiceFactory") - protected OpenIdServiceFactory openIdServiceFactory; - - @Autowired - @Qualifier("centralAuthenticationService") - protected CentralAuthenticationService centralAuthenticationService; - - @Autowired - @Qualifier("defaultAuthenticationSystemSupport") - protected AuthenticationSystemSupport authenticationSystemSupport; - - public OpenIdServiceFactory getOpenIdServiceFactory() { - return openIdServiceFactory; - } - - public CentralAuthenticationService getCentralAuthenticationService() { - return centralAuthenticationService; - } - - public AuthenticationSystemSupport getAuthenticationSystemSupport() { - return authenticationSystemSupport; + @ImportAutoConfiguration({ + RefreshAutoConfiguration.class, + MailSenderAutoConfiguration.class, + ThymeleafAutoConfiguration.class + }) + @SpringBootConfiguration + @Import({ + CasCoreServicesConfiguration.class, + CasRegisteredServicesTestConfiguration.class, + CasCoreMultifactorAuthenticationConfiguration.class, + CasCoreAuthenticationConfiguration.class, + CasCoreServicesAuthenticationConfiguration.class, + CasCoreAuthenticationPolicyConfiguration.class, + CasCoreAuthenticationPrincipalConfiguration.class, + CasCoreAuthenticationMetadataConfiguration.class, + CasCoreAuthenticationSupportConfiguration.class, + CasCoreAuthenticationHandlersConfiguration.class, + CasCookieConfiguration.class, + CasCoreLogoutConfiguration.class, + CasPersonDirectoryConfiguration.class, + CasCoreConfiguration.class, + CasMultifactorAuthenticationWebflowConfiguration.class, + CasCoreAuthenticationServiceSelectionStrategyConfiguration.class, + CasThemesConfiguration.class, + CasThymeleafConfiguration.class, + CasProtocolViewsConfiguration.class, + CasCoreWebConfiguration.class, + CasDefaultServiceTicketIdGeneratorsConfiguration.class, + CasCoreTicketIdGeneratorsConfiguration.class, + CasWebApplicationServiceFactoryConfiguration.class, + CasCoreHttpConfiguration.class, + CasCoreValidationConfiguration.class, + CasValidationConfiguration.class, + CasCoreTicketsConfiguration.class, + CasCoreTicketCatalogConfiguration.class, + CasWebflowContextConfiguration.class, + CasCoreWebflowConfiguration.class, + CasCoreUtilConfiguration.class, + OpenIdUniqueTicketIdGeneratorConfiguration.class, + OpenIdServiceFactoryConfiguration.class, + OpenIdAuthenticationEventExecutionPlanConfiguration.class, + OpenIdConfiguration.class + }) + public static class SharedTestConfiguration { } } diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/handler/support/OpenIdCredentialsAuthenticationHandlerTests.java b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/handler/support/OpenIdCredentialsAuthenticationHandlerTests.java index 0f0ca191b14f..0c3044f4d740 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/handler/support/OpenIdCredentialsAuthenticationHandlerTests.java +++ b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/handler/support/OpenIdCredentialsAuthenticationHandlerTests.java @@ -23,7 +23,9 @@ /** * @author Scott Battaglia * @since 3.1 + * @deprecated 6.2 */ +@Deprecated public class OpenIdCredentialsAuthenticationHandlerTests extends AbstractOpenIdTests { private static final String TGT_ID = "test"; diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceFactoryTests.java b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceFactoryTests.java index b820f00bfb6a..4146ca44386c 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceFactoryTests.java +++ b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceFactoryTests.java @@ -13,8 +13,10 @@ * Test cases for {@link OpenIdServiceFactory}. * * @author Misagh Moayyed + * @deprecated 6.2 * @since 4.2 */ +@Deprecated public class OpenIdServiceFactoryTests { @Test diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceTests.java b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceTests.java index ca46806584b6..5f7c73970a42 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceTests.java +++ b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/authentication/principal/OpenIdServiceTests.java @@ -1,5 +1,7 @@ package org.apereo.cas.support.openid.authentication.principal; +import org.apereo.cas.CentralAuthenticationService; +import org.apereo.cas.authentication.AuthenticationSystemSupport; import org.apereo.cas.authentication.CoreAuthenticationTestUtils; import org.apereo.cas.services.DefaultServicesManager; import org.apereo.cas.services.ServiceRegistry; @@ -13,6 +15,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openid4java.association.Association; +import org.openid4java.server.ServerManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationEventPublisher; import org.springframework.mock.web.MockHttpServletRequest; @@ -26,15 +31,39 @@ /** * @author Scott Battaglia * @since 3.1 + * @deprecated 6.2 */ @Slf4j +@Deprecated public class OpenIdServiceTests extends AbstractOpenIdTests { private static final File JSON_FILE = new File(FileUtils.getTempDirectoryPath(), "openIdService.json"); + private static final ObjectMapper MAPPER = new ObjectMapper().findAndRegisterModules(); + private static final String OPEN_ID_PREFIX_URL = "http://openid.ja-sig.org/battags"; + private static final String RETURN_TO_URL = "http://www.ja-sig.org/?service=fa"; + private final MockHttpServletRequest request = new MockHttpServletRequest(); + + @Autowired + @Qualifier("serverManager") + private ServerManager serverManager; + + @Autowired + @Qualifier("openIdServiceFactory") + private OpenIdServiceFactory openIdServiceFactory; + + @Autowired + @Qualifier("centralAuthenticationService") + private CentralAuthenticationService centralAuthenticationService; + + @Autowired + @Qualifier("defaultAuthenticationSystemSupport") + private AuthenticationSystemSupport authenticationSystemSupport; + private OpenIdService openIdService; + private Association association; @BeforeEach @@ -63,7 +92,7 @@ public void verifyGetResponse() { request.addParameter(OpenIdProtocolConstants.OPENID_ASSOCHANDLE, association.getHandle()); openIdService = openIdServiceFactory.createService(request); - val ctx = CoreAuthenticationTestUtils.getAuthenticationResult(getAuthenticationSystemSupport(), openIdService); + val ctx = CoreAuthenticationTestUtils.getAuthenticationResult(authenticationSystemSupport, openIdService); val tgt = centralAuthenticationService.createTicketGrantingTicket(ctx).getId(); val st = centralAuthenticationService.grantServiceTicket(tgt, openIdService, ctx).getId(); @@ -96,7 +125,7 @@ public void verifyExpiredAssociationGetResponse() { request.addParameter(OpenIdProtocolConstants.OPENID_ASSOCHANDLE, association.getHandle()); openIdService = openIdServiceFactory.createService(request); - val ctx = CoreAuthenticationTestUtils.getAuthenticationResult(getAuthenticationSystemSupport(), openIdService); + val ctx = CoreAuthenticationTestUtils.getAuthenticationResult(authenticationSystemSupport, openIdService); val tgt = centralAuthenticationService.createTicketGrantingTicket(ctx).getId(); val st = centralAuthenticationService.grantServiceTicket(tgt, openIdService, ctx).getId(); centralAuthenticationService.validateServiceTicket(st, openIdService); diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/mvc/SmartOpenIdControllerTests.java b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/mvc/SmartOpenIdControllerTests.java index 8f30a0a5e2e9..1bb75b5805cc 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/mvc/SmartOpenIdControllerTests.java +++ b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/mvc/SmartOpenIdControllerTests.java @@ -18,7 +18,9 @@ * * @author Frederic Esnault * @since 3.0.0 + * @deprecated 6.2 */ +@Deprecated public class SmartOpenIdControllerTests extends AbstractOpenIdTests { private static final String OPENID_MODE_PARAM = "openid.mode"; diff --git a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/support/OpenIdPostUrlHandlerMappingTests.java b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/support/OpenIdPostUrlHandlerMappingTests.java index ff15d52f814c..2d857a08efbf 100644 --- a/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/support/OpenIdPostUrlHandlerMappingTests.java +++ b/support/cas-server-support-openid/src/test/java/org/apereo/cas/support/openid/web/support/OpenIdPostUrlHandlerMappingTests.java @@ -14,7 +14,9 @@ /** * @author Scott Battaglia * @since 3.1 + * @deprecated 6.2 */ +@Deprecated public class OpenIdPostUrlHandlerMappingTests extends AbstractOpenIdTests { private static final String LOGIN_URL_PATH = "/login"; diff --git a/support/cas-server-support-openid/src/test/resources/log4j2.xml b/support/cas-server-support-openid/src/test/resources/log4j2.xml index bb5b51578ed0..7a75d1fea38b 100644 --- a/support/cas-server-support-openid/src/test/resources/log4j2.xml +++ b/support/cas-server-support-openid/src/test/resources/log4j2.xml @@ -8,6 +8,7 @@ + diff --git a/support/cas-server-support-saml-googleapps/build.gradle b/support/cas-server-support-saml-googleapps/build.gradle index cbe6123e75a1..7cf3faa5b8b1 100644 --- a/support/cas-server-support-saml-googleapps/build.gradle +++ b/support/cas-server-support-saml-googleapps/build.gradle @@ -39,5 +39,5 @@ dependencies { testImplementation project(":core:cas-server-core-webflow") testImplementation project(":core:cas-server-core-webflow-api") - testImplementation libraries.thymeleaf + testImplementation project(":support:cas-server-support-thymeleaf") } diff --git a/support/cas-server-support-saml-mdui/build.gradle b/support/cas-server-support-saml-mdui/build.gradle index 47af4204b4c2..5dd6ea17d7f5 100644 --- a/support/cas-server-support-saml-mdui/build.gradle +++ b/support/cas-server-support-saml-mdui/build.gradle @@ -39,6 +39,5 @@ dependencies { testImplementation project(":core:cas-server-core-services-authentication") testImplementation project(":support:cas-server-support-person-directory") testImplementation project(":support:cas-server-support-validation") - - testImplementation libraries.thymeleaf + testImplementation project(":support:cas-server-support-thymeleaf") } diff --git a/support/cas-server-support-saml/build.gradle b/support/cas-server-support-saml/build.gradle index a9e2da22dba3..181fc877cae4 100644 --- a/support/cas-server-support-saml/build.gradle +++ b/support/cas-server-support-saml/build.gradle @@ -37,6 +37,7 @@ dependencies { testImplementation project(":support:cas-server-support-validation") testImplementation project(":support:cas-server-support-person-directory") testImplementation project(":support:cas-server-support-reports") + testImplementation project(":support:cas-server-support-thymeleaf") testImplementation project(path: ":core:cas-server-core-authentication", configuration: "tests") testImplementation project(path: ":core:cas-server-core-services", configuration: "tests") @@ -45,5 +46,4 @@ dependencies { testImplementation project(path: ":core:cas-server-core-authentication-api", configuration: "tests") testImplementation project(path: ":support:cas-server-support-reports", configuration: "tests") - testImplementation libraries.thymeleaf } diff --git a/support/cas-server-support-themes-collection/build.gradle b/support/cas-server-support-themes-collection/build.gradle index ba76bf88ddf0..b8fb68ea39d5 100644 --- a/support/cas-server-support-themes-collection/build.gradle +++ b/support/cas-server-support-themes-collection/build.gradle @@ -1,8 +1,5 @@ description = "Apereo CAS Themes Collection" dependencies { - implementation libraries.thymeleaf - implementation libraries.thymeleafdialect - implementation project(":core:cas-server-core-services-api") implementation project(":core:cas-server-core-services") implementation project(":core:cas-server-core-web-api") diff --git a/support/cas-server-support-themes-core/build.gradle b/support/cas-server-support-themes-core/build.gradle index b30e0cb8a524..81402e50f82e 100644 --- a/support/cas-server-support-themes-core/build.gradle +++ b/support/cas-server-support-themes-core/build.gradle @@ -1,8 +1,5 @@ description = "Apereo CAS Web Application Themes Support" dependencies { - implementation libraries.thymeleaf - implementation libraries.thymeleafdialect - implementation project(":core:cas-server-core-services") implementation project(":core:cas-server-core-services-api") implementation project(":core:cas-server-core-web-api") diff --git a/support/cas-server-support-themes/build.gradle b/support/cas-server-support-themes/build.gradle index 5289174c4c60..e80ab859c871 100644 --- a/support/cas-server-support-themes/build.gradle +++ b/support/cas-server-support-themes/build.gradle @@ -1,8 +1,5 @@ description = "Apereo CAS Web Application Themes Support" dependencies { - implementation libraries.thymeleaf - implementation libraries.thymeleafdialect - implementation project(":core:cas-server-core-services-api") implementation project(":core:cas-server-core-web-api") implementation project(":core:cas-server-core-util-api") diff --git a/support/cas-server-support-themes/src/main/java/org/apereo/cas/services/web/config/CasThemesConfiguration.java b/support/cas-server-support-themes/src/main/java/org/apereo/cas/services/web/config/CasThemesConfiguration.java index a5b07d7fccd9..6ed289c9e097 100644 --- a/support/cas-server-support-themes/src/main/java/org/apereo/cas/services/web/config/CasThemesConfiguration.java +++ b/support/cas-server-support-themes/src/main/java/org/apereo/cas/services/web/config/CasThemesConfiguration.java @@ -3,46 +3,25 @@ import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.ServicesManager; -import org.apereo.cas.services.web.CasThymeleafLoginFormDirector; -import org.apereo.cas.services.web.CasThymeleafOutputTemplateHandler; -import org.apereo.cas.services.web.CasThymeleafViewResolverConfigurer; import org.apereo.cas.services.web.ChainingThemeResolver; import org.apereo.cas.services.web.RegisteredServiceThemeResolver; import org.apereo.cas.services.web.RequestHeaderThemeResolver; -import org.apereo.cas.services.web.ThemeBasedViewResolver; -import org.apereo.cas.services.web.ThemeViewResolver; -import org.apereo.cas.services.web.ThemeViewResolverFactory; -import org.apereo.cas.util.CollectionUtils; import lombok.val; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; -import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.OrderComparator; import org.springframework.web.servlet.ThemeResolver; -import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.theme.CookieThemeResolver; import org.springframework.web.servlet.theme.FixedThemeResolver; import org.springframework.web.servlet.theme.SessionThemeResolver; -import org.thymeleaf.dialect.IPostProcessorDialect; -import org.thymeleaf.postprocessor.IPostProcessor; -import org.thymeleaf.postprocessor.PostProcessor; -import org.thymeleaf.spring5.SpringTemplateEngine; -import org.thymeleaf.spring5.view.ThymeleafViewResolver; -import org.thymeleaf.templatemode.TemplateMode; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -54,7 +33,6 @@ */ @Configuration("casThemesConfiguration") @EnableConfigurationProperties(CasConfigurationProperties.class) -@Import(ThymeleafAutoConfiguration.class) public class CasThemesConfiguration { @Autowired @Qualifier("authenticationServiceSelectionPlan") @@ -67,58 +45,6 @@ public class CasThemesConfiguration { @Autowired private CasConfigurationProperties casProperties; - @Autowired - private ThymeleafProperties thymeleafProperties; - - @Autowired - private ConfigurableApplicationContext applicationContext; - - @Autowired - @Qualifier("thymeleafViewResolver") - private ObjectProvider thymeleafViewResolver; - - @Autowired - private List thymeleafViewResolverConfigurers; - - @ConditionalOnMissingBean(name = "casPropertiesThymeleafViewResolverConfigurer") - @Bean - public CasThymeleafViewResolverConfigurer casPropertiesThymeleafViewResolverConfigurer() { - return new CasThymeleafViewResolverConfigurer() { - @Override - public int getOrder() { - return 0; - } - - @Override - public void configureThymeleafViewResolver(final ThymeleafViewResolver thymeleafViewResolver) { - thymeleafViewResolver.addStaticVariable("cas", casProperties); - thymeleafViewResolver.addStaticVariable("casProperties", casProperties); - } - }; - } - - @ConditionalOnMissingBean(name = "registeredServiceViewResolver") - @Bean - public ViewResolver registeredServiceViewResolver() { - val resolver = new ThemeBasedViewResolver(themeResolver(), themeViewResolverFactory()); - resolver.setOrder(thymeleafViewResolver.getObject().getOrder() - 1); - return resolver; - } - - @ConditionalOnMissingBean(name = "themeViewResolverFactory") - @Bean - public ThemeViewResolverFactory themeViewResolverFactory() { - val factory = new ThemeViewResolver.Factory(nonCachingThymeleafViewResolver(), thymeleafProperties); - factory.setApplicationContext(applicationContext); - return factory; - } - - @ConditionalOnMissingBean(name = "casThymeleafLoginFormDirector") - @Bean - public CasThymeleafLoginFormDirector casThymeleafLoginFormDirector() { - return new CasThymeleafLoginFormDirector(); - } - @Bean public Map serviceThemeResolverSupportedBrowsers() { val map = new HashMap(); @@ -170,49 +96,5 @@ public ThemeResolver themeResolver() { return chainingThemeResolver; } - private ThymeleafViewResolver nonCachingThymeleafViewResolver() { - val r = new ThymeleafViewResolver(); - - val thymeleafResolver = this.thymeleafViewResolver.getObject(); - r.setAlwaysProcessRedirectAndForward(thymeleafResolver.getAlwaysProcessRedirectAndForward()); - r.setApplicationContext(thymeleafResolver.getApplicationContext()); - r.setCacheUnresolved(thymeleafResolver.isCacheUnresolved()); - r.setCharacterEncoding(thymeleafResolver.getCharacterEncoding()); - r.setContentType(thymeleafResolver.getContentType()); - r.setExcludedViewNames(thymeleafResolver.getExcludedViewNames()); - r.setOrder(thymeleafResolver.getOrder()); - r.setRedirectContextRelative(thymeleafResolver.isRedirectContextRelative()); - r.setRedirectHttp10Compatible(thymeleafResolver.isRedirectHttp10Compatible()); - r.setStaticVariables(thymeleafResolver.getStaticVariables()); - r.setForceContentType(thymeleafResolver.getForceContentType()); - - val engine = SpringTemplateEngine.class.cast(thymeleafResolver.getTemplateEngine()); - engine.addDialect(new IPostProcessorDialect() { - @Override - public int getDialectPostProcessorPrecedence() { - return Integer.MAX_VALUE; - } - - @Override - public Set getPostProcessors() { - return CollectionUtils.wrapSet(new PostProcessor(TemplateMode.parse(thymeleafProperties.getMode()), - CasThymeleafOutputTemplateHandler.class, Integer.MAX_VALUE)); - } - - @Override - public String getName() { - return CasThymeleafOutputTemplateHandler.class.getSimpleName(); - } - }); - - r.setTemplateEngine(engine); - r.setViewNames(thymeleafResolver.getViewNames()); - r.setCache(false); - - thymeleafViewResolverConfigurers.stream() - .sorted(OrderComparator.INSTANCE) - .forEach(configurer -> configurer.configureThymeleafViewResolver(r)); - - return r; - } + } diff --git a/support/cas-server-support-themes/src/test/java/org/apereo/cas/services/web/ServiceThemeResolverTests.java b/support/cas-server-support-themes/src/test/java/org/apereo/cas/services/web/ServiceThemeResolverTests.java index 63439b9da3a2..94841149a950 100644 --- a/support/cas-server-support-themes/src/test/java/org/apereo/cas/services/web/ServiceThemeResolverTests.java +++ b/support/cas-server-support-themes/src/test/java/org/apereo/cas/services/web/ServiceThemeResolverTests.java @@ -27,7 +27,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; -import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.mock.web.MockHttpServletRequest; @@ -60,7 +59,6 @@ CasWebApplicationServiceFactoryConfiguration.class, CasCoreConfiguration.class, CasCoreUtilConfiguration.class, - ThymeleafAutoConfiguration.class, MailSenderAutoConfiguration.class, RefreshAutoConfiguration.class }, diff --git a/support/cas-server-support-thymeleaf/build.gradle b/support/cas-server-support-thymeleaf/build.gradle new file mode 100644 index 000000000000..f116c0018f67 --- /dev/null +++ b/support/cas-server-support-thymeleaf/build.gradle @@ -0,0 +1,36 @@ +description = "Apereo CAS Support for Thymeleaf Template Engine" +dependencies { + api libraries.thymeleaf + + implementation project(":core:cas-server-core-cookie-api") + implementation project(":core:cas-server-core-util-api") + implementation project(":core:cas-server-core-web-api") + implementation project(":core:cas-server-core-authentication-api") + implementation project(":core:cas-server-core-configuration-api") + implementation project(":core:cas-server-core-web") + implementation project(":core:cas-server-core-util") + implementation project(":core:cas-server-core-validation-api") + + implementation project(":support:cas-server-support-themes") + + testImplementation project(":core:cas-server-core") + testImplementation project(":core:cas-server-core-tickets") + testImplementation project(":core:cas-server-coGroovyRegisteredAccessStrategyGroovyRegisteredAccessStrategyre-logout-api") + testImplementation project(":core:cas-server-core-monitor") + testImplementation project(":core:cas-server-core-web") + testImplementation project(":core:cas-server-core-util") + testImplementation project(":core:cas-server-core-authentication") + testImplementation project(":core:cas-server-core-authentication-mfa") + testImplementation project(":core:cas-server-core-cookie") + testImplementation project(":core:cas-server-core-services") + testImplementation project(":core:cas-server-core-validation") + testImplementation project(":core:cas-server-core-configuration") + testImplementation project(":core:cas-server-core-logout") + + testImplementation project(path: ":core:cas-server-core-webflow", configuration: "tests") + testImplementation project(path: ":core:cas-server-core-services", configuration: "tests") + testImplementation project(path: ":core:cas-server-core-authentication", configuration: "tests") + testImplementation project(path: ":core:cas-server-core-authentication-api", configuration: "tests") + testImplementation project(path: ":core:cas-server-core-util-api", configuration: "tests") + testImplementation project(path: ":core:cas-server-core", configuration: "tests") +} diff --git a/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/config/CasThymeleafConfiguration.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/config/CasThymeleafConfiguration.java new file mode 100644 index 000000000000..3087ee22ffbf --- /dev/null +++ b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/config/CasThymeleafConfiguration.java @@ -0,0 +1,222 @@ +package org.apereo.cas.config; + +import org.apereo.cas.configuration.CasConfigurationProperties; +import org.apereo.cas.services.web.CasThymeleafLoginFormDirector; +import org.apereo.cas.services.web.CasThymeleafOutputTemplateHandler; +import org.apereo.cas.services.web.CasThymeleafViewResolverConfigurer; +import org.apereo.cas.services.web.ThemeBasedViewResolver; +import org.apereo.cas.services.web.ThemeViewResolver; +import org.apereo.cas.services.web.ThemeViewResolverFactory; +import org.apereo.cas.util.CollectionUtils; +import org.apereo.cas.validation.CasProtocolViewFactory; +import org.apereo.cas.web.view.CasProtocolThymeleafViewFactory; +import org.apereo.cas.web.view.ChainingTemplateViewResolver; +import org.apereo.cas.web.view.RestfulUrlTemplateResolver; +import org.apereo.cas.web.view.ThemeFileTemplateResolver; + +import lombok.val; +import org.apache.commons.lang3.StringUtils; +import org.jooq.lambda.Unchecked; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; +import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.OrderComparator; +import org.springframework.util.ResourceUtils; +import org.springframework.web.servlet.ThemeResolver; +import org.springframework.web.servlet.ViewResolver; +import org.thymeleaf.dialect.IPostProcessorDialect; +import org.thymeleaf.postprocessor.IPostProcessor; +import org.thymeleaf.postprocessor.PostProcessor; +import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.spring5.view.ThymeleafViewResolver; +import org.thymeleaf.templatemode.TemplateMode; +import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver; +import org.thymeleaf.templateresolver.AbstractTemplateResolver; +import org.thymeleaf.templateresolver.FileTemplateResolver; + +import java.util.List; +import java.util.Set; + +/** + * This is {@link CasThymeleafConfiguration}. + * + * @author Misagh Moayyed + * @since 6.2.0 + */ +@Configuration(value = "casThymeleafConfiguration", proxyBeanMethods = false) +@EnableConfigurationProperties(CasConfigurationProperties.class) +@ConditionalOnClass(SpringTemplateEngine.class) +@Import(ThymeleafAutoConfiguration.class) +public class CasThymeleafConfiguration { + @Autowired + private CasConfigurationProperties casProperties; + + @Autowired + private ConfigurableApplicationContext applicationContext; + + @Autowired + @Qualifier("themeResolver") + private ObjectProvider themeResolver; + + @Autowired + @Qualifier("thymeleafViewResolver") + private ObjectProvider thymeleafViewResolver; + + @Autowired + private List thymeleafViewResolverConfigurers; + + @Autowired + private SpringTemplateEngine springTemplateEngine; + + @Autowired + private ThymeleafProperties thymeleafProperties; + + @Bean + @RefreshScope + public AbstractTemplateResolver chainingTemplateViewResolver() { + val chain = new ChainingTemplateViewResolver(); + + val templatePrefixes = casProperties.getView().getTemplatePrefixes(); + templatePrefixes.forEach(Unchecked.consumer(prefix -> { + val prefixPath = ResourceUtils.getFile(prefix).getCanonicalPath(); + val viewPath = StringUtils.appendIfMissing(prefixPath, "/"); + + val rest = casProperties.getView().getRest(); + if (StringUtils.isNotBlank(rest.getUrl())) { + val url = new RestfulUrlTemplateResolver(casProperties); + configureTemplateViewResolver(url); + chain.addResolver(url); + } + + val theme = new ThemeFileTemplateResolver(casProperties); + configureTemplateViewResolver(theme); + theme.setPrefix(viewPath + "themes/%s/"); + chain.addResolver(theme); + + val file = new FileTemplateResolver(); + configureTemplateViewResolver(file); + file.setPrefix(viewPath); + chain.addResolver(file); + })); + + chain.initialize(); + return chain; + } + + @ConditionalOnMissingBean(name = "casPropertiesThymeleafViewResolverConfigurer") + @Bean + @RefreshScope + public CasThymeleafViewResolverConfigurer casPropertiesThymeleafViewResolverConfigurer() { + return new CasThymeleafViewResolverConfigurer() { + @Override + public int getOrder() { + return 0; + } + + @Override + public void configureThymeleafViewResolver(final ThymeleafViewResolver thymeleafViewResolver) { + thymeleafViewResolver.addStaticVariable("cas", casProperties); + thymeleafViewResolver.addStaticVariable("casProperties", casProperties); + } + }; + } + + @ConditionalOnMissingBean(name = "registeredServiceViewResolver") + @Bean + @Autowired + @RefreshScope + public ViewResolver registeredServiceViewResolver(@Qualifier("themeViewResolverFactory") final ThemeViewResolverFactory themeViewResolverFactory) { + val resolver = new ThemeBasedViewResolver(this.themeResolver.getObject(), themeViewResolverFactory); + resolver.setOrder(thymeleafViewResolver.getObject().getOrder() - 1); + return resolver; + } + + @ConditionalOnMissingBean(name = "casThymeleafLoginFormDirector") + @Bean + @RefreshScope + public CasThymeleafLoginFormDirector casThymeleafLoginFormDirector() { + return new CasThymeleafLoginFormDirector(); + } + + @ConditionalOnMissingBean(name = "themeViewResolverFactory") + @Bean + @RefreshScope + public ThemeViewResolverFactory themeViewResolverFactory() { + val factory = new ThemeViewResolver.Factory(nonCachingThymeleafViewResolver(), thymeleafProperties); + factory.setApplicationContext(applicationContext); + return factory; + } + + @ConditionalOnMissingBean(name = "casProtocolViewFactory") + @Bean + @RefreshScope + public CasProtocolViewFactory casProtocolViewFactory() { + return new CasProtocolThymeleafViewFactory(this.springTemplateEngine, this.thymeleafProperties); + } + + private ThymeleafViewResolver nonCachingThymeleafViewResolver() { + val r = new ThymeleafViewResolver(); + + val thymeleafResolver = this.thymeleafViewResolver.getObject(); + r.setAlwaysProcessRedirectAndForward(thymeleafResolver.getAlwaysProcessRedirectAndForward()); + r.setApplicationContext(thymeleafResolver.getApplicationContext()); + r.setCacheUnresolved(thymeleafResolver.isCacheUnresolved()); + r.setCharacterEncoding(thymeleafResolver.getCharacterEncoding()); + r.setContentType(thymeleafResolver.getContentType()); + r.setExcludedViewNames(thymeleafResolver.getExcludedViewNames()); + r.setOrder(thymeleafResolver.getOrder()); + r.setRedirectContextRelative(thymeleafResolver.isRedirectContextRelative()); + r.setRedirectHttp10Compatible(thymeleafResolver.isRedirectHttp10Compatible()); + r.setStaticVariables(thymeleafResolver.getStaticVariables()); + r.setForceContentType(thymeleafResolver.getForceContentType()); + + val engine = SpringTemplateEngine.class.cast(thymeleafResolver.getTemplateEngine()); + engine.addDialect(new IPostProcessorDialect() { + @Override + public int getDialectPostProcessorPrecedence() { + return Integer.MAX_VALUE; + } + + @Override + public Set getPostProcessors() { + return CollectionUtils.wrapSet(new PostProcessor(TemplateMode.parse(thymeleafProperties.getMode()), + CasThymeleafOutputTemplateHandler.class, Integer.MAX_VALUE)); + } + + @Override + public String getName() { + return CasThymeleafOutputTemplateHandler.class.getSimpleName(); + } + }); + + r.setTemplateEngine(engine); + r.setViewNames(thymeleafResolver.getViewNames()); + r.setCache(false); + + thymeleafViewResolverConfigurers.stream() + .sorted(OrderComparator.INSTANCE) + .forEach(configurer -> configurer.configureThymeleafViewResolver(r)); + + return r; + } + + private void configureTemplateViewResolver(final AbstractConfigurableTemplateResolver resolver) { + resolver.setCacheable(thymeleafProperties.isCache()); + resolver.setCharacterEncoding(thymeleafProperties.getEncoding().name()); + resolver.setCheckExistence(thymeleafProperties.isCheckTemplateLocation()); + resolver.setForceTemplateMode(true); + resolver.setOrder(0); + resolver.setSuffix(".html"); + resolver.setTemplateMode(thymeleafProperties.getMode()); + } +} diff --git a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/CasThymeleafLoginFormDirector.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/CasThymeleafLoginFormDirector.java similarity index 100% rename from support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/CasThymeleafLoginFormDirector.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/CasThymeleafLoginFormDirector.java diff --git a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/CasThymeleafOutputTemplateHandler.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/CasThymeleafOutputTemplateHandler.java similarity index 100% rename from support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/CasThymeleafOutputTemplateHandler.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/CasThymeleafOutputTemplateHandler.java diff --git a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/CasThymeleafViewResolverConfigurer.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/CasThymeleafViewResolverConfigurer.java similarity index 100% rename from support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/CasThymeleafViewResolverConfigurer.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/CasThymeleafViewResolverConfigurer.java diff --git a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeBasedViewResolver.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeBasedViewResolver.java similarity index 91% rename from support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeBasedViewResolver.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeBasedViewResolver.java index 63b5c8fe797c..61f3cb4e8df9 100644 --- a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeBasedViewResolver.java +++ b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeBasedViewResolver.java @@ -2,6 +2,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import lombok.val; @@ -27,6 +28,7 @@ @Slf4j @Setter @Getter +@RequiredArgsConstructor public class ThemeBasedViewResolver implements ViewResolver, Ordered { private final ThemeResolver themeResolver; @@ -37,11 +39,6 @@ public class ThemeBasedViewResolver implements ViewResolver, Ordered { private int order = LOWEST_PRECEDENCE; - public ThemeBasedViewResolver(final ThemeResolver themeResolver, final ThemeViewResolverFactory viewResolverFactory) { - this.themeResolver = themeResolver; - this.viewResolverFactory = viewResolverFactory; - } - @Override @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS") public View resolveViewName(final String viewName, final Locale locale) { @@ -71,7 +68,6 @@ protected ViewResolver getViewResolver(final String theme) { if (resolvers.containsKey(theme)) { return resolvers.get(theme); } - val resolver = viewResolverFactory.create(theme); resolvers.put(theme, resolver); return resolver; diff --git a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeViewResolver.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeViewResolver.java similarity index 88% rename from support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeViewResolver.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeViewResolver.java index d17353cdce88..20e2c8534892 100644 --- a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeViewResolver.java +++ b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeViewResolver.java @@ -51,17 +51,20 @@ private void configureTemplateThemeDefaultLocation(final AbstractThymeleafView t val path = thymeleafProperties.getPrefix().concat(templateName).concat(thymeleafProperties.getSuffix()); LOGGER.trace("Attempting to locate theme location at [{}]", path); val location = new TemplateLocation(path); - if (location.exists(getApplicationContext())) { + val applicationContext = getApplicationContext(); + if (applicationContext != null && location.exists(applicationContext)) { thymeleafView.setTemplateName(templateName); } } /** - * {@link ThemeViewResolverFactory} that will create a ThemeViewResolver for the specified theme. + * {@link ThemeViewResolverFactory} that will + * create a {@link ThemeViewResolver} for the specified theme. */ @Getter @Setter @Slf4j + @RequiredArgsConstructor public static class Factory implements ThemeViewResolverFactory, ApplicationContextAware { private final ViewResolver delegate; @@ -70,11 +73,6 @@ public static class Factory implements ThemeViewResolverFactory, ApplicationCont private ApplicationContext applicationContext; - public Factory(final ViewResolver delegate, final ThymeleafProperties thymeleafProperties) { - this.delegate = delegate; - this.thymeleafProperties = thymeleafProperties; - } - @Override public ThemeViewResolver create(final String theme) { LOGGER.trace("Creating theme view resolver based on theme [{}]", theme); diff --git a/support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeViewResolverFactory.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeViewResolverFactory.java similarity index 100% rename from support/cas-server-support-themes-core/src/main/java/org/apereo/cas/services/web/ThemeViewResolverFactory.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/services/web/ThemeViewResolverFactory.java diff --git a/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/CasProtocolThymeleafViewFactory.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/CasProtocolThymeleafViewFactory.java new file mode 100644 index 000000000000..efadc1fbd778 --- /dev/null +++ b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/CasProtocolThymeleafViewFactory.java @@ -0,0 +1,32 @@ +package org.apereo.cas.web.view; + +import org.apereo.cas.validation.CasProtocolViewFactory; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.web.servlet.View; +import org.thymeleaf.spring5.SpringTemplateEngine; + +/** + * This is {@link CasProtocolThymeleafViewFactory}. + * + * @author Misagh Moayyed + * @since 6.2.0 + */ +@RequiredArgsConstructor +@Slf4j +public class CasProtocolThymeleafViewFactory implements CasProtocolViewFactory { + private final SpringTemplateEngine springTemplateEngine; + + private final ThymeleafProperties thymeleafProperties; + + @Override + public View create(final ConfigurableApplicationContext applicationContext, + final String viewName, final String contentType) { + LOGGER.trace("Creating CAS protocol view [{}] with content type of [{}]", viewName, contentType); + return new CasProtocolView(viewName, applicationContext, + springTemplateEngine, thymeleafProperties, contentType); + } +} diff --git a/core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/CasProtocolView.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/CasProtocolView.java similarity index 100% rename from core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/CasProtocolView.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/CasProtocolView.java diff --git a/core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/ChainingTemplateViewResolver.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/ChainingTemplateViewResolver.java similarity index 100% rename from core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/ChainingTemplateViewResolver.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/ChainingTemplateViewResolver.java diff --git a/core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/RestfulUrlTemplateResolver.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/RestfulUrlTemplateResolver.java similarity index 100% rename from core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/RestfulUrlTemplateResolver.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/RestfulUrlTemplateResolver.java diff --git a/core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/ThemeFileTemplateResolver.java b/support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/ThemeFileTemplateResolver.java similarity index 100% rename from core/cas-server-core-web-api/src/main/java/org/apereo/cas/web/view/ThemeFileTemplateResolver.java rename to support/cas-server-support-thymeleaf/src/main/java/org/apereo/cas/web/view/ThemeFileTemplateResolver.java diff --git a/support/cas-server-support-thymeleaf/src/main/resources/META-INF/spring.factories b/support/cas-server-support-thymeleaf/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..61280c7beabe --- /dev/null +++ b/support/cas-server-support-thymeleaf/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apereo.cas.config.CasThymeleafConfiguration diff --git a/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/AllTestsSuite.java b/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/AllTestsSuite.java new file mode 100644 index 000000000000..519d860d7310 --- /dev/null +++ b/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/AllTestsSuite.java @@ -0,0 +1,24 @@ +package org.apereo.cas; + +import org.apereo.cas.web.view.CasThymeleafConfigurationTests; +import org.apereo.cas.web.view.ChainingTemplateViewResolverTests; +import org.apereo.cas.web.view.RestfulUrlTemplateResolverTests; + +import org.junit.platform.runner.JUnitPlatform; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.runner.RunWith; + +/** + * This is {@link AllTestsSuite}. + * + * @author Misagh Moayyed + * @since 6.0.0-RC3 + */ +@SelectClasses({ + ChainingTemplateViewResolverTests.class, + RestfulUrlTemplateResolverTests.class, + CasThymeleafConfigurationTests.class +}) +@RunWith(JUnitPlatform.class) +public class AllTestsSuite { +} diff --git a/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/web/view/CasThymeleafConfigurationTests.java b/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/web/view/CasThymeleafConfigurationTests.java new file mode 100644 index 000000000000..5d846e6c3af8 --- /dev/null +++ b/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/web/view/CasThymeleafConfigurationTests.java @@ -0,0 +1,69 @@ +package org.apereo.cas.web.view; + +import org.apereo.cas.config.CasCoreAuthenticationConfiguration; +import org.apereo.cas.config.CasCoreAuthenticationMetadataConfiguration; +import org.apereo.cas.config.CasCoreAuthenticationPrincipalConfiguration; +import org.apereo.cas.config.CasCoreAuthenticationSupportConfiguration; +import org.apereo.cas.config.CasCoreConfiguration; +import org.apereo.cas.config.CasCoreHttpConfiguration; +import org.apereo.cas.config.CasCoreServicesConfiguration; +import org.apereo.cas.config.CasCoreTicketIdGeneratorsConfiguration; +import org.apereo.cas.config.CasCoreTicketsConfiguration; +import org.apereo.cas.config.CasCoreUtilConfiguration; +import org.apereo.cas.config.CasCoreViewsConfiguration; +import org.apereo.cas.config.CasCoreWebConfiguration; +import org.apereo.cas.config.CasPersonDirectoryTestConfiguration; +import org.apereo.cas.config.CasThymeleafConfiguration; +import org.apereo.cas.config.support.CasWebApplicationServiceFactoryConfiguration; +import org.apereo.cas.logout.config.CasCoreLogoutConfiguration; +import org.apereo.cas.services.web.config.CasThemesConfiguration; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.thymeleaf.templateresolver.AbstractTemplateResolver; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * This is {@link CasThymeleafConfigurationTests}. + * + * @author Misagh Moayyed + * @since 6.2.0 + */ +@SpringBootTest(classes = { + RefreshAutoConfiguration.class, + CasCoreUtilConfiguration.class, + CasCoreTicketIdGeneratorsConfiguration.class, + CasCoreTicketsConfiguration.class, + CasCoreLogoutConfiguration.class, + CasPersonDirectoryTestConfiguration.class, + CasCoreAuthenticationConfiguration.class, + CasCoreAuthenticationPrincipalConfiguration.class, + CasCoreAuthenticationSupportConfiguration.class, + CasCoreAuthenticationMetadataConfiguration.class, + CasWebApplicationServiceFactoryConfiguration.class, + CasCoreWebConfiguration.class, + CasCoreHttpConfiguration.class, + CasCoreConfiguration.class, + CasCoreServicesConfiguration.class, + CasThemesConfiguration.class, + CasThymeleafConfiguration.class, + CasCoreViewsConfiguration.class +}, + properties = { + "cas.view.rest.url=http://localhost:8182", + "cas.view.template-prefixes=file:/templates" + }) +public class CasThymeleafConfigurationTests { + @Autowired + @Qualifier("chainingTemplateViewResolver") + private AbstractTemplateResolver chainingTemplateViewResolver; + + @Test + public void verifyOperation() { + assertNotNull(chainingTemplateViewResolver); + } +} diff --git a/core/cas-server-core-web-api/src/test/java/org/apereo/cas/web/view/ChainingTemplateViewResolverTests.java b/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/web/view/ChainingTemplateViewResolverTests.java similarity index 100% rename from core/cas-server-core-web-api/src/test/java/org/apereo/cas/web/view/ChainingTemplateViewResolverTests.java rename to support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/web/view/ChainingTemplateViewResolverTests.java diff --git a/core/cas-server-core-web-api/src/test/java/org/apereo/cas/web/view/RestfulUrlTemplateResolverTests.java b/support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/web/view/RestfulUrlTemplateResolverTests.java similarity index 100% rename from core/cas-server-core-web-api/src/test/java/org/apereo/cas/web/view/RestfulUrlTemplateResolverTests.java rename to support/cas-server-support-thymeleaf/src/test/java/org/apereo/cas/web/view/RestfulUrlTemplateResolverTests.java diff --git a/support/cas-server-support-thymeleaf/src/test/resources/log4j2.xml b/support/cas-server-support-thymeleaf/src/test/resources/log4j2.xml new file mode 100644 index 000000000000..6f73872f4838 --- /dev/null +++ b/support/cas-server-support-thymeleaf/src/test/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/support/cas-server-support-validation-core/build.gradle b/support/cas-server-support-validation-core/build.gradle index a05832be98e9..dd6aa30ed98a 100644 --- a/support/cas-server-support-validation-core/build.gradle +++ b/support/cas-server-support-validation-core/build.gradle @@ -1,7 +1,5 @@ description = "Apereo CAS Web Application Protocol Validation" dependencies { - implementation libraries.thymeleaf - implementation project(":core:cas-server-core-authentication-attributes") implementation project(":core:cas-server-core-authentication-mfa") implementation project(":core:cas-server-core-authentication-mfa-api") @@ -41,9 +39,7 @@ dependencies { testImplementation project(":core:cas-server-core-util") testImplementation project(":core:cas-server-core-configuration") - testCompileOnly "javax.servlet:jstl:$javaxJstlVersion" - - testImplementation libraries.thymeleaf - testImplementation libraries.thymeleafdialect + + testImplementation project(":support:cas-server-support-thymeleaf") } diff --git a/support/cas-server-support-validation/build.gradle b/support/cas-server-support-validation/build.gradle index f9d66a9d14e6..234738fec6d1 100644 --- a/support/cas-server-support-validation/build.gradle +++ b/support/cas-server-support-validation/build.gradle @@ -1,7 +1,5 @@ description = "Apereo CAS Web Application Protocol Validation" dependencies { - implementation libraries.thymeleaf - implementation project(":core:cas-server-core-authentication-attributes") implementation project(":core:cas-server-core-authentication-mfa") implementation project(":core:cas-server-core-authentication-mfa-api") @@ -46,7 +44,5 @@ dependencies { testImplementation project(path: ":core:cas-server-core-services", configuration: "tests") testCompileOnly "javax.servlet:jstl:$javaxJstlVersion" - - testImplementation libraries.thymeleaf - testImplementation libraries.thymeleafdialect + testImplementation project(":support:cas-server-support-thymeleaf") } diff --git a/support/cas-server-support-validation/src/main/java/org/apereo/cas/web/config/CasProtocolViewsConfiguration.java b/support/cas-server-support-validation/src/main/java/org/apereo/cas/web/config/CasProtocolViewsConfiguration.java index d69fa091e55f..4014def183c9 100644 --- a/support/cas-server-support-validation/src/main/java/org/apereo/cas/web/config/CasProtocolViewsConfiguration.java +++ b/support/cas-server-support-validation/src/main/java/org/apereo/cas/web/config/CasProtocolViewsConfiguration.java @@ -1,19 +1,20 @@ package org.apereo.cas.web.config; import org.apereo.cas.configuration.CasConfigurationProperties; -import org.apereo.cas.web.view.CasProtocolView; +import org.apereo.cas.validation.CasProtocolViewFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.http.MediaType; -import org.thymeleaf.spring5.SpringTemplateEngine; +import org.springframework.web.servlet.View; /** * This is {@link CasProtocolViewsConfiguration} that attempts to create Spring-managed beans @@ -29,14 +30,12 @@ public class CasProtocolViewsConfiguration { @Autowired private ConfigurableApplicationContext applicationContext; - @Autowired - private SpringTemplateEngine springTemplateEngine; - @Autowired private CasConfigurationProperties casProperties; @Autowired - private ThymeleafProperties thymeleafProperties; + @Qualifier("casProtocolViewFactory") + private ObjectProvider casProtocolViewFactory; /** * The CAS protocol views. @@ -45,59 +44,58 @@ public class CasProtocolViewsConfiguration { public class CasProtocolViews { @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView cas2SuccessView() { - return new CasProtocolView(casProperties.getView().getCas2().getSuccess(), - applicationContext, - springTemplateEngine, thymeleafProperties, + public View cas2SuccessView() { + return casProtocolViewFactory.getObject().create(applicationContext, + casProperties.getView().getCas2().getSuccess(), MediaType.APPLICATION_XML_VALUE); } @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView cas2ServiceFailureView() { - return new CasProtocolView(casProperties.getView().getCas2().getFailure(), applicationContext, - springTemplateEngine, thymeleafProperties); + public View cas2ServiceFailureView() { + return casProtocolViewFactory.getObject().create(applicationContext, + casProperties.getView().getCas2().getFailure()); } @ConditionalOnProperty(prefix = "cas.sso", name = "proxyAuthnEnabled", havingValue = "true", matchIfMissing = true) @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView cas2ProxyFailureView() { - return new CasProtocolView(casProperties.getView().getCas2().getProxy().getFailure(), applicationContext, - springTemplateEngine, thymeleafProperties, + public View cas2ProxyFailureView() { + return casProtocolViewFactory.getObject().create(applicationContext, + casProperties.getView().getCas2().getProxy().getFailure(), MediaType.APPLICATION_XML_VALUE); } @ConditionalOnProperty(prefix = "cas.sso", name = "proxyAuthnEnabled", havingValue = "true", matchIfMissing = true) @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView cas2ProxySuccessView() { - return new CasProtocolView(casProperties.getView().getCas2().getProxy().getSuccess(), - applicationContext, springTemplateEngine, thymeleafProperties, + public View cas2ProxySuccessView() { + return casProtocolViewFactory.getObject().create(applicationContext, + casProperties.getView().getCas2().getProxy().getSuccess(), MediaType.APPLICATION_XML_VALUE); } @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView cas3SuccessView() { - return new CasProtocolView(casProperties.getView().getCas3().getSuccess(), - applicationContext, springTemplateEngine, thymeleafProperties); + public View cas3SuccessView() { + return casProtocolViewFactory.getObject().create(applicationContext, + casProperties.getView().getCas3().getSuccess()); } @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView cas3ServiceFailureView() { - return new CasProtocolView(casProperties.getView().getCas3().getFailure(), - applicationContext, springTemplateEngine, thymeleafProperties, + public View cas3ServiceFailureView() { + return casProtocolViewFactory.getObject().create(applicationContext, + casProperties.getView().getCas3().getFailure(), MediaType.APPLICATION_XML_VALUE); } @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView casPostResponseView() { - return new CasProtocolView("protocol/casPostResponseView", - applicationContext, springTemplateEngine, thymeleafProperties); + public View casPostResponseView() { + return casProtocolViewFactory.getObject().create(applicationContext, + "protocol/casPostResponseView"); } } @@ -108,27 +106,26 @@ public CasProtocolView casPostResponseView() { public class OAuthProtocolViews { @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView oauthConfirmView() { - return new CasProtocolView("protocol/oauth/confirm", applicationContext, springTemplateEngine, thymeleafProperties); + public View oauthConfirmView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/oauth/confirm"); } @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView oauthDeviceCodeApprovalView() { - return new CasProtocolView("protocol/oauth/deviceCodeApproval", applicationContext, springTemplateEngine, thymeleafProperties); + public View oauthDeviceCodeApprovalView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/oauth/deviceCodeApproval"); } @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView oauthDeviceCodeApprovedView() { - return new CasProtocolView("protocol/oauth/deviceCodeApproved", applicationContext, springTemplateEngine, thymeleafProperties); + public View oauthDeviceCodeApprovedView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/oauth/deviceCodeApproved"); } @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView oauthSessionStaleMismatchErrorView() { - return new CasProtocolView("protocol/oauth/sessionStaleMismatchError", - applicationContext, springTemplateEngine, thymeleafProperties); + public View oauthSessionStaleMismatchErrorView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/oauth/sessionStaleMismatchError"); } } @@ -139,45 +136,9 @@ public CasProtocolView oauthSessionStaleMismatchErrorView() { public class OidcProtocolViews { @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView oidcConfirmView() { - return new CasProtocolView("protocol/oidc/confirm", applicationContext, springTemplateEngine, thymeleafProperties); + public View oidcConfirmView() { + return casProtocolViewFactory.getObject().create(applicationContext, "protocol/oidc/confirm"); } } - - /** - * The openid protocol views. - */ - @Configuration("OpenIdProtocolViews") - public class OpenIdProtocolViews { - @Bean - @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView casOpenIdServiceFailureView() { - return new CasProtocolView("protocol/openid/casOpenIdServiceFailureView", - applicationContext, springTemplateEngine, thymeleafProperties); - } - - @Bean - @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView casOpenIdServiceSuccessView() { - return new CasProtocolView("protocol/openid/casOpenIdServiceSuccessView", applicationContext, - springTemplateEngine, thymeleafProperties); - } - - @Bean - @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView casOpenIdAssociationSuccessView() { - return new CasProtocolView("protocol/openid/casOpenIdAssociationSuccessView", applicationContext, - springTemplateEngine, thymeleafProperties); - } - - @Bean - @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public CasProtocolView openIdProviderView() { - return new CasProtocolView("protocol/openid/user", applicationContext, - springTemplateEngine, thymeleafProperties); - } - - } - } diff --git a/support/cas-server-support-validation/src/main/resources/META-INF/spring.factories b/support/cas-server-support-validation/src/main/resources/META-INF/spring.factories index 30562047509a..670af33e038f 100644 --- a/support/cas-server-support-validation/src/main/resources/META-INF/spring.factories +++ b/support/cas-server-support-validation/src/main/resources/META-INF/spring.factories @@ -1 +1 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apereo.cas.web.config.CasProtocolViewsConfiguration,org.apereo.cas.web.config.CasValidationConfiguration \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apereo.cas.web.config.CasValidationConfiguration diff --git a/support/cas-server-support-wsfederation/build.gradle b/support/cas-server-support-wsfederation/build.gradle index 548bb3bdb7b1..a869d6a85908 100644 --- a/support/cas-server-support-wsfederation/build.gradle +++ b/support/cas-server-support-wsfederation/build.gradle @@ -50,5 +50,5 @@ dependencies { testImplementation project(":core:cas-server-core-services-authentication") testImplementation project(":core:cas-server-core-services-registry") - testImplementation libraries.thymeleaf + testImplementation project(":support:cas-server-support-thymeleaf") } diff --git a/webapp/cas-server-webapp-config/build.gradle b/webapp/cas-server-webapp-config/build.gradle index 03127523171d..d40546256357 100644 --- a/webapp/cas-server-webapp-config/build.gradle +++ b/webapp/cas-server-webapp-config/build.gradle @@ -20,7 +20,9 @@ dependencies { testImplementation project(":support:cas-server-support-person-directory") testImplementation project(":support:cas-server-support-themes") - + testImplementation project(":support:cas-server-support-thymeleaf") + testImplementation project(":support:cas-server-support-validation") + testImplementation project(":core:cas-server-core") testImplementation project(":core:cas-server-core-audit") testImplementation project(":core:cas-server-core-audit-api") diff --git a/webapp/cas-server-webapp-config/src/test/java/org/apereo/cas/BaseCasWebflowSessionContextConfigurationTests.java b/webapp/cas-server-webapp-config/src/test/java/org/apereo/cas/BaseCasWebflowSessionContextConfigurationTests.java index 75e3916315fc..8ef627780099 100644 --- a/webapp/cas-server-webapp-config/src/test/java/org/apereo/cas/BaseCasWebflowSessionContextConfigurationTests.java +++ b/webapp/cas-server-webapp-config/src/test/java/org/apereo/cas/BaseCasWebflowSessionContextConfigurationTests.java @@ -26,6 +26,7 @@ import org.apereo.cas.config.CasFiltersConfiguration; import org.apereo.cas.config.CasPersonDirectoryConfiguration; import org.apereo.cas.config.CasPropertiesConfiguration; +import org.apereo.cas.config.CasThymeleafConfiguration; import org.apereo.cas.config.CasWebAppConfiguration; import org.apereo.cas.config.support.CasWebApplicationServiceFactoryConfiguration; import org.apereo.cas.configuration.CasConfigurationProperties; @@ -35,6 +36,7 @@ import org.apereo.cas.util.ResourceUtils; import org.apereo.cas.validation.config.CasCoreValidationConfiguration; import org.apereo.cas.web.config.CasCookieConfiguration; +import org.apereo.cas.web.config.CasProtocolViewsConfiguration; import org.apereo.cas.web.config.CasSupportActionsConfiguration; import org.apereo.cas.web.flow.config.CasCoreWebflowConfiguration; import org.apereo.cas.web.flow.config.CasMultifactorAuthenticationWebflowConfiguration; @@ -50,6 +52,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration; import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; +import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; @@ -88,7 +91,12 @@ */ @SpringBootTest(classes = { MailSenderAutoConfiguration.class, + AopAutoConfiguration.class, + RefreshAutoConfiguration.class, + ThymeleafAutoConfiguration.class, CasThemesConfiguration.class, + CasThymeleafConfiguration.class, + CasProtocolViewsConfiguration.class, CasFiltersConfiguration.class, CasPropertiesConfiguration.class, CasWebAppConfiguration.class, @@ -121,12 +129,11 @@ CasCoreAuthenticationServiceSelectionStrategyConfiguration.class, CasCoreAuditConfiguration.class, CasPersonDirectoryConfiguration.class, - AopAutoConfiguration.class, - RefreshAutoConfiguration.class, CasCoreMultifactorAuthenticationConfiguration.class }, properties = { "spring.mail.host=localhost", - "spring.mail.port=25000" + "spring.mail.port=25000", + "cas.webflow.base-path=classpath:/webflow" }) @EnableConfigurationProperties(CasConfigurationProperties.class) @EnableAspectJAutoProxy(proxyTargetClass = true) diff --git a/webapp/cas-server-webapp-init-tomcat/src/main/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryCustomizer.java b/webapp/cas-server-webapp-init-tomcat/src/main/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryCustomizer.java index 1789bf9029d4..6f31aa79d1cc 100644 --- a/webapp/cas-server-webapp-init-tomcat/src/main/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryCustomizer.java +++ b/webapp/cas-server-webapp-init-tomcat/src/main/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryCustomizer.java @@ -1,6 +1,7 @@ package org.apereo.cas.tomcat; import org.apereo.cas.configuration.CasConfigurationProperties; +import org.apereo.cas.configuration.model.core.web.tomcat.CasEmbeddedApacheTomcatHttpProxyProperties; import org.apereo.cas.configuration.support.Beans; import org.apereo.cas.util.ResourceUtils; @@ -13,6 +14,7 @@ import org.apache.catalina.valves.rewrite.RewriteValve; import org.apache.commons.lang3.StringUtils; import org.apache.coyote.AbstractProtocol; +import org.apache.coyote.ajp.AbstractAjpProtocol; import org.apache.coyote.ajp.AjpNio2Protocol; import org.apache.coyote.ajp.AjpNioProtocol; import org.apache.coyote.http11.Http11AprProtocol; @@ -42,6 +44,7 @@ @Slf4j public class CasTomcatServletWebServerFactoryCustomizer extends ServletWebServerFactoryCustomizer { private final CasConfigurationProperties casProperties; + private final ServerProperties serverProperties; public CasTomcatServletWebServerFactoryCustomizer(final ServerProperties serverProperties, @@ -51,6 +54,50 @@ public CasTomcatServletWebServerFactoryCustomizer(final ServerProperties serverP this.serverProperties = serverProperties; } + private static void configureConnectorForProtocol(final Connector connector, + final CasEmbeddedApacheTomcatHttpProxyProperties proxy) { + val handler = ReflectionUtils.findField(connector.getClass(), "protocolHandler"); + if (handler != null) { + ReflectionUtils.makeAccessible(handler); + if ("HTTP/2".equalsIgnoreCase(proxy.getProtocol())) { + ReflectionUtils.setField(handler, connector, new Http2Protocol()); + } else { + var protocolHandlerInstance = (AbstractProtocol) null; + switch (proxy.getProtocol()) { + case "AJP/2": + protocolHandlerInstance = new AjpNio2Protocol(); + val ajp1 = AbstractAjpProtocol.class.cast(protocolHandlerInstance); + ajp1.setSecretRequired(proxy.isSecure()); + ajp1.setSecret(proxy.getSecret()); + break; + case "AJP/1.3": + protocolHandlerInstance = new AjpNioProtocol(); + val ajp2 = AbstractAjpProtocol.class.cast(protocolHandlerInstance); + ajp2.setSecretRequired(proxy.isSecure()); + ajp2.setSecret(proxy.getSecret()); + break; + case "APR": + protocolHandlerInstance = new Http11AprProtocol(); + break; + case "HTTP/1.2": + protocolHandlerInstance = new Http11Nio2Protocol(); + break; + case "HTTP/1.1": + default: + protocolHandlerInstance = new Http11NioProtocol(); + break; + } + protocolHandlerInstance.setPort(connector.getPort()); + ReflectionUtils.setField(handler, connector, protocolHandlerInstance); + } + val handlerClass = ReflectionUtils.findField(connector.getClass(), "protocolHandlerClassName"); + if (handlerClass != null) { + ReflectionUtils.makeAccessible(handlerClass); + ReflectionUtils.setField(handlerClass, connector, connector.getProtocolHandler().getClass().getName()); + } + } + } + @Override public void customize(final ConfigurableServletWebServerFactory factory) { if (factory instanceof TomcatServletWebServerFactory) { @@ -64,7 +111,7 @@ public void customize(final ConfigurableServletWebServerFactory factory) { configureBasicAuthn(tomcat); finalizeConnectors(tomcat); } else { - LOGGER.error("Servlet web server factory [{}] does not support Apache Tomcat and cannot be customized!", factory); + LOGGER.error("Servlet web server factory [{}] does not support Apache Tomcat and cannot be customized.", factory); } } @@ -172,8 +219,9 @@ private void configureHttpProxy(final TomcatServletWebServerFactory tomcat) { if (StringUtils.isNotBlank(proxy.getProtocol())) { LOGGER.debug("Setting HTTP proxying protocol to [{}]", proxy.getProtocol()); - configureConnectorForProtocol(connector, proxy.getProtocol()); + configureConnectorForProtocol(connector, proxy); } + if (proxy.getRedirectPort() > 0) { LOGGER.debug("Setting HTTP proxying redirect port to [{}]", proxy.getRedirectPort()); connector.setRedirectPort(proxy.getRedirectPort()); @@ -206,6 +254,12 @@ private void configureAjp(final TomcatServletWebServerFactory tomcat) { ajpConnector.setMaxPostSize(ajp.getMaxPostSize()); ajpConnector.addUpgradeProtocol(new Http2Protocol()); + val handler = (AbstractAjpProtocol) ajpConnector.getProtocolHandler(); + if (handler != null) { + handler.setSecretRequired(ajp.isSecure()); + handler.setSecret(ajp.getSecret()); + } + if (ajp.getProxyPort() > 0) { LOGGER.debug("Set AJP proxy port to [{}]", ajp.getProxyPort()); ajpConnector.setProxyPort(ajp.getProxyPort()); @@ -236,43 +290,6 @@ private void configureSSLValve(final TomcatServletWebServerFactory tomcat) { } } - private static void configureConnectorForProtocol(final Connector connector, final String protocol) { - val handler = ReflectionUtils.findField(connector.getClass(), "protocolHandler"); - if (handler != null) { - ReflectionUtils.makeAccessible(handler); - if ("HTTP/2".equalsIgnoreCase(protocol)) { - ReflectionUtils.setField(handler, connector, new Http2Protocol()); - } else { - var protocolHandlerInstance = (AbstractProtocol) null; - switch (protocol) { - case "AJP/2": - protocolHandlerInstance = new AjpNio2Protocol(); - break; - case "AJP/1.3": - protocolHandlerInstance = new AjpNioProtocol(); - break; - case "APR": - protocolHandlerInstance = new Http11AprProtocol(); - break; - case "HTTP/1.2": - protocolHandlerInstance = new Http11Nio2Protocol(); - break; - case "HTTP/1.1": - default: - protocolHandlerInstance = new Http11NioProtocol(); - break; - } - protocolHandlerInstance.setPort(connector.getPort()); - ReflectionUtils.setField(handler, connector, protocolHandlerInstance); - } - val handlerClass = ReflectionUtils.findField(connector.getClass(), "protocolHandlerClassName"); - if (handlerClass != null) { - ReflectionUtils.makeAccessible(handlerClass); - ReflectionUtils.setField(handlerClass, connector, connector.getProtocolHandler().getClass().getName()); - } - } - } - private void configureRewriteValve(final TomcatServletWebServerFactory tomcat) { val res = casProperties.getServer().getTomcat().getRewriteValve().getLocation(); if (ResourceUtils.doesResourceExist(res)) { diff --git a/webapp/cas-server-webapp-init-tomcat/src/test/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryTests.java b/webapp/cas-server-webapp-init-tomcat/src/test/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryTests.java index b4ad48333093..b3a3f4bf6f8b 100644 --- a/webapp/cas-server-webapp-init-tomcat/src/test/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryTests.java +++ b/webapp/cas-server-webapp-init-tomcat/src/test/java/org/apereo/cas/tomcat/CasTomcatServletWebServerFactoryTests.java @@ -33,10 +33,12 @@ "cas.server.tomcat.httpProxy.enabled=true", "cas.server.tomcat.httpProxy.secure=true", "cas.server.tomcat.httpProxy.scheme=https", + "cas.server.tomcat.httpProxy.secret=s3cr3t", "cas.server.tomcat.http.enabled=true", "cas.server.tomcat.http.port=9190", "cas.server.tomcat.ajp.enabled=true", "cas.server.tomcat.ajp.port=9944", + "cas.server.tomcat.ajp.secret=s3cr3t", "cas.server.tomcat.basicAuthn.enabled=true", "cas.server.tomcat.extAccessLog.enabled=true", "cas.server.tomcat.rewriteValve.location=classpath:/container/tomcat/rewrite.config"