diff --git a/README.md b/README.md index ede48b35..b0715c6f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ This plugin provides the basis for using Keycloak as Identity Management solutio Password grant exchanges are only supported for Keycloak's internally managed users and users of an LDAP / Keberos User federation. Hence without SSO you will only be able to login with users managed by such connections. Current version: `7.20.0-SNAPSHOT`
-Latest tests with: Keycloak `21.1.1`, `19.0.3-legacy`, Camunda `7.20.0-alpha4` +Latest tests with: Keycloak `21.1.1`, `19.0.3-legacy`, Camunda `7.20.0-alpha5` #### Features Changes in version `7.20.0` @@ -135,7 +135,7 @@ Maven Dependencies: org.camunda.bpm.extension camunda-platform-7-keycloak - 7.18.0 + 7.20.0 ``` @@ -298,28 +298,30 @@ Last but not least add a security configuration and enable OAuth2 SSO: ```java /** -* Camunda Web application SSO configuration for usage with KeycloakIdentityProviderPlugin. -*/ + * Camunda Web application SSO configuration for usage with KeycloakIdentityProviderPlugin. + */ @ConditionalOnMissingClass("org.springframework.test.context.junit.jupiter.SpringExtension") +@EnableWebSecurity @Configuration -@Order(SecurityProperties.BASIC_AUTH_ORDER - 10) -public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter { +public class WebAppSecurityConfig { - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .csrf().ignoringAntMatchers("/api/**") - .and() - .requestMatchers().antMatchers("/**").and() - .authorizeRequests(authorizeRequests -> - authorizeRequests - .antMatchers("/app/**", "/api/**", "/lib/**") - .authenticated() - .anyRequest() - .permitAll() - ) - .oauth2Login() - ; + @Bean + @Order(1) + public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception { + return http + .csrf(csrf -> csrf + .ignoringRequestMatchers(antMatcher("/api/**"), antMatcher("/engine-rest/**"))) + .authorizeHttpRequests(authorize -> authorize + .requestMatchers( + antMatcher("/assets/**"), + antMatcher("/app/**"), + antMatcher("/api/**"), + antMatcher("/lib/**")) + .authenticated() + .anyRequest() + .permitAll()) + .oauth2Login(withDefaults()) + .build(); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -334,6 +336,16 @@ public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter { return filterRegistration; } + // The ForwardedHeaderFilter is required to correctly assemble the redirect URL for OAUth2 login. + // Without the filter, Spring generates an HTTP URL even though the container route is accessed through HTTPS. + @Bean + public FilterRegistrationBean forwardedHeaderFilter() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(); + filterRegistrationBean.setFilter(new ForwardedHeaderFilter()); + filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return filterRegistrationBean; + } + @Bean @Order(0) public RequestContextListener requestContextListener() { @@ -465,4 +477,3 @@ Brought to you by: ## License License: [Apache License 2.0](https://opensource.org/licenses/Apache-2.0) - diff --git a/examples/jwt/README.md b/examples/jwt/README.md index 9d0f924d..6d64ad96 100644 --- a/examples/jwt/README.md +++ b/examples/jwt/README.md @@ -67,9 +67,10 @@ In order to setup Spring Boot's OAuth2 security add the following Maven dependen With all that stuff in place we then need a Web Security Configuration as follows: ```java +@ConditionalOnMissingClass("org.springframework.test.context.junit.jupiter.SpringExtension") @Configuration -@Order(SecurityProperties.BASIC_AUTH_ORDER - 10) -public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter { +@EnableWebSecurity +public class WebAppSecurityConfig { private static final int AFTER_SPRING_SECURITY_FILTER_CHAIN_ORDER = 201; private static final String API_FILTER_PATTERN = "/api/*"; @@ -81,22 +82,24 @@ public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter { @Inject private KeycloakCockpitConfiguration keycloakCockpitConfiguration; - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception { String path = camundaBpmProperties.getWebapp().getApplicationPath(); - http - .csrf().ignoringAntMatchers("/api/**", "/engine-rest/**") - .and() - .requestMatchers().antMatchers("/**").and() - .authorizeRequests(authz -> authz - .antMatchers( "/").permitAll() - .antMatchers(path + "/app/**").permitAll() - .antMatchers(path + "/lib/**").permitAll() - .antMatchers(path + "/api/engine/engine/**").permitAll() - .antMatchers(path + "/api/*/plugin/*/static/app/plugin.css").permitAll() - .antMatchers(path + "/api/*/plugin/*/static/app/plugin.js").permitAll() + return http + .csrf(csrf -> csrf + .ignoringRequestMatchers(antMatcher(path + "/api/**"), antMatcher("/engine-rest/**"))) + .securityMatcher("/**") + .authorizeHttpRequests(authz -> authz + .requestMatchers(antMatcher("/")).permitAll() + .requestMatchers(antMatcher(path + "/app/**")).permitAll() + .requestMatchers(antMatcher(path + "/assets/**")).permitAll() + .requestMatchers(antMatcher(path + "/lib/**")).permitAll() + .requestMatchers(antMatcher(path + "/api/engine/engine/**")).permitAll() + .requestMatchers(antMatcher(path + "/api/*/plugin/*/static/app/plugin.css")).permitAll() + .requestMatchers(antMatcher(path + "/api/*/plugin/*/static/app/plugin.js")).permitAll() .anyRequest().authenticated()) - .oauth2ResourceServer(oauth2 -> oauth2.jwt()); + .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())) + .build(); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -105,7 +108,7 @@ public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter { String camundaWebappPath = camundaBpmProperties.getWebapp().getApplicationPath(); FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); - filterRegistration.setFilter(new KeycloakJwtAuthenticationFilter()); + filterRegistration.setFilter(new KeycloakJwtAuthenticationFilter(camundaWebappPath)); filterRegistration.setInitParameters(Collections.singletonMap("authentication-provider", "org.camunda.bpm.extension.keycloak.auth.KeycloakJwtAuthenticationProvider")); filterRegistration.setName(AUTHENTICATION_FILTER_NAME); filterRegistration.setOrder(AFTER_SPRING_SECURITY_FILTER_CHAIN_ORDER); @@ -156,16 +159,15 @@ Also for camunda 7.18+ you need to configure CSP header: camunda.bpm: webapp: header-security: - content-security-policy-value=: "base-uri 'self'; - script-src $NONCE 'strict-dynamic' 'unsafe-eval' https: 'self' 'unsafe-inline'; - style-src 'unsafe-inline' 'self'; - connect-src ${plugin.cockpit.keycloak.keycloakUrl} 'self'; - default-src 'self'; - img-src 'self' data:; - block-all-mixed-content;form-action 'self'; - frame-ancestors 'none';object-src 'none'; - sandbox allow-forms allow-scripts allow-same-origin allow-popups allow-downloads" - + content-security-policy-value: "base-uri 'self'; + script-src $NONCE 'strict-dynamic' 'unsafe-eval' https: 'self' 'unsafe-inline'; + style-src 'unsafe-inline' 'self'; + connect-src ${keycloak.url} 'self'; + default-src 'self'; + img-src 'self' data:; + block-all-mixed-content;form-action 'self'; + frame-ancestors 'none';object-src 'none'; + sandbox allow-forms allow-scripts allow-same-origin allow-popups allow-downloads" ``` Now you are ready for the last step: activate Keycloak on the client side. @@ -184,5 +186,5 @@ export default { }; ``` -The referenced script takes care of loading `/js/keycloak.min.js` from your Keycloak server and integrates the Authorization into the Cockpit app +The referenced script takes care of loading the [Keycloak Javascript adapter](https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter) and integrates the Authorization into the Cockpit app using parameters as configured in the Spring Boot config file. \ No newline at end of file diff --git a/examples/run/README.md b/examples/run/README.md index c694c5d5..e5928b96 100644 --- a/examples/run/README.md +++ b/examples/run/README.md @@ -9,15 +9,13 @@ Please be aware that you must use the provided ``*-run-x.y.z.jar`` (fat jar, pac For the records - included dependencies are: -* org.apache.httpcomponents:httpclient - * org.apache.httpcomponents:httpcore - * commons-codec:commons-codec +* org.apache.httpcomponents:client5 + * org.apache.httpcomponents:core5 * com.google.code.gson:gson * com.github.ben-manes.caffeine:caffeine * org.checkerframework:checker-qual * com.google.errorprone:error_prone_annotations - The ``com.google.code.gson`` and ``com.github.ben-manes.caffeine`` dependencies are shaded into the ``keycloakjar`` package namespace. Please be aware ``httpclient`` dependencies (including transitive ones) are not(!) shaded. ## Configure the Keycloak Identity Provider Plugin diff --git a/examples/sso-kubernetes/src/main/java/org/camunda/bpm/extension/keycloak/showcase/sso/WebAppSecurityConfig.java b/examples/sso-kubernetes/src/main/java/org/camunda/bpm/extension/keycloak/showcase/sso/WebAppSecurityConfig.java index 33dbf1c3..ef261386 100644 --- a/examples/sso-kubernetes/src/main/java/org/camunda/bpm/extension/keycloak/showcase/sso/WebAppSecurityConfig.java +++ b/examples/sso-kubernetes/src/main/java/org/camunda/bpm/extension/keycloak/showcase/sso/WebAppSecurityConfig.java @@ -30,9 +30,9 @@ public class WebAppSecurityConfig { @Inject private KeycloakLogoutHandler keycloakLogoutHandler; - @Bean - @Order(1) - public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception { + @Bean + @Order(1) + public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf .ignoringRequestMatchers(antMatcher("/api/**"), antMatcher("/engine-rest/**"))) @@ -51,7 +51,7 @@ public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception { .logoutSuccessHandler(keycloakLogoutHandler) ) .build(); - } + } @SuppressWarnings({ "rawtypes", "unchecked" }) @Bean diff --git a/examples/tomcat/README.md b/examples/tomcat/README.md index c18d89b0..eda39620 100644 --- a/examples/tomcat/README.md +++ b/examples/tomcat/README.md @@ -17,9 +17,8 @@ For the records - included dependencies are: * org.springframework:spring-beans * org.springframework:spring-core * org.springframework:spring-jcl -* org.apache.httpcomponents:httpclient - * org.apache.httpcomponents:httpcore - * commons-codec:commons-codec +* org.apache.httpcomponents:client5 + * org.apache.httpcomponents:core5 * com.github.ben-manes.caffeine:caffeine * org.checkerframework:checker-qual * com.google.errorprone:error_prone_annotations