From 526bc16f7e6aa2a91632ced6472e939619663a74 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Fri, 10 Dec 2021 09:53:33 -0300 Subject: [PATCH 01/35] Remove usage of WebSecurityConfigurerAdapter Switch to expose a SecurityFilterChain Bean Closes gh-52 --- .../java/example/SecurityConfiguration.java | 23 +++++------ .../java/example/SecurityConfiguration.java | 31 +++++++-------- .../java/example/SecurityConfiguration.java | 27 ++++++------- .../java/example/SecurityConfiguration.java | 3 +- .../java/example/DataSourceConfiguration.java | 3 +- .../java/example/SecurityConfiguration.java | 33 ++++++++-------- .../java/example/SecurityConfiguration.java | 21 +++++----- .../java/example/SecurityConfiguration.java | 23 +++++------ .../java/example/SecurityConfiguration.java | 23 +++++------ .../java/example/SecurityConfiguration.java | 39 ++++++++++--------- ...h2ResourceServerSecurityConfiguration.java | 27 ++++++------- 11 files changed, 129 insertions(+), 124 deletions(-) diff --git a/servlet/java-configuration/authentication/preauth/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/authentication/preauth/src/main/java/example/SecurityConfiguration.java index 72b2b24..c2c4592 100644 --- a/servlet/java-configuration/authentication/preauth/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/authentication/preauth/src/main/java/example/SecurityConfiguration.java @@ -18,26 +18,27 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .antMatchers("/login", "/resources/**").permitAll() - .anyRequest().authenticated() - ) - .jee((jee) -> jee.mappableRoles("USER", "ADMIN")); + .authorizeHttpRequests((authorize) -> authorize + .antMatchers("/login", "/resources/**").permitAll() + .anyRequest().authenticated() + ) + .jee((jee) -> jee.mappableRoles("USER", "ADMIN")); + // @formatter:on + return http.build(); } - // @formatter:on // @formatter:off @Bean diff --git a/servlet/java-configuration/authentication/remember-me/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/authentication/remember-me/src/main/java/example/SecurityConfiguration.java index 9fcee8f..f8c4fab 100644 --- a/servlet/java-configuration/authentication/remember-me/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/authentication/remember-me/src/main/java/example/SecurityConfiguration.java @@ -18,31 +18,30 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; - -import static org.springframework.security.config.Customizer.withDefaults; +import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Override - // @formatter:off - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http, UserDetailsService users) throws Exception { + // @formatter:off http - .authorizeRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .formLogin((form) -> form - .loginPage("/login") - .permitAll() - ) - .rememberMe(withDefaults()); + .authorizeRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .formLogin((form) -> form + .loginPage("/login") + .permitAll() + ) + .rememberMe((rememberMe) -> rememberMe.userDetailsService(users)); + // @formatter:on + return http.build(); } - // @formatter:on // @formatter:off @Bean diff --git a/servlet/java-configuration/authentication/username-password/form/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/authentication/username-password/form/src/main/java/example/SecurityConfiguration.java index 1a99d4d..1189f50 100644 --- a/servlet/java-configuration/authentication/username-password/form/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/authentication/username-password/form/src/main/java/example/SecurityConfiguration.java @@ -18,28 +18,29 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Override - // @formatter:off - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off http - .authorizeRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .formLogin((form) -> form - .loginPage("/login") - .permitAll() - ); + .authorizeRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .formLogin((form) -> form + .loginPage("/login") + .permitAll() + ); + // @formatter:on + return http.build(); } - // @formatter:on // @formatter:off @Bean diff --git a/servlet/java-configuration/authentication/username-password/in-memory/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/authentication/username-password/in-memory/src/main/java/example/SecurityConfiguration.java index c7cd1d5..ba51243 100644 --- a/servlet/java-configuration/authentication/username-password/in-memory/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/authentication/username-password/in-memory/src/main/java/example/SecurityConfiguration.java @@ -17,14 +17,13 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { // @formatter:off @Bean diff --git a/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/DataSourceConfiguration.java b/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/DataSourceConfiguration.java index 014a521..b9c464f 100644 --- a/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/DataSourceConfiguration.java +++ b/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/DataSourceConfiguration.java @@ -34,7 +34,8 @@ public class DataSourceConfiguration { @Bean public DataSource dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.HSQL).build(); + return builder.setType(EmbeddedDatabaseType.HSQL) + .addScript("classpath:org/springframework/security/core/userdetails/jdbc/users.ddl").build(); } } diff --git a/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/SecurityConfiguration.java index 2ab407f..ef80bbb 100644 --- a/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/authentication/username-password/jdbc/src/main/java/example/SecurityConfiguration.java @@ -17,28 +17,27 @@ package example; import javax.sql.DataSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.provisioning.JdbcUserDetailsManager; +import org.springframework.security.provisioning.UserDetailsManager; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Autowired - DataSource dataSource; - - // @formatter:off - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .jdbcAuthentication() - .dataSource(this.dataSource) - .withDefaultSchema() - .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")) - .withUser(User.withDefaultPasswordEncoder().username("admin").password("password").roles("ADMIN", "USER")); + @Bean + UserDetailsManager users(DataSource dataSource) { + UserDetails user = User.builder().username("user") + .password("{bcrypt}$2a$10$AiyMWI4UBLozgXq6itzyVuxrtofjcPzn/WS3fOrcqgzdax9jB7Io.").roles("USER").build(); + UserDetails admin = User.builder().username("admin") + .password("{bcrypt}$2a$10$AiyMWI4UBLozgXq6itzyVuxrtofjcPzn/WS3fOrcqgzdax9jB7Io.").roles("USER", "ADMIN") + .build(); + JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource); + users.createUser(user); + users.createUser(admin); + return users; } - // @formatter:on } diff --git a/servlet/java-configuration/authentication/x509/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/authentication/x509/src/main/java/example/SecurityConfiguration.java index 11554d6..e8223dd 100644 --- a/servlet/java-configuration/authentication/x509/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/authentication/x509/src/main/java/example/SecurityConfiguration.java @@ -18,27 +18,28 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Override - // @formatter:off - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .x509(withDefaults()); + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .x509(withDefaults()); + // @formatter:on + return http.build(); } - // @formatter:on // @formatter:off @Bean diff --git a/servlet/java-configuration/hello-mvc-security/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/hello-mvc-security/src/main/java/example/SecurityConfiguration.java index 039e3fb..64d6fbb 100644 --- a/servlet/java-configuration/hello-mvc-security/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/hello-mvc-security/src/main/java/example/SecurityConfiguration.java @@ -18,28 +18,29 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Override - // @formatter:off - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .httpBasic(withDefaults()) - .formLogin(withDefaults()); + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .httpBasic(withDefaults()) + .formLogin(withDefaults()); + // @formatter:on + return http.build(); } - // @formatter:on // @formatter:off @Bean diff --git a/servlet/java-configuration/hello-security-explicit/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/hello-security-explicit/src/main/java/example/SecurityConfiguration.java index 039e3fb..64d6fbb 100644 --- a/servlet/java-configuration/hello-security-explicit/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/hello-security-explicit/src/main/java/example/SecurityConfiguration.java @@ -18,28 +18,29 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Override - // @formatter:off - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .httpBasic(withDefaults()) - .formLogin(withDefaults()); + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .httpBasic(withDefaults()) + .formLogin(withDefaults()); + // @formatter:on + return http.build(); } - // @formatter:on // @formatter:off @Bean diff --git a/servlet/java-configuration/max-sessions/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/max-sessions/src/main/java/example/SecurityConfiguration.java index e8e41ad..02db070 100644 --- a/servlet/java-configuration/max-sessions/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/max-sessions/src/main/java/example/SecurityConfiguration.java @@ -20,17 +20,35 @@ import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .formLogin(withDefaults()) + .sessionManagement((sessions) -> sessions + .sessionConcurrency((concurrency) -> concurrency + .maximumSessions(1) + .expiredUrl("/login?expired") + ) + ); + // @formatter:on + return http.build(); + } // @formatter:off @Bean @@ -44,21 +62,4 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { } // @formatter:on - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .formLogin(withDefaults()) - .sessionManagement((sessions) -> sessions - .sessionConcurrency((concurrency) -> concurrency - .maximumSessions(1) - .expiredUrl("/login?expired") - ) - ); - } - // @formatter:on - } diff --git a/servlet/spring-boot/java/oauth2/resource-server/static/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java b/servlet/spring-boot/java/oauth2/resource-server/static/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java index bcaa99d..edb3e1b 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/static/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java +++ b/servlet/spring-boot/java/oauth2/resource-server/static/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java @@ -19,35 +19,36 @@ import java.security.interfaces.RSAPublicKey; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.web.SecurityFilterChain; /** * OAuth2 Resource Server Configuration. * * @author Josh Cummings */ -@EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { +@Configuration +public class OAuth2ResourceServerSecurityConfiguration { @Value("${spring.security.oauth2.resourceserver.jwt.key-value}") RSAPublicKey key; - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .mvcMatchers("/message/**").hasAuthority("SCOPE_message:read") - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2) -> oauth2 - .jwt((jwt) -> jwt.decoder(jwtDecoder())) - ); + .authorizeHttpRequests((authorize) -> authorize + .mvcMatchers("/message/**").hasAuthority("SCOPE_message:read") + .anyRequest().authenticated() + ) + .oauth2ResourceServer((oauth2) -> oauth2 + .jwt((jwt) -> jwt.decoder(jwtDecoder())) + ); // @formatter:on + return http.build(); } @Bean From 1181eb2b3d6795a4ce53ef6c23d5ab553ab378d7 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Fri, 10 Dec 2021 13:50:35 -0300 Subject: [PATCH 02/35] Fix broken links in README Closes gh-53 --- servlet/java-configuration/saml2/login/README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servlet/java-configuration/saml2/login/README.adoc b/servlet/java-configuration/saml2/login/README.adoc index 013c61f..d8b17db 100644 --- a/servlet/java-configuration/saml2/login/README.adoc +++ b/servlet/java-configuration/saml2/login/README.adoc @@ -6,7 +6,7 @@ It uses https://simplesamlphp.org/[SimpleSAMLphp] as its asserting party. The sample application uses Spring Boot and the `spring-security-saml2-service-provider` module which is new in Spring Security 5.2. -The https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[SAML 2.0 Logout feature] is new in Spring Security 5.6. +The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[SAML 2.0 Logout feature] is new in Spring Security 5.6. == Goals @@ -27,7 +27,7 @@ The following features are implemented in the MVP: On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. -You can refer to the https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. +You can refer to the https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. == Run the Sample From 6b3e6546aa89af04429bd1b5b206e588f41d91f6 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Fri, 10 Dec 2021 14:13:06 -0300 Subject: [PATCH 03/35] Fix broken links Issue gh-53 --- .../spring-boot/java/saml2/login-single-tenant/README.adoc | 4 ++-- servlet/spring-boot/java/saml2/login/README.adoc | 4 ++-- .../spring-boot/java/saml2/refreshable-metadata/README.adoc | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc index 95bc623..37f3427 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc +++ b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc @@ -6,7 +6,7 @@ It uses https://simplesamlphp.org/[SimpleSAMLphp] as its asserting party. The sample application uses Spring Boot and the `spring-security-saml2-service-provider` module which is new in Spring Security 5.2. -The https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[SAML 2.0 Logout feature] is new in Spring Security 5.6. +The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[SAML 2.0 Logout feature] is new in Spring Security 5.6. == Goals @@ -27,7 +27,7 @@ The following features are implemented in the MVP: On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. -You can refer to the https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. +You can refer to the https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. == Run the Sample diff --git a/servlet/spring-boot/java/saml2/login/README.adoc b/servlet/spring-boot/java/saml2/login/README.adoc index 95bc623..37f3427 100644 --- a/servlet/spring-boot/java/saml2/login/README.adoc +++ b/servlet/spring-boot/java/saml2/login/README.adoc @@ -6,7 +6,7 @@ It uses https://simplesamlphp.org/[SimpleSAMLphp] as its asserting party. The sample application uses Spring Boot and the `spring-security-saml2-service-provider` module which is new in Spring Security 5.2. -The https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[SAML 2.0 Logout feature] is new in Spring Security 5.6. +The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[SAML 2.0 Logout feature] is new in Spring Security 5.6. == Goals @@ -27,7 +27,7 @@ The following features are implemented in the MVP: On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. -You can refer to the https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. +You can refer to the https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. == Run the Sample diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc index 2c9e1ed..9c39ca0 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc @@ -6,7 +6,7 @@ It uses https://simplesamlphp.org/[SimpleSAMLphp] as its asserting party. The sample application uses Spring Boot and the `spring-security-saml2-service-provider` module which is new in Spring Security 5.2. -The https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[SAML 2.0 Logout feature] is new in Spring Security 5.6. +The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[SAML 2.0 Logout feature] is new in Spring Security 5.6. == Goals @@ -27,7 +27,7 @@ The following features are implemented in the MVP: On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. -You can refer to the https://docs.spring.io/spring-security/site/docs/5.6.0-SNAPSHOT/reference/html5/#servlet-saml2login-logout[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. +You can refer to the https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[reference documentation] for more details about the RP- and AP-initiated SAML 2.0 Logout. === Refreshable Asserting Party Metadata From 08166219c70998b498f0fd6a9a1d1450dece5560 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Fri, 10 Dec 2021 14:25:44 -0300 Subject: [PATCH 04/35] Use Spring Security 5.7.0-SNAPSHOT --- gradle.properties | 4 ++-- reactive/rsocket/hello-security/gradle.properties | 4 ++-- reactive/webflux-fn/hello-security/gradle.properties | 4 ++-- reactive/webflux-fn/hello/gradle.properties | 4 ++-- .../authentication/username-password/form/gradle.properties | 4 ++-- reactive/webflux/java/authentication/x509/gradle.properties | 4 ++-- .../webflux/java/hello-security-explicit/gradle.properties | 4 ++-- reactive/webflux/java/hello-security/gradle.properties | 4 ++-- reactive/webflux/java/hello/gradle.properties | 4 ++-- reactive/webflux/java/method/gradle.properties | 4 ++-- reactive/webflux/java/oauth2/login/gradle.properties | 4 ++-- .../webflux/java/oauth2/resource-server/gradle.properties | 4 ++-- reactive/webflux/java/oauth2/webclient/gradle.properties | 4 ++-- reactive/webflux/kotlin/hello-security/gradle.properties | 2 +- servlet/java-configuration/aspectj/build.gradle | 6 +++--- .../java-configuration/authentication/preauth/build.gradle | 4 ++-- .../authentication/remember-me/build.gradle | 4 ++-- .../authentication/username-password/form/build.gradle | 4 ++-- .../authentication/username-password/in-memory/build.gradle | 4 ++-- .../authentication/username-password/jdbc/build.gradle | 4 ++-- .../authentication/username-password/ldap/build.gradle | 4 ++-- servlet/java-configuration/authentication/x509/build.gradle | 4 ++-- servlet/java-configuration/data/build.gradle | 4 ++-- servlet/java-configuration/hello-mvc-security/build.gradle | 4 ++-- .../java-configuration/hello-security-explicit/build.gradle | 4 ++-- servlet/java-configuration/hello-security/build.gradle | 4 ++-- servlet/java-configuration/max-sessions/build.gradle | 4 ++-- servlet/java-configuration/saml2/login/build.gradle | 2 +- .../authentication/username-password/mfa/gradle.properties | 4 ++-- .../user-details-service/custom-user/gradle.properties | 4 ++-- .../java/hello-security-explicit/gradle.properties | 4 ++-- servlet/spring-boot/java/hello-security/gradle.properties | 4 ++-- servlet/spring-boot/java/hello/gradle.properties | 4 ++-- servlet/spring-boot/java/jwt/login/gradle.properties | 4 ++-- servlet/spring-boot/java/ldap/gradle.properties | 4 ++-- .../java/oauth2/authorization-server/gradle.properties | 4 ++-- servlet/spring-boot/java/oauth2/login/gradle.properties | 4 ++-- .../oauth2/resource-server/hello-security/gradle.properties | 4 ++-- .../java/oauth2/resource-server/jwe/gradle.properties | 4 ++-- .../oauth2/resource-server/multi-tenancy/gradle.properties | 4 ++-- .../java/oauth2/resource-server/opaque/gradle.properties | 4 ++-- .../java/oauth2/resource-server/static/gradle.properties | 4 ++-- servlet/spring-boot/java/oauth2/webclient/gradle.properties | 4 ++-- .../java/saml2/login-single-tenant/gradle.properties | 4 ++-- servlet/spring-boot/java/saml2/login/gradle.properties | 4 ++-- .../java/saml2/refreshable-metadata/gradle.properties | 4 ++-- servlet/spring-boot/kotlin/hello-security/gradle.properties | 2 +- servlet/xml/java/contacts/build.gradle | 2 +- servlet/xml/java/contacts/gradle.properties | 2 +- servlet/xml/java/dms/build.gradle | 2 +- servlet/xml/java/dms/gradle.properties | 2 +- servlet/xml/java/helloworld/build.gradle | 2 +- servlet/xml/java/helloworld/gradle.properties | 2 +- servlet/xml/java/preauth/build.gradle | 2 +- servlet/xml/java/preauth/gradle.properties | 2 +- 55 files changed, 100 insertions(+), 100 deletions(-) diff --git a/gradle.properties b/gradle.properties index 02622be..2f11f4c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true org.gradle.caching=true diff --git a/reactive/rsocket/hello-security/gradle.properties b/reactive/rsocket/hello-security/gradle.properties index c924e36..e19a862 100644 --- a/reactive/rsocket/hello-security/gradle.properties +++ b/reactive/rsocket/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux-fn/hello-security/gradle.properties b/reactive/webflux-fn/hello-security/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux-fn/hello-security/gradle.properties +++ b/reactive/webflux-fn/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux-fn/hello/gradle.properties b/reactive/webflux-fn/hello/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux-fn/hello/gradle.properties +++ b/reactive/webflux-fn/hello/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/authentication/username-password/form/gradle.properties b/reactive/webflux/java/authentication/username-password/form/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/authentication/username-password/form/gradle.properties +++ b/reactive/webflux/java/authentication/username-password/form/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/authentication/x509/gradle.properties b/reactive/webflux/java/authentication/x509/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/authentication/x509/gradle.properties +++ b/reactive/webflux/java/authentication/x509/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/hello-security-explicit/gradle.properties b/reactive/webflux/java/hello-security-explicit/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/hello-security-explicit/gradle.properties +++ b/reactive/webflux/java/hello-security-explicit/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/hello-security/gradle.properties b/reactive/webflux/java/hello-security/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/hello-security/gradle.properties +++ b/reactive/webflux/java/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/hello/gradle.properties b/reactive/webflux/java/hello/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/hello/gradle.properties +++ b/reactive/webflux/java/hello/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/method/gradle.properties b/reactive/webflux/java/method/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/method/gradle.properties +++ b/reactive/webflux/java/method/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/oauth2/login/gradle.properties b/reactive/webflux/java/oauth2/login/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/oauth2/login/gradle.properties +++ b/reactive/webflux/java/oauth2/login/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/oauth2/resource-server/gradle.properties b/reactive/webflux/java/oauth2/resource-server/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/oauth2/resource-server/gradle.properties +++ b/reactive/webflux/java/oauth2/resource-server/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/java/oauth2/webclient/gradle.properties b/reactive/webflux/java/oauth2/webclient/gradle.properties index c924e36..e19a862 100644 --- a/reactive/webflux/java/oauth2/webclient/gradle.properties +++ b/reactive/webflux/java/oauth2/webclient/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/reactive/webflux/kotlin/hello-security/gradle.properties b/reactive/webflux/kotlin/hello-security/gradle.properties index 778a95c..e7f8e22 100644 --- a/reactive/webflux/kotlin/hello-security/gradle.properties +++ b/reactive/webflux/kotlin/hello-security/gradle.properties @@ -1 +1 @@ -spring-security.version=5.6.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/java-configuration/aspectj/build.gradle b/servlet/java-configuration/aspectj/build.gradle index ea899bd..8fa35b2 100644 --- a/servlet/java-configuration/aspectj/build.gradle +++ b/servlet/java-configuration/aspectj/build.gradle @@ -9,12 +9,12 @@ repositories { } dependencies { - aspect platform("org.springframework:spring-framework-bom:5.3.0") + aspect platform("org.springframework:spring-framework-bom:5.3.13") aspect platform("org.springframework.security:spring-security-bom:5.4.0-SNAPSHOT") aspect "org.springframework.security:spring-security-aspects" - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/preauth/build.gradle b/servlet/java-configuration/authentication/preauth/build.gradle index f829173..0204ed2 100644 --- a/servlet/java-configuration/authentication/preauth/build.gradle +++ b/servlet/java-configuration/authentication/preauth/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/remember-me/build.gradle b/servlet/java-configuration/authentication/remember-me/build.gradle index 2605113..d62bab3 100644 --- a/servlet/java-configuration/authentication/remember-me/build.gradle +++ b/servlet/java-configuration/authentication/remember-me/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/username-password/form/build.gradle b/servlet/java-configuration/authentication/username-password/form/build.gradle index f829173..0204ed2 100644 --- a/servlet/java-configuration/authentication/username-password/form/build.gradle +++ b/servlet/java-configuration/authentication/username-password/form/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle index f829173..0204ed2 100644 --- a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle +++ b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle index 21c8e20..a7bf0a4 100644 --- a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle +++ b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.hsqldb:hsqldb:2.5.1" diff --git a/servlet/java-configuration/authentication/username-password/ldap/build.gradle b/servlet/java-configuration/authentication/username-password/ldap/build.gradle index 3de229b..ee984e6 100644 --- a/servlet/java-configuration/authentication/username-password/ldap/build.gradle +++ b/servlet/java-configuration/authentication/username-password/ldap/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/x509/build.gradle b/servlet/java-configuration/authentication/x509/build.gradle index 2f773aa..0e0dc5f 100644 --- a/servlet/java-configuration/authentication/x509/build.gradle +++ b/servlet/java-configuration/authentication/x509/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/data/build.gradle b/servlet/java-configuration/data/build.gradle index d0456ff..5e92657 100644 --- a/servlet/java-configuration/data/build.gradle +++ b/servlet/java-configuration/data/build.gradle @@ -9,9 +9,9 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") + implementation platform("org.springframework:spring-framework-bom:5.3.13") implementation platform("org.springframework.data:spring-data-releasetrain:Neumann-SR5") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/hello-mvc-security/build.gradle b/servlet/java-configuration/hello-mvc-security/build.gradle index f829173..0204ed2 100644 --- a/servlet/java-configuration/hello-mvc-security/build.gradle +++ b/servlet/java-configuration/hello-mvc-security/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/hello-security-explicit/build.gradle b/servlet/java-configuration/hello-security-explicit/build.gradle index a3e000c..31dc731 100644 --- a/servlet/java-configuration/hello-security-explicit/build.gradle +++ b/servlet/java-configuration/hello-security-explicit/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/hello-security/build.gradle b/servlet/java-configuration/hello-security/build.gradle index a3e000c..31dc731 100644 --- a/servlet/java-configuration/hello-security/build.gradle +++ b/servlet/java-configuration/hello-security/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/max-sessions/build.gradle b/servlet/java-configuration/max-sessions/build.gradle index f829173..0204ed2 100644 --- a/servlet/java-configuration/max-sessions/build.gradle +++ b/servlet/java-configuration/max-sessions/build.gradle @@ -13,8 +13,8 @@ repositories { } dependencies { - implementation platform("org.springframework:spring-framework-bom:5.3.0") - implementation platform("org.springframework.security:spring-security-bom:5.5.0-SNAPSHOT") + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/saml2/login/build.gradle b/servlet/java-configuration/saml2/login/build.gradle index da355e3..1dbb9db 100644 --- a/servlet/java-configuration/saml2/login/build.gradle +++ b/servlet/java-configuration/saml2/login/build.gradle @@ -36,7 +36,7 @@ dependencies { implementation "org.opensaml:opensaml-saml-impl:4.1.1" } implementation platform("org.springframework:spring-framework-bom:5.3.11") - implementation platform("org.springframework.security:spring-security-bom:5.6.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties b/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties +++ b/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties +++ b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/hello-security-explicit/gradle.properties b/servlet/spring-boot/java/hello-security-explicit/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/hello-security-explicit/gradle.properties +++ b/servlet/spring-boot/java/hello-security-explicit/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/hello-security/gradle.properties b/servlet/spring-boot/java/hello-security/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/hello-security/gradle.properties +++ b/servlet/spring-boot/java/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/hello/gradle.properties b/servlet/spring-boot/java/hello/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/hello/gradle.properties +++ b/servlet/spring-boot/java/hello/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/jwt/login/gradle.properties b/servlet/spring-boot/java/jwt/login/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/jwt/login/gradle.properties +++ b/servlet/spring-boot/java/jwt/login/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/ldap/gradle.properties b/servlet/spring-boot/java/ldap/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/ldap/gradle.properties +++ b/servlet/spring-boot/java/ldap/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties b/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties +++ b/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/login/gradle.properties b/servlet/spring-boot/java/oauth2/login/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/login/gradle.properties +++ b/servlet/spring-boot/java/oauth2/login/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/webclient/gradle.properties b/servlet/spring-boot/java/oauth2/webclient/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/oauth2/webclient/gradle.properties +++ b/servlet/spring-boot/java/oauth2/webclient/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties b/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties +++ b/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/saml2/login/gradle.properties b/servlet/spring-boot/java/saml2/login/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/saml2/login/gradle.properties +++ b/servlet/spring-boot/java/saml2/login/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties b/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties index c924e36..e19a862 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties @@ -1,2 +1,2 @@ -version=5.6.0-SNAPSHOT -spring-security.version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/spring-boot/kotlin/hello-security/gradle.properties b/servlet/spring-boot/kotlin/hello-security/gradle.properties index 778a95c..e7f8e22 100644 --- a/servlet/spring-boot/kotlin/hello-security/gradle.properties +++ b/servlet/spring-boot/kotlin/hello-security/gradle.properties @@ -1 +1 @@ -spring-security.version=5.6.0-SNAPSHOT +spring-security.version=5.7.0-SNAPSHOT diff --git a/servlet/xml/java/contacts/build.gradle b/servlet/xml/java/contacts/build.gradle index 8e02c87..bda493d 100644 --- a/servlet/xml/java/contacts/build.gradle +++ b/servlet/xml/java/contacts/build.gradle @@ -13,7 +13,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.6.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.springframework:spring-framework-bom:5.3.9") implementation platform("org.junit:junit-bom:5.7.0") diff --git a/servlet/xml/java/contacts/gradle.properties b/servlet/xml/java/contacts/gradle.properties index 99e4019..ca3504e 100644 --- a/servlet/xml/java/contacts/gradle.properties +++ b/servlet/xml/java/contacts/gradle.properties @@ -1 +1 @@ -version=5.6.0-SNAPSHOT \ No newline at end of file +version=5.7.0-SNAPSHOT \ No newline at end of file diff --git a/servlet/xml/java/dms/build.gradle b/servlet/xml/java/dms/build.gradle index c9c2586..0015ff1 100644 --- a/servlet/xml/java/dms/build.gradle +++ b/servlet/xml/java/dms/build.gradle @@ -9,7 +9,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.6.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.springframework:spring-framework-bom:5.3.9") implementation platform("org.junit:junit-bom:5.7.0") diff --git a/servlet/xml/java/dms/gradle.properties b/servlet/xml/java/dms/gradle.properties index f309933..e170fd3 100644 --- a/servlet/xml/java/dms/gradle.properties +++ b/servlet/xml/java/dms/gradle.properties @@ -1 +1 @@ -version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT diff --git a/servlet/xml/java/helloworld/build.gradle b/servlet/xml/java/helloworld/build.gradle index 702108f..c0fcb95 100644 --- a/servlet/xml/java/helloworld/build.gradle +++ b/servlet/xml/java/helloworld/build.gradle @@ -13,7 +13,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.6.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/xml/java/helloworld/gradle.properties b/servlet/xml/java/helloworld/gradle.properties index f309933..e170fd3 100644 --- a/servlet/xml/java/helloworld/gradle.properties +++ b/servlet/xml/java/helloworld/gradle.properties @@ -1 +1 @@ -version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT diff --git a/servlet/xml/java/preauth/build.gradle b/servlet/xml/java/preauth/build.gradle index 27eae7b..629b78e 100644 --- a/servlet/xml/java/preauth/build.gradle +++ b/servlet/xml/java/preauth/build.gradle @@ -9,7 +9,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.6.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/xml/java/preauth/gradle.properties b/servlet/xml/java/preauth/gradle.properties index f309933..e170fd3 100644 --- a/servlet/xml/java/preauth/gradle.properties +++ b/servlet/xml/java/preauth/gradle.properties @@ -1 +1 @@ -version=5.6.0-SNAPSHOT +version=5.7.0-SNAPSHOT From 0818005c464cd1140217257cfff07816ba78b1d1 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Fri, 10 Dec 2021 14:28:52 -0300 Subject: [PATCH 05/35] Increase timeout for WebTestClient Sometimes the tests fail with the message Timeout on blocking read for 5000000000 NANOSECONDS --- .../hello-security/src/test/java/example/HelloTests.java | 2 +- reactive/webflux-fn/hello/src/test/java/example/HelloTests.java | 2 +- .../src/test/java/example/HelloSecurityTests.java | 2 +- .../src/test/java/example/HelloSecurityTests.java | 2 +- .../webflux/java/hello/src/test/java/example/HelloTests.java | 2 +- .../src/test/java/example/HelloMethodApplicationTests.java | 2 +- .../src/integTest/java/example/OAuth2LoginApplicationTests.java | 2 +- .../example/ServerOAuth2ResourceServerApplicationITests.java | 2 +- .../src/test/java/example/OAuth2WebClientControllerTests.java | 2 +- .../java/example/OAuth2WebClientWebFluxApplicationTests.java | 2 +- .../RegisteredOAuth2AuthorizedClientControllerTests.java | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/reactive/webflux-fn/hello-security/src/test/java/example/HelloTests.java b/reactive/webflux-fn/hello-security/src/test/java/example/HelloTests.java index 0984d43..c338dfd 100644 --- a/reactive/webflux-fn/hello-security/src/test/java/example/HelloTests.java +++ b/reactive/webflux-fn/hello-security/src/test/java/example/HelloTests.java @@ -28,7 +28,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @since 5.0 */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class HelloTests { @Autowired diff --git a/reactive/webflux-fn/hello/src/test/java/example/HelloTests.java b/reactive/webflux-fn/hello/src/test/java/example/HelloTests.java index 023c9c2..62effa3 100644 --- a/reactive/webflux-fn/hello/src/test/java/example/HelloTests.java +++ b/reactive/webflux-fn/hello/src/test/java/example/HelloTests.java @@ -27,7 +27,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @since 5.0 */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class HelloTests { @Autowired diff --git a/reactive/webflux/java/hello-security-explicit/src/test/java/example/HelloSecurityTests.java b/reactive/webflux/java/hello-security-explicit/src/test/java/example/HelloSecurityTests.java index 6e7985e..4038b8b 100644 --- a/reactive/webflux/java/hello-security-explicit/src/test/java/example/HelloSecurityTests.java +++ b/reactive/webflux/java/hello-security-explicit/src/test/java/example/HelloSecurityTests.java @@ -28,7 +28,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @since 5.0 */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class HelloSecurityTests { @Autowired diff --git a/reactive/webflux/java/hello-security/src/test/java/example/HelloSecurityTests.java b/reactive/webflux/java/hello-security/src/test/java/example/HelloSecurityTests.java index 5eedc6f..8bcb762 100644 --- a/reactive/webflux/java/hello-security/src/test/java/example/HelloSecurityTests.java +++ b/reactive/webflux/java/hello-security/src/test/java/example/HelloSecurityTests.java @@ -28,7 +28,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @since 5.0 */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class HelloSecurityTests { @Autowired diff --git a/reactive/webflux/java/hello/src/test/java/example/HelloTests.java b/reactive/webflux/java/hello/src/test/java/example/HelloTests.java index 05a0f9f..54a365b 100644 --- a/reactive/webflux/java/hello/src/test/java/example/HelloTests.java +++ b/reactive/webflux/java/hello/src/test/java/example/HelloTests.java @@ -27,7 +27,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @since 5.0 */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class HelloTests { @Autowired diff --git a/reactive/webflux/java/method/src/test/java/example/HelloMethodApplicationTests.java b/reactive/webflux/java/method/src/test/java/example/HelloMethodApplicationTests.java index 6782827..1b8f701 100644 --- a/reactive/webflux/java/method/src/test/java/example/HelloMethodApplicationTests.java +++ b/reactive/webflux/java/method/src/test/java/example/HelloMethodApplicationTests.java @@ -28,7 +28,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @since 5.0 */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class HelloMethodApplicationTests { @Autowired diff --git a/reactive/webflux/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java b/reactive/webflux/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java index bfe9169..a8d1233 100644 --- a/reactive/webflux/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java +++ b/reactive/webflux/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java @@ -36,7 +36,7 @@ import static org.springframework.security.test.web.reactive.server.SecurityMock * @author Rob Winch */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class OAuth2LoginApplicationTests { @Autowired diff --git a/reactive/webflux/java/oauth2/resource-server/src/integTest/java/example/ServerOAuth2ResourceServerApplicationITests.java b/reactive/webflux/java/oauth2/resource-server/src/integTest/java/example/ServerOAuth2ResourceServerApplicationITests.java index 26984a1..9b5146a 100644 --- a/reactive/webflux/java/oauth2/resource-server/src/integTest/java/example/ServerOAuth2ResourceServerApplicationITests.java +++ b/reactive/webflux/java/oauth2/resource-server/src/integTest/java/example/ServerOAuth2ResourceServerApplicationITests.java @@ -36,7 +36,7 @@ import static org.hamcrest.Matchers.containsString; * @since 5.1 */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") @ActiveProfiles("test") public class ServerOAuth2ResourceServerApplicationITests { diff --git a/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientControllerTests.java b/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientControllerTests.java index a0fb55e..eca57b5 100644 --- a/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientControllerTests.java +++ b/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientControllerTests.java @@ -39,7 +39,7 @@ import static org.springframework.security.test.web.reactive.server.SecurityMock @WebFluxTest @Import({ SecurityConfiguration.class, OAuth2WebClientController.class }) -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class OAuth2WebClientControllerTests { private static MockWebServer web = new MockWebServer(); diff --git a/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientWebFluxApplicationTests.java b/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientWebFluxApplicationTests.java index 59a1258..29b244e 100644 --- a/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientWebFluxApplicationTests.java +++ b/reactive/webflux/java/oauth2/webclient/src/test/java/example/OAuth2WebClientWebFluxApplicationTests.java @@ -27,7 +27,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @author Rob Winch */ @SpringBootTest -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class OAuth2WebClientWebFluxApplicationTests { @Autowired diff --git a/reactive/webflux/java/oauth2/webclient/src/test/java/example/RegisteredOAuth2AuthorizedClientControllerTests.java b/reactive/webflux/java/oauth2/webclient/src/test/java/example/RegisteredOAuth2AuthorizedClientControllerTests.java index 4bca019..d59a67a 100644 --- a/reactive/webflux/java/oauth2/webclient/src/test/java/example/RegisteredOAuth2AuthorizedClientControllerTests.java +++ b/reactive/webflux/java/oauth2/webclient/src/test/java/example/RegisteredOAuth2AuthorizedClientControllerTests.java @@ -39,7 +39,7 @@ import static org.springframework.security.test.web.reactive.server.SecurityMock @WebFluxTest @Import({ SecurityConfiguration.class, RegisteredOAuth2AuthorizedClientController.class }) -@AutoConfigureWebTestClient +@AutoConfigureWebTestClient(timeout = "36000") public class RegisteredOAuth2AuthorizedClientControllerTests { private static MockWebServer web = new MockWebServer(); From 0e91e6300ee0c904c070021991bb0f12e5aca29b Mon Sep 17 00:00:00 2001 From: Eleftheria Stein Date: Mon, 13 Dec 2021 17:04:46 +0100 Subject: [PATCH 06/35] Prevent gradle cache on tests Closes gh-54 --- reactive/rsocket/hello-security/build.gradle | 1 + reactive/webflux-fn/hello-security/build.gradle | 1 + reactive/webflux-fn/hello/build.gradle | 1 + .../java/authentication/username-password/form/build.gradle | 1 + reactive/webflux/java/authentication/x509/build.gradle | 1 + reactive/webflux/java/hello-security-explicit/build.gradle | 1 + reactive/webflux/java/hello-security/build.gradle | 1 + reactive/webflux/java/hello/build.gradle | 1 + reactive/webflux/java/method/build.gradle | 1 + reactive/webflux/java/oauth2/login/build.gradle | 1 + reactive/webflux/java/oauth2/resource-server/build.gradle | 1 + reactive/webflux/java/oauth2/webclient/build.gradle | 1 + servlet/java-configuration/aspectj/build.gradle | 1 + servlet/java-configuration/authentication/preauth/build.gradle | 1 + .../java-configuration/authentication/remember-me/build.gradle | 1 + .../authentication/username-password/form/build.gradle | 1 + .../authentication/username-password/in-memory/build.gradle | 1 + .../authentication/username-password/jdbc/build.gradle | 1 + .../authentication/username-password/ldap/build.gradle | 1 + servlet/java-configuration/authentication/x509/build.gradle | 1 + servlet/java-configuration/data/build.gradle | 1 + servlet/java-configuration/hello-mvc-security/build.gradle | 1 + servlet/java-configuration/hello-security-explicit/build.gradle | 1 + servlet/java-configuration/hello-security/build.gradle | 1 + servlet/java-configuration/max-sessions/build.gradle | 1 + servlet/java-configuration/saml2/login/build.gradle | 1 + .../java/authentication/username-password/mfa/build.gradle | 1 + .../user-details-service/custom-user/build.gradle | 1 + servlet/spring-boot/java/hello-security-explicit/build.gradle | 1 + servlet/spring-boot/java/hello-security/build.gradle | 1 + servlet/spring-boot/java/hello/build.gradle | 1 + servlet/spring-boot/java/jwt/login/build.gradle | 1 + servlet/spring-boot/java/ldap/build.gradle | 1 + .../spring-boot/java/oauth2/authorization-server/build.gradle | 1 + servlet/spring-boot/java/oauth2/login/build.gradle | 1 + .../java/oauth2/resource-server/hello-security/build.gradle | 1 + servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle | 1 + .../java/oauth2/resource-server/multi-tenancy/build.gradle | 1 + .../spring-boot/java/oauth2/resource-server/opaque/build.gradle | 1 + .../spring-boot/java/oauth2/resource-server/static/build.gradle | 1 + servlet/spring-boot/java/oauth2/webclient/build.gradle | 1 + servlet/spring-boot/java/saml2/login-single-tenant/build.gradle | 1 + servlet/spring-boot/java/saml2/login/build.gradle | 1 + servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle | 1 + servlet/xml/java/contacts/build.gradle | 1 + servlet/xml/java/dms/build.gradle | 1 + servlet/xml/java/helloworld/build.gradle | 1 + servlet/xml/java/preauth/build.gradle | 1 + 48 files changed, 48 insertions(+) diff --git a/reactive/rsocket/hello-security/build.gradle b/reactive/rsocket/hello-security/build.gradle index 08c1b7a..c9f5bea 100644 --- a/reactive/rsocket/hello-security/build.gradle +++ b/reactive/rsocket/hello-security/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux-fn/hello-security/build.gradle b/reactive/webflux-fn/hello-security/build.gradle index 4672588..079ebfa 100644 --- a/reactive/webflux-fn/hello-security/build.gradle +++ b/reactive/webflux-fn/hello-security/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux-fn/hello/build.gradle b/reactive/webflux-fn/hello/build.gradle index ba297ea..48787e1 100644 --- a/reactive/webflux-fn/hello/build.gradle +++ b/reactive/webflux-fn/hello/build.gradle @@ -19,4 +19,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux/java/authentication/username-password/form/build.gradle b/reactive/webflux/java/authentication/username-password/form/build.gradle index 6f764bc..3a87b5a 100644 --- a/reactive/webflux/java/authentication/username-password/form/build.gradle +++ b/reactive/webflux/java/authentication/username-password/form/build.gradle @@ -25,4 +25,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux/java/authentication/x509/build.gradle b/reactive/webflux/java/authentication/x509/build.gradle index 9df6ad5..7724f7f 100644 --- a/reactive/webflux/java/authentication/x509/build.gradle +++ b/reactive/webflux/java/authentication/x509/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux/java/hello-security-explicit/build.gradle b/reactive/webflux/java/hello-security-explicit/build.gradle index f1be420..1373932 100644 --- a/reactive/webflux/java/hello-security-explicit/build.gradle +++ b/reactive/webflux/java/hello-security-explicit/build.gradle @@ -23,4 +23,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux/java/hello-security/build.gradle b/reactive/webflux/java/hello-security/build.gradle index f2c9f31..643a9e1 100644 --- a/reactive/webflux/java/hello-security/build.gradle +++ b/reactive/webflux/java/hello-security/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux/java/hello/build.gradle b/reactive/webflux/java/hello/build.gradle index ba297ea..48787e1 100644 --- a/reactive/webflux/java/hello/build.gradle +++ b/reactive/webflux/java/hello/build.gradle @@ -19,4 +19,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux/java/method/build.gradle b/reactive/webflux/java/method/build.gradle index 59149a4..274f493 100644 --- a/reactive/webflux/java/method/build.gradle +++ b/reactive/webflux/java/method/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/reactive/webflux/java/oauth2/login/build.gradle b/reactive/webflux/java/oauth2/login/build.gradle index 77ce222..1d68334 100644 --- a/reactive/webflux/java/oauth2/login/build.gradle +++ b/reactive/webflux/java/oauth2/login/build.gradle @@ -24,4 +24,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/reactive/webflux/java/oauth2/resource-server/build.gradle b/reactive/webflux/java/oauth2/resource-server/build.gradle index 323671c..f3b87ee 100644 --- a/reactive/webflux/java/oauth2/resource-server/build.gradle +++ b/reactive/webflux/java/oauth2/resource-server/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/reactive/webflux/java/oauth2/webclient/build.gradle b/reactive/webflux/java/oauth2/webclient/build.gradle index 6fd4302..5dd6557 100644 --- a/reactive/webflux/java/oauth2/webclient/build.gradle +++ b/reactive/webflux/java/oauth2/webclient/build.gradle @@ -24,4 +24,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/java-configuration/aspectj/build.gradle b/servlet/java-configuration/aspectj/build.gradle index 8fa35b2..0c3a5ce 100644 --- a/servlet/java-configuration/aspectj/build.gradle +++ b/servlet/java-configuration/aspectj/build.gradle @@ -31,6 +31,7 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/authentication/preauth/build.gradle b/servlet/java-configuration/authentication/preauth/build.gradle index 0204ed2..da70ce5 100644 --- a/servlet/java-configuration/authentication/preauth/build.gradle +++ b/servlet/java-configuration/authentication/preauth/build.gradle @@ -34,4 +34,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/authentication/remember-me/build.gradle b/servlet/java-configuration/authentication/remember-me/build.gradle index d62bab3..72f6d29 100644 --- a/servlet/java-configuration/authentication/remember-me/build.gradle +++ b/servlet/java-configuration/authentication/remember-me/build.gradle @@ -34,4 +34,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/java-configuration/authentication/username-password/form/build.gradle b/servlet/java-configuration/authentication/username-password/form/build.gradle index 0204ed2..da70ce5 100644 --- a/servlet/java-configuration/authentication/username-password/form/build.gradle +++ b/servlet/java-configuration/authentication/username-password/form/build.gradle @@ -34,4 +34,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle index 0204ed2..da70ce5 100644 --- a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle +++ b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle @@ -34,4 +34,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle index a7bf0a4..784397c 100644 --- a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle +++ b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle @@ -36,4 +36,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/authentication/username-password/ldap/build.gradle b/servlet/java-configuration/authentication/username-password/ldap/build.gradle index ee984e6..8f259a5 100644 --- a/servlet/java-configuration/authentication/username-password/ldap/build.gradle +++ b/servlet/java-configuration/authentication/username-password/ldap/build.gradle @@ -37,4 +37,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/authentication/x509/build.gradle b/servlet/java-configuration/authentication/x509/build.gradle index 0e0dc5f..64e648e 100644 --- a/servlet/java-configuration/authentication/x509/build.gradle +++ b/servlet/java-configuration/authentication/x509/build.gradle @@ -32,4 +32,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/data/build.gradle b/servlet/java-configuration/data/build.gradle index 5e92657..74166e9 100644 --- a/servlet/java-configuration/data/build.gradle +++ b/servlet/java-configuration/data/build.gradle @@ -34,4 +34,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/hello-mvc-security/build.gradle b/servlet/java-configuration/hello-mvc-security/build.gradle index 0204ed2..da70ce5 100644 --- a/servlet/java-configuration/hello-mvc-security/build.gradle +++ b/servlet/java-configuration/hello-mvc-security/build.gradle @@ -34,4 +34,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/hello-security-explicit/build.gradle b/servlet/java-configuration/hello-security-explicit/build.gradle index 31dc731..167e521 100644 --- a/servlet/java-configuration/hello-security-explicit/build.gradle +++ b/servlet/java-configuration/hello-security-explicit/build.gradle @@ -37,4 +37,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/hello-security/build.gradle b/servlet/java-configuration/hello-security/build.gradle index 31dc731..167e521 100644 --- a/servlet/java-configuration/hello-security/build.gradle +++ b/servlet/java-configuration/hello-security/build.gradle @@ -37,4 +37,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/max-sessions/build.gradle b/servlet/java-configuration/max-sessions/build.gradle index 0204ed2..da70ce5 100644 --- a/servlet/java-configuration/max-sessions/build.gradle +++ b/servlet/java-configuration/max-sessions/build.gradle @@ -34,4 +34,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/java-configuration/saml2/login/build.gradle b/servlet/java-configuration/saml2/login/build.gradle index 1dbb9db..8d9bbaf 100644 --- a/servlet/java-configuration/saml2/login/build.gradle +++ b/servlet/java-configuration/saml2/login/build.gradle @@ -63,4 +63,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle b/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle index e7eb1fe..b502b28 100644 --- a/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle +++ b/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle @@ -24,4 +24,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle index ac4c3dd..2796d64 100644 --- a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle +++ b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle @@ -20,4 +20,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/hello-security-explicit/build.gradle b/servlet/spring-boot/java/hello-security-explicit/build.gradle index 0d9c1ba..74b173d 100644 --- a/servlet/spring-boot/java/hello-security-explicit/build.gradle +++ b/servlet/spring-boot/java/hello-security-explicit/build.gradle @@ -23,4 +23,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/hello-security/build.gradle b/servlet/spring-boot/java/hello-security/build.gradle index 662dd84..e2db7a5 100644 --- a/servlet/spring-boot/java/hello-security/build.gradle +++ b/servlet/spring-boot/java/hello-security/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/hello/build.gradle b/servlet/spring-boot/java/hello/build.gradle index 2bd2f7a..6c50f75 100644 --- a/servlet/spring-boot/java/hello/build.gradle +++ b/servlet/spring-boot/java/hello/build.gradle @@ -19,4 +19,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/jwt/login/build.gradle b/servlet/spring-boot/java/jwt/login/build.gradle index 87e0599..e5a1bc1 100644 --- a/servlet/spring-boot/java/jwt/login/build.gradle +++ b/servlet/spring-boot/java/jwt/login/build.gradle @@ -19,4 +19,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/ldap/build.gradle b/servlet/spring-boot/java/ldap/build.gradle index 712f303..e59eec9 100644 --- a/servlet/spring-boot/java/ldap/build.gradle +++ b/servlet/spring-boot/java/ldap/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle index 37e1766..5926c4d 100644 --- a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle +++ b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle @@ -22,4 +22,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/oauth2/login/build.gradle b/servlet/spring-boot/java/oauth2/login/build.gradle index da06a71..c4cc1c5 100644 --- a/servlet/spring-boot/java/oauth2/login/build.gradle +++ b/servlet/spring-boot/java/oauth2/login/build.gradle @@ -24,4 +24,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle index 229e679..93f7b42 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle @@ -21,4 +21,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle index 946d72d..7418de1 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle @@ -37,4 +37,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle index 7ce49e7..1fadca1 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle @@ -38,4 +38,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle index 78a71b0..5e41753 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle @@ -38,4 +38,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle index 946d72d..7418de1 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle @@ -37,4 +37,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/spring-boot/java/oauth2/webclient/build.gradle b/servlet/spring-boot/java/oauth2/webclient/build.gradle index e4a399f..0cc2cda 100644 --- a/servlet/spring-boot/java/oauth2/webclient/build.gradle +++ b/servlet/spring-boot/java/oauth2/webclient/build.gradle @@ -42,4 +42,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle index 1da6b4a..14b81f8 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle +++ b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle @@ -30,4 +30,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/spring-boot/java/saml2/login/build.gradle b/servlet/spring-boot/java/saml2/login/build.gradle index 1da6b4a..14b81f8 100644 --- a/servlet/spring-boot/java/saml2/login/build.gradle +++ b/servlet/spring-boot/java/saml2/login/build.gradle @@ -30,4 +30,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle index 83fb5f4..2688de9 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle @@ -30,4 +30,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } \ No newline at end of file diff --git a/servlet/xml/java/contacts/build.gradle b/servlet/xml/java/contacts/build.gradle index bda493d..0218bff 100644 --- a/servlet/xml/java/contacts/build.gradle +++ b/servlet/xml/java/contacts/build.gradle @@ -50,4 +50,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/xml/java/dms/build.gradle b/servlet/xml/java/dms/build.gradle index 0015ff1..37a6e57 100644 --- a/servlet/xml/java/dms/build.gradle +++ b/servlet/xml/java/dms/build.gradle @@ -38,4 +38,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/xml/java/helloworld/build.gradle b/servlet/xml/java/helloworld/build.gradle index c0fcb95..1c9e135 100644 --- a/servlet/xml/java/helloworld/build.gradle +++ b/servlet/xml/java/helloworld/build.gradle @@ -35,4 +35,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } diff --git a/servlet/xml/java/preauth/build.gradle b/servlet/xml/java/preauth/build.gradle index 629b78e..edd0795 100644 --- a/servlet/xml/java/preauth/build.gradle +++ b/servlet/xml/java/preauth/build.gradle @@ -29,4 +29,5 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() + outputs.upToDateWhen { false } } From 48e440150721afd9cc0fe7e30f7549bf8b5b4889 Mon Sep 17 00:00:00 2001 From: Eleftheria Stein Date: Mon, 13 Dec 2021 17:36:14 +0100 Subject: [PATCH 07/35] Temporarily disable tests on SAML2 samples Issue gh-55 --- settings.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/settings.gradle b/settings.gradle index 2231310..d690f20 100644 --- a/settings.gradle +++ b/settings.gradle @@ -39,7 +39,7 @@ include ":servlet:java-configuration:hello-mvc-security" include ":servlet:java-configuration:hello-security" include ":servlet:java-configuration:hello-security-explicit" include ":servlet:java-configuration:max-sessions" -include ":servlet:java-configuration:saml2:login" +//include ":servlet:java-configuration:saml2:login" include ":servlet:spring-boot:java:authentication:username-password:user-details-service:custom-user" include ":servlet:spring-boot:java:authentication:username-password:mfa" include ":servlet:spring-boot:java:hello" @@ -55,9 +55,9 @@ include ":servlet:spring-boot:java:oauth2:resource-server:multi-tenancy" include ":servlet:spring-boot:java:oauth2:resource-server:opaque" include ":servlet:spring-boot:java:oauth2:resource-server:static" include ":servlet:spring-boot:java:oauth2:webclient" -include ":servlet:spring-boot:java:saml2:login" -include ":servlet:spring-boot:java:saml2:login-single-tenant" -include ":servlet:spring-boot:java:saml2:refreshable-metadata" +//include ":servlet:spring-boot:java:saml2:login" +//include ":servlet:spring-boot:java:saml2:login-single-tenant" +//include ":servlet:spring-boot:java:saml2:refreshable-metadata" include ":servlet:spring-boot:kotlin:hello-security" include ":servlet:xml:java:helloworld" include ":servlet:xml:java:preauth" From 0e4e7c73731ed7818318e828b2b94fffef2275af Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Wed, 15 Dec 2021 09:22:31 -0300 Subject: [PATCH 08/35] Remove remaining usage of WebSecurityConfigurerAdapter --- .../java/example/SecurityConfiguration.java | 23 +++++++------- .../src/main/java/example/RestConfig.java | 31 ++++++++++--------- .../example/web/HelloControllerTests.java | 3 ++ .../example/OAuth2LoginApplicationTests.java | 27 ++++++++-------- ...h2ResourceServerSecurityConfiguration.java | 21 +++++++------ .../OAuth2ResourceServerControllerTests.java | 2 ++ ...h2ResourceServerSecurityConfiguration.java | 19 ++++++------ ...h2ResourceServerSecurityConfiguration.java | 30 +++++++++--------- .../OAuth2ResourceServerControllerTests.java | 2 ++ .../java/example/SecurityConfiguration.java | 23 +++++++------- .../security/samples/config/SecurityConfig.kt | 18 ++++++----- 11 files changed, 108 insertions(+), 91 deletions(-) diff --git a/servlet/spring-boot/java/hello-security-explicit/src/main/java/example/SecurityConfiguration.java b/servlet/spring-boot/java/hello-security-explicit/src/main/java/example/SecurityConfiguration.java index d8c65b8..e5475ae 100644 --- a/servlet/spring-boot/java/hello-security-explicit/src/main/java/example/SecurityConfiguration.java +++ b/servlet/spring-boot/java/hello-security-explicit/src/main/java/example/SecurityConfiguration.java @@ -20,10 +20,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @@ -34,19 +34,20 @@ import static org.springframework.security.config.Customizer.withDefaults; */ @Configuration @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Override - // @formatter:off - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .httpBasic(withDefaults()) - .formLogin(withDefaults()); + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .httpBasic(withDefaults()) + .formLogin(withDefaults()); + // @formatter:on + return http.build(); } - // @formatter:on // @formatter:off @Bean diff --git a/servlet/spring-boot/java/jwt/login/src/main/java/example/RestConfig.java b/servlet/spring-boot/java/jwt/login/src/main/java/example/RestConfig.java index 7f7de48..478e14e 100644 --- a/servlet/spring-boot/java/jwt/login/src/main/java/example/RestConfig.java +++ b/servlet/spring-boot/java/jwt/login/src/main/java/example/RestConfig.java @@ -31,7 +31,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.User; @@ -43,6 +42,7 @@ import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; /** * Security configuration for the main application. @@ -50,7 +50,7 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; * @author Josh Cummings */ @Configuration -public class RestConfig extends WebSecurityConfigurerAdapter { +public class RestConfig { @Value("${jwt.public.key}") RSAPublicKey key; @@ -58,22 +58,23 @@ public class RestConfig extends WebSecurityConfigurerAdapter { @Value("${jwt.private.key}") RSAPrivateKey priv; - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .csrf((csrf) -> csrf.ignoringAntMatchers("/token")) - .httpBasic(Customizer.withDefaults()) - .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) - .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .exceptionHandling((exceptions) -> exceptions - .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()) - .accessDeniedHandler(new BearerTokenAccessDeniedHandler()) - ); + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .csrf((csrf) -> csrf.ignoringAntMatchers("/token")) + .httpBasic(Customizer.withDefaults()) + .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) + .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .exceptionHandling((exceptions) -> exceptions + .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()) + .accessDeniedHandler(new BearerTokenAccessDeniedHandler()) + ); // @formatter:on + return http.build(); } @Bean diff --git a/servlet/spring-boot/java/jwt/login/src/test/java/example/web/HelloControllerTests.java b/servlet/spring-boot/java/jwt/login/src/test/java/example/web/HelloControllerTests.java index 74919f1..8e3ba1d 100644 --- a/servlet/spring-boot/java/jwt/login/src/test/java/example/web/HelloControllerTests.java +++ b/servlet/spring-boot/java/jwt/login/src/test/java/example/web/HelloControllerTests.java @@ -16,10 +16,12 @@ package example.web; +import example.RestConfig; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.context.annotation.Import; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -35,6 +37,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @author Josh Cummings */ @WebMvcTest({ HelloController.class, TokenController.class }) +@Import(RestConfig.class) public class HelloControllerTests { @Autowired diff --git a/servlet/spring-boot/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java b/servlet/spring-boot/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java index ddbfe89..5f44272 100644 --- a/servlet/spring-boot/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java +++ b/servlet/spring-boot/java/oauth2/login/src/integTest/java/example/OAuth2LoginApplicationTests.java @@ -44,7 +44,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.http.HttpStatus; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; @@ -63,6 +62,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @@ -328,21 +328,22 @@ public class OAuth2LoginApplicationTests { } @EnableWebSecurity - public static class SecurityTestConfig extends WebSecurityConfigurerAdapter { + public static class SecurityTestConfig { - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .oauth2Login((oauth2) -> oauth2 - .tokenEndpoint((token) -> token.accessTokenResponseClient(mockAccessTokenResponseClient())) - .userInfoEndpoint((userInfo) -> userInfo.userService(mockUserService())) - ); + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .oauth2Login((oauth2) -> oauth2 + .tokenEndpoint((token) -> token.accessTokenResponseClient(mockAccessTokenResponseClient())) + .userInfoEndpoint((userInfo) -> userInfo.userService(mockUserService())) + ); + // @formatter:on + return http.build(); } - // @formatter:on private OAuth2AccessTokenResponseClient mockAccessTokenResponseClient() { OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("access-token-1234") diff --git a/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java b/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java index 973752f..c0dc343 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java +++ b/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java @@ -20,10 +20,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.web.SecurityFilterChain; /** * OAuth resource configuration. @@ -31,22 +31,23 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; * @author Josh Cummings */ @EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { +public class OAuth2ResourceServerSecurityConfiguration { @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}") String jwkSetUri; - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .antMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read") - .antMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write") - .anyRequest().authenticated() - ) - .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); + .authorizeHttpRequests((authorize) -> authorize + .antMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read") + .antMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write") + .anyRequest().authenticated() + ) + .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); // @formatter:on + return http.build(); } @Bean diff --git a/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/test/java/example/OAuth2ResourceServerControllerTests.java b/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/test/java/example/OAuth2ResourceServerControllerTests.java index be3e33e..4e0fd33 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/test/java/example/OAuth2ResourceServerControllerTests.java +++ b/servlet/spring-boot/java/oauth2/resource-server/hello-security/src/test/java/example/OAuth2ResourceServerControllerTests.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.context.annotation.Import; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.test.web.servlet.MockMvc; @@ -36,6 +37,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * */ @WebMvcTest(OAuth2ResourceServerController.class) +@Import(OAuth2ResourceServerSecurityConfiguration.class) public class OAuth2ResourceServerControllerTests { @Autowired diff --git a/servlet/spring-boot/java/oauth2/resource-server/jwe/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java b/servlet/spring-boot/java/oauth2/resource-server/jwe/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java index 5ce1aa2..effb9d3 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/jwe/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java +++ b/servlet/spring-boot/java/oauth2/resource-server/jwe/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java @@ -42,9 +42,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @@ -54,7 +54,7 @@ import static org.springframework.security.config.Customizer.withDefaults; * @author Josh Cummings */ @EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { +public class OAuth2ResourceServerSecurityConfiguration { private final JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256; @@ -68,16 +68,17 @@ public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfig @Value("${sample.jwe-key-value}") RSAPrivateKey key; - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .antMatchers("/message/**").hasAuthority("SCOPE_message:read") - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults())); + .authorizeHttpRequests((authorize) -> authorize + .antMatchers("/message/**").hasAuthority("SCOPE_message:read") + .anyRequest().authenticated() + ) + .oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults())); // @formatter:on + return http.build(); } @Bean diff --git a/servlet/spring-boot/java/oauth2/resource-server/opaque/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java b/servlet/spring-boot/java/oauth2/resource-server/opaque/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java index b3cee2f..baef8ca 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/opaque/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java +++ b/servlet/spring-boot/java/oauth2/resource-server/opaque/src/main/java/example/OAuth2ResourceServerSecurityConfiguration.java @@ -16,10 +16,11 @@ package example; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.SecurityFilterChain; /** * OAuth2 Security Configuration. @@ -27,7 +28,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur * @author Josh Cummings */ @EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { +public class OAuth2ResourceServerSecurityConfiguration { @Value("${spring.security.oauth2.resourceserver.opaque.introspection-uri}") String introspectionUri; @@ -38,22 +39,23 @@ public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfig @Value("${spring.security.oauth2.resourceserver.opaque.introspection-client-secret}") String clientSecret; - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .mvcMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read") - .mvcMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write") - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2) -> oauth2 - .opaqueToken((opaque) -> opaque - .introspectionUri(this.introspectionUri) - .introspectionClientCredentials(this.clientId, this.clientSecret) + .authorizeHttpRequests((authorize) -> authorize + .mvcMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read") + .mvcMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write") + .anyRequest().authenticated() ) - ); + .oauth2ResourceServer((oauth2) -> oauth2 + .opaqueToken((opaque) -> opaque + .introspectionUri(this.introspectionUri) + .introspectionClientCredentials(this.clientId, this.clientSecret) + ) + ); // @formatter:on + return http.build(); } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/opaque/src/test/java/example/OAuth2ResourceServerControllerTests.java b/servlet/spring-boot/java/oauth2/resource-server/opaque/src/test/java/example/OAuth2ResourceServerControllerTests.java index 037932e..f11f28f 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/opaque/src/test/java/example/OAuth2ResourceServerControllerTests.java +++ b/servlet/spring-boot/java/oauth2/resource-server/opaque/src/test/java/example/OAuth2ResourceServerControllerTests.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.context.annotation.Import; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; import org.springframework.test.web.servlet.MockMvc; @@ -36,6 +37,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @since 5.3 */ @WebMvcTest(OAuth2ResourceServerController.class) +@Import(OAuth2ResourceServerSecurityConfiguration.class) public class OAuth2ResourceServerControllerTests { @Autowired diff --git a/servlet/spring-boot/java/oauth2/webclient/src/main/java/example/SecurityConfiguration.java b/servlet/spring-boot/java/oauth2/webclient/src/main/java/example/SecurityConfiguration.java index 238fc89..40e8145 100644 --- a/servlet/spring-boot/java/oauth2/webclient/src/main/java/example/SecurityConfiguration.java +++ b/servlet/spring-boot/java/oauth2/webclient/src/main/java/example/SecurityConfiguration.java @@ -18,11 +18,11 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @@ -32,20 +32,21 @@ import static org.springframework.security.config.Customizer.withDefaults; * @author Joe Grandja */ @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // @formatter:off http - .authorizeHttpRequests((authorize) -> authorize - .mvcMatchers("/", "/public/**").permitAll() - .anyRequest().authenticated() - ) - .formLogin(withDefaults()) - .oauth2Login(withDefaults()) - .oauth2Client(withDefaults()); + .authorizeHttpRequests((authorize) -> authorize + .mvcMatchers("/", "/public/**").permitAll() + .anyRequest().authenticated() + ) + .formLogin(withDefaults()) + .oauth2Login(withDefaults()) + .oauth2Client(withDefaults()); // @formatter:on + return http.build(); } @Bean diff --git a/servlet/spring-boot/kotlin/hello-security/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt b/servlet/spring-boot/kotlin/hello-security/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt index 043f29e..296d19b 100644 --- a/servlet/spring-boot/kotlin/hello-security/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt +++ b/servlet/spring-boot/kotlin/hello-security/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt @@ -19,32 +19,34 @@ package org.springframework.security.samples.config import org.springframework.context.annotation.Bean import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.config.web.servlet.invoke import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.security.provisioning.InMemoryUserDetailsManager +import org.springframework.security.web.SecurityFilterChain /** * @author Eleftheria Stein */ @EnableWebSecurity -class SecurityConfig : WebSecurityConfigurerAdapter() { +class SecurityConfig { - override fun configure(http: HttpSecurity) { - http { + @Bean + fun filterChain(http: HttpSecurity): SecurityFilterChain { + http { authorizeRequests { authorize("/css/**", permitAll) authorize("/user/**", hasAuthority("ROLE_USER")) } - formLogin { - loginPage = "/log-in" - } + formLogin { + loginPage = "/log-in" + } } + return http.build() } @Bean - public override fun userDetailsService(): UserDetailsService { + fun userDetailsService(): UserDetailsService { val userDetails = User.withDefaultPasswordEncoder() .username("user") .password("password") From 73fbaa99502d527171fc2402092b9aa97e2a1539 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Fri, 14 Jan 2022 13:39:45 -0600 Subject: [PATCH 09/35] Add milestone repository Closes gh-58 --- reactive/rsocket/hello-security/build.gradle | 1 + reactive/webflux-fn/hello-security/build.gradle | 1 + reactive/webflux-fn/hello/build.gradle | 1 + .../java/authentication/username-password/form/build.gradle | 1 + reactive/webflux/java/authentication/x509/build.gradle | 1 + reactive/webflux/java/hello-security-explicit/build.gradle | 1 + reactive/webflux/java/hello-security/build.gradle | 1 + reactive/webflux/java/hello/build.gradle | 1 + reactive/webflux/java/method/build.gradle | 1 + reactive/webflux/java/oauth2/login/build.gradle | 1 + reactive/webflux/java/oauth2/resource-server/build.gradle | 1 + reactive/webflux/java/oauth2/webclient/build.gradle | 1 + servlet/java-configuration/aspectj/build.gradle | 1 + servlet/java-configuration/authentication/preauth/build.gradle | 1 + .../java-configuration/authentication/remember-me/build.gradle | 1 + .../authentication/username-password/form/build.gradle | 1 + .../authentication/username-password/in-memory/build.gradle | 1 + .../authentication/username-password/jdbc/build.gradle | 1 + .../authentication/username-password/ldap/build.gradle | 1 + servlet/java-configuration/authentication/x509/build.gradle | 1 + servlet/java-configuration/data/build.gradle | 1 + servlet/java-configuration/hello-mvc-security/build.gradle | 1 + servlet/java-configuration/hello-security-explicit/build.gradle | 1 + servlet/java-configuration/hello-security/build.gradle | 1 + servlet/java-configuration/max-sessions/build.gradle | 1 + servlet/java-configuration/saml2/login/build.gradle | 1 + .../java/authentication/username-password/mfa/build.gradle | 1 + .../user-details-service/custom-user/build.gradle | 1 + servlet/spring-boot/java/hello-security-explicit/build.gradle | 1 + servlet/spring-boot/java/hello-security/build.gradle | 1 + servlet/spring-boot/java/hello/build.gradle | 1 + servlet/spring-boot/java/jwt/login/build.gradle | 1 + servlet/spring-boot/java/ldap/build.gradle | 1 + .../spring-boot/java/oauth2/authorization-server/build.gradle | 1 + servlet/spring-boot/java/oauth2/login/build.gradle | 1 + .../java/oauth2/resource-server/hello-security/build.gradle | 1 + servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle | 1 + .../java/oauth2/resource-server/multi-tenancy/build.gradle | 1 + .../spring-boot/java/oauth2/resource-server/opaque/build.gradle | 1 + .../spring-boot/java/oauth2/resource-server/static/build.gradle | 1 + servlet/spring-boot/java/oauth2/webclient/build.gradle | 1 + servlet/spring-boot/java/saml2/login-single-tenant/build.gradle | 1 + servlet/spring-boot/java/saml2/login/build.gradle | 1 + servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle | 1 + servlet/xml/java/contacts/build.gradle | 1 + servlet/xml/java/dms/build.gradle | 1 + servlet/xml/java/helloworld/build.gradle | 1 + servlet/xml/java/preauth/build.gradle | 1 + 48 files changed, 48 insertions(+) diff --git a/reactive/rsocket/hello-security/build.gradle b/reactive/rsocket/hello-security/build.gradle index c9f5bea..a059ebd 100644 --- a/reactive/rsocket/hello-security/build.gradle +++ b/reactive/rsocket/hello-security/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux-fn/hello-security/build.gradle b/reactive/webflux-fn/hello-security/build.gradle index 079ebfa..ad05231 100644 --- a/reactive/webflux-fn/hello-security/build.gradle +++ b/reactive/webflux-fn/hello-security/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux-fn/hello/build.gradle b/reactive/webflux-fn/hello/build.gradle index 48787e1..0c20136 100644 --- a/reactive/webflux-fn/hello/build.gradle +++ b/reactive/webflux-fn/hello/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/authentication/username-password/form/build.gradle b/reactive/webflux/java/authentication/username-password/form/build.gradle index 3a87b5a..1c7b8ab 100644 --- a/reactive/webflux/java/authentication/username-password/form/build.gradle +++ b/reactive/webflux/java/authentication/username-password/form/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/authentication/x509/build.gradle b/reactive/webflux/java/authentication/x509/build.gradle index 7724f7f..d046093 100644 --- a/reactive/webflux/java/authentication/x509/build.gradle +++ b/reactive/webflux/java/authentication/x509/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/hello-security-explicit/build.gradle b/reactive/webflux/java/hello-security-explicit/build.gradle index 1373932..414e2ab 100644 --- a/reactive/webflux/java/hello-security-explicit/build.gradle +++ b/reactive/webflux/java/hello-security-explicit/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/hello-security/build.gradle b/reactive/webflux/java/hello-security/build.gradle index 643a9e1..aaef98f 100644 --- a/reactive/webflux/java/hello-security/build.gradle +++ b/reactive/webflux/java/hello-security/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/hello/build.gradle b/reactive/webflux/java/hello/build.gradle index 48787e1..0c20136 100644 --- a/reactive/webflux/java/hello/build.gradle +++ b/reactive/webflux/java/hello/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/method/build.gradle b/reactive/webflux/java/method/build.gradle index 274f493..0f1c849 100644 --- a/reactive/webflux/java/method/build.gradle +++ b/reactive/webflux/java/method/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/oauth2/login/build.gradle b/reactive/webflux/java/oauth2/login/build.gradle index 1d68334..8baf4b3 100644 --- a/reactive/webflux/java/oauth2/login/build.gradle +++ b/reactive/webflux/java/oauth2/login/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/oauth2/resource-server/build.gradle b/reactive/webflux/java/oauth2/resource-server/build.gradle index f3b87ee..f14ab31 100644 --- a/reactive/webflux/java/oauth2/resource-server/build.gradle +++ b/reactive/webflux/java/oauth2/resource-server/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/oauth2/webclient/build.gradle b/reactive/webflux/java/oauth2/webclient/build.gradle index 5dd6557..03051e2 100644 --- a/reactive/webflux/java/oauth2/webclient/build.gradle +++ b/reactive/webflux/java/oauth2/webclient/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/aspectj/build.gradle b/servlet/java-configuration/aspectj/build.gradle index 0c3a5ce..05a0175 100644 --- a/servlet/java-configuration/aspectj/build.gradle +++ b/servlet/java-configuration/aspectj/build.gradle @@ -5,6 +5,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/preauth/build.gradle b/servlet/java-configuration/authentication/preauth/build.gradle index da70ce5..c71c7b2 100644 --- a/servlet/java-configuration/authentication/preauth/build.gradle +++ b/servlet/java-configuration/authentication/preauth/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/remember-me/build.gradle b/servlet/java-configuration/authentication/remember-me/build.gradle index 72f6d29..494e8c3 100644 --- a/servlet/java-configuration/authentication/remember-me/build.gradle +++ b/servlet/java-configuration/authentication/remember-me/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/form/build.gradle b/servlet/java-configuration/authentication/username-password/form/build.gradle index da70ce5..c71c7b2 100644 --- a/servlet/java-configuration/authentication/username-password/form/build.gradle +++ b/servlet/java-configuration/authentication/username-password/form/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle index da70ce5..c71c7b2 100644 --- a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle +++ b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle index 784397c..c7830bd 100644 --- a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle +++ b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/ldap/build.gradle b/servlet/java-configuration/authentication/username-password/ldap/build.gradle index 8f259a5..8d426d4 100644 --- a/servlet/java-configuration/authentication/username-password/ldap/build.gradle +++ b/servlet/java-configuration/authentication/username-password/ldap/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/x509/build.gradle b/servlet/java-configuration/authentication/x509/build.gradle index 64e648e..53401f2 100644 --- a/servlet/java-configuration/authentication/x509/build.gradle +++ b/servlet/java-configuration/authentication/x509/build.gradle @@ -9,6 +9,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/data/build.gradle b/servlet/java-configuration/data/build.gradle index 74166e9..9b13bcc 100644 --- a/servlet/java-configuration/data/build.gradle +++ b/servlet/java-configuration/data/build.gradle @@ -5,6 +5,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/hello-mvc-security/build.gradle b/servlet/java-configuration/hello-mvc-security/build.gradle index da70ce5..c71c7b2 100644 --- a/servlet/java-configuration/hello-mvc-security/build.gradle +++ b/servlet/java-configuration/hello-mvc-security/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/hello-security-explicit/build.gradle b/servlet/java-configuration/hello-security-explicit/build.gradle index 167e521..a116554 100644 --- a/servlet/java-configuration/hello-security-explicit/build.gradle +++ b/servlet/java-configuration/hello-security-explicit/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/hello-security/build.gradle b/servlet/java-configuration/hello-security/build.gradle index 167e521..a116554 100644 --- a/servlet/java-configuration/hello-security/build.gradle +++ b/servlet/java-configuration/hello-security/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/max-sessions/build.gradle b/servlet/java-configuration/max-sessions/build.gradle index da70ce5..c71c7b2 100644 --- a/servlet/java-configuration/max-sessions/build.gradle +++ b/servlet/java-configuration/max-sessions/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/saml2/login/build.gradle b/servlet/java-configuration/saml2/login/build.gradle index 8d9bbaf..469e541 100644 --- a/servlet/java-configuration/saml2/login/build.gradle +++ b/servlet/java-configuration/saml2/login/build.gradle @@ -25,6 +25,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } } diff --git a/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle b/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle index b502b28..e31cf48 100644 --- a/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle +++ b/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle index 2796d64..cb31c44 100644 --- a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle +++ b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/hello-security-explicit/build.gradle b/servlet/spring-boot/java/hello-security-explicit/build.gradle index 74b173d..516bea3 100644 --- a/servlet/spring-boot/java/hello-security-explicit/build.gradle +++ b/servlet/spring-boot/java/hello-security-explicit/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/hello-security/build.gradle b/servlet/spring-boot/java/hello-security/build.gradle index e2db7a5..0dd6f9e 100644 --- a/servlet/spring-boot/java/hello-security/build.gradle +++ b/servlet/spring-boot/java/hello-security/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/hello/build.gradle b/servlet/spring-boot/java/hello/build.gradle index 6c50f75..c57d291 100644 --- a/servlet/spring-boot/java/hello/build.gradle +++ b/servlet/spring-boot/java/hello/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/jwt/login/build.gradle b/servlet/spring-boot/java/jwt/login/build.gradle index e5a1bc1..5b1e976 100644 --- a/servlet/spring-boot/java/jwt/login/build.gradle +++ b/servlet/spring-boot/java/jwt/login/build.gradle @@ -6,6 +6,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/ldap/build.gradle b/servlet/spring-boot/java/ldap/build.gradle index e59eec9..7ee8f9f 100644 --- a/servlet/spring-boot/java/ldap/build.gradle +++ b/servlet/spring-boot/java/ldap/build.gradle @@ -6,6 +6,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle index 5926c4d..77363bb 100644 --- a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle +++ b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/login/build.gradle b/servlet/spring-boot/java/oauth2/login/build.gradle index c4cc1c5..3af5d5b 100644 --- a/servlet/spring-boot/java/oauth2/login/build.gradle +++ b/servlet/spring-boot/java/oauth2/login/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle index 93f7b42..22700ab 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle index 7418de1..06d5e7b 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle @@ -23,6 +23,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle index 1fadca1..13b802c 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle @@ -23,6 +23,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle index 5e41753..c0ff1b4 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle @@ -23,6 +23,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle index 7418de1..06d5e7b 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle @@ -23,6 +23,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/webclient/build.gradle b/servlet/spring-boot/java/oauth2/webclient/build.gradle index 0cc2cda..28cd9a0 100644 --- a/servlet/spring-boot/java/oauth2/webclient/build.gradle +++ b/servlet/spring-boot/java/oauth2/webclient/build.gradle @@ -23,6 +23,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle index 14b81f8..305d20e 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle +++ b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } } diff --git a/servlet/spring-boot/java/saml2/login/build.gradle b/servlet/spring-boot/java/saml2/login/build.gradle index 14b81f8..305d20e 100644 --- a/servlet/spring-boot/java/saml2/login/build.gradle +++ b/servlet/spring-boot/java/saml2/login/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } } diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle index 2688de9..4c0427d 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle @@ -7,6 +7,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } } diff --git a/servlet/xml/java/contacts/build.gradle b/servlet/xml/java/contacts/build.gradle index 0218bff..6d83ebf 100644 --- a/servlet/xml/java/contacts/build.gradle +++ b/servlet/xml/java/contacts/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/xml/java/dms/build.gradle b/servlet/xml/java/dms/build.gradle index 37a6e57..f0232dd 100644 --- a/servlet/xml/java/dms/build.gradle +++ b/servlet/xml/java/dms/build.gradle @@ -5,6 +5,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/xml/java/helloworld/build.gradle b/servlet/xml/java/helloworld/build.gradle index 1c9e135..c3a2e05 100644 --- a/servlet/xml/java/helloworld/build.gradle +++ b/servlet/xml/java/helloworld/build.gradle @@ -9,6 +9,7 @@ apply from: "gradle/gretty.gradle" repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/xml/java/preauth/build.gradle b/servlet/xml/java/preauth/build.gradle index edd0795..b45b649 100644 --- a/servlet/xml/java/preauth/build.gradle +++ b/servlet/xml/java/preauth/build.gradle @@ -5,6 +5,7 @@ plugins { repositories { jcenter() + maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } From a19471b5109d8ee980f238f82f5c59386f563ed9 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg <5248162+sjohnr@users.noreply.github.com> Date: Thu, 20 Jan 2022 11:24:56 -0600 Subject: [PATCH 10/35] Update Spring Authorization Server to 0.2.1 --- .../spring-boot/java/oauth2/authorization-server/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle index 77363bb..0406027 100644 --- a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle +++ b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle @@ -13,7 +13,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.security:spring-security-oauth2-authorization-server:0.2.0' + implementation 'org.springframework.security:spring-security-oauth2-authorization-server:0.2.1' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' From 2ddf0a2fa9b7543ab893884cdfd255a3600b76be Mon Sep 17 00:00:00 2001 From: Eleftheria Stein Date: Mon, 31 Jan 2022 12:37:36 +0100 Subject: [PATCH 11/35] Update LDAP samples to use LdapBindAuthenticationManagerFactory Closes gh-61 --- .../java/example/SecurityConfiguration.java | 40 +++++++----------- .../src/main/java/example/SecurityConfig.java | 42 +++++++------------ 2 files changed, 29 insertions(+), 53 deletions(-) diff --git a/servlet/java-configuration/authentication/username-password/ldap/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/authentication/username-password/ldap/src/main/java/example/SecurityConfiguration.java index 32840f1..8d48f1e 100644 --- a/servlet/java-configuration/authentication/username-password/ldap/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/authentication/username-password/ldap/src/main/java/example/SecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,39 +17,29 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.ldap.core.support.BaseLdapPathContextSource; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.ldap.DefaultSpringSecurityContextSource; -import org.springframework.security.ldap.authentication.BindAuthenticator; -import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; -import org.springframework.security.ldap.authentication.LdapAuthenticator; -import org.springframework.security.ldap.server.UnboundIdContainer; +import org.springframework.security.config.ldap.EmbeddedLdapServerContextSourceFactoryBean; +import org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory; +import org.springframework.security.ldap.userdetails.PersonContextMapper; @EnableWebSecurity public class SecurityConfiguration { @Bean - UnboundIdContainer ldapContainer() { - UnboundIdContainer result = new UnboundIdContainer("dc=springframework,dc=org", "classpath:users.ldif"); - result.setPort(0); - return result; + public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { + EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean = EmbeddedLdapServerContextSourceFactoryBean + .fromEmbeddedLdapServer(); + contextSourceFactoryBean.setPort(0); + return contextSourceFactoryBean; } @Bean - DefaultSpringSecurityContextSource contextSource(UnboundIdContainer container) { - return new DefaultSpringSecurityContextSource( - "ldap://localhost:" + container.getPort() + "/dc=springframework,dc=org"); - } - - @Bean - BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) { - BindAuthenticator authenticator = new BindAuthenticator(contextSource); - authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" }); - return authenticator; - } - - @Bean - LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) { - return new LdapAuthenticationProvider(authenticator); + AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { + LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); + factory.setUserDnPatterns("uid={0},ou=people"); + factory.setUserDetailsContextMapper(new PersonContextMapper()); + return factory.createAuthenticationManager(); } } diff --git a/servlet/spring-boot/java/ldap/src/main/java/example/SecurityConfig.java b/servlet/spring-boot/java/ldap/src/main/java/example/SecurityConfig.java index d8024ee..82be683 100644 --- a/servlet/spring-boot/java/ldap/src/main/java/example/SecurityConfig.java +++ b/servlet/spring-boot/java/ldap/src/main/java/example/SecurityConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +18,10 @@ package example; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.support.BaseLdapPathContextSource; -import org.springframework.security.ldap.DefaultSpringSecurityContextSource; -import org.springframework.security.ldap.authentication.BindAuthenticator; -import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; -import org.springframework.security.ldap.authentication.LdapAuthenticator; -import org.springframework.security.ldap.server.UnboundIdContainer; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.ldap.EmbeddedLdapServerContextSourceFactoryBean; +import org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory; import org.springframework.security.ldap.userdetails.PersonContextMapper; /** @@ -36,30 +33,19 @@ import org.springframework.security.ldap.userdetails.PersonContextMapper; public class SecurityConfig { @Bean - UnboundIdContainer ldapContainer() { - UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:users.ldif"); - container.setPort(0); - return container; + public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { + EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean = EmbeddedLdapServerContextSourceFactoryBean + .fromEmbeddedLdapServer(); + contextSourceFactoryBean.setPort(0); + return contextSourceFactoryBean; } @Bean - ContextSource contextSource(UnboundIdContainer container) { - int port = container.getPort(); - return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org"); - } - - @Bean - BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) { - BindAuthenticator authenticator = new BindAuthenticator(contextSource); - authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" }); - return authenticator; - } - - @Bean - LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) { - LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator); - provider.setUserDetailsContextMapper(new PersonContextMapper()); - return provider; + AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { + LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); + factory.setUserDnPatterns("uid={0},ou=people"); + factory.setUserDetailsContextMapper(new PersonContextMapper()); + return factory.createAuthenticationManager(); } } From 802311ac7018b79808e7847b427c47eebf4b1348 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 6 Jan 2022 11:41:54 -0300 Subject: [PATCH 12/35] SAML 2.0 Login & Logout XML Sample Issue gh-57 --- .../xml/java/saml2/login-logout/build.gradle | 45 + .../java/saml2/login-logout/gradle.properties | 1 + .../saml2/login-logout/gradle/gretty.gradle | 41 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58910 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + servlet/xml/java/saml2/login-logout/gradlew | 185 + .../xml/java/saml2/login-logout/gradlew.bat | 104 + .../main/java/example/IndexController.java | 43 + .../main/java/example/WebConfiguration.java | 84 + .../resources/credentials/idp-certificate.crt | 24 + .../resources/credentials/rp-certificate.crt | 16 + .../main/resources/credentials/rp-private.key | 16 + .../src/main/resources/logback.xml | 14 + .../src/main/webapp/META-INF/MANIFEST.MF | 2 + .../main/webapp/WEB-INF/spring-servlet.xml | 24 + .../main/webapp/WEB-INF/spring/security.xml | 53 + .../main/webapp/WEB-INF/templates/index.html | 60 + .../src/main/webapp/WEB-INF/web.xml | 41 + .../resources/css/bootstrap-responsive.css | 1092 +++ .../main/webapp/resources/css/bootstrap.css | 6039 +++++++++++++++++ .../src/main/webapp/resources/img/favicon.ico | Bin 0 -> 1150 bytes .../src/main/webapp/resources/img/logo.png | Bin 0 -> 1123 bytes settings.gradle | 1 + 23 files changed, 7890 insertions(+) create mode 100644 servlet/xml/java/saml2/login-logout/build.gradle create mode 100644 servlet/xml/java/saml2/login-logout/gradle.properties create mode 100644 servlet/xml/java/saml2/login-logout/gradle/gretty.gradle create mode 100644 servlet/xml/java/saml2/login-logout/gradle/wrapper/gradle-wrapper.jar create mode 100644 servlet/xml/java/saml2/login-logout/gradle/wrapper/gradle-wrapper.properties create mode 100755 servlet/xml/java/saml2/login-logout/gradlew create mode 100755 servlet/xml/java/saml2/login-logout/gradlew.bat create mode 100644 servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java create mode 100644 servlet/xml/java/saml2/login-logout/src/main/java/example/WebConfiguration.java create mode 100644 servlet/xml/java/saml2/login-logout/src/main/resources/credentials/idp-certificate.crt create mode 100644 servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-certificate.crt create mode 100644 servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-private.key create mode 100644 servlet/xml/java/saml2/login-logout/src/main/resources/logback.xml create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/META-INF/MANIFEST.MF create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring-servlet.xml create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/web.xml create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap-responsive.css create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap.css create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/resources/img/favicon.ico create mode 100644 servlet/xml/java/saml2/login-logout/src/main/webapp/resources/img/logo.png diff --git a/servlet/xml/java/saml2/login-logout/build.gradle b/servlet/xml/java/saml2/login-logout/build.gradle new file mode 100644 index 0000000..8f4f24c --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/build.gradle @@ -0,0 +1,45 @@ +plugins { + id "java" + id "org.gretty" version "3.0.6" + id "war" +} + +apply from: "gradle/gretty.gradle" + +repositories { + mavenCentral() + maven { url "https://repo.spring.io/snapshot" } + maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } +} + +dependencies { + constraints { + implementation "org.opensaml:opensaml-core:4.1.1" + implementation "org.opensaml:opensaml-saml-api:4.1.1" + implementation "org.opensaml:opensaml-saml-impl:4.1.1" + } + implementation platform("org.springframework:spring-framework-bom:5.3.13") + implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.junit:junit-bom:5.7.0") + + implementation "org.springframework.security:spring-security-config" + implementation "org.springframework.security:spring-security-web" + implementation "org.springframework.security:spring-security-saml2-service-provider" + implementation "org.springframework:spring-webmvc" + implementation "org.thymeleaf:thymeleaf-spring5:3.0.11.RELEASE" + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE' + + testImplementation "org.assertj:assertj-core:3.18.0" + testImplementation "org.springframework:spring-test" + testImplementation "org.springframework.security:spring-security-test" + testImplementation "org.junit.jupiter:junit-jupiter-api" + testImplementation 'net.sourceforge.htmlunit:htmlunit:2.49.1' + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + + +} + +tasks.withType(Test).configureEach { + useJUnitPlatform() + outputs.upToDateWhen { false } +} diff --git a/servlet/xml/java/saml2/login-logout/gradle.properties b/servlet/xml/java/saml2/login-logout/gradle.properties new file mode 100644 index 0000000..e170fd3 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/gradle.properties @@ -0,0 +1 @@ +version=5.7.0-SNAPSHOT diff --git a/servlet/xml/java/saml2/login-logout/gradle/gretty.gradle b/servlet/xml/java/saml2/login-logout/gradle/gretty.gradle new file mode 100644 index 0000000..6949bdf --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/gradle/gretty.gradle @@ -0,0 +1,41 @@ +gretty { + servletContainer = "tomcat9" + contextPath = "/" + fileLogEnabled = false + integrationTestTask = 'integrationTest' +} + +Task prepareAppServerForIntegrationTests = project.tasks.create('prepareAppServerForIntegrationTests') { + group = 'Verification' + description = 'Prepares the app server for integration tests' + doFirst { + project.gretty { + httpPort = -1 + } + } +} + +project.tasks.matching { it.name == "appBeforeIntegrationTest" }.all { task -> + task.dependsOn prepareAppServerForIntegrationTests +} + +project.tasks.matching { it.name == "integrationTest" }.all { + task -> task.doFirst { + def gretty = project.gretty + String host = project.gretty.host ?: 'localhost' + boolean isHttps = gretty.httpsEnabled + Integer httpPort = integrationTest.systemProperties['gretty.httpPort'] + Integer httpsPort = integrationTest.systemProperties['gretty.httpsPort'] + int port = isHttps ? httpsPort : httpPort + String contextPath = project.gretty.contextPath + String httpBaseUrl = "http://${host}:${httpPort}${contextPath}" + String httpsBaseUrl = "https://${host}:${httpsPort}${contextPath}" + String baseUrl = isHttps ? httpsBaseUrl : httpBaseUrl + integrationTest.systemProperty 'app.port', port + integrationTest.systemProperty 'app.httpPort', httpPort + integrationTest.systemProperty 'app.httpsPort', httpsPort + integrationTest.systemProperty 'app.baseURI', baseUrl + integrationTest.systemProperty 'app.httpBaseURI', httpBaseUrl + integrationTest.systemProperty 'app.httpsBaseURI', httpsBaseUrl + } +} \ No newline at end of file diff --git a/servlet/xml/java/saml2/login-logout/gradle/wrapper/gradle-wrapper.jar b/servlet/xml/java/saml2/login-logout/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..62d4c053550b91381bbd28b1afc82d634bf73a8a GIT binary patch literal 58910 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 literal 0 HcmV?d00001 diff --git a/servlet/xml/java/saml2/login-logout/gradle/wrapper/gradle-wrapper.properties b/servlet/xml/java/saml2/login-logout/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d2880ba --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/servlet/xml/java/saml2/login-logout/gradlew b/servlet/xml/java/saml2/login-logout/gradlew new file mode 100755 index 0000000..fbd7c51 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/servlet/xml/java/saml2/login-logout/gradlew.bat b/servlet/xml/java/saml2/login-logout/gradlew.bat new file mode 100755 index 0000000..a9f778a --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java b/servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java new file mode 100644 index 0000000..56c51f8 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example; + +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class IndexController { + + private final RelyingPartyRegistrationRepository repository; + + public IndexController(RelyingPartyRegistrationRepository repository) { + this.repository = repository; + } + + @GetMapping("/") + public String index(Model model, @AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) { + String emailAddress = principal.getFirstAttribute("emailAddress"); + model.addAttribute("emailAddress", emailAddress); + model.addAttribute("userAttributes", principal.getAttributes()); + return "index"; + } + +} diff --git a/servlet/xml/java/saml2/login-logout/src/main/java/example/WebConfiguration.java b/servlet/xml/java/saml2/login-logout/src/main/java/example/WebConfiguration.java new file mode 100644 index 0000000..c21d1e1 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/java/example/WebConfiguration.java @@ -0,0 +1,84 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example; + +import java.util.List; + +import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect; +import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring5.view.ThymeleafViewResolver; +import org.thymeleaf.templatemode.TemplateMode; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@EnableWebMvc +public class WebConfiguration implements WebMvcConfigurer, ApplicationContextAware { + + private ApplicationContext context; + + @Override + public void addArgumentResolvers(List resolvers) { + AuthenticationPrincipalArgumentResolver principalArgumentResolver = new AuthenticationPrincipalArgumentResolver(); + principalArgumentResolver + .setBeanResolver(new BeanFactoryResolver(this.context.getAutowireCapableBeanFactory())); + resolvers.add(principalArgumentResolver); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + + @Bean + public SpringResourceTemplateResolver templateResolver() { + SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); + templateResolver.setApplicationContext(this.context); + templateResolver.setPrefix("/WEB-INF/templates/"); + templateResolver.setSuffix(".html"); + templateResolver.setTemplateMode(TemplateMode.HTML); + templateResolver.setCacheable(false); + return templateResolver; + } + + @Bean + public SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) { + SpringTemplateEngine templateEngine = new SpringTemplateEngine(); + templateEngine.setTemplateResolver(templateResolver); + templateEngine.setEnableSpringELCompiler(true); + templateEngine.addDialect(new SpringSecurityDialect()); + return templateEngine; + } + + @Bean + public ThymeleafViewResolver viewResolver(SpringTemplateEngine templateEngine) { + ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); + viewResolver.setTemplateEngine(templateEngine); + return viewResolver; + } + +} diff --git a/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/idp-certificate.crt b/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/idp-certificate.crt new file mode 100644 index 0000000..9c4ee07 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/idp-certificate.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD +VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD +VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX +c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw +aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ +BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa +BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD +DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr +QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62 +E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz +2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW +RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ +nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5 +cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph +iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5 +ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO +nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v +ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu +xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z +V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3 +lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk +-----END CERTIFICATE----- diff --git a/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-certificate.crt b/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-certificate.crt new file mode 100644 index 0000000..b907e2f --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-certificate.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC +VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG +A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD +DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1 +MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES +MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN +TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos +vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM ++U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG +y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi +XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+ +qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD +RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B +-----END CERTIFICATE----- diff --git a/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-private.key b/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-private.key new file mode 100644 index 0000000..73196e0 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/resources/credentials/rp-private.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE +VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK +cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6 +Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn +x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5 +wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd +vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY +8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX +oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx +EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0 +KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt +YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr +9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM +INrtuLp4YHbgk1mi +-----END PRIVATE KEY----- diff --git a/servlet/xml/java/saml2/login-logout/src/main/resources/logback.xml b/servlet/xml/java/saml2/login-logout/src/main/resources/logback.xml new file mode 100644 index 0000000..177a25d --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/META-INF/MANIFEST.MF b/servlet/xml/java/saml2/login-logout/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..58630c0 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring-servlet.xml b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring-servlet.xml new file mode 100644 index 0000000..2419598 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring-servlet.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml new file mode 100644 index 0000000..95b8ff4 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html new file mode 100644 index 0000000..dd65ffd --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html @@ -0,0 +1,60 @@ + + + + + + Spring Security - SAML 2.0 Login & Logout + + + + + +
+ +
+
+

SAML 2.0 Login & Single Logout with Spring Security

+

You are successfully logged in as

+

You're email address is

+

All Your Attributes

+
+
+
+
+ +
Visit the SAML 2.0 Login & Logout documentation for more details.
+
+ + + diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/web.xml b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..5deecb1 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,41 @@ + + + + + + contextConfigLocation + + /WEB-INF/spring/*.xml + + + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + springSecurityFilterChain + /* + + + + org.springframework.web.context.ContextLoaderListener + + + + spring + org.springframework.web.servlet.DispatcherServlet + + + + spring + / + + diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap-responsive.css b/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap-responsive.css new file mode 100644 index 0000000..ba09bc8 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap-responsive.css @@ -0,0 +1,1092 @@ +/*! + * Bootstrap Responsive v2.2.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +@-ms-viewport { + width: device-width; +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.hidden { + display: none; + visibility: hidden; +} + +.visible-phone { + display: none !important; +} + +.visible-tablet { + display: none !important; +} + +.hidden-desktop { + display: none !important; +} + +.visible-desktop { + display: inherit !important; +} + +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} + +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} + +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.564102564102564%; + *margin-left: 2.5109110747408616%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.564102564102564%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.45299145299145%; + *width: 91.39979996362975%; + } + .row-fluid .span10 { + width: 82.90598290598291%; + *width: 82.8527914166212%; + } + .row-fluid .span9 { + width: 74.35897435897436%; + *width: 74.30578286961266%; + } + .row-fluid .span8 { + width: 65.81196581196582%; + *width: 65.75877432260411%; + } + .row-fluid .span7 { + width: 57.26495726495726%; + *width: 57.21176577559556%; + } + .row-fluid .span6 { + width: 48.717948717948715%; + *width: 48.664757228587014%; + } + .row-fluid .span5 { + width: 40.17094017094017%; + *width: 40.11774868157847%; + } + .row-fluid .span4 { + width: 31.623931623931625%; + *width: 31.570740134569924%; + } + .row-fluid .span3 { + width: 23.076923076923077%; + *width: 23.023731587561375%; + } + .row-fluid .span2 { + width: 14.52991452991453%; + *width: 14.476723040552828%; + } + .row-fluid .span1 { + width: 5.982905982905983%; + *width: 5.929714493544281%; + } + .row-fluid .offset12 { + margin-left: 105.12820512820512%; + *margin-left: 105.02182214948171%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256410257%; + *margin-left: 102.45771958537915%; + } + .row-fluid .offset11 { + margin-left: 96.58119658119658%; + *margin-left: 96.47481360247316%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709401709402%; + *margin-left: 93.91071103837061%; + } + .row-fluid .offset10 { + margin-left: 88.03418803418803%; + *margin-left: 87.92780505546462%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547008548%; + *margin-left: 85.36370249136206%; + } + .row-fluid .offset9 { + margin-left: 79.48717948717949%; + *margin-left: 79.38079650845607%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692307693%; + *margin-left: 76.81669394435352%; + } + .row-fluid .offset8 { + margin-left: 70.94017094017094%; + *margin-left: 70.83378796144753%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606837606839%; + *margin-left: 68.26968539734497%; + } + .row-fluid .offset7 { + margin-left: 62.393162393162385%; + *margin-left: 62.28677941443899%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905982905982%; + *margin-left: 59.72267685033642%; + } + .row-fluid .offset6 { + margin-left: 53.84615384615384%; + *margin-left: 53.739770867430444%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128205128%; + *margin-left: 51.175668303327875%; + } + .row-fluid .offset5 { + margin-left: 45.299145299145295%; + *margin-left: 45.1927623204219%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504273504273%; + *margin-left: 42.62865975631933%; + } + .row-fluid .offset4 { + margin-left: 36.75213675213675%; + *margin-left: 36.645753773413354%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803418803419%; + *margin-left: 34.081651209310785%; + } + .row-fluid .offset3 { + margin-left: 28.205128205128204%; + *margin-left: 28.0987452264048%; + } + .row-fluid .offset3:first-child { + margin-left: 25.641025641025642%; + *margin-left: 25.53464266230224%; + } + .row-fluid .offset2 { + margin-left: 19.65811965811966%; + *margin-left: 19.551736679396257%; + } + .row-fluid .offset2:first-child { + margin-left: 17.094017094017094%; + *margin-left: 16.98763411529369%; + } + .row-fluid .offset1 { + margin-left: 11.11111111111111%; + *margin-left: 11.004728132387708%; + } + .row-fluid .offset1:first-child { + margin-left: 8.547008547008547%; + *margin-left: 8.440625568285142%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} + +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.7624309392265194%; + *margin-left: 2.709239449864817%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.7624309392265194%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.43646408839778%; + *width: 91.38327259903608%; + } + .row-fluid .span10 { + width: 82.87292817679558%; + *width: 82.81973668743387%; + } + .row-fluid .span9 { + width: 74.30939226519337%; + *width: 74.25620077583166%; + } + .row-fluid .span8 { + width: 65.74585635359117%; + *width: 65.69266486422946%; + } + .row-fluid .span7 { + width: 57.18232044198895%; + *width: 57.12912895262725%; + } + .row-fluid .span6 { + width: 48.61878453038674%; + *width: 48.56559304102504%; + } + .row-fluid .span5 { + width: 40.05524861878453%; + *width: 40.00205712942283%; + } + .row-fluid .span4 { + width: 31.491712707182323%; + *width: 31.43852121782062%; + } + .row-fluid .span3 { + width: 22.92817679558011%; + *width: 22.87498530621841%; + } + .row-fluid .span2 { + width: 14.3646408839779%; + *width: 14.311449394616199%; + } + .row-fluid .span1 { + width: 5.801104972375691%; + *width: 5.747913483013988%; + } + .row-fluid .offset12 { + margin-left: 105.52486187845304%; + *margin-left: 105.41847889972962%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243093922652%; + *margin-left: 102.6560479605031%; + } + .row-fluid .offset11 { + margin-left: 96.96132596685082%; + *margin-left: 96.8549429881274%; + } + .row-fluid .offset11:first-child { + margin-left: 94.1988950276243%; + *margin-left: 94.09251204890089%; + } + .row-fluid .offset10 { + margin-left: 88.39779005524862%; + *margin-left: 88.2914070765252%; + } + .row-fluid .offset10:first-child { + margin-left: 85.6353591160221%; + *margin-left: 85.52897613729868%; + } + .row-fluid .offset9 { + margin-left: 79.8342541436464%; + *margin-left: 79.72787116492299%; + } + .row-fluid .offset9:first-child { + margin-left: 77.07182320441989%; + *margin-left: 76.96544022569647%; + } + .row-fluid .offset8 { + margin-left: 71.2707182320442%; + *margin-left: 71.16433525332079%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729281768%; + *margin-left: 68.40190431409427%; + } + .row-fluid .offset7 { + margin-left: 62.70718232044199%; + *margin-left: 62.600799341718584%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138121547%; + *margin-left: 59.838368402492065%; + } + .row-fluid .offset6 { + margin-left: 54.14364640883978%; + *margin-left: 54.037263430116376%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121546961326%; + *margin-left: 51.27483249088986%; + } + .row-fluid .offset5 { + margin-left: 45.58011049723757%; + *margin-left: 45.47372751851417%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767955801105%; + *margin-left: 42.71129657928765%; + } + .row-fluid .offset4 { + margin-left: 37.01657458563536%; + *margin-left: 36.91019160691196%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414364640884%; + *margin-left: 34.14776066768544%; + } + .row-fluid .offset3 { + margin-left: 28.45303867403315%; + *margin-left: 28.346655695309746%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773480663%; + *margin-left: 25.584224756083227%; + } + .row-fluid .offset2 { + margin-left: 19.88950276243094%; + *margin-left: 19.783119783707537%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182320442%; + *margin-left: 17.02068884448102%; + } + .row-fluid .offset1 { + margin-left: 11.32596685082873%; + *margin-left: 11.219583872105325%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591160221%; + *margin-left: 8.457152932878806%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} + +@media (max-width: 767px) { + body { + padding-right: 20px; + padding-left: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-right: -20px; + margin-left: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + width: auto; + clear: none; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + display: block; + float: none; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + right: 20px; + left: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} + +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 20px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-right: 10px; + padding-left: 10px; + } + .media .pull-left, + .media .pull-right { + display: block; + float: none; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + right: 10px; + left: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} + +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 20px; + } + .navbar-fixed-bottom { + margin-top: 20px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-right: 10px; + padding-left: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .dropdown-menu a:hover { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:hover { + background-color: #111111; + } + .nav-collapse.in .btn-group { + padding: 0; + margin-top: 5px; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + display: none; + float: none; + max-width: none; + padding: 0; + margin: 0 15px; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10px 15px; + margin: 10px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + height: 0; + overflow: hidden; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-right: 10px; + padding-left: 10px; + } +} + +@media (min-width: 980px) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap.css b/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap.css new file mode 100644 index 0000000..22aa0c1 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/css/bootstrap.css @@ -0,0 +1,6039 @@ +/*! + * Bootstrap v2.2.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +audio:not([controls]) { + display: none; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +a:hover, +a:active { + outline: 0; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + width: auto\9; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +#map_canvas img, +.google-maps img { + max-width: none; +} + +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button, +input { + *overflow: visible; + line-height: normal; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 0.5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: #ffffff; +} + +a { + color: #0088cc; + text-decoration: none; +} + +a:hover { + color: #005580; + text-decoration: underline; +} + +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} + +.row { + margin-left: -20px; + *zoom: 1; +} + +.row:before, +.row:after { + display: table; + line-height: 0; + content: ""; +} + +.row:after { + clear: both; +} + +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} + +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.span12 { + width: 940px; +} + +.span11 { + width: 860px; +} + +.span10 { + width: 780px; +} + +.span9 { + width: 700px; +} + +.span8 { + width: 620px; +} + +.span7 { + width: 540px; +} + +.span6 { + width: 460px; +} + +.span5 { + width: 380px; +} + +.span4 { + width: 300px; +} + +.span3 { + width: 220px; +} + +.span2 { + width: 140px; +} + +.span1 { + width: 60px; +} + +.offset12 { + margin-left: 980px; +} + +.offset11 { + margin-left: 900px; +} + +.offset10 { + margin-left: 820px; +} + +.offset9 { + margin-left: 740px; +} + +.offset8 { + margin-left: 660px; +} + +.offset7 { + margin-left: 580px; +} + +.offset6 { + margin-left: 500px; +} + +.offset5 { + margin-left: 420px; +} + +.offset4 { + margin-left: 340px; +} + +.offset3 { + margin-left: 260px; +} + +.offset2 { + margin-left: 180px; +} + +.offset1 { + margin-left: 100px; +} + +.row-fluid { + width: 100%; + *zoom: 1; +} + +.row-fluid:before, +.row-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.row-fluid:after { + clear: both; +} + +.row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} + +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.127659574468085%; +} + +.row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; +} + +.row-fluid .span11 { + width: 91.48936170212765%; + *width: 91.43617021276594%; +} + +.row-fluid .span10 { + width: 82.97872340425532%; + *width: 82.92553191489361%; +} + +.row-fluid .span9 { + width: 74.46808510638297%; + *width: 74.41489361702126%; +} + +.row-fluid .span8 { + width: 65.95744680851064%; + *width: 65.90425531914893%; +} + +.row-fluid .span7 { + width: 57.44680851063829%; + *width: 57.39361702127659%; +} + +.row-fluid .span6 { + width: 48.93617021276595%; + *width: 48.88297872340425%; +} + +.row-fluid .span5 { + width: 40.42553191489362%; + *width: 40.37234042553192%; +} + +.row-fluid .span4 { + width: 31.914893617021278%; + *width: 31.861702127659576%; +} + +.row-fluid .span3 { + width: 23.404255319148934%; + *width: 23.351063829787233%; +} + +.row-fluid .span2 { + width: 14.893617021276595%; + *width: 14.840425531914894%; +} + +.row-fluid .span1 { + width: 6.382978723404255%; + *width: 6.329787234042553%; +} + +.row-fluid .offset12 { + margin-left: 104.25531914893617%; + *margin-left: 104.14893617021275%; +} + +.row-fluid .offset12:first-child { + margin-left: 102.12765957446808%; + *margin-left: 102.02127659574467%; +} + +.row-fluid .offset11 { + margin-left: 95.74468085106382%; + *margin-left: 95.6382978723404%; +} + +.row-fluid .offset11:first-child { + margin-left: 93.61702127659574%; + *margin-left: 93.51063829787232%; +} + +.row-fluid .offset10 { + margin-left: 87.23404255319149%; + *margin-left: 87.12765957446807%; +} + +.row-fluid .offset10:first-child { + margin-left: 85.1063829787234%; + *margin-left: 84.99999999999999%; +} + +.row-fluid .offset9 { + margin-left: 78.72340425531914%; + *margin-left: 78.61702127659572%; +} + +.row-fluid .offset9:first-child { + margin-left: 76.59574468085106%; + *margin-left: 76.48936170212764%; +} + +.row-fluid .offset8 { + margin-left: 70.2127659574468%; + *margin-left: 70.10638297872339%; +} + +.row-fluid .offset8:first-child { + margin-left: 68.08510638297872%; + *margin-left: 67.9787234042553%; +} + +.row-fluid .offset7 { + margin-left: 61.70212765957446%; + *margin-left: 61.59574468085106%; +} + +.row-fluid .offset7:first-child { + margin-left: 59.574468085106375%; + *margin-left: 59.46808510638297%; +} + +.row-fluid .offset6 { + margin-left: 53.191489361702125%; + *margin-left: 53.085106382978715%; +} + +.row-fluid .offset6:first-child { + margin-left: 51.063829787234035%; + *margin-left: 50.95744680851063%; +} + +.row-fluid .offset5 { + margin-left: 44.68085106382979%; + *margin-left: 44.57446808510638%; +} + +.row-fluid .offset5:first-child { + margin-left: 42.5531914893617%; + *margin-left: 42.4468085106383%; +} + +.row-fluid .offset4 { + margin-left: 36.170212765957444%; + *margin-left: 36.06382978723405%; +} + +.row-fluid .offset4:first-child { + margin-left: 34.04255319148936%; + *margin-left: 33.93617021276596%; +} + +.row-fluid .offset3 { + margin-left: 27.659574468085104%; + *margin-left: 27.5531914893617%; +} + +.row-fluid .offset3:first-child { + margin-left: 25.53191489361702%; + *margin-left: 25.425531914893618%; +} + +.row-fluid .offset2 { + margin-left: 19.148936170212764%; + *margin-left: 19.04255319148936%; +} + +.row-fluid .offset2:first-child { + margin-left: 17.02127659574468%; + *margin-left: 16.914893617021278%; +} + +.row-fluid .offset1 { + margin-left: 10.638297872340425%; + *margin-left: 10.53191489361702%; +} + +.row-fluid .offset1:first-child { + margin-left: 8.51063829787234%; + *margin-left: 8.404255319148938%; +} + +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} + +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} + +.container:before, +.container:after { + display: table; + line-height: 0; + content: ""; +} + +.container:after { + clear: both; +} + +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} + +.container-fluid:before, +.container-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.container-fluid:after { + clear: both; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 21px; + font-weight: 200; + line-height: 30px; +} + +small { + font-size: 85%; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +cite { + font-style: normal; +} + +.muted { + color: #999999; +} + +a.muted:hover { + color: #808080; +} + +.text-warning { + color: #c09853; +} + +a.text-warning:hover { + color: #a47e3c; +} + +.text-error { + color: #b94a48; +} + +a.text-error:hover { + color: #953b39; +} + +.text-info { + color: #3a87ad; +} + +a.text-info:hover { + color: #2d6987; +} + +.text-success { + color: #468847; +} + +a.text-success:hover { + color: #356635; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; + text-rendering: optimizelegibility; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} + +h1, +h2, +h3 { + line-height: 40px; +} + +h1 { + font-size: 38.5px; +} + +h2 { + font-size: 31.5px; +} + +h3 { + font-size: 24.5px; +} + +h4 { + font-size: 17.5px; +} + +h5 { + font-size: 14px; +} + +h6 { + font-size: 11.9px; +} + +h1 small { + font-size: 24.5px; +} + +h2 small { + font-size: 17.5px; +} + +h3 small { + font-size: 14px; +} + +h4 small { + font-size: 14px; +} + +.page-header { + padding-bottom: 9px; + margin: 20px 0 30px; + border-bottom: 1px solid #eeeeee; +} + +ul, +ol { + padding: 0; + margin: 0 0 10px 25px; +} + +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} + +li { + line-height: 20px; +} + +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; +} + +ul.inline > li, +ol.inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} + +dl { + margin-bottom: 20px; +} + +dt, +dd { + line-height: 20px; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 10px; +} + +.dl-horizontal { + *zoom: 1; +} + +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + line-height: 0; + content: ""; +} + +.dl-horizontal:after { + clear: both; +} + +.dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; +} + +.dl-horizontal dd { + margin-left: 180px; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 0 0 0 15px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + margin-bottom: 0; + font-size: 16px; + font-weight: 300; + line-height: 25px; +} + +blockquote small { + display: block; + line-height: 20px; + color: #999999; +} + +blockquote small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} + +blockquote.pull-right small:before { + content: ''; +} + +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} + +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 20px; +} + +code, +pre { + padding: 0 3px 2px; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +code { + padding: 2px 4px; + color: #d14; + white-space: nowrap; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 20px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +pre.prettyprint { + margin-bottom: 20px; +} + +pre code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +form { + margin: 0 0 20px; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: 40px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +legend small { + font-size: 15px; + color: #999999; +} + +label, +input, +button, +select, +textarea { + font-size: 14px; + font-weight: normal; + line-height: 20px; +} + +input, +button, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +label { + display: block; + margin-bottom: 5px; +} + +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555555; + vertical-align: middle; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +input, +textarea, +.uneditable-input { + width: 206px; +} + +textarea { + height: auto; +} + +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} + +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + *margin-top: 0; + line-height: normal; +} + +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} + +select, +input[type="file"] { + height: 30px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + + line-height: 30px; +} + +select { + width: 220px; + background-color: #ffffff; + border: 1px solid #cccccc; +} + +select[multiple], +select[size] { + height: auto; +} + +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.uneditable-input, +.uneditable-textarea { + color: #999999; + cursor: not-allowed; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); +} + +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} + +.uneditable-textarea { + width: auto; + height: auto; +} + +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} + +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} + +.radio, +.checkbox { + min-height: 20px; + padding-left: 20px; +} + +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} + +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} + +.input-mini { + width: 60px; +} + +.input-small { + width: 90px; +} + +.input-medium { + width: 150px; +} + +.input-large { + width: 210px; +} + +.input-xlarge { + width: 270px; +} + +.input-xxlarge { + width: 530px; +} + +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} + +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + +input, +textarea, +.uneditable-input { + margin-left: 0; +} + +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} + +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} + +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} + +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} + +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} + +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} + +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} + +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} + +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} + +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} + +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} + +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} + +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} + +.controls-row { + *zoom: 1; +} + +.controls-row:before, +.controls-row:after { + display: table; + line-height: 0; + content: ""; +} + +.controls-row:after { + clear: both; +} + +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} + +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + +.control-group.warning .control-label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} + +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} + +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.control-group.error .control-label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} + +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} + +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.control-group.success .control-label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} + +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} + +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.control-group.info .control-label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} + +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} + +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} + +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} + +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; +} + +input:focus:invalid:focus, +textarea:focus:invalid:focus, +select:focus:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + +.form-actions { + padding: 19px 20px 20px; + margin-top: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} + +.form-actions:before, +.form-actions:after { + display: table; + line-height: 0; + content: ""; +} + +.form-actions:after { + clear: both; +} + +.help-block, +.help-inline { + color: #595959; +} + +.help-block { + display: block; + margin-bottom: 10px; +} + +.help-inline { + display: inline-block; + *display: inline; + padding-left: 5px; + vertical-align: middle; + *zoom: 1; +} + +.input-append, +.input-prepend { + margin-bottom: 5px; + font-size: 0; + white-space: nowrap; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu { + font-size: 14px; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} + +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} + +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn, +.input-append .btn-group > .dropdown-toggle, +.input-prepend .btn-group > .dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} + +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} + +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input + .btn-group .btn:last-child, +.input-append select + .btn-group .btn:last-child, +.input-append .uneditable-input + .btn-group .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} + +.input-append .add-on:last-child, +.input-append .btn:last-child, +.input-append .btn-group:last-child > .dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +/* Allow for input prepend/append in search forms */ + +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + margin-bottom: 0; + vertical-align: middle; + *zoom: 1; +} + +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} + +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} + +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} + +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + +.control-group { + margin-bottom: 10px; +} + +legend + .control-group { + margin-top: 20px; + -webkit-margin-top-collapse: separate; +} + +.form-horizontal .control-group { + margin-bottom: 20px; + *zoom: 1; +} + +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + line-height: 0; + content: ""; +} + +.form-horizontal .control-group:after { + clear: both; +} + +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} + +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} + +.form-horizontal .controls:first-child { + *padding-left: 180px; +} + +.form-horizontal .help-block { + margin-bottom: 0; +} + +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block, +.form-horizontal .uneditable-input + .help-block, +.form-horizontal .input-prepend + .help-block, +.form-horizontal .input-append + .help-block { + margin-top: 10px; +} + +.form-horizontal .form-actions { + padding-left: 180px; +} + +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table th { + font-weight: bold; +} + +.table thead th { + vertical-align: bottom; +} + +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} + +.table tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table .table { + background-color: #ffffff; +} + +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} + +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} + +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} + +.table-bordered thead:first-child tr:first-child > th:first-child, +.table-bordered tbody:first-child tr:first-child > td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered thead:first-child tr:first-child > th:last-child, +.table-bordered tbody:first-child tr:first-child > td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:first-child, +.table-bordered tbody:last-child tr:last-child > td:first-child, +.table-bordered tfoot:last-child tr:last-child > td:first-child { + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:last-child, +.table-bordered tbody:last-child tr:last-child > td:last-child, +.table-bordered tfoot:last-child tr:last-child > td:last-child { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; +} + +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} + +.table-hover tbody tr:hover td, +.table-hover tbody tr:hover th { + background-color: #f5f5f5; +} + +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} + +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} + +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} + +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} + +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} + +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} + +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} + +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} + +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} + +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} + +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} + +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} + +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} + +.table tbody tr.success td { + background-color: #dff0d8; +} + +.table tbody tr.error td { + background-color: #f2dede; +} + +.table tbody tr.warning td { + background-color: #fcf8e3; +} + +.table tbody tr.info td { + background-color: #d9edf7; +} + +.table-hover tbody tr.success:hover td { + background-color: #d0e9c6; +} + +.table-hover tbody tr.error:hover td { + background-color: #ebcccc; +} + +.table-hover tbody tr.warning:hover td { + background-color: #faf2cc; +} + +.table-hover tbody tr.info:hover td { + background-color: #c4e3f3; +} + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + margin-top: 1px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; +} + +/* White icons with optional class, or on hover/active states of certain elements */ + +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"] { + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.icon-glass { + background-position: 0 0; +} + +.icon-music { + background-position: -24px 0; +} + +.icon-search { + background-position: -48px 0; +} + +.icon-envelope { + background-position: -72px 0; +} + +.icon-heart { + background-position: -96px 0; +} + +.icon-star { + background-position: -120px 0; +} + +.icon-star-empty { + background-position: -144px 0; +} + +.icon-user { + background-position: -168px 0; +} + +.icon-film { + background-position: -192px 0; +} + +.icon-th-large { + background-position: -216px 0; +} + +.icon-th { + background-position: -240px 0; +} + +.icon-th-list { + background-position: -264px 0; +} + +.icon-ok { + background-position: -288px 0; +} + +.icon-remove { + background-position: -312px 0; +} + +.icon-zoom-in { + background-position: -336px 0; +} + +.icon-zoom-out { + background-position: -360px 0; +} + +.icon-off { + background-position: -384px 0; +} + +.icon-signal { + background-position: -408px 0; +} + +.icon-cog { + background-position: -432px 0; +} + +.icon-trash { + background-position: -456px 0; +} + +.icon-home { + background-position: 0 -24px; +} + +.icon-file { + background-position: -24px -24px; +} + +.icon-time { + background-position: -48px -24px; +} + +.icon-road { + background-position: -72px -24px; +} + +.icon-download-alt { + background-position: -96px -24px; +} + +.icon-download { + background-position: -120px -24px; +} + +.icon-upload { + background-position: -144px -24px; +} + +.icon-inbox { + background-position: -168px -24px; +} + +.icon-play-circle { + background-position: -192px -24px; +} + +.icon-repeat { + background-position: -216px -24px; +} + +.icon-refresh { + background-position: -240px -24px; +} + +.icon-list-alt { + background-position: -264px -24px; +} + +.icon-lock { + background-position: -287px -24px; +} + +.icon-flag { + background-position: -312px -24px; +} + +.icon-headphones { + background-position: -336px -24px; +} + +.icon-volume-off { + background-position: -360px -24px; +} + +.icon-volume-down { + background-position: -384px -24px; +} + +.icon-volume-up { + background-position: -408px -24px; +} + +.icon-qrcode { + background-position: -432px -24px; +} + +.icon-barcode { + background-position: -456px -24px; +} + +.icon-tag { + background-position: 0 -48px; +} + +.icon-tags { + background-position: -25px -48px; +} + +.icon-book { + background-position: -48px -48px; +} + +.icon-bookmark { + background-position: -72px -48px; +} + +.icon-print { + background-position: -96px -48px; +} + +.icon-camera { + background-position: -120px -48px; +} + +.icon-font { + background-position: -144px -48px; +} + +.icon-bold { + background-position: -167px -48px; +} + +.icon-italic { + background-position: -192px -48px; +} + +.icon-text-height { + background-position: -216px -48px; +} + +.icon-text-width { + background-position: -240px -48px; +} + +.icon-align-left { + background-position: -264px -48px; +} + +.icon-align-center { + background-position: -288px -48px; +} + +.icon-align-right { + background-position: -312px -48px; +} + +.icon-align-justify { + background-position: -336px -48px; +} + +.icon-list { + background-position: -360px -48px; +} + +.icon-indent-left { + background-position: -384px -48px; +} + +.icon-indent-right { + background-position: -408px -48px; +} + +.icon-facetime-video { + background-position: -432px -48px; +} + +.icon-picture { + background-position: -456px -48px; +} + +.icon-pencil { + background-position: 0 -72px; +} + +.icon-map-marker { + background-position: -24px -72px; +} + +.icon-adjust { + background-position: -48px -72px; +} + +.icon-tint { + background-position: -72px -72px; +} + +.icon-edit { + background-position: -96px -72px; +} + +.icon-share { + background-position: -120px -72px; +} + +.icon-check { + background-position: -144px -72px; +} + +.icon-move { + background-position: -168px -72px; +} + +.icon-step-backward { + background-position: -192px -72px; +} + +.icon-fast-backward { + background-position: -216px -72px; +} + +.icon-backward { + background-position: -240px -72px; +} + +.icon-play { + background-position: -264px -72px; +} + +.icon-pause { + background-position: -288px -72px; +} + +.icon-stop { + background-position: -312px -72px; +} + +.icon-forward { + background-position: -336px -72px; +} + +.icon-fast-forward { + background-position: -360px -72px; +} + +.icon-step-forward { + background-position: -384px -72px; +} + +.icon-eject { + background-position: -408px -72px; +} + +.icon-chevron-left { + background-position: -432px -72px; +} + +.icon-chevron-right { + background-position: -456px -72px; +} + +.icon-plus-sign { + background-position: 0 -96px; +} + +.icon-minus-sign { + background-position: -24px -96px; +} + +.icon-remove-sign { + background-position: -48px -96px; +} + +.icon-ok-sign { + background-position: -72px -96px; +} + +.icon-question-sign { + background-position: -96px -96px; +} + +.icon-info-sign { + background-position: -120px -96px; +} + +.icon-screenshot { + background-position: -144px -96px; +} + +.icon-remove-circle { + background-position: -168px -96px; +} + +.icon-ok-circle { + background-position: -192px -96px; +} + +.icon-ban-circle { + background-position: -216px -96px; +} + +.icon-arrow-left { + background-position: -240px -96px; +} + +.icon-arrow-right { + background-position: -264px -96px; +} + +.icon-arrow-up { + background-position: -289px -96px; +} + +.icon-arrow-down { + background-position: -312px -96px; +} + +.icon-share-alt { + background-position: -336px -96px; +} + +.icon-resize-full { + background-position: -360px -96px; +} + +.icon-resize-small { + background-position: -384px -96px; +} + +.icon-plus { + background-position: -408px -96px; +} + +.icon-minus { + background-position: -433px -96px; +} + +.icon-asterisk { + background-position: -456px -96px; +} + +.icon-exclamation-sign { + background-position: 0 -120px; +} + +.icon-gift { + background-position: -24px -120px; +} + +.icon-leaf { + background-position: -48px -120px; +} + +.icon-fire { + background-position: -72px -120px; +} + +.icon-eye-open { + background-position: -96px -120px; +} + +.icon-eye-close { + background-position: -120px -120px; +} + +.icon-warning-sign { + background-position: -144px -120px; +} + +.icon-plane { + background-position: -168px -120px; +} + +.icon-calendar { + background-position: -192px -120px; +} + +.icon-random { + width: 16px; + background-position: -216px -120px; +} + +.icon-comment { + background-position: -240px -120px; +} + +.icon-magnet { + background-position: -264px -120px; +} + +.icon-chevron-up { + background-position: -288px -120px; +} + +.icon-chevron-down { + background-position: -313px -119px; +} + +.icon-retweet { + background-position: -336px -120px; +} + +.icon-shopping-cart { + background-position: -360px -120px; +} + +.icon-folder-close { + background-position: -384px -120px; +} + +.icon-folder-open { + width: 16px; + background-position: -408px -120px; +} + +.icon-resize-vertical { + background-position: -432px -119px; +} + +.icon-resize-horizontal { + background-position: -456px -118px; +} + +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + +.dropup, +.dropdown { + position: relative; +} + +.dropdown-toggle { + *margin-bottom: -3px; +} + +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.dropdown-menu li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} + +.dropdown-menu li > a:hover, +.dropdown-menu li > a:focus, +.dropdown-submenu:hover > a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu .active > a, +.dropdown-menu .active > a:hover { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + outline: 0; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu .disabled > a, +.dropdown-menu .disabled > a:hover { + color: #999999; +} + +.dropdown-menu .disabled > a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.open { + *z-index: 1000; +} + +.open > .dropdown-menu { + display: block; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} + +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} + +.dropdown-submenu > a:after { + display: block; + float: right; + width: 0; + height: 0; + margin-top: 5px; + margin-right: -10px; + border-color: transparent; + border-left-color: #cccccc; + border-style: solid; + border-width: 5px 0 5px 5px; + content: " "; +} + +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} + +.dropdown-submenu.pull-left { + float: none; +} + +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.dropdown .dropdown-menu .nav-header { + padding-right: 20px; + padding-left: 20px; +} + +.typeahead { + z-index: 1051; + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} + +.collapse.in { + height: auto; +} + +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.btn { + display: inline-block; + *display: inline; + padding: 4px 12px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + cursor: pointer; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #bbbbbb; + *border: 0; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #a2a2a2; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:first-child { + *margin-left: 0; +} + +.btn:hover { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active, +.btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-large { + padding: 11px 19px; + font-size: 17.5px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} + +.btn-small { + padding: 2px 10px; + font-size: 11.9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} + +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} + +.btn-mini { + padding: 0 6px; + font-size: 10.5px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} + +.btn { + border-color: #c5c5c5; + border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); +} + +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + *background-color: #0044cc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-primary:hover, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} + +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + *background-color: #f89406; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-warning:hover, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} + +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + *background-color: #bd362f; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-danger:hover, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} + +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + *background-color: #51a351; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-success:hover, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} + +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} + +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + *background-color: #2f96b4; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-info:hover, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} + +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + *background-color: #222222; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-inverse:hover, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} + +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} + +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} + +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-link { + color: #0088cc; + cursor: pointer; + border-color: transparent; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-link:hover { + color: #005580; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover { + color: #333333; + text-decoration: none; +} + +.btn-group { + position: relative; + display: inline-block; + *display: inline; + *margin-left: .3em; + font-size: 0; + white-space: nowrap; + vertical-align: middle; + *zoom: 1; +} + +.btn-group:first-child { + *margin-left: 0; +} + +.btn-group + .btn-group { + margin-left: 5px; +} + +.btn-toolbar { + margin-top: 10px; + margin-bottom: 10px; + font-size: 0; +} + +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group { + margin-left: 5px; +} + +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group > .btn + .btn { + margin-left: -1px; +} + +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: 14px; +} + +.btn-group > .btn-mini { + font-size: 10.5px; +} + +.btn-group > .btn-small { + font-size: 11.9px; +} + +.btn-group > .btn-large { + font-size: 17.5px; +} + +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group > .btn + .dropdown-toggle { + *padding-top: 5px; + padding-right: 8px; + *padding-bottom: 5px; + padding-left: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group > .btn-mini + .dropdown-toggle { + *padding-top: 2px; + padding-right: 5px; + *padding-bottom: 2px; + padding-left: 5px; +} + +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} + +.btn-group > .btn-large + .dropdown-toggle { + *padding-top: 7px; + padding-right: 12px; + *padding-bottom: 7px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} + +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} + +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} + +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} + +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} + +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} + +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} + +.btn .caret { + margin-top: 8px; + margin-left: 0; +} + +.btn-mini .caret, +.btn-small .caret, +.btn-large .caret { + margin-top: 6px; +} + +.btn-large .caret { + border-top-width: 5px; + border-right-width: 5px; + border-left-width: 5px; +} + +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; +} + +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group-vertical > .btn + .btn { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.btn-group-vertical > .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.btn-group-vertical > .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.btn-group-vertical > .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 20px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.alert, +.alert h4 { + color: #c09853; +} + +.alert h4 { + margin: 0; +} + +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; +} + +.alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success h4 { + color: #468847; +} + +.alert-danger, +.alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-danger h4, +.alert-error h4 { + color: #b94a48; +} + +.alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info h4 { + color: #3a87ad; +} + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} + +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} + +.alert-block p + p { + margin-top: 5px; +} + +.nav { + margin-bottom: 20px; + margin-left: 0; + list-style: none; +} + +.nav > li > a { + display: block; +} + +.nav > li > a:hover { + text-decoration: none; + background-color: #eeeeee; +} + +.nav > li > a > img { + max-width: none; +} + +.nav > .pull-right { + float: right; +} + +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 20px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} + +.nav li + .nav-header { + margin-top: 9px; +} + +.nav-list { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 0; +} + +.nav-list > li > a, +.nav-list .nav-header { + margin-right: -15px; + margin-left: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.nav-list > li > a { + padding: 3px 15px; +} + +.nav-list > .active > a, +.nav-list > .active > a:hover { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} + +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} + +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-tabs, +.nav-pills { + *zoom: 1; +} + +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + line-height: 0; + content: ""; +} + +.nav-tabs:after, +.nav-pills:after { + clear: both; +} + +.nav-tabs > li, +.nav-pills > li { + float: left; +} + +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} + +.nav-tabs { + border-bottom: 1px solid #ddd; +} + +.nav-tabs > li { + margin-bottom: -1px; +} + +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 20px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} + +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.nav-pills > .active > a, +.nav-pills > .active > a:hover { + color: #ffffff; + background-color: #0088cc; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li > a { + margin-right: 0; +} + +.nav-tabs.nav-stacked { + border-bottom: 0; +} + +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; +} + +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.nav-tabs.nav-stacked > li > a:hover { + z-index: 2; + border-color: #ddd; +} + +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} + +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} + +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.nav .dropdown-toggle .caret { + margin-top: 6px; + border-top-color: #0088cc; + border-bottom-color: #0088cc; +} + +.nav .dropdown-toggle:hover .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} + +/* move down carets for tabs */ + +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} + +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.nav > .dropdown.active > a:hover { + cursor: pointer; +} + +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} + +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} + +.tabs-stacked .open > a:hover { + border-color: #999999; +} + +.tabbable { + *zoom: 1; +} + +.tabbable:before, +.tabbable:after { + display: table; + line-height: 0; + content: ""; +} + +.tabbable:after { + clear: both; +} + +.tab-content { + overflow: auto; +} + +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} + +.tab-content > .active, +.pill-content > .active { + display: block; +} + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} + +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} + +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.tabs-below > .nav-tabs > li > a:hover { + border-top-color: #ddd; + border-bottom-color: transparent; +} + +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover { + border-color: transparent #ddd #ddd #ddd; +} + +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} + +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} + +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.tabs-left > .nav-tabs > li > a:hover { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} + +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} + +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} + +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.tabs-right > .nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} + +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} + +.nav > .disabled > a { + color: #999999; +} + +.nav > .disabled > a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; +} + +.navbar { + *position: relative; + *z-index: 2; + margin-bottom: 20px; + overflow: visible; +} + +.navbar-inner { + min-height: 40px; + padding-right: 20px; + padding-left: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + *zoom: 1; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); +} + +.navbar-inner:before, +.navbar-inner:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-inner:after { + clear: both; +} + +.navbar .container { + width: auto; +} + +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + +.navbar .brand { + display: block; + float: left; + padding: 10px 20px 10px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .brand:hover { + text-decoration: none; +} + +.navbar-text { + margin-bottom: 0; + line-height: 40px; + color: #777777; +} + +.navbar-link { + color: #777777; +} + +.navbar-link:hover { + color: #333333; +} + +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-right: 1px solid #ffffff; + border-left: 1px solid #f2f2f2; +} + +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} + +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn { + margin-top: 0; +} + +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} + +.navbar-form:before, +.navbar-form:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-form:after { + clear: both; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} + +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} + +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 5px; + white-space: nowrap; +} + +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} + +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} + +.navbar-search .search-query { + padding: 4px 14px; + margin-bottom: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.navbar-static-top { + position: static; + margin-bottom: 0; +} + +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} + +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-right: 0; + padding-left: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.navbar-fixed-top { + top: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar-fixed-bottom { + bottom: 0; +} + +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} + +.navbar .nav > li { + float: left; +} + +.navbar .nav > li > a { + float: none; + padding: 10px 15px 10px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + color: #333333; + text-decoration: none; + background-color: transparent; +} + +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} + +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-right: 5px; + margin-left: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + *background-color: #e5e5e5; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} + +.navbar .btn-navbar:hover, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} + +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} + +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} + +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + +.navbar .nav > li > .dropdown-menu:before { + position: absolute; + top: -7px; + left: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.navbar .nav > li > .dropdown-menu:after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-left: 6px solid transparent; + content: ''; +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + top: auto; + bottom: -7px; + border-top: 7px solid #ccc; + border-bottom: 0; + border-top-color: rgba(0, 0, 0, 0.2); +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + top: auto; + bottom: -6px; + border-top: 6px solid #ffffff; + border-bottom: 0; +} + +.navbar .nav li.dropdown > a:hover .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + color: #555555; + background-color: #e5e5e5; +} + +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + right: 12px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + right: 13px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + right: 100%; + left: auto; + margin-right: -1px; + margin-left: 0; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + border-color: #252525; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); +} + +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; +} + +.navbar-inverse .brand { + color: #999999; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} + +.navbar-inverse .divider-vertical { + border-right-color: #222222; + border-left-color: #111111; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .nav li.dropdown > a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} + +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + outline: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} + +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + *background-color: #040404; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} + +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 20px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; + *display: inline; + text-shadow: 0 1px 0 #ffffff; + *zoom: 1; +} + +.breadcrumb > li > .divider { + padding: 0 5px; + color: #ccc; +} + +.breadcrumb > .active { + color: #999999; +} + +.pagination { + margin: 20px 0; +} + +.pagination ul { + display: inline-block; + *display: inline; + margin-bottom: 0; + margin-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.pagination ul > li { + display: inline; +} + +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 20px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} + +.pagination ul > li > a:hover, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} + +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} + +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover { + color: #999999; + cursor: default; + background-color: transparent; +} + +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.pagination-centered { + text-align: center; +} + +.pagination-right { + text-align: right; +} + +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 17.5px; +} + +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-bottom-left-radius: 3px; + border-bottom-left-radius: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-topleft: 3px; +} + +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + border-bottom-right-radius: 3px; + -moz-border-radius-topright: 3px; + -moz-border-radius-bottomright: 3px; +} + +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 11.9px; +} + +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 0 6px; + font-size: 10.5px; +} + +.pager { + margin: 20px 0; + text-align: center; + list-style: none; + *zoom: 1; +} + +.pager:before, +.pager:after { + display: table; + line-height: 0; + content: ""; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.pager li > a:hover { + text-decoration: none; + background-color: #f5f5f5; +} + +.pager .next > a, +.pager .next > span { + float: right; +} + +.pager .previous > a, +.pager .previous > span { + float: left; +} + +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > span { + color: #999999; + cursor: default; + background-color: #fff; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: 1050; + width: 560px; + margin-left: -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +.modal.fade { + top: -25%; + -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; + -moz-transition: opacity 0.3s linear, top 0.3s ease-out; + -o-transition: opacity 0.3s linear, top 0.3s ease-out; + transition: opacity 0.3s linear, top 0.3s ease-out; +} + +.modal.fade.in { + top: 10%; +} + +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} + +.modal-header .close { + margin-top: 2px; +} + +.modal-header h3 { + margin: 0; + line-height: 30px; +} + +.modal-body { + position: relative; + max-height: 400px; + padding: 15px; + overflow-y: auto; +} + +.modal-form { + margin-bottom: 0; +} + +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + line-height: 0; + content: ""; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.tooltip.top { + margin-top: -3px; +} + +.tooltip.right { + margin-left: 3px; +} + +.tooltip.bottom { + margin-top: 3px; +} + +.tooltip.left { + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + width: 236px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.popover .arrow { + border-width: 11px; +} + +.popover .arrow:after { + border-width: 10px; + content: ""; +} + +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} + +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-top-color: #ffffff; + border-bottom-width: 0; +} + +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); + border-left-width: 0; +} + +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; +} + +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-top-width: 0; +} + +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-bottom-color: #ffffff; + border-top-width: 0; +} + +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); + border-right-width: 0; +} + +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + border-left-color: #ffffff; + border-right-width: 0; +} + +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} + +.thumbnails:before, +.thumbnails:after { + display: table; + line-height: 0; + content: ""; +} + +.thumbnails:after { + clear: both; +} + +.row-fluid .thumbnails { + margin-left: 0; +} + +.thumbnails > li { + float: left; + margin-bottom: 20px; + margin-left: 20px; +} + +.thumbnail { + display: block; + padding: 4px; + line-height: 20px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +a.thumbnail:hover { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} + +.thumbnail > img { + display: block; + max-width: 100%; + margin-right: auto; + margin-left: auto; +} + +.thumbnail .caption { + padding: 9px; + color: #555555; +} + +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} + +.media, +.media .media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media-object { + display: block; +} + +.media-heading { + margin: 0 0 5px; +} + +.media .pull-left { + margin-right: 10px; +} + +.media .pull-right { + margin-left: 10px; +} + +.media-list { + margin-left: 0; + list-style: none; +} + +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 11.844px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; +} + +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.badge { + padding-right: 9px; + padding-left: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} + +.label:empty, +.badge:empty { + display: none; +} + +a.label:hover, +a.badge:hover { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label-important, +.badge-important { + background-color: #b94a48; +} + +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} + +.label-warning, +.badge-warning { + background-color: #f89406; +} + +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} + +.label-success, +.badge-success { + background-color: #468847; +} + +.label-success[href], +.badge-success[href] { + background-color: #356635; +} + +.label-info, +.badge-info { + background-color: #3a87ad; +} + +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} + +.label-inverse, +.badge-inverse { + background-color: #333333; +} + +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} + +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} + +.btn-mini .label, +.btn-mini .badge { + top: 0; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress .bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + color: #ffffff; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); +} + +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} + +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} + +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} + +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} + +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.accordion { + margin-bottom: 20px; +} + +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.accordion-heading { + border-bottom: 0; +} + +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} + +.accordion-toggle { + cursor: pointer; +} + +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} + +.carousel { + position: relative; + margin-bottom: 20px; + line-height: 1; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel-inner > .item > img { + display: block; + line-height: 1; +} + +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} + +.carousel-inner > .active { + left: 0; +} + +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel-inner > .next { + left: 100%; +} + +.carousel-inner > .prev { + left: -100%; +} + +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} + +.carousel-inner > .active.left { + left: -100%; +} + +.carousel-inner > .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.right { + right: 15px; + left: auto; +} + +.carousel-control:hover { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-caption { + position: absolute; + right: 0; + bottom: 0; + left: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} + +.carousel-caption h4, +.carousel-caption p { + line-height: 20px; + color: #ffffff; +} + +.carousel-caption h4 { + margin: 0 0 5px; +} + +.carousel-caption p { + margin-bottom: 0; +} + +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 30px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; + color: inherit; +} + +.hero-unit li { + line-height: 30px; +} + +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.hide { + display: none; +} + +.show { + display: block; +} + +.invisible { + visibility: hidden; +} + +.affix { + position: fixed; +} diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/img/favicon.ico b/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bfb9974019d4b8b978cf34ea67f0c6804f9b76c6 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xFHzK2NLHVUHb1iG2*{Y zgB?lOds5VYkY4wx3IDZQ-O0jkQ{(?@bp=p{C&v8O!h&CXe)}H=o_u_bRSig==8O!i zc=x^YFzx?WUq6CX4M?xX{1U9V`}7vL{^Em6v8n;-RbSqS702#f2-g1(NLsD$!KwzN zS8ZJ%R&20o^8Y{o{{6r8@eNjaxV~+({;Q#3&F!=Q_uf49|L5O7|6hFn@<03RE(|p= zJ*s<_{YS^WcP_&H4q|}(5Orn`hB}ZwmBSk_@QDwvQS^g2lONu|PzTm~V&{JqG(7$3 zJq-P6H_u_H1L;#fa{vQto;~z`$*ZUT-~ayo|MAc7|C=7&#!v&-2UM#}0rtFhmTdiw fS5E!E^7Zq7x2tDJHpBVK>Hp`xeEbh92gs!XBi)`m literal 0 HcmV?d00001 diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/img/logo.png b/servlet/xml/java/saml2/login-logout/src/main/webapp/resources/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..393230883fb4e04829339f7c21b6ab6de7393891 GIT binary patch literal 1123 zcmV-p1f2VcP)P000yS1^@s74{)X{0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$1xZ9fRCwBAJakr>0R&#%?*!8<7oReN z=>H6CRSb*_9RHalf3;=rzRt=3!VW|AET)fs_IS z5aXsJDqv;5UUXCgspCNG%qCu4_4m8Q8J4{_Ss=DB)G%-}OK$)A_qiv-M@As~;az|L zf?EIsP`7cheE7}?GEN=HaRK6^jLh%PvWj*xv3z@3xac)oRV-9P3=lIhyklZuIsRdC zEQk#P>`Ve}KmUCIvzcu9Tf*+YVP*hfGu{$8D7y_HfEf3kMztIrynEOQW2*Ct&A;*G zO}I7VlHICc5w;GSdsSFH1nBbCHtgWCZ;JNm@ z`XjO?7RGP?S?;`M26}|$>d$}7+Q^c380tVF%ErLJjAqwYZkA^sS%0xHF}~wxlbiD5 z`(qoR=no*41>&DbDvtqiaJE+C3lIk&fG}bK9l%ly6jZSM`Nu#MU}XG#RzRlJh~>*q zMz|m_S~GwYKiDe_40nN83W%$i`8HWIa~)&&`WqMkpMdet@v+_G7x%2~|M)9$Tk#!Y zFw1)&gOROi`~TlS|Ne#uGBU8e{?G6W7-39r7#RMspu`I>^-8__`jg@PS4KGI{tDI)dzZ%dlS>M59FF!%vUTz)~b4X4$^~`TvW7 ziIMs5zyE)kk;ES}`AIdM1t$C=P~wMS3!!9Ap!k0ap%i9haWBalIY!4O?f(oAd%r+4 zo)k2;d7%b_#Jhnp-2~zQ1Q@}XCIrw@0t|34GX2=V#dp;1>j!5(mhZn98K?y0fw-87 z<#h&!;7V3zwkKdg)?fU+ET4a(DSgvn03;_eFu%5AV7&(n_piW02N=Hpff?mLFf{&g z0%=ZYR2CvOD9l@{`Ma!o3LDu1eZb}An>(a<~Q?=8{fF=xc@W!MoZ)b z{O|{gu_$Ev|5TXe{Z}Tay7jj||2@OO@D(hD)hsRsjrwm4H=1C4P6p+J-x==O;Vpp} zelf7T{l>^3$L4GI;MZmjLQek2%+jdD-YkChTV1vRXZ6WjKj)c!`-fi-2P5B87D!gv za)A+O=YLR%j|aSGxW@31vGu^;|BMVrzfaux>)%&1LJk9(o(#_DK=28Oy@5ho@oC^@ z5m@o{&xh3zgTK{rGV>dM|NF@cm+`=a;1wp Date: Thu, 17 Mar 2022 09:19:45 -0300 Subject: [PATCH 13/35] Re-enable SAML 2.0 samples with Okta IdP Closes gh-55 --- .idea/misc.xml | 2 +- .../example/Saml2JavaConfigurationITests.java | 57 ++++--- .../main/java/example/IndexController.java | 2 +- .../java/example/SecurityConfiguration.java | 8 +- .../src/main/resources/templates/index.html | 5 - .../saml2/login-single-tenant/build.gradle | 4 +- .../example/Saml2LoginApplicationITests.java | 88 ++++------- .../main/java/example/IndexController.java | 2 +- .../java/example/SecurityConfiguration.java | 42 +++++- .../src/main/resources/application.yml | 15 -- .../src/main/resources/templates/index.html | 5 - .../spring-boot/java/saml2/login/build.gradle | 4 +- .../java/saml2/login/gradle.properties | 1 + .../example/Saml2LoginApplicationITests.java | 141 +++++++++--------- .../main/java/example/IndexController.java | 2 +- .../java/example/SecurityConfiguration.java | 47 ++++++ .../login/src/main/resources/application.yml | 18 --- .../src/main/resources/templates/index.html | 5 - .../saml2/refreshable-metadata/build.gradle | 4 +- .../example/Saml2LoginApplicationITests.java | 96 ++++-------- .../main/java/example/IndexController.java | 2 +- .../src/main/resources/application.yml | 2 +- .../src/main/resources/templates/index.html | 5 - .../xml/java/saml2/login-logout/build.gradle | 3 +- .../java/example/LocalHostWebClient.java | 53 +++++++ .../java/example/Saml2XmlITests.java | 112 ++++++++++++++ .../main/java/example/IndexController.java | 2 +- .../main/webapp/WEB-INF/spring/security.xml | 33 +--- .../main/webapp/WEB-INF/templates/index.html | 5 - settings.gradle | 8 +- 30 files changed, 451 insertions(+), 322 deletions(-) create mode 100644 servlet/xml/java/saml2/login-logout/src/integTest/java/example/LocalHostWebClient.java create mode 100644 servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 5821b2f..37183b0 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java index 3cd5ac1..fbf0cc9 100644 --- a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java +++ b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java @@ -16,15 +16,14 @@ package example; -import java.io.IOException; - +import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlInput; import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -40,6 +39,8 @@ import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import static org.assertj.core.api.Assertions.assertThat; + @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = ApplicationConfiguration.class) @WebAppConfiguration @@ -66,35 +67,45 @@ public class Saml2JavaConfigurationITests { @Test void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { - HtmlPage relyingParty = performLogin(); - Assertions.assertThat(relyingParty.asText()).contains("You're email address is testuser@spring.security.saml"); + performLogin(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); } @Test void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement rpLogoutButton = relyingParty.getHtmlElementById("rp_logout_button"); + performLogin(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); HtmlPage loginPage = rpLogoutButton.click(); - Assertions.assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); } - @Test - void logoutWhenAssertingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement apLogoutButton = relyingParty.getHtmlElementById("ap_logout_button"); - HtmlPage loginPage = apLogoutButton.click(); - Assertions.assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); - } - - private HtmlPage performLogin() throws IOException { + private void performLogin() throws Exception { HtmlPage login = this.webClient.getPage("/"); - HtmlForm form = login.getFormByName("f"); + this.webClient.waitForBackgroundJavaScript(10000); + HtmlForm form = findForm(login); HtmlInput username = form.getInputByName("username"); - HtmlInput password = form.getInputByName("password"); - HtmlSubmitInput submit = login.getHtmlElementById("submit_button"); - username.setValueAttribute("user"); - password.setValueAttribute("password"); - return submit.click(); + HtmlPasswordInput password = form.getInputByName("password"); + HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); + username.type("testuser@spring.security.saml"); + password.type("12345678"); + submit.click(); + this.webClient.waitForBackgroundJavaScript(10000); + } + + private HtmlForm findForm(HtmlPage login) { + for (HtmlForm form : login.getForms()) { + try { + if (form.getId().equals("form19")) { + return form; + } + } + catch (ElementNotFoundException ex) { + // Continue + } + } + throw new IllegalStateException("Could not resolve login form"); } } diff --git a/servlet/java-configuration/saml2/login/src/main/java/example/IndexController.java b/servlet/java-configuration/saml2/login/src/main/java/example/IndexController.java index 7c6cdce..424d3f0 100644 --- a/servlet/java-configuration/saml2/login/src/main/java/example/IndexController.java +++ b/servlet/java-configuration/saml2/login/src/main/java/example/IndexController.java @@ -31,7 +31,7 @@ public class IndexController { @GetMapping("/") public String index(Model model, @AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) { - String emailAddress = principal.getFirstAttribute("emailAddress"); + String emailAddress = principal.getFirstAttribute("email"); model.addAttribute("emailAddress", emailAddress); model.addAttribute("userAttributes", principal.getAttributes()); return "index"; diff --git a/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java index ab2311c..b5a9abe 100644 --- a/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.security.saml2.provider.service.registration.InMemory import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity @@ -57,13 +58,16 @@ public class SecurityConfiguration { @Bean RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations - .fromMetadataLocation("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php") + .fromMetadataLocation("https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata") .registrationId("one") .decryptionX509Credentials( (c) -> c.add(Saml2X509Credential.decryption(this.privateKey, relyingPartyCertificate()))) .signingX509Credentials( (c) -> c.add(Saml2X509Credential.signing(this.privateKey, relyingPartyCertificate()))) - .build(); + .singleLogoutServiceLocation( + "https://dev-05937739.okta.com/app/dev-05937739_springgsecuritysaml2idp_1/exk46xofd8NZvFCpS5d7/slo/saml") + .singleLogoutServiceResponseLocation("http://localhost:8080/logout/saml2/slo") + .singleLogoutServiceBinding(Saml2MessageBinding.POST).build(); return new InMemoryRelyingPartyRegistrationRepository(relyingPartyRegistration); } diff --git a/servlet/java-configuration/saml2/login/src/main/resources/templates/index.html b/servlet/java-configuration/saml2/login/src/main/resources/templates/index.html index 2970e85..2e970b2 100644 --- a/servlet/java-configuration/saml2/login/src/main/resources/templates/index.html +++ b/servlet/java-configuration/saml2/login/src/main/resources/templates/index.html @@ -36,11 +36,6 @@ -
diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle index 305d20e..0769f02 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle +++ b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle @@ -24,7 +24,7 @@ dependencies { implementation 'org.springframework.security:spring-security-saml2-service-provider' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - testImplementation 'net.sourceforge.htmlunit:htmlunit' + testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } @@ -32,4 +32,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java index ca034ad..5382a08 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -16,19 +16,13 @@ package example; -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpSession; - +import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlInput; import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,30 +30,14 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.web.MockHttpSession; import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { - static final String SIGNED_RESPONSE = "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNWJhOGEyMGJlMDE3NzIxZTFiYjhmYmIwNGUxNzRhNDNjN2FiOTFjYTkxIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMS0xMC0xMlQxNDozMzowOFoiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwODAvbG9naW4vc2FtbDIvc3NvIiBJblJlc3BvbnNlVG89IkFSUTI3OTZiZGMtOWJiOC00OTNmLWI3ZTMtMWE2MjI5ZjFiN2U0Ij48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9zaW1wbGVzYW1scGhwLmFwcHMucGNmb25lLmlvL3NhbWwyL2lkcC9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjXzViYThhMjBiZTAxNzcyMWUxYmI4ZmJiMDRlMTc0YTQzYzdhYjkxY2E5MSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPkNzM3U4UXRsSXlaMFZrRXRaU0FhL1VyUFJPbnRxT2syYVhaUmM0cVJSM2M9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPm9ZdW1idWQxYmU0ZWZJTUNGVGdqVG4ydGdOSUJpZlc4L3BRZGJtcUVCVzNaM29VVVdTWnEraDQvd3VWdlBEQ2l5R2J4d28yQ3lhS3JzdW1sUG5rWk9sR3lrdUYzbkFPRkJSaEd1OEdobWdObktiZkVuWXVzQldlOFdiZkFpYXJ6UUQwWHBOYmdsSHpsaE96Umk5ZnR1YkNjbTVQbGFEQytpVW1BbXYxT2ZSYnA5MncrVGhUbEtwaTQzeDNsQW1vRkk0ZlJQbUN2R1FGd0JYSEM5bG1KNGU1MUgwMm1nS2NTTTlnaGFLSk9DeTRxRVVjMVk3RE5xOTM4UHllcEZzVzRzc0I1VmJRalpxTjFBcmZOa0RTOTRKOXBvZFNsSktBcnlyKy9GZml6a0VndmJneVhwajgwclVLcThtNHN3SkE1KzV4NUZMY21xZ2UxR2FRRTVUQUxoUT09PC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRUV6Q0NBdnVnQXdJQkFnSUpBSWMxcXpMcnYrNW5NQTBHQ1NxR1NJYjNEUUVCQ3dVQU1JR2ZNUXN3Q1FZRFZRUUdFd0pWVXpFTE1Ba0dBMVVFQ0F3Q1EwOHhGREFTQmdOVkJBY01DME5oYzNSc1pTQlNiMk5yTVJ3d0dnWURWUVFLREJOVFlXMXNJRlJsYzNScGJtY2dVMlZ5ZG1WeU1Rc3dDUVlEVlFRTERBSkpWREVnTUI0R0ExVUVBd3dYYzJsdGNHeGxjMkZ0YkhCb2NDNWpabUZ3Y0hNdWFXOHhJREFlQmdrcWhraUc5dzBCQ1FFV0VXWm9ZVzVwYTBCd2FYWnZkR0ZzTG1sdk1CNFhEVEUxTURJeU16SXlORFV3TTFvWERUSTFNREl5TWpJeU5EVXdNMW93Z1o4eEN6QUpCZ05WQkFZVEFsVlRNUXN3Q1FZRFZRUUlEQUpEVHpFVU1CSUdBMVVFQnd3TFEyRnpkR3hsSUZKdlkyc3hIREFhQmdOVkJBb01FMU5oYld3Z1ZHVnpkR2x1WnlCVFpYSjJaWEl4Q3pBSkJnTlZCQXNNQWtsVU1TQXdIZ1lEVlFRRERCZHphVzF3YkdWellXMXNjR2h3TG1ObVlYQndjeTVwYnpFZ01CNEdDU3FHU0liM0RRRUpBUllSWm1oaGJtbHJRSEJwZG05MFlXd3VhVzh3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzRjbjYyRTF4THFwTjM0UG1icktCYmtPWEZqeldnSjliK3BYdWFSZnQ2QTMzOXV1SVFlb2VINXFlU0tSVlRsMzJMMGdkejJaaXZMd1pYVytjcXZmdFZXMXR2RUh2ekpGeXhlVFczZkNVZUNRc2ViTG5BMnFSYTA3Umt4VG82TmYyNDRtV1dSRG9kY29IRWZEVVNieGZUWjZJRXhTb2pTSVUyUm5ENldsbFlXRmREMUdGcEJKT21RQjhyQWM4d0pJQmRIRmRRblg4VHRsN2haNnJ0Z3FFWU16WVZNdUoyRjJyMUhTVTF6U0F2d3BkWVA2clJHRlJKRWZkQTltbTNXS2ZOTFNjNWNsanowWC9UWHkwdlZsQVY5NWw5cWNmRnpQbXJrTklzdDlGWlN3cHZCNDlMeUFWa2UwNEZRUFB3TGdWSDRncGhpSkgzanZaN0krSjVsUzhWQWdNQkFBR2pVREJPTUIwR0ExVWREZ1FXQkJUVHlQNkNjNUhsQko1K3VjVkN3R2M1b2dLTkd6QWZCZ05WSFNNRUdEQVdnQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBTUJnTlZIUk1FQlRBREFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUF2TVM0RVFlUC9pcFY0ak9HNWxPNi90WUNiL2lKZUFkdU9uUmhrSmswRGJYMzI5bERMWmhUVEwveC93LzltdUNWY3ZMcnpFcDZQTitWV2Z3NUU1Rld0Wk4weWhHdFA5Uit2Wm5yVitvYzJ6R0Qrbm8xL3lTRk9lM0VpSkNPNWRlaHhLallFbUJSdjVzVS9MWkZLWnBvektOL0JNRWE2Q3FMdXhiemI3eWt4VnI3RVZGWHdsdFB4ekU5VG1MOU9BQ05OeUY1ZUpIV01STWxsYXJVdmtjWGxoNHB1eDRrczllNnpWOURRQnkyemRzOWYxSTNxeGcwZVg2Sm5HclhpL1ppQ1QrbEpnVmUzWkZYaWVqaUxBaUtCMDRzWFczdGkwTFczbHgxM1kxWWxRNC90bHBnVGdmSUp4S1Y2bnlQaUxvSzBueXdiTWQrdnBBaXJEdDJPYytoazwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIElEPSJfYTU2NmRiYzE1Yjg0ZDUxNTFmOWJjODgwOTYzOWUyMWI3ZTMwNjg4MGFmIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMS0xMC0xMlQxNDozMzowOFoiPjxzYW1sOklzc3Vlcj5odHRwczovL3NpbXBsZXNhbWxwaHAuYXBwcy5wY2ZvbmUuaW8vc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfYTU2NmRiYzE1Yjg0ZDUxNTFmOWJjODgwOTYzOWUyMWI3ZTMwNjg4MGFmIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+STdLWlpiY0V2R2xrb2NWSWtuT0tOQ1MwbXVvbmlDU1ViYzBwQ2NOR3phRT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+cjdkMzJtUzB0MXNJaXNEZzcway9xdGVQNWVFb1BtV2tGT3pGQytjaTdPZmNPUmZ3WmZpd0pwSlVKb2RjRE1TZjlRRjVZOFQ1ckp2VXp3UFZwZHBnVFVzUmpQaUQ4K0JxWS9QMnhQc1didE5wYks3cEhjZmlDQWU5aTAvc2o4MktZajVUZlozMlBGV1RGSmVOSVdtUkhxNnlLRVRySmpmM0UvNVBsb0g1WmMyMFljZUMxRkloR0M1akpjSXl3cXVST01WZ1NGdWhVZ0VVaHJMOVpHVUtYdE5KL2dva0JobjloTjFMM1lCdnRYYm9kUExxN0NlUXpvT1pTWDV6bllRbTR4elhiSnN1OHVKOWEva3haUnNPd2JnUXZOcU5sL2t5akgvVTBxVk9HVjJ3ZzhDemhXMy9PN2RRb3VPQVNPV1pxVitpU3dYdFIweE95Zm1kQnlJZXRBPT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRXpDQ0F2dWdBd0lCQWdJSkFJYzFxekxydis1bk1BMEdDU3FHU0liM0RRRUJDd1VBTUlHZk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEZEQVNCZ05WQkFjTUMwTmhjM1JzWlNCU2IyTnJNUnd3R2dZRFZRUUtEQk5UWVcxc0lGUmxjM1JwYm1jZ1UyVnlkbVZ5TVFzd0NRWURWUVFMREFKSlZERWdNQjRHQTFVRUF3d1hjMmx0Y0d4bGMyRnRiSEJvY0M1alptRndjSE11YVc4eElEQWVCZ2txaGtpRzl3MEJDUUVXRVdab1lXNXBhMEJ3YVhadmRHRnNMbWx2TUI0WERURTFNREl5TXpJeU5EVXdNMW9YRFRJMU1ESXlNakl5TkRVd00xb3dnWjh4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSkRUekVVTUJJR0ExVUVCd3dMUTJGemRHeGxJRkp2WTJzeEhEQWFCZ05WQkFvTUUxTmhiV3dnVkdWemRHbHVaeUJUWlhKMlpYSXhDekFKQmdOVkJBc01Ba2xVTVNBd0hnWURWUVFEREJkemFXMXdiR1Z6WVcxc2NHaHdMbU5tWVhCd2N5NXBiekVnTUI0R0NTcUdTSWIzRFFFSkFSWVJabWhoYm1sclFIQnBkbTkwWVd3dWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDNGNuNjJFMXhMcXBOMzRQbWJyS0Jia09YRmp6V2dKOWIrcFh1YVJmdDZBMzM5dXVJUWVvZUg1cWVTS1JWVGwzMkwwZ2R6Mlppdkx3WlhXK2NxdmZ0VlcxdHZFSHZ6SkZ5eGVUVzNmQ1VlQ1FzZWJMbkEycVJhMDdSa3hUbzZOZjI0NG1XV1JEb2Rjb0hFZkRVU2J4ZlRaNklFeFNvalNJVTJSbkQ2V2xsWVdGZEQxR0ZwQkpPbVFCOHJBYzh3SklCZEhGZFFuWDhUdGw3aFo2cnRncUVZTXpZVk11SjJGMnIxSFNVMXpTQXZ3cGRZUDZyUkdGUkpFZmRBOW1tM1dLZk5MU2M1Y2xqejBYL1RYeTB2VmxBVjk1bDlxY2ZGelBtcmtOSXN0OUZaU3dwdkI0OUx5QVZrZTA0RlFQUHdMZ1ZINGdwaGlKSDNqdlo3SStKNWxTOFZBZ01CQUFHalVEQk9NQjBHQTFVZERnUVdCQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBZkJnTlZIU01FR0RBV2dCVFR5UDZDYzVIbEJKNSt1Y1ZDd0djNW9nS05HekFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZNUzRFUWVQL2lwVjRqT0c1bE82L3RZQ2IvaUplQWR1T25SaGtKazBEYlgzMjlsRExaaFRUTC94L3cvOW11Q1ZjdkxyekVwNlBOK1ZXZnc1RTVGV3RaTjB5aEd0UDlSK3ZabnJWK29jMnpHRCtubzEveVNGT2UzRWlKQ081ZGVoeEtqWUVtQlJ2NXNVL0xaRktacG96S04vQk1FYTZDcUx1eGJ6Yjd5a3hWcjdFVkZYd2x0UHh6RTlUbUw5T0FDTk55RjVlSkhXTVJNbGxhclV2a2NYbGg0cHV4NGtzOWU2elY5RFFCeTJ6ZHM5ZjFJM3F4ZzBlWDZKbkdyWGkvWmlDVCtsSmdWZTNaRlhpZWppTEFpS0IwNHNYVzN0aTBMVzNseDEzWTFZbFE0L3RscGdUZ2ZJSnhLVjZueVBpTG9LMG55d2JNZCt2cEFpckR0Mk9jK2hrPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vbG9jYWxob3N0OjgwODAvc2FtbDIvbWV0YWRhdGEiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj50ZXN0dXNlckBzcHJpbmcuc2VjdXJpdHkuc2FtbDwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjA1My0wNi0yMFQxNjoxOTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL2xvY2FsaG9zdDo4MDgwL2xvZ2luL3NhbWwyL3NzbyIgSW5SZXNwb25zZVRvPSJBUlEyNzk2YmRjLTliYjgtNDkzZi1iN2UzLTFhNjIyOWYxYjdlNCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDIxLTEwLTEyVDE0OjMyOjM4WiIgTm90T25PckFmdGVyPSIyMDUzLTA2LTIwVDE2OjE5OjQ4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwOi8vbG9jYWxob3N0OjgwODAvc2FtbDIvbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDIxLTEwLTEyVDE0OjMzOjA4WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyMS0xMC0xMlQyMjozMzowOFoiIFNlc3Npb25JbmRleD0iXzlkNTg3ODM2YWFmMTBjNmM2ODhmMTc0YTZkYzYyNDZlZjEyNTQ4Mzk2MyI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0dXNlckBzcHJpbmcuc2VjdXJpdHkuc2FtbDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+bWVtYmVyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXI8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZW1haWxBZGRyZXNzIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0dXNlckBzcHJpbmcuc2VjdXJpdHkuc2FtbDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg=="; - - static final Map> USER_ATTRIBUTES = new LinkedHashMap<>(); - - static { - USER_ATTRIBUTES.put("uid", Arrays.asList("testuser@spring.security.saml")); - USER_ATTRIBUTES.put("eduPersonAffiliation", Arrays.asList("member", "user")); - USER_ATTRIBUTES.put("emailAddress", Arrays.asList("testuser@spring.security.saml")); - } - @Autowired MockMvc mvc; @@ -71,51 +49,47 @@ public class Saml2LoginApplicationITests { this.webClient.getCookieManager().clearCookies(); } - @Test - void indexWhenSamlResponseThenShowsUserInformation() throws Exception { - HttpSession session = this.mvc.perform(get("http://localhost:8080/")).andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost:8080/saml2/authenticate/metadata")).andReturn().getRequest() - .getSession(); - - this.mvc.perform(post("http://localhost:8080/login/saml2/sso").param("SAMLResponse", SIGNED_RESPONSE) - .session((MockHttpSession) session)).andExpect(redirectedUrl("http://localhost:8080/")); - - this.mvc.perform(get("http://localhost:8080/").session((MockHttpSession) session)) - .andExpect(model().attribute("emailAddress", "testuser@spring.security.saml")) - .andExpect(model().attribute("userAttributes", USER_ATTRIBUTES)); - } - @Test void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { - HtmlPage relyingParty = performLogin(); - assertThat(relyingParty.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); + performLogin(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); } @Test void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement rpLogoutButton = relyingParty.getHtmlElementById("rp_logout_button"); + performLogin(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); HtmlPage loginPage = rpLogoutButton.click(); assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); } - @Test - void logoutWhenAssertingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement apLogoutButton = relyingParty.getHtmlElementById("ap_logout_button"); - HtmlPage loginPage = apLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + private void performLogin() throws Exception { + HtmlPage login = this.webClient.getPage("/"); + this.webClient.waitForBackgroundJavaScript(10000); + HtmlForm form = findForm(login); + HtmlInput username = form.getInputByName("username"); + HtmlPasswordInput password = form.getInputByName("password"); + HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); + username.type("testuser@spring.security.saml"); + password.type("12345678"); + submit.click(); + this.webClient.waitForBackgroundJavaScript(10000); } - private HtmlPage performLogin() throws IOException { - HtmlPage assertingParty = this.webClient.getPage("/"); - HtmlForm form = assertingParty.getFormByName("f"); - HtmlInput username = form.getInputByName("username"); - HtmlInput password = form.getInputByName("password"); - HtmlSubmitInput submit = assertingParty.getHtmlElementById("submit_button"); - username.setValueAttribute("user"); - password.setValueAttribute("password"); - return submit.click(); + private HtmlForm findForm(HtmlPage login) { + for (HtmlForm form : login.getForms()) { + try { + if (form.getId().equals("form19")) { + return form; + } + } + catch (ElementNotFoundException ex) { + // Continue + } + } + throw new IllegalStateException("Could not resolve login form"); } } diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/IndexController.java b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/IndexController.java index 81c4c9f..4d22891 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/IndexController.java +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/IndexController.java @@ -27,7 +27,7 @@ public class IndexController { @GetMapping("/") public String index(Model model, @AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) { - String emailAddress = principal.getFirstAttribute("emailAddress"); + String emailAddress = principal.getFirstAttribute("email"); model.addAttribute("emailAddress", emailAddress); model.addAttribute("userAttributes", principal.getAttributes()); return "index"; diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/SecurityConfiguration.java b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/SecurityConfiguration.java index 7898c30..804ab8e 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/SecurityConfiguration.java +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/java/example/SecurityConfiguration.java @@ -16,13 +16,26 @@ package example; +import java.io.InputStream; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; + +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver; +import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter; @@ -39,7 +52,7 @@ public class SecurityConfiguration { .authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated() ) - .saml2Login((saml2) -> saml2.loginProcessingUrl("/login/saml2/sso")) + .saml2Login(Customizer.withDefaults()) .saml2Logout(Customizer.withDefaults()); // @formatter:on @@ -49,7 +62,7 @@ public class SecurityConfiguration { @Bean RelyingPartyRegistrationResolver relyingPartyRegistrationResolver( RelyingPartyRegistrationRepository registrations) { - return new DefaultRelyingPartyRegistrationResolver((id) -> registrations.findByRegistrationId("metadata")); + return new DefaultRelyingPartyRegistrationResolver((id) -> registrations.findByRegistrationId("two")); } @Bean @@ -65,4 +78,29 @@ public class SecurityConfiguration { return filter; } + @Bean + RelyingPartyRegistrationRepository repository( + @Value("classpath:credentials/rp-private.key") RSAPrivateKey privateKey) { + RelyingPartyRegistration two = RelyingPartyRegistrations + .fromMetadataLocation("https://dev-05937739.okta.com/app/exk4842vmapcMkohr5d7/sso/saml/metadata") + .registrationId("two") + .signingX509Credentials( + (c) -> c.add(Saml2X509Credential.signing(privateKey, relyingPartyCertificate()))) + .singleLogoutServiceLocation( + "https://dev-05937739.okta.com/app/dev-05937739_springsecuritysaml2idptwo_1/exk4842vmapcMkohr5d7/slo/saml") + .singleLogoutServiceResponseLocation("http://localhost:8080/logout/saml2/slo") + .singleLogoutServiceBinding(Saml2MessageBinding.POST).build(); + return new InMemoryRelyingPartyRegistrationRepository(two); + } + + X509Certificate relyingPartyCertificate() { + Resource resource = new ClassPathResource("credentials/rp-certificate.crt"); + try (InputStream is = resource.getInputStream()) { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is); + } + catch (Exception ex) { + throw new UnsupportedOperationException(ex); + } + } + } diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/application.yml b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/application.yml index 333d229..e112d19 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/application.yml +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/application.yml @@ -1,17 +1,2 @@ -spring: - security: - saml2: - relyingparty: - registration: - metadata: - entity-id: "{baseUrl}/saml2/metadata" - acs.location: "{baseUrl}/login/saml2/sso" - signing.credentials: - - private-key-location: classpath:credentials/rp-private.key - certificate-location: classpath:credentials/rp-certificate.crt - identityprovider: - metadata-uri: https://simplesamlphp.apps.pcfone.io/saml2/idp/metadata.php - - logging.level: org.springframework.security: TRACE diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/templates/index.html b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/templates/index.html index 2970e85..2e970b2 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/templates/index.html +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/main/resources/templates/index.html @@ -36,11 +36,6 @@ -
diff --git a/servlet/spring-boot/java/saml2/login/build.gradle b/servlet/spring-boot/java/saml2/login/build.gradle index 305d20e..0769f02 100644 --- a/servlet/spring-boot/java/saml2/login/build.gradle +++ b/servlet/spring-boot/java/saml2/login/build.gradle @@ -24,7 +24,7 @@ dependencies { implementation 'org.springframework.security:spring-security-saml2-service-provider' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - testImplementation 'net.sourceforge.htmlunit:htmlunit' + testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } @@ -32,4 +32,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/saml2/login/gradle.properties b/servlet/spring-boot/java/saml2/login/gradle.properties index e19a862..0e2ed66 100644 --- a/servlet/spring-boot/java/saml2/login/gradle.properties +++ b/servlet/spring-boot/java/saml2/login/gradle.properties @@ -1,2 +1,3 @@ version=5.7.0-SNAPSHOT spring-security.version=5.7.0-SNAPSHOT +selenium-htmlunit.version=2.44.0 diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index 2ddcfaf..00c98b9 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -16,50 +16,30 @@ package example; -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpSession; - +import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlInput; import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.web.MockHttpSession; import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { - static final String SIGNED_RESPONSE = "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfY2UyYjU4NTVjZjU5YmEyNDc4OTUyOGRkOGQzZDcyOGRiMGViZjNlNzNiIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMS0wMS0yMFQwMTowMzoyNFoiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwODAvbG9naW4vc2FtbDIvc3NvL29uZSIgSW5SZXNwb25zZVRvPSJBUlFjOThmMjAwLWRjZjctNGRmNC1hNTIyLTA3MjA2MjA4YjA3ZCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vc2ltcGxlc2FtbC1mb3Itc3ByaW5nLXNhbWwuYXBwcy5wY2ZvbmUuaW8vc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfY2UyYjU4NTVjZjU5YmEyNDc4OTUyOGRkOGQzZDcyOGRiMGViZjNlNzNiIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+TXJUNS8wdTRScTl3QmMvejFQd2FrNURXZm1xOGlOVk52NldHZWFnZUVzUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+WWg1UFE5cFVBbkkvOW5tNzgxZ040bThTS0U4T1VRTDVOMlI3ZklPZmtHVmMzRjhzNlJlR3hRNWFneUFXYnQzUDRwQ3RWeGtqbk4rMk5KeUw4QmhRMHN0dEovb2JFTHJGUldLemYyYUJaS2NCN0JHTFNtRXdoUFE3N3BHL0psMjBhaDQyaGRyWFc3TE9Ob0VZOHMyY093dm16NkQ2bW9YQWprMHV2UEVTNjhUVndxU2VmT3JwNXV0QmRSQUt6cUJRQ2NQWFJCdnB5NWJ3QkpDL2RKNE5QLzJpalE3N2I3eWhvVDQ0R21hSUduSGo0YVFaeG9kY1JuNU9oQ2hYRk4ydUk2YW1mT0ZYOThjUXZ5KzhDWm9YYUZRMnJmT2dPbGdzbmNGWGMwaXhYK05MSjlvSlJWT2hxRVpjY2JoZ3hPM2hpQ2UwemRuZHloVWxpaGMwdFU2OVlBPT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRXpDQ0F2dWdBd0lCQWdJSkFJYzFxekxydis1bk1BMEdDU3FHU0liM0RRRUJDd1VBTUlHZk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEZEQVNCZ05WQkFjTUMwTmhjM1JzWlNCU2IyTnJNUnd3R2dZRFZRUUtEQk5UWVcxc0lGUmxjM1JwYm1jZ1UyVnlkbVZ5TVFzd0NRWURWUVFMREFKSlZERWdNQjRHQTFVRUF3d1hjMmx0Y0d4bGMyRnRiSEJvY0M1alptRndjSE11YVc4eElEQWVCZ2txaGtpRzl3MEJDUUVXRVdab1lXNXBhMEJ3YVhadmRHRnNMbWx2TUI0WERURTFNREl5TXpJeU5EVXdNMW9YRFRJMU1ESXlNakl5TkRVd00xb3dnWjh4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSkRUekVVTUJJR0ExVUVCd3dMUTJGemRHeGxJRkp2WTJzeEhEQWFCZ05WQkFvTUUxTmhiV3dnVkdWemRHbHVaeUJUWlhKMlpYSXhDekFKQmdOVkJBc01Ba2xVTVNBd0hnWURWUVFEREJkemFXMXdiR1Z6WVcxc2NHaHdMbU5tWVhCd2N5NXBiekVnTUI0R0NTcUdTSWIzRFFFSkFSWVJabWhoYm1sclFIQnBkbTkwWVd3dWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDNGNuNjJFMXhMcXBOMzRQbWJyS0Jia09YRmp6V2dKOWIrcFh1YVJmdDZBMzM5dXVJUWVvZUg1cWVTS1JWVGwzMkwwZ2R6Mlppdkx3WlhXK2NxdmZ0VlcxdHZFSHZ6SkZ5eGVUVzNmQ1VlQ1FzZWJMbkEycVJhMDdSa3hUbzZOZjI0NG1XV1JEb2Rjb0hFZkRVU2J4ZlRaNklFeFNvalNJVTJSbkQ2V2xsWVdGZEQxR0ZwQkpPbVFCOHJBYzh3SklCZEhGZFFuWDhUdGw3aFo2cnRncUVZTXpZVk11SjJGMnIxSFNVMXpTQXZ3cGRZUDZyUkdGUkpFZmRBOW1tM1dLZk5MU2M1Y2xqejBYL1RYeTB2VmxBVjk1bDlxY2ZGelBtcmtOSXN0OUZaU3dwdkI0OUx5QVZrZTA0RlFQUHdMZ1ZINGdwaGlKSDNqdlo3SStKNWxTOFZBZ01CQUFHalVEQk9NQjBHQTFVZERnUVdCQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBZkJnTlZIU01FR0RBV2dCVFR5UDZDYzVIbEJKNSt1Y1ZDd0djNW9nS05HekFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZNUzRFUWVQL2lwVjRqT0c1bE82L3RZQ2IvaUplQWR1T25SaGtKazBEYlgzMjlsRExaaFRUTC94L3cvOW11Q1ZjdkxyekVwNlBOK1ZXZnc1RTVGV3RaTjB5aEd0UDlSK3ZabnJWK29jMnpHRCtubzEveVNGT2UzRWlKQ081ZGVoeEtqWUVtQlJ2NXNVL0xaRktacG96S04vQk1FYTZDcUx1eGJ6Yjd5a3hWcjdFVkZYd2x0UHh6RTlUbUw5T0FDTk55RjVlSkhXTVJNbGxhclV2a2NYbGg0cHV4NGtzOWU2elY5RFFCeTJ6ZHM5ZjFJM3F4ZzBlWDZKbkdyWGkvWmlDVCtsSmdWZTNaRlhpZWppTEFpS0IwNHNYVzN0aTBMVzNseDEzWTFZbFE0L3RscGdUZ2ZJSnhLVjZueVBpTG9LMG55d2JNZCt2cEFpckR0Mk9jK2hrPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9Il9jYjYzYmMzNmMyYzAzYjRlMWJjZDViMWIwY2MyZTE2NWQwNDQ1NDZlODgiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDIxLTAxLTIwVDAxOjAzOjI0WiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vc2ltcGxlc2FtbC1mb3Itc3ByaW5nLXNhbWwuYXBwcy5wY2ZvbmUuaW8vc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfY2I2M2JjMzZjMmMwM2I0ZTFiY2Q1YjFiMGNjMmUxNjVkMDQ0NTQ2ZTg4Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+VWFtN2NHVGlCd2xuRDBJdGd5aU5KVjN2Z0NPNytZZkRxSWJrWERkR3hrQT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+RkhpWUpETDlKTXM1Y2V5WXhUVVgrUndEQm45RFYzVE81dDFham4raGFtb1c2MUpBY0JaNjEwUHpYMzN3alA3Mk1kYmdDWnR5ZmNrSktZUUpPT0szRkxLTkJLQkphOTNsSS9rZWZjTXRTUGxBU2hESm9ydmU0U0tWa29WbzZLVnB0eC9OTnowRkhJNURFZTZiUUVjZWFiNERVNDFVdEpQMHUyWm16ejVjNC83VzhLdmt6MkxMbXhWZlE3Q2todmgvNzBhWHlkWVBVRml3bE4vV1lTV3JYVU9oOXNFTDFiZGVlQzFkYnpaeVdNNldnSkdRMUpJblBnSGd0YTlxMU96eGliOFlLRXpQSUMzVEZldkU1Y0phMFQvd1NzOVIxN0JSR09OclhTTWQvRCt4YkY0Z3lIYW5EZFlOYVN2TzdIS2p4bzRwYk1aY05peDhMTkVYZGtiZEx3PT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRXpDQ0F2dWdBd0lCQWdJSkFJYzFxekxydis1bk1BMEdDU3FHU0liM0RRRUJDd1VBTUlHZk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEZEQVNCZ05WQkFjTUMwTmhjM1JzWlNCU2IyTnJNUnd3R2dZRFZRUUtEQk5UWVcxc0lGUmxjM1JwYm1jZ1UyVnlkbVZ5TVFzd0NRWURWUVFMREFKSlZERWdNQjRHQTFVRUF3d1hjMmx0Y0d4bGMyRnRiSEJvY0M1alptRndjSE11YVc4eElEQWVCZ2txaGtpRzl3MEJDUUVXRVdab1lXNXBhMEJ3YVhadmRHRnNMbWx2TUI0WERURTFNREl5TXpJeU5EVXdNMW9YRFRJMU1ESXlNakl5TkRVd00xb3dnWjh4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSkRUekVVTUJJR0ExVUVCd3dMUTJGemRHeGxJRkp2WTJzeEhEQWFCZ05WQkFvTUUxTmhiV3dnVkdWemRHbHVaeUJUWlhKMlpYSXhDekFKQmdOVkJBc01Ba2xVTVNBd0hnWURWUVFEREJkemFXMXdiR1Z6WVcxc2NHaHdMbU5tWVhCd2N5NXBiekVnTUI0R0NTcUdTSWIzRFFFSkFSWVJabWhoYm1sclFIQnBkbTkwWVd3dWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDNGNuNjJFMXhMcXBOMzRQbWJyS0Jia09YRmp6V2dKOWIrcFh1YVJmdDZBMzM5dXVJUWVvZUg1cWVTS1JWVGwzMkwwZ2R6Mlppdkx3WlhXK2NxdmZ0VlcxdHZFSHZ6SkZ5eGVUVzNmQ1VlQ1FzZWJMbkEycVJhMDdSa3hUbzZOZjI0NG1XV1JEb2Rjb0hFZkRVU2J4ZlRaNklFeFNvalNJVTJSbkQ2V2xsWVdGZEQxR0ZwQkpPbVFCOHJBYzh3SklCZEhGZFFuWDhUdGw3aFo2cnRncUVZTXpZVk11SjJGMnIxSFNVMXpTQXZ3cGRZUDZyUkdGUkpFZmRBOW1tM1dLZk5MU2M1Y2xqejBYL1RYeTB2VmxBVjk1bDlxY2ZGelBtcmtOSXN0OUZaU3dwdkI0OUx5QVZrZTA0RlFQUHdMZ1ZINGdwaGlKSDNqdlo3SStKNWxTOFZBZ01CQUFHalVEQk9NQjBHQTFVZERnUVdCQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBZkJnTlZIU01FR0RBV2dCVFR5UDZDYzVIbEJKNSt1Y1ZDd0djNW9nS05HekFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZNUzRFUWVQL2lwVjRqT0c1bE82L3RZQ2IvaUplQWR1T25SaGtKazBEYlgzMjlsRExaaFRUTC94L3cvOW11Q1ZjdkxyekVwNlBOK1ZXZnc1RTVGV3RaTjB5aEd0UDlSK3ZabnJWK29jMnpHRCtubzEveVNGT2UzRWlKQ081ZGVoeEtqWUVtQlJ2NXNVL0xaRktacG96S04vQk1FYTZDcUx1eGJ6Yjd5a3hWcjdFVkZYd2x0UHh6RTlUbUw5T0FDTk55RjVlSkhXTVJNbGxhclV2a2NYbGg0cHV4NGtzOWU2elY5RFFCeTJ6ZHM5ZjFJM3F4ZzBlWDZKbkdyWGkvWmlDVCtsSmdWZTNaRlhpZWppTEFpS0IwNHNYVzN0aTBMVzNseDEzWTFZbFE0L3RscGdUZ2ZJSnhLVjZueVBpTG9LMG55d2JNZCt2cEFpckR0Mk9jK2hrPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vbG9jYWxob3N0OjgwODAvc2FtbDIvc2VydmljZS1wcm92aWRlci1tZXRhZGF0YS9vbmUiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj50ZXN0dXNlckBzcHJpbmcuc2VjdXJpdHkuc2FtbDwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjA1Mi0wOS0yOFQwMjo1MDowNFoiIFJlY2lwaWVudD0iaHR0cDovL2xvY2FsaG9zdDo4MDgwL2xvZ2luL3NhbWwyL3Nzby9vbmUiIEluUmVzcG9uc2VUbz0iQVJRYzk4ZjIwMC1kY2Y3LTRkZjQtYTUyMi0wNzIwNjIwOGIwN2QiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAyMS0wMS0yMFQwMTowMjo1NFoiIE5vdE9uT3JBZnRlcj0iMjA1Mi0wOS0yOFQwMjo1MDowNFoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbWwyL3NlcnZpY2UtcHJvdmlkZXItbWV0YWRhdGEvb25lPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAyMS0wMS0yMFQwMDo0ODoyOVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDEtMjBUMDg6NDg6MjlaIiBTZXNzaW9uSW5kZXg9Il9lN2ExYTllNDk1YmZlMjI2NjQ5ZThkY2MzN2UxNDE1NDQ5NTIxNWQ2ZWIiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdHVzZXJAc3ByaW5nLnNlY3VyaXR5LnNhbWw8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPm1lbWJlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVtYWlsQWRkcmVzcyIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdHVzZXJAc3ByaW5nLnNlY3VyaXR5LnNhbWw8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4="; - - static final Map> USER_ATTRIBUTES = new LinkedHashMap<>(); - - static { - USER_ATTRIBUTES.put("uid", Arrays.asList("testuser@spring.security.saml")); - USER_ATTRIBUTES.put("eduPersonAffiliation", Arrays.asList("member", "user")); - USER_ATTRIBUTES.put("emailAddress", Arrays.asList("testuser@spring.security.saml")); - } - @Autowired MockMvc mvc; @@ -71,51 +51,78 @@ public class Saml2LoginApplicationITests { this.webClient.getCookieManager().clearCookies(); } - @Test - void indexWhenSamlResponseThenShowsUserInformation() throws Exception { - HttpSession session = this.mvc.perform(get("http://localhost:8080/")).andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost:8080/login")).andReturn().getRequest().getSession(); - - this.mvc.perform(post("http://localhost:8080/login/saml2/sso/one").param("SAMLResponse", SIGNED_RESPONSE) - .session((MockHttpSession) session)).andExpect(redirectedUrl("http://localhost:8080/")); - - this.mvc.perform(get("http://localhost:8080/").session((MockHttpSession) session)) - .andExpect(model().attribute("emailAddress", "testuser@spring.security.saml")) - .andExpect(model().attribute("userAttributes", USER_ATTRIBUTES)); - } - - @Test - void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { - HtmlPage relyingParty = performLogin(); - assertThat(relyingParty.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); - } - - @Test - void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement rpLogoutButton = relyingParty.getHtmlElementById("rp_logout_button"); - HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); - } - - @Test - void logoutWhenAssertingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement apLogoutButton = relyingParty.getHtmlElementById("ap_logout_button"); - HtmlPage loginPage = apLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); - } - - private HtmlPage performLogin() throws IOException { + private void performLogin(String registrationId) throws Exception { HtmlPage login = this.webClient.getPage("/"); - HtmlPage assertingParty = login.getAnchorByHref("/saml2/authenticate/one").click(); - HtmlForm form = assertingParty.getFormByName("f"); + login.getAnchorByHref("/saml2/authenticate/" + registrationId).click(); + this.webClient.waitForBackgroundJavaScript(10000); + HtmlPage okta = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + this.webClient.waitForBackgroundJavaScript(10000); + HtmlForm form = findForm(okta); HtmlInput username = form.getInputByName("username"); - HtmlInput password = form.getInputByName("password"); - HtmlSubmitInput submit = assertingParty.getHtmlElementById("submit_button"); - username.setValueAttribute("user"); - password.setValueAttribute("password"); - return submit.click(); + HtmlPasswordInput password = form.getInputByName("password"); + HtmlSubmitInput submit = okta.getHtmlElementById("okta-signin-submit"); + username.type("testuser@spring.security.saml"); + password.type("12345678"); + submit.click(); + this.webClient.waitForBackgroundJavaScript(10000); + } + + private HtmlForm findForm(HtmlPage login) { + for (HtmlForm form : login.getForms()) { + try { + if (form.getId().equals("form19")) { + return form; + } + } + catch (ElementNotFoundException ex) { + // Continue + } + } + throw new IllegalStateException("Could not resolve login form"); + } + + @DisplayName("Tenant one tests") + @Nested + class TenantOneTests { + + @Test + void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { + performLogin("one"); + HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + } + + @Test + void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { + performLogin("one"); + HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); + HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); + HtmlPage loginPage = rpLogoutButton.click(); + assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + } + + } + + @DisplayName("Tenant two tests") + @Nested + class TenantTwoTests { + + @Test + void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { + performLogin("two"); + HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + } + + @Test + void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { + performLogin("two"); + HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); + HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); + HtmlPage loginPage = rpLogoutButton.click(); + assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + } + } } diff --git a/servlet/spring-boot/java/saml2/login/src/main/java/example/IndexController.java b/servlet/spring-boot/java/saml2/login/src/main/java/example/IndexController.java index 81c4c9f..4d22891 100644 --- a/servlet/spring-boot/java/saml2/login/src/main/java/example/IndexController.java +++ b/servlet/spring-boot/java/saml2/login/src/main/java/example/IndexController.java @@ -27,7 +27,7 @@ public class IndexController { @GetMapping("/") public String index(Model model, @AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) { - String emailAddress = principal.getFirstAttribute("emailAddress"); + String emailAddress = principal.getFirstAttribute("email"); model.addAttribute("emailAddress", emailAddress); model.addAttribute("userAttributes", principal.getAttributes()); return "index"; diff --git a/servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java b/servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java index 1214b95..a58d88f 100644 --- a/servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java +++ b/servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java @@ -16,13 +16,26 @@ package example; +import java.io.InputStream; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; + +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver; +import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter; @@ -59,4 +72,38 @@ public class SecurityConfiguration { return filter; } + @Bean + RelyingPartyRegistrationRepository repository( + @Value("classpath:credentials/rp-private.key") RSAPrivateKey privateKey) { + RelyingPartyRegistration one = RelyingPartyRegistrations + .fromMetadataLocation("https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata") + .registrationId("one") + .signingX509Credentials( + (c) -> c.add(Saml2X509Credential.signing(privateKey, relyingPartyCertificate()))) + .singleLogoutServiceLocation( + "https://dev-05937739.okta.com/app/dev-05937739_springgsecuritysaml2idp_1/exk46xofd8NZvFCpS5d7/slo/saml") + .singleLogoutServiceResponseLocation("http://localhost:8080/logout/saml2/slo") + .singleLogoutServiceBinding(Saml2MessageBinding.POST).build(); + RelyingPartyRegistration two = RelyingPartyRegistrations + .fromMetadataLocation("https://dev-05937739.okta.com/app/exk4842vmapcMkohr5d7/sso/saml/metadata") + .registrationId("two") + .signingX509Credentials( + (c) -> c.add(Saml2X509Credential.signing(privateKey, relyingPartyCertificate()))) + .singleLogoutServiceLocation( + "https://dev-05937739.okta.com/app/dev-05937739_springsecuritysaml2idptwo_1/exk4842vmapcMkohr5d7/slo/saml") + .singleLogoutServiceResponseLocation("http://localhost:8080/logout/saml2/slo") + .singleLogoutServiceBinding(Saml2MessageBinding.POST).build(); + return new InMemoryRelyingPartyRegistrationRepository(one, two); + } + + X509Certificate relyingPartyCertificate() { + Resource resource = new ClassPathResource("credentials/rp-certificate.crt"); + try (InputStream is = resource.getInputStream()) { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is); + } + catch (Exception ex) { + throw new UnsupportedOperationException(ex); + } + } + } diff --git a/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml b/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml index bb0f783..e112d19 100644 --- a/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml +++ b/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml @@ -1,20 +1,2 @@ -spring: - security: - saml2: - relyingparty: - registration: - one: - signing.credentials: &rp-metadata - - private-key-location: classpath:credentials/rp-private.key - certificate-location: classpath:credentials/rp-certificate.crt - identityprovider: - metadata-uri: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php - two: - signing.credentials: *rp-metadata - decryption.credentials: *rp-metadata - identityprovider: - metadata-uri: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php - - logging.level: org.springframework.security: TRACE diff --git a/servlet/spring-boot/java/saml2/login/src/main/resources/templates/index.html b/servlet/spring-boot/java/saml2/login/src/main/resources/templates/index.html index 2970e85..2e970b2 100644 --- a/servlet/spring-boot/java/saml2/login/src/main/resources/templates/index.html +++ b/servlet/spring-boot/java/saml2/login/src/main/resources/templates/index.html @@ -36,11 +36,6 @@ -
diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle index 4c0427d..b933682 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle @@ -24,7 +24,7 @@ dependencies { implementation 'org.springframework.security:spring-security-saml2-service-provider' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - testImplementation 'net.sourceforge.htmlunit:htmlunit' + testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } @@ -32,4 +32,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java index 00334e8..b73e69c 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,12 @@ package example; -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpSession; - +import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlInput; import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,30 +29,14 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.web.MockHttpSession; import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { - static final String SIGNED_RESPONSE = "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfY2UyYjU4NTVjZjU5YmEyNDc4OTUyOGRkOGQzZDcyOGRiMGViZjNlNzNiIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMS0wMS0yMFQwMTowMzoyNFoiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwODAvbG9naW4vc2FtbDIvc3NvL29uZSIgSW5SZXNwb25zZVRvPSJBUlFjOThmMjAwLWRjZjctNGRmNC1hNTIyLTA3MjA2MjA4YjA3ZCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vc2ltcGxlc2FtbC1mb3Itc3ByaW5nLXNhbWwuYXBwcy5wY2ZvbmUuaW8vc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfY2UyYjU4NTVjZjU5YmEyNDc4OTUyOGRkOGQzZDcyOGRiMGViZjNlNzNiIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+TXJUNS8wdTRScTl3QmMvejFQd2FrNURXZm1xOGlOVk52NldHZWFnZUVzUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+WWg1UFE5cFVBbkkvOW5tNzgxZ040bThTS0U4T1VRTDVOMlI3ZklPZmtHVmMzRjhzNlJlR3hRNWFneUFXYnQzUDRwQ3RWeGtqbk4rMk5KeUw4QmhRMHN0dEovb2JFTHJGUldLemYyYUJaS2NCN0JHTFNtRXdoUFE3N3BHL0psMjBhaDQyaGRyWFc3TE9Ob0VZOHMyY093dm16NkQ2bW9YQWprMHV2UEVTNjhUVndxU2VmT3JwNXV0QmRSQUt6cUJRQ2NQWFJCdnB5NWJ3QkpDL2RKNE5QLzJpalE3N2I3eWhvVDQ0R21hSUduSGo0YVFaeG9kY1JuNU9oQ2hYRk4ydUk2YW1mT0ZYOThjUXZ5KzhDWm9YYUZRMnJmT2dPbGdzbmNGWGMwaXhYK05MSjlvSlJWT2hxRVpjY2JoZ3hPM2hpQ2UwemRuZHloVWxpaGMwdFU2OVlBPT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRXpDQ0F2dWdBd0lCQWdJSkFJYzFxekxydis1bk1BMEdDU3FHU0liM0RRRUJDd1VBTUlHZk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEZEQVNCZ05WQkFjTUMwTmhjM1JzWlNCU2IyTnJNUnd3R2dZRFZRUUtEQk5UWVcxc0lGUmxjM1JwYm1jZ1UyVnlkbVZ5TVFzd0NRWURWUVFMREFKSlZERWdNQjRHQTFVRUF3d1hjMmx0Y0d4bGMyRnRiSEJvY0M1alptRndjSE11YVc4eElEQWVCZ2txaGtpRzl3MEJDUUVXRVdab1lXNXBhMEJ3YVhadmRHRnNMbWx2TUI0WERURTFNREl5TXpJeU5EVXdNMW9YRFRJMU1ESXlNakl5TkRVd00xb3dnWjh4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSkRUekVVTUJJR0ExVUVCd3dMUTJGemRHeGxJRkp2WTJzeEhEQWFCZ05WQkFvTUUxTmhiV3dnVkdWemRHbHVaeUJUWlhKMlpYSXhDekFKQmdOVkJBc01Ba2xVTVNBd0hnWURWUVFEREJkemFXMXdiR1Z6WVcxc2NHaHdMbU5tWVhCd2N5NXBiekVnTUI0R0NTcUdTSWIzRFFFSkFSWVJabWhoYm1sclFIQnBkbTkwWVd3dWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDNGNuNjJFMXhMcXBOMzRQbWJyS0Jia09YRmp6V2dKOWIrcFh1YVJmdDZBMzM5dXVJUWVvZUg1cWVTS1JWVGwzMkwwZ2R6Mlppdkx3WlhXK2NxdmZ0VlcxdHZFSHZ6SkZ5eGVUVzNmQ1VlQ1FzZWJMbkEycVJhMDdSa3hUbzZOZjI0NG1XV1JEb2Rjb0hFZkRVU2J4ZlRaNklFeFNvalNJVTJSbkQ2V2xsWVdGZEQxR0ZwQkpPbVFCOHJBYzh3SklCZEhGZFFuWDhUdGw3aFo2cnRncUVZTXpZVk11SjJGMnIxSFNVMXpTQXZ3cGRZUDZyUkdGUkpFZmRBOW1tM1dLZk5MU2M1Y2xqejBYL1RYeTB2VmxBVjk1bDlxY2ZGelBtcmtOSXN0OUZaU3dwdkI0OUx5QVZrZTA0RlFQUHdMZ1ZINGdwaGlKSDNqdlo3SStKNWxTOFZBZ01CQUFHalVEQk9NQjBHQTFVZERnUVdCQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBZkJnTlZIU01FR0RBV2dCVFR5UDZDYzVIbEJKNSt1Y1ZDd0djNW9nS05HekFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZNUzRFUWVQL2lwVjRqT0c1bE82L3RZQ2IvaUplQWR1T25SaGtKazBEYlgzMjlsRExaaFRUTC94L3cvOW11Q1ZjdkxyekVwNlBOK1ZXZnc1RTVGV3RaTjB5aEd0UDlSK3ZabnJWK29jMnpHRCtubzEveVNGT2UzRWlKQ081ZGVoeEtqWUVtQlJ2NXNVL0xaRktacG96S04vQk1FYTZDcUx1eGJ6Yjd5a3hWcjdFVkZYd2x0UHh6RTlUbUw5T0FDTk55RjVlSkhXTVJNbGxhclV2a2NYbGg0cHV4NGtzOWU2elY5RFFCeTJ6ZHM5ZjFJM3F4ZzBlWDZKbkdyWGkvWmlDVCtsSmdWZTNaRlhpZWppTEFpS0IwNHNYVzN0aTBMVzNseDEzWTFZbFE0L3RscGdUZ2ZJSnhLVjZueVBpTG9LMG55d2JNZCt2cEFpckR0Mk9jK2hrPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9Il9jYjYzYmMzNmMyYzAzYjRlMWJjZDViMWIwY2MyZTE2NWQwNDQ1NDZlODgiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDIxLTAxLTIwVDAxOjAzOjI0WiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vc2ltcGxlc2FtbC1mb3Itc3ByaW5nLXNhbWwuYXBwcy5wY2ZvbmUuaW8vc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfY2I2M2JjMzZjMmMwM2I0ZTFiY2Q1YjFiMGNjMmUxNjVkMDQ0NTQ2ZTg4Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+VWFtN2NHVGlCd2xuRDBJdGd5aU5KVjN2Z0NPNytZZkRxSWJrWERkR3hrQT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+RkhpWUpETDlKTXM1Y2V5WXhUVVgrUndEQm45RFYzVE81dDFham4raGFtb1c2MUpBY0JaNjEwUHpYMzN3alA3Mk1kYmdDWnR5ZmNrSktZUUpPT0szRkxLTkJLQkphOTNsSS9rZWZjTXRTUGxBU2hESm9ydmU0U0tWa29WbzZLVnB0eC9OTnowRkhJNURFZTZiUUVjZWFiNERVNDFVdEpQMHUyWm16ejVjNC83VzhLdmt6MkxMbXhWZlE3Q2todmgvNzBhWHlkWVBVRml3bE4vV1lTV3JYVU9oOXNFTDFiZGVlQzFkYnpaeVdNNldnSkdRMUpJblBnSGd0YTlxMU96eGliOFlLRXpQSUMzVEZldkU1Y0phMFQvd1NzOVIxN0JSR09OclhTTWQvRCt4YkY0Z3lIYW5EZFlOYVN2TzdIS2p4bzRwYk1aY05peDhMTkVYZGtiZEx3PT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRXpDQ0F2dWdBd0lCQWdJSkFJYzFxekxydis1bk1BMEdDU3FHU0liM0RRRUJDd1VBTUlHZk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEZEQVNCZ05WQkFjTUMwTmhjM1JzWlNCU2IyTnJNUnd3R2dZRFZRUUtEQk5UWVcxc0lGUmxjM1JwYm1jZ1UyVnlkbVZ5TVFzd0NRWURWUVFMREFKSlZERWdNQjRHQTFVRUF3d1hjMmx0Y0d4bGMyRnRiSEJvY0M1alptRndjSE11YVc4eElEQWVCZ2txaGtpRzl3MEJDUUVXRVdab1lXNXBhMEJ3YVhadmRHRnNMbWx2TUI0WERURTFNREl5TXpJeU5EVXdNMW9YRFRJMU1ESXlNakl5TkRVd00xb3dnWjh4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSkRUekVVTUJJR0ExVUVCd3dMUTJGemRHeGxJRkp2WTJzeEhEQWFCZ05WQkFvTUUxTmhiV3dnVkdWemRHbHVaeUJUWlhKMlpYSXhDekFKQmdOVkJBc01Ba2xVTVNBd0hnWURWUVFEREJkemFXMXdiR1Z6WVcxc2NHaHdMbU5tWVhCd2N5NXBiekVnTUI0R0NTcUdTSWIzRFFFSkFSWVJabWhoYm1sclFIQnBkbTkwWVd3dWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDNGNuNjJFMXhMcXBOMzRQbWJyS0Jia09YRmp6V2dKOWIrcFh1YVJmdDZBMzM5dXVJUWVvZUg1cWVTS1JWVGwzMkwwZ2R6Mlppdkx3WlhXK2NxdmZ0VlcxdHZFSHZ6SkZ5eGVUVzNmQ1VlQ1FzZWJMbkEycVJhMDdSa3hUbzZOZjI0NG1XV1JEb2Rjb0hFZkRVU2J4ZlRaNklFeFNvalNJVTJSbkQ2V2xsWVdGZEQxR0ZwQkpPbVFCOHJBYzh3SklCZEhGZFFuWDhUdGw3aFo2cnRncUVZTXpZVk11SjJGMnIxSFNVMXpTQXZ3cGRZUDZyUkdGUkpFZmRBOW1tM1dLZk5MU2M1Y2xqejBYL1RYeTB2VmxBVjk1bDlxY2ZGelBtcmtOSXN0OUZaU3dwdkI0OUx5QVZrZTA0RlFQUHdMZ1ZINGdwaGlKSDNqdlo3SStKNWxTOFZBZ01CQUFHalVEQk9NQjBHQTFVZERnUVdCQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBZkJnTlZIU01FR0RBV2dCVFR5UDZDYzVIbEJKNSt1Y1ZDd0djNW9nS05HekFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZNUzRFUWVQL2lwVjRqT0c1bE82L3RZQ2IvaUplQWR1T25SaGtKazBEYlgzMjlsRExaaFRUTC94L3cvOW11Q1ZjdkxyekVwNlBOK1ZXZnc1RTVGV3RaTjB5aEd0UDlSK3ZabnJWK29jMnpHRCtubzEveVNGT2UzRWlKQ081ZGVoeEtqWUVtQlJ2NXNVL0xaRktacG96S04vQk1FYTZDcUx1eGJ6Yjd5a3hWcjdFVkZYd2x0UHh6RTlUbUw5T0FDTk55RjVlSkhXTVJNbGxhclV2a2NYbGg0cHV4NGtzOWU2elY5RFFCeTJ6ZHM5ZjFJM3F4ZzBlWDZKbkdyWGkvWmlDVCtsSmdWZTNaRlhpZWppTEFpS0IwNHNYVzN0aTBMVzNseDEzWTFZbFE0L3RscGdUZ2ZJSnhLVjZueVBpTG9LMG55d2JNZCt2cEFpckR0Mk9jK2hrPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vbG9jYWxob3N0OjgwODAvc2FtbDIvc2VydmljZS1wcm92aWRlci1tZXRhZGF0YS9vbmUiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj50ZXN0dXNlckBzcHJpbmcuc2VjdXJpdHkuc2FtbDwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjA1Mi0wOS0yOFQwMjo1MDowNFoiIFJlY2lwaWVudD0iaHR0cDovL2xvY2FsaG9zdDo4MDgwL2xvZ2luL3NhbWwyL3Nzby9vbmUiIEluUmVzcG9uc2VUbz0iQVJRYzk4ZjIwMC1kY2Y3LTRkZjQtYTUyMi0wNzIwNjIwOGIwN2QiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAyMS0wMS0yMFQwMTowMjo1NFoiIE5vdE9uT3JBZnRlcj0iMjA1Mi0wOS0yOFQwMjo1MDowNFoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbWwyL3NlcnZpY2UtcHJvdmlkZXItbWV0YWRhdGEvb25lPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAyMS0wMS0yMFQwMDo0ODoyOVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDEtMjBUMDg6NDg6MjlaIiBTZXNzaW9uSW5kZXg9Il9lN2ExYTllNDk1YmZlMjI2NjQ5ZThkY2MzN2UxNDE1NDQ5NTIxNWQ2ZWIiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdHVzZXJAc3ByaW5nLnNlY3VyaXR5LnNhbWw8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPm1lbWJlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVtYWlsQWRkcmVzcyIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdHVzZXJAc3ByaW5nLnNlY3VyaXR5LnNhbWw8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4="; - - static final Map> USER_ATTRIBUTES = new LinkedHashMap<>(); - - static { - USER_ATTRIBUTES.put("uid", Arrays.asList("testuser@spring.security.saml")); - USER_ATTRIBUTES.put("eduPersonAffiliation", Arrays.asList("member", "user")); - USER_ATTRIBUTES.put("emailAddress", Arrays.asList("testuser@spring.security.saml")); - } - @Autowired MockMvc mvc; @@ -71,51 +48,38 @@ public class Saml2LoginApplicationITests { this.webClient.getCookieManager().clearCookies(); } - @Test - void indexWhenSamlResponseThenShowsUserInformation() throws Exception { - HttpSession session = this.mvc.perform(get("http://localhost:8080/")).andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost:8080/saml2/authenticate/one")).andReturn().getRequest() - .getSession(); - - this.mvc.perform(post("http://localhost:8080/login/saml2/sso/one").param("SAMLResponse", SIGNED_RESPONSE) - .session((MockHttpSession) session)).andExpect(redirectedUrl("http://localhost:8080/")); - - this.mvc.perform(get("http://localhost:8080/").session((MockHttpSession) session)) - .andExpect(model().attribute("emailAddress", "testuser@spring.security.saml")) - .andExpect(model().attribute("userAttributes", USER_ATTRIBUTES)); - } - @Test void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { - HtmlPage relyingParty = performLogin(); - assertThat(relyingParty.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); + performLogin(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); } - @Test - void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement rpLogoutButton = relyingParty.getHtmlElementById("rp_logout_button"); - HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); - } - - @Test - void logoutWhenAssertingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - HtmlPage relyingParty = performLogin(); - HtmlElement apLogoutButton = relyingParty.getHtmlElementById("ap_logout_button"); - HtmlPage loginPage = apLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); - } - - private HtmlPage performLogin() throws IOException { - HtmlPage assertingParty = this.webClient.getPage("/"); - HtmlForm form = assertingParty.getFormByName("f"); + private void performLogin() throws Exception { + HtmlPage login = this.webClient.getPage("/"); + this.webClient.waitForBackgroundJavaScript(10000); + HtmlForm form = findForm(login); HtmlInput username = form.getInputByName("username"); - HtmlInput password = form.getInputByName("password"); - HtmlSubmitInput submit = assertingParty.getHtmlElementById("submit_button"); - username.setValueAttribute("user"); - password.setValueAttribute("password"); - return submit.click(); + HtmlPasswordInput password = form.getInputByName("password"); + HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); + username.type("testuser@spring.security.saml"); + password.type("12345678"); + submit.click(); + this.webClient.waitForBackgroundJavaScript(10000); + } + + private HtmlForm findForm(HtmlPage login) { + for (HtmlForm form : login.getForms()) { + try { + if (form.getId().equals("form19")) { + return form; + } + } + catch (ElementNotFoundException ex) { + // Continue + } + } + throw new IllegalStateException("Could not resolve login form"); } } diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/java/example/IndexController.java b/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/java/example/IndexController.java index 43d6606..1d83021 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/java/example/IndexController.java +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/java/example/IndexController.java @@ -27,7 +27,7 @@ public class IndexController { @GetMapping("/") public String index(Model model, @AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) { - String emailAddress = principal.getFirstAttribute("emailAddress"); + String emailAddress = principal.getFirstAttribute("email"); model.addAttribute("emailAddress", emailAddress); model.addAttribute("userAttributes", principal.getAttributes()); return "index"; diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/application.yml b/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/application.yml index 157a113..297e69f 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/application.yml +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/application.yml @@ -8,7 +8,7 @@ spring: - private-key-location: classpath:credentials/rp-private.key certificate-location: classpath:credentials/rp-certificate.crt identityprovider: - metadata-uri: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php + metadata-uri: https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata logging.level: org.springframework.security: TRACE diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/templates/index.html b/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/templates/index.html index 2970e85..2e970b2 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/templates/index.html +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/templates/index.html @@ -36,11 +36,6 @@ -
diff --git a/servlet/xml/java/saml2/login-logout/build.gradle b/servlet/xml/java/saml2/login-logout/build.gradle index 8f4f24c..12442ec 100644 --- a/servlet/xml/java/saml2/login-logout/build.gradle +++ b/servlet/xml/java/saml2/login-logout/build.gradle @@ -1,5 +1,6 @@ plugins { id "java" + id "nebula.integtest" version "8.2.0" id "org.gretty" version "3.0.6" id "war" } @@ -33,7 +34,7 @@ dependencies { testImplementation "org.springframework:spring-test" testImplementation "org.springframework.security:spring-security-test" testImplementation "org.junit.jupiter:junit-jupiter-api" - testImplementation 'net.sourceforge.htmlunit:htmlunit:2.49.1' + testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") diff --git a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/LocalHostWebClient.java b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/LocalHostWebClient.java new file mode 100644 index 0000000..8cefa23 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/LocalHostWebClient.java @@ -0,0 +1,53 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example; + +import java.io.IOException; + +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebClient; + +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; + +/** + * {@link WebClient} will automatically prefix relative URLs with + * localhost:${local.server.port}. + * + * @author Phillip Webb + * @since 1.4.0 + */ +public class LocalHostWebClient extends WebClient { + + private final Environment environment; + + public LocalHostWebClient(Environment environment) { + Assert.notNull(environment, "Environment must not be null"); + this.environment = environment; + } + + @Override + public

P getPage(String url) throws IOException, FailingHttpStatusCodeException { + if (url.startsWith("/")) { + String port = this.environment.getProperty("local.server.port", "8080"); + url = "http://localhost:" + port + url; + } + return super.getPage(url); + } + +} diff --git a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java new file mode 100644 index 0000000..fb72938 --- /dev/null +++ b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example; + +import com.gargoylesoftware.htmlunit.ElementNotFoundException; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlInput; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring-servlet.xml", + "file:src/main/webapp/WEB-INF/spring/security.xml" }) +@WebAppConfiguration +public class Saml2XmlITests { + + private MockMvc mvc; + + private WebClient webClient; + + @Autowired + WebApplicationContext webApplicationContext; + + @Autowired + Environment environment; + + @BeforeEach + void setup() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext) + .apply(SecurityMockMvcConfigurers.springSecurity()).build(); + this.webClient = MockMvcWebClientBuilder.mockMvcSetup(this.mvc) + .withDelegate(new LocalHostWebClient(this.environment)).build(); + this.webClient.getCookieManager().clearCookies(); + } + + @Test + void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { + performLogin(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + } + + @Test + void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { + performLogin(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); + HtmlPage loginPage = rpLogoutButton.click(); + assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + } + + private void performLogin() throws Exception { + HtmlPage login = this.webClient.getPage("/"); + this.webClient.waitForBackgroundJavaScript(10000); + HtmlForm form = findForm(login); + HtmlInput username = form.getInputByName("username"); + HtmlPasswordInput password = form.getInputByName("password"); + HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); + username.type("testuser@spring.security.saml"); + password.type("12345678"); + submit.click(); + this.webClient.waitForBackgroundJavaScript(10000); + } + + private HtmlForm findForm(HtmlPage login) { + for (HtmlForm form : login.getForms()) { + try { + if (form.getId().equals("form19")) { + return form; + } + } + catch (ElementNotFoundException ex) { + // Continue + } + } + throw new IllegalStateException("Could not resolve login form"); + } + +} diff --git a/servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java b/servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java index 56c51f8..9644b60 100644 --- a/servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java +++ b/servlet/xml/java/saml2/login-logout/src/main/java/example/IndexController.java @@ -34,7 +34,7 @@ public class IndexController { @GetMapping("/") public String index(Model model, @AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) { - String emailAddress = principal.getFirstAttribute("emailAddress"); + String emailAddress = principal.getFirstAttribute("email"); model.addAttribute("emailAddress", emailAddress); model.addAttribute("userAttributes", principal.getAttributes()); return "index"; diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml index 95b8ff4..dcb3466 100644 --- a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/spring/security.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - + @@ -14,40 +14,15 @@ - - - - - - - - + metadata-location="https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata" + single-logout-service-location="https://dev-05937739.okta.com/app/dev-05937739_springgsecuritysaml2idp_1/exk46xofd8NZvFCpS5d7/slo/saml" + single-logout-service-response-location="{baseUrl}/logout/saml2/slo"> - - - - diff --git a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html index dd65ffd..7bba2e3 100644 --- a/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html +++ b/servlet/xml/java/saml2/login-logout/src/main/webapp/WEB-INF/templates/index.html @@ -36,11 +36,6 @@ -

diff --git a/settings.gradle b/settings.gradle index fcbbf0f..7484ac5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -39,7 +39,7 @@ include ":servlet:java-configuration:hello-mvc-security" include ":servlet:java-configuration:hello-security" include ":servlet:java-configuration:hello-security-explicit" include ":servlet:java-configuration:max-sessions" -//include ":servlet:java-configuration:saml2:login" +include ":servlet:java-configuration:saml2:login" include ":servlet:spring-boot:java:authentication:username-password:user-details-service:custom-user" include ":servlet:spring-boot:java:authentication:username-password:mfa" include ":servlet:spring-boot:java:hello" @@ -55,9 +55,9 @@ include ":servlet:spring-boot:java:oauth2:resource-server:multi-tenancy" include ":servlet:spring-boot:java:oauth2:resource-server:opaque" include ":servlet:spring-boot:java:oauth2:resource-server:static" include ":servlet:spring-boot:java:oauth2:webclient" -//include ":servlet:spring-boot:java:saml2:login" -//include ":servlet:spring-boot:java:saml2:login-single-tenant" -//include ":servlet:spring-boot:java:saml2:refreshable-metadata" +include ":servlet:spring-boot:java:saml2:login" +include ":servlet:spring-boot:java:saml2:login-single-tenant" +include ":servlet:spring-boot:java:saml2:refreshable-metadata" include ":servlet:spring-boot:kotlin:hello-security" include ":servlet:xml:java:helloworld" include ":servlet:xml:java:preauth" From a4c998ed7752f0aa18cd92396d7df9851d18e53c Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 28 Mar 2022 11:00:25 -0300 Subject: [PATCH 14/35] Update README for SAML 2.0 samples --- .../saml2/login/README.adoc | 19 ++++++++++--------- .../saml2/login-single-tenant/README.adoc | 12 ++++++------ .../spring-boot/java/saml2/login/README.adoc | 12 ++++++------ .../saml2/refreshable-metadata/README.adoc | 18 ++++++++++-------- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/servlet/java-configuration/saml2/login/README.adoc b/servlet/java-configuration/saml2/login/README.adoc index d8b17db..cda90b5 100644 --- a/servlet/java-configuration/saml2/login/README.adoc +++ b/servlet/java-configuration/saml2/login/README.adoc @@ -12,18 +12,18 @@ The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[S === SAML 2.0 Login -`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. The following features are implemented in the MVP: 1. Receive and validate a SAML 2.0 Response containing an assertion, and create a corresponding authentication in Spring Security 2. Send a SAML 2.0 AuthNRequest to an Identity Provider 3. Provide a framework for components used in SAML 2.0 authentication that can be swapped by configuration -4. Work against the SimpleSAMLphp reference implementation +4. Work against the Okta SAML 2.0 IDP reference implementation === SAML 2.0 Single Logout -`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. @@ -31,20 +31,21 @@ You can refer to the https://docs.spring.io/spring-security/reference/servlet/sa == Run the Sample -=== Start up the application - -You should run the application war in a servlet container like Tomcat +=== Start up the Sample Boot Application +``` + ./gradlew :spring-security-samples-boot-saml2login:bootRun +``` === Open a Browser http://localhost:8080/ -You will be redirect to the SimpleSAMLphp IDP +You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: user -Password: password +User: testuser@spring.security.saml +Password: 12345678 ``` diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc index 37f3427..cda90b5 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc +++ b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc @@ -12,18 +12,18 @@ The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[S === SAML 2.0 Login -`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. The following features are implemented in the MVP: 1. Receive and validate a SAML 2.0 Response containing an assertion, and create a corresponding authentication in Spring Security 2. Send a SAML 2.0 AuthNRequest to an Identity Provider 3. Provide a framework for components used in SAML 2.0 authentication that can be swapped by configuration -4. Work against the SimpleSAMLphp reference implementation +4. Work against the Okta SAML 2.0 IDP reference implementation === SAML 2.0 Single Logout -`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. @@ -40,12 +40,12 @@ You can refer to the https://docs.spring.io/spring-security/reference/servlet/sa http://localhost:8080/ -You will be redirect to the SimpleSAMLphp IDP +You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: user -Password: password +User: testuser@spring.security.saml +Password: 12345678 ``` diff --git a/servlet/spring-boot/java/saml2/login/README.adoc b/servlet/spring-boot/java/saml2/login/README.adoc index 37f3427..cda90b5 100644 --- a/servlet/spring-boot/java/saml2/login/README.adoc +++ b/servlet/spring-boot/java/saml2/login/README.adoc @@ -12,18 +12,18 @@ The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[S === SAML 2.0 Login -`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. The following features are implemented in the MVP: 1. Receive and validate a SAML 2.0 Response containing an assertion, and create a corresponding authentication in Spring Security 2. Send a SAML 2.0 AuthNRequest to an Identity Provider 3. Provide a framework for components used in SAML 2.0 authentication that can be swapped by configuration -4. Work against the SimpleSAMLphp reference implementation +4. Work against the Okta SAML 2.0 IDP reference implementation === SAML 2.0 Single Logout -`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. @@ -40,12 +40,12 @@ You can refer to the https://docs.spring.io/spring-security/reference/servlet/sa http://localhost:8080/ -You will be redirect to the SimpleSAMLphp IDP +You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: user -Password: password +User: testuser@spring.security.saml +Password: 12345678 ``` diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc index 9c39ca0..9694ed0 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc @@ -1,4 +1,4 @@ -= SAML 2.0 Login & Logout Sample += SAML 2.0 Refreshable Metadata This guide provides instructions on setting up this SAML 2.0 Login & Logout sample application. It uses https://simplesamlphp.org/[SimpleSAMLphp] as its asserting party. @@ -12,18 +12,18 @@ The https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html[S === SAML 2.0 Login -`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. The following features are implemented in the MVP: 1. Receive and validate a SAML 2.0 Response containing an assertion, and create a corresponding authentication in Spring Security 2. Send a SAML 2.0 AuthNRequest to an Identity Provider 3. Provide a framework for components used in SAML 2.0 authentication that can be swapped by configuration -4. Work against the SimpleSAMLphp reference implementation +4. Work against the Okta SAML 2.0 IDP reference implementation === SAML 2.0 Single Logout -`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. +`saml2Logout()` supports RP- and AP-initiated SAML 2.0 Single Logout via the HTTP-POST and HTTP-REDIRECT bindings against the https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/[Okta SAML 2.0 IDP] reference implementation. On this sample, the SAML 2.0 Logout is using the HTTP-POST binding. @@ -38,19 +38,21 @@ This particular implementation uses a `@Scheduled` annotation to update its meta === Start up the Sample Boot Application ``` - ./gradlew :servlet:spring-boot:java:saml2:refreshable-metadata:bootRun + ./gradlew :spring-security-samples-boot-saml2login:bootRun ``` === Open a Browser http://localhost:8080/ -You will be redirect to the SimpleSAMLphp IDP +You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: user -Password: password +User: testuser@spring.security.saml +Password: 12345678 ``` + + From dbf3fbb63513dbf140bb2cda49218a1160f2998e Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Mon, 28 Mar 2022 11:22:43 -0500 Subject: [PATCH 15/35] Update to Spring Authorization Server 0.2.3 --- .../spring-boot/java/oauth2/authorization-server/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle index 0406027..24b0cf1 100644 --- a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle +++ b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle @@ -13,7 +13,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.security:spring-security-oauth2-authorization-server:0.2.1' + implementation 'org.springframework.security:spring-security-oauth2-authorization-server:0.2.3' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' From b67e18fb82d58a0c4875d801b39d4fd33f1eb1c7 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Fri, 21 Jan 2022 08:51:15 -0300 Subject: [PATCH 16/35] Add init script to be used in Spring Security CI Issue https://github.com/spring-projects/spring-security/issues/10344 --- spring-security-ci.gradle | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 spring-security-ci.gradle diff --git a/spring-security-ci.gradle b/spring-security-ci.gradle new file mode 100644 index 0000000..fa710e7 --- /dev/null +++ b/spring-security-ci.gradle @@ -0,0 +1,34 @@ +allprojects { + repositories { + exclusiveContent { + forRepository { + maven { + url "file://$localRepositoryPath" + } + } + filter { + includeVersion "org.springframework.security", "spring-security-acl", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-aspects", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-bom", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-cas", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-config", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-core", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-crypto", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-data", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-ldap", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-messaging", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-oauth2-client", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-oauth2-core", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-oauth2-jose", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-oauth2-resource-server", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-openid", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-remoting", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-rsocket", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-saml2-service-provider", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-taglibs", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-test", "$springSecurityVersion" + includeVersion "org.springframework.security", "spring-security-web", "$springSecurityVersion" + } + } + } +} From 5834a59ce6bacf5fb09f555c38ebf331c018e648 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 12 May 2022 10:05:52 -0300 Subject: [PATCH 17/35] Use Spring Security 5.8.0-SNAPSHOT Issue gh-77 --- gradle.properties | 4 ++-- reactive/rsocket/hello-security/gradle.properties | 4 ++-- reactive/webflux-fn/hello-security/gradle.properties | 4 ++-- reactive/webflux-fn/hello/gradle.properties | 4 ++-- .../authentication/username-password/form/gradle.properties | 4 ++-- reactive/webflux/java/authentication/x509/gradle.properties | 4 ++-- .../webflux/java/hello-security-explicit/gradle.properties | 4 ++-- reactive/webflux/java/hello-security/gradle.properties | 4 ++-- reactive/webflux/java/hello/gradle.properties | 4 ++-- reactive/webflux/java/method/gradle.properties | 4 ++-- reactive/webflux/java/oauth2/login/gradle.properties | 4 ++-- .../webflux/java/oauth2/resource-server/gradle.properties | 4 ++-- reactive/webflux/java/oauth2/webclient/gradle.properties | 4 ++-- reactive/webflux/kotlin/hello-security/gradle.properties | 2 +- servlet/java-configuration/aspectj/build.gradle | 2 +- .../java-configuration/authentication/preauth/build.gradle | 2 +- .../authentication/remember-me/build.gradle | 4 ++-- .../authentication/username-password/form/build.gradle | 2 +- .../authentication/username-password/in-memory/build.gradle | 2 +- .../authentication/username-password/jdbc/build.gradle | 2 +- .../authentication/username-password/ldap/build.gradle | 2 +- servlet/java-configuration/authentication/x509/build.gradle | 2 +- servlet/java-configuration/data/build.gradle | 2 +- servlet/java-configuration/hello-mvc-security/build.gradle | 2 +- .../java-configuration/hello-security-explicit/build.gradle | 2 +- servlet/java-configuration/hello-security/build.gradle | 2 +- servlet/java-configuration/max-sessions/build.gradle | 2 +- servlet/java-configuration/saml2/login/build.gradle | 2 +- .../authentication/username-password/mfa/gradle.properties | 4 ++-- .../user-details-service/custom-user/gradle.properties | 4 ++-- .../java/hello-security-explicit/gradle.properties | 4 ++-- servlet/spring-boot/java/hello-security/gradle.properties | 4 ++-- servlet/spring-boot/java/hello/gradle.properties | 4 ++-- servlet/spring-boot/java/jwt/login/gradle.properties | 4 ++-- servlet/spring-boot/java/ldap/gradle.properties | 4 ++-- .../java/oauth2/authorization-server/gradle.properties | 4 ++-- servlet/spring-boot/java/oauth2/login/gradle.properties | 4 ++-- .../oauth2/resource-server/hello-security/gradle.properties | 4 ++-- .../java/oauth2/resource-server/jwe/gradle.properties | 4 ++-- .../oauth2/resource-server/multi-tenancy/gradle.properties | 4 ++-- .../java/oauth2/resource-server/opaque/gradle.properties | 4 ++-- .../java/oauth2/resource-server/static/gradle.properties | 4 ++-- servlet/spring-boot/java/oauth2/webclient/gradle.properties | 4 ++-- .../java/saml2/login-single-tenant/gradle.properties | 4 ++-- servlet/spring-boot/java/saml2/login/gradle.properties | 4 ++-- .../java/saml2/refreshable-metadata/gradle.properties | 4 ++-- servlet/spring-boot/kotlin/hello-security/gradle.properties | 2 +- servlet/xml/java/contacts/build.gradle | 2 +- servlet/xml/java/contacts/gradle.properties | 2 +- servlet/xml/java/dms/build.gradle | 2 +- servlet/xml/java/dms/gradle.properties | 2 +- servlet/xml/java/helloworld/build.gradle | 2 +- servlet/xml/java/helloworld/gradle.properties | 2 +- servlet/xml/java/preauth/build.gradle | 2 +- servlet/xml/java/preauth/gradle.properties | 2 +- servlet/xml/java/saml2/login-logout/build.gradle | 2 +- servlet/xml/java/saml2/login-logout/gradle.properties | 2 +- 57 files changed, 89 insertions(+), 89 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2f11f4c..c500dd4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true org.gradle.caching=true diff --git a/reactive/rsocket/hello-security/gradle.properties b/reactive/rsocket/hello-security/gradle.properties index e19a862..bd54891 100644 --- a/reactive/rsocket/hello-security/gradle.properties +++ b/reactive/rsocket/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux-fn/hello-security/gradle.properties b/reactive/webflux-fn/hello-security/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux-fn/hello-security/gradle.properties +++ b/reactive/webflux-fn/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux-fn/hello/gradle.properties b/reactive/webflux-fn/hello/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux-fn/hello/gradle.properties +++ b/reactive/webflux-fn/hello/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/authentication/username-password/form/gradle.properties b/reactive/webflux/java/authentication/username-password/form/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/authentication/username-password/form/gradle.properties +++ b/reactive/webflux/java/authentication/username-password/form/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/authentication/x509/gradle.properties b/reactive/webflux/java/authentication/x509/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/authentication/x509/gradle.properties +++ b/reactive/webflux/java/authentication/x509/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/hello-security-explicit/gradle.properties b/reactive/webflux/java/hello-security-explicit/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/hello-security-explicit/gradle.properties +++ b/reactive/webflux/java/hello-security-explicit/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/hello-security/gradle.properties b/reactive/webflux/java/hello-security/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/hello-security/gradle.properties +++ b/reactive/webflux/java/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/hello/gradle.properties b/reactive/webflux/java/hello/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/hello/gradle.properties +++ b/reactive/webflux/java/hello/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/method/gradle.properties b/reactive/webflux/java/method/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/method/gradle.properties +++ b/reactive/webflux/java/method/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/oauth2/login/gradle.properties b/reactive/webflux/java/oauth2/login/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/oauth2/login/gradle.properties +++ b/reactive/webflux/java/oauth2/login/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/oauth2/resource-server/gradle.properties b/reactive/webflux/java/oauth2/resource-server/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/oauth2/resource-server/gradle.properties +++ b/reactive/webflux/java/oauth2/resource-server/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/java/oauth2/webclient/gradle.properties b/reactive/webflux/java/oauth2/webclient/gradle.properties index e19a862..bd54891 100644 --- a/reactive/webflux/java/oauth2/webclient/gradle.properties +++ b/reactive/webflux/java/oauth2/webclient/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/reactive/webflux/kotlin/hello-security/gradle.properties b/reactive/webflux/kotlin/hello-security/gradle.properties index e7f8e22..685b044 100644 --- a/reactive/webflux/kotlin/hello-security/gradle.properties +++ b/reactive/webflux/kotlin/hello-security/gradle.properties @@ -1 +1 @@ -spring-security.version=5.7.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/java-configuration/aspectj/build.gradle b/servlet/java-configuration/aspectj/build.gradle index 05a0175..c7545f1 100644 --- a/servlet/java-configuration/aspectj/build.gradle +++ b/servlet/java-configuration/aspectj/build.gradle @@ -15,7 +15,7 @@ dependencies { aspect "org.springframework.security:spring-security-aspects" implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/preauth/build.gradle b/servlet/java-configuration/authentication/preauth/build.gradle index c71c7b2..7951bbf 100644 --- a/servlet/java-configuration/authentication/preauth/build.gradle +++ b/servlet/java-configuration/authentication/preauth/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/remember-me/build.gradle b/servlet/java-configuration/authentication/remember-me/build.gradle index 494e8c3..7951bbf 100644 --- a/servlet/java-configuration/authentication/remember-me/build.gradle +++ b/servlet/java-configuration/authentication/remember-me/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" @@ -36,4 +36,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/java-configuration/authentication/username-password/form/build.gradle b/servlet/java-configuration/authentication/username-password/form/build.gradle index c71c7b2..7951bbf 100644 --- a/servlet/java-configuration/authentication/username-password/form/build.gradle +++ b/servlet/java-configuration/authentication/username-password/form/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle index c71c7b2..7951bbf 100644 --- a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle +++ b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle index c7830bd..d86f41c 100644 --- a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle +++ b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.hsqldb:hsqldb:2.5.1" diff --git a/servlet/java-configuration/authentication/username-password/ldap/build.gradle b/servlet/java-configuration/authentication/username-password/ldap/build.gradle index 8d426d4..93a00fd 100644 --- a/servlet/java-configuration/authentication/username-password/ldap/build.gradle +++ b/servlet/java-configuration/authentication/username-password/ldap/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/authentication/x509/build.gradle b/servlet/java-configuration/authentication/x509/build.gradle index 53401f2..298d306 100644 --- a/servlet/java-configuration/authentication/x509/build.gradle +++ b/servlet/java-configuration/authentication/x509/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/data/build.gradle b/servlet/java-configuration/data/build.gradle index 9b13bcc..0813bd4 100644 --- a/servlet/java-configuration/data/build.gradle +++ b/servlet/java-configuration/data/build.gradle @@ -12,7 +12,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") implementation platform("org.springframework.data:spring-data-releasetrain:Neumann-SR5") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/hello-mvc-security/build.gradle b/servlet/java-configuration/hello-mvc-security/build.gradle index c71c7b2..7951bbf 100644 --- a/servlet/java-configuration/hello-mvc-security/build.gradle +++ b/servlet/java-configuration/hello-mvc-security/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/hello-security-explicit/build.gradle b/servlet/java-configuration/hello-security-explicit/build.gradle index a116554..1bca326 100644 --- a/servlet/java-configuration/hello-security-explicit/build.gradle +++ b/servlet/java-configuration/hello-security-explicit/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/hello-security/build.gradle b/servlet/java-configuration/hello-security/build.gradle index a116554..1bca326 100644 --- a/servlet/java-configuration/hello-security/build.gradle +++ b/servlet/java-configuration/hello-security/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/max-sessions/build.gradle b/servlet/java-configuration/max-sessions/build.gradle index c71c7b2..7951bbf 100644 --- a/servlet/java-configuration/max-sessions/build.gradle +++ b/servlet/java-configuration/max-sessions/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/java-configuration/saml2/login/build.gradle b/servlet/java-configuration/saml2/login/build.gradle index 469e541..68f729f 100644 --- a/servlet/java-configuration/saml2/login/build.gradle +++ b/servlet/java-configuration/saml2/login/build.gradle @@ -37,7 +37,7 @@ dependencies { implementation "org.opensaml:opensaml-saml-impl:4.1.1" } implementation platform("org.springframework:spring-framework-bom:5.3.11") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties b/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties +++ b/servlet/spring-boot/java/authentication/username-password/mfa/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties +++ b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/hello-security-explicit/gradle.properties b/servlet/spring-boot/java/hello-security-explicit/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/hello-security-explicit/gradle.properties +++ b/servlet/spring-boot/java/hello-security-explicit/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/hello-security/gradle.properties b/servlet/spring-boot/java/hello-security/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/hello-security/gradle.properties +++ b/servlet/spring-boot/java/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/hello/gradle.properties b/servlet/spring-boot/java/hello/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/hello/gradle.properties +++ b/servlet/spring-boot/java/hello/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/jwt/login/gradle.properties b/servlet/spring-boot/java/jwt/login/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/jwt/login/gradle.properties +++ b/servlet/spring-boot/java/jwt/login/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/ldap/gradle.properties b/servlet/spring-boot/java/ldap/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/ldap/gradle.properties +++ b/servlet/spring-boot/java/ldap/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties b/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties +++ b/servlet/spring-boot/java/oauth2/authorization-server/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/login/gradle.properties b/servlet/spring-boot/java/oauth2/login/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/login/gradle.properties +++ b/servlet/spring-boot/java/oauth2/login/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/hello-security/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/jwe/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/opaque/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties b/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties +++ b/servlet/spring-boot/java/oauth2/resource-server/static/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/oauth2/webclient/gradle.properties b/servlet/spring-boot/java/oauth2/webclient/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/oauth2/webclient/gradle.properties +++ b/servlet/spring-boot/java/oauth2/webclient/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties b/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties +++ b/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/java/saml2/login/gradle.properties b/servlet/spring-boot/java/saml2/login/gradle.properties index 0e2ed66..eeb7238 100644 --- a/servlet/spring-boot/java/saml2/login/gradle.properties +++ b/servlet/spring-boot/java/saml2/login/gradle.properties @@ -1,3 +1,3 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT selenium-htmlunit.version=2.44.0 diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties b/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties index e19a862..bd54891 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties @@ -1,2 +1,2 @@ -version=5.7.0-SNAPSHOT -spring-security.version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/spring-boot/kotlin/hello-security/gradle.properties b/servlet/spring-boot/kotlin/hello-security/gradle.properties index e7f8e22..685b044 100644 --- a/servlet/spring-boot/kotlin/hello-security/gradle.properties +++ b/servlet/spring-boot/kotlin/hello-security/gradle.properties @@ -1 +1 @@ -spring-security.version=5.7.0-SNAPSHOT +spring-security.version=5.8.0-SNAPSHOT diff --git a/servlet/xml/java/contacts/build.gradle b/servlet/xml/java/contacts/build.gradle index 6d83ebf..9290adc 100644 --- a/servlet/xml/java/contacts/build.gradle +++ b/servlet/xml/java/contacts/build.gradle @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.springframework:spring-framework-bom:5.3.9") implementation platform("org.junit:junit-bom:5.7.0") diff --git a/servlet/xml/java/contacts/gradle.properties b/servlet/xml/java/contacts/gradle.properties index ca3504e..071e1d2 100644 --- a/servlet/xml/java/contacts/gradle.properties +++ b/servlet/xml/java/contacts/gradle.properties @@ -1 +1 @@ -version=5.7.0-SNAPSHOT \ No newline at end of file +version=5.8.0-SNAPSHOT diff --git a/servlet/xml/java/dms/build.gradle b/servlet/xml/java/dms/build.gradle index f0232dd..f9d78e1 100644 --- a/servlet/xml/java/dms/build.gradle +++ b/servlet/xml/java/dms/build.gradle @@ -10,7 +10,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.springframework:spring-framework-bom:5.3.9") implementation platform("org.junit:junit-bom:5.7.0") diff --git a/servlet/xml/java/dms/gradle.properties b/servlet/xml/java/dms/gradle.properties index e170fd3..071e1d2 100644 --- a/servlet/xml/java/dms/gradle.properties +++ b/servlet/xml/java/dms/gradle.properties @@ -1 +1 @@ -version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT diff --git a/servlet/xml/java/helloworld/build.gradle b/servlet/xml/java/helloworld/build.gradle index c3a2e05..f98dc71 100644 --- a/servlet/xml/java/helloworld/build.gradle +++ b/servlet/xml/java/helloworld/build.gradle @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/xml/java/helloworld/gradle.properties b/servlet/xml/java/helloworld/gradle.properties index e170fd3..071e1d2 100644 --- a/servlet/xml/java/helloworld/gradle.properties +++ b/servlet/xml/java/helloworld/gradle.properties @@ -1 +1 @@ -version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT diff --git a/servlet/xml/java/preauth/build.gradle b/servlet/xml/java/preauth/build.gradle index b45b649..d518edf 100644 --- a/servlet/xml/java/preauth/build.gradle +++ b/servlet/xml/java/preauth/build.gradle @@ -10,7 +10,7 @@ repositories { } dependencies { - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/xml/java/preauth/gradle.properties b/servlet/xml/java/preauth/gradle.properties index e170fd3..071e1d2 100644 --- a/servlet/xml/java/preauth/gradle.properties +++ b/servlet/xml/java/preauth/gradle.properties @@ -1 +1 @@ -version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT diff --git a/servlet/xml/java/saml2/login-logout/build.gradle b/servlet/xml/java/saml2/login-logout/build.gradle index 12442ec..c48e4ba 100644 --- a/servlet/xml/java/saml2/login-logout/build.gradle +++ b/servlet/xml/java/saml2/login-logout/build.gradle @@ -20,7 +20,7 @@ dependencies { implementation "org.opensaml:opensaml-saml-impl:4.1.1" } implementation platform("org.springframework:spring-framework-bom:5.3.13") - implementation platform("org.springframework.security:spring-security-bom:5.7.0-SNAPSHOT") + implementation platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") implementation platform("org.junit:junit-bom:5.7.0") implementation "org.springframework.security:spring-security-config" diff --git a/servlet/xml/java/saml2/login-logout/gradle.properties b/servlet/xml/java/saml2/login-logout/gradle.properties index e170fd3..071e1d2 100644 --- a/servlet/xml/java/saml2/login-logout/gradle.properties +++ b/servlet/xml/java/saml2/login-logout/gradle.properties @@ -1 +1 @@ -version=5.7.0-SNAPSHOT +version=5.8.0-SNAPSHOT From 8de55d289deb54a284cc05618dc54d3f22f94051 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 17 Jan 2022 12:26:21 -0300 Subject: [PATCH 18/35] Switch from jcenter() to mavenCentral() --- servlet/xml/java/helloworld/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet/xml/java/helloworld/build.gradle b/servlet/xml/java/helloworld/build.gradle index f98dc71..6570927 100644 --- a/servlet/xml/java/helloworld/build.gradle +++ b/servlet/xml/java/helloworld/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } From 1e354a358728e651b1a1dc07926f1f21eb527b30 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 17 Jan 2022 12:44:01 -0300 Subject: [PATCH 19/35] Switch remaining jcenter() to mavenCentral() --- reactive/rsocket/hello-security/build.gradle | 2 +- reactive/webflux-fn/hello-security/build.gradle | 2 +- reactive/webflux-fn/hello/build.gradle | 2 +- .../java/authentication/username-password/form/build.gradle | 2 +- reactive/webflux/java/authentication/x509/build.gradle | 2 +- reactive/webflux/java/hello-security-explicit/build.gradle | 2 +- reactive/webflux/java/hello-security/build.gradle | 2 +- reactive/webflux/java/hello/build.gradle | 2 +- reactive/webflux/java/method/build.gradle | 2 +- reactive/webflux/java/oauth2/login/build.gradle | 4 ++-- reactive/webflux/java/oauth2/resource-server/build.gradle | 4 ++-- reactive/webflux/java/oauth2/webclient/build.gradle | 4 ++-- servlet/java-configuration/aspectj/build.gradle | 2 +- .../java-configuration/authentication/preauth/build.gradle | 2 +- .../authentication/remember-me/build.gradle | 2 +- .../authentication/username-password/form/build.gradle | 2 +- .../authentication/username-password/in-memory/build.gradle | 2 +- .../authentication/username-password/jdbc/build.gradle | 2 +- .../authentication/username-password/ldap/build.gradle | 2 +- servlet/java-configuration/authentication/x509/build.gradle | 2 +- servlet/java-configuration/data/build.gradle | 2 +- servlet/java-configuration/hello-mvc-security/build.gradle | 2 +- .../java-configuration/hello-security-explicit/build.gradle | 2 +- servlet/java-configuration/hello-security/build.gradle | 2 +- servlet/java-configuration/max-sessions/build.gradle | 2 +- servlet/java-configuration/saml2/login/build.gradle | 2 +- .../java/authentication/username-password/mfa/build.gradle | 2 +- .../user-details-service/custom-user/build.gradle | 2 +- servlet/spring-boot/java/hello-security-explicit/build.gradle | 2 +- servlet/spring-boot/java/hello-security/build.gradle | 2 +- servlet/spring-boot/java/hello/build.gradle | 2 +- servlet/spring-boot/java/jwt/login/build.gradle | 2 +- servlet/spring-boot/java/ldap/build.gradle | 2 +- .../spring-boot/java/oauth2/authorization-server/build.gradle | 2 +- servlet/spring-boot/java/oauth2/login/build.gradle | 2 +- .../java/oauth2/resource-server/hello-security/build.gradle | 4 ++-- .../spring-boot/java/oauth2/resource-server/jwe/build.gradle | 4 ++-- .../java/oauth2/resource-server/multi-tenancy/build.gradle | 2 +- .../java/oauth2/resource-server/opaque/build.gradle | 4 ++-- .../java/oauth2/resource-server/static/build.gradle | 4 ++-- servlet/spring-boot/java/oauth2/webclient/build.gradle | 4 ++-- .../spring-boot/java/saml2/login-single-tenant/build.gradle | 2 +- servlet/spring-boot/java/saml2/login/build.gradle | 2 +- .../spring-boot/java/saml2/refreshable-metadata/build.gradle | 2 +- servlet/xml/java/contacts/build.gradle | 2 +- servlet/xml/java/dms/build.gradle | 2 +- servlet/xml/java/preauth/build.gradle | 2 +- 47 files changed, 55 insertions(+), 55 deletions(-) diff --git a/reactive/rsocket/hello-security/build.gradle b/reactive/rsocket/hello-security/build.gradle index a059ebd..30585ac 100644 --- a/reactive/rsocket/hello-security/build.gradle +++ b/reactive/rsocket/hello-security/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux-fn/hello-security/build.gradle b/reactive/webflux-fn/hello-security/build.gradle index ad05231..d500ed8 100644 --- a/reactive/webflux-fn/hello-security/build.gradle +++ b/reactive/webflux-fn/hello-security/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux-fn/hello/build.gradle b/reactive/webflux-fn/hello/build.gradle index 0c20136..444de4e 100644 --- a/reactive/webflux-fn/hello/build.gradle +++ b/reactive/webflux-fn/hello/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/authentication/username-password/form/build.gradle b/reactive/webflux/java/authentication/username-password/form/build.gradle index 1c7b8ab..4ed2d27 100644 --- a/reactive/webflux/java/authentication/username-password/form/build.gradle +++ b/reactive/webflux/java/authentication/username-password/form/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/authentication/x509/build.gradle b/reactive/webflux/java/authentication/x509/build.gradle index d046093..90195b4 100644 --- a/reactive/webflux/java/authentication/x509/build.gradle +++ b/reactive/webflux/java/authentication/x509/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/hello-security-explicit/build.gradle b/reactive/webflux/java/hello-security-explicit/build.gradle index 414e2ab..7ef4928 100644 --- a/reactive/webflux/java/hello-security-explicit/build.gradle +++ b/reactive/webflux/java/hello-security-explicit/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/hello-security/build.gradle b/reactive/webflux/java/hello-security/build.gradle index aaef98f..0150ff3 100644 --- a/reactive/webflux/java/hello-security/build.gradle +++ b/reactive/webflux/java/hello-security/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/hello/build.gradle b/reactive/webflux/java/hello/build.gradle index 0c20136..444de4e 100644 --- a/reactive/webflux/java/hello/build.gradle +++ b/reactive/webflux/java/hello/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/method/build.gradle b/reactive/webflux/java/method/build.gradle index 0f1c849..680af93 100644 --- a/reactive/webflux/java/method/build.gradle +++ b/reactive/webflux/java/method/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/reactive/webflux/java/oauth2/login/build.gradle b/reactive/webflux/java/oauth2/login/build.gradle index 8baf4b3..3920308 100644 --- a/reactive/webflux/java/oauth2/login/build.gradle +++ b/reactive/webflux/java/oauth2/login/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -26,4 +26,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/reactive/webflux/java/oauth2/resource-server/build.gradle b/reactive/webflux/java/oauth2/resource-server/build.gradle index f14ab31..0274f0d 100644 --- a/reactive/webflux/java/oauth2/resource-server/build.gradle +++ b/reactive/webflux/java/oauth2/resource-server/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -23,4 +23,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/reactive/webflux/java/oauth2/webclient/build.gradle b/reactive/webflux/java/oauth2/webclient/build.gradle index 03051e2..c80b454 100644 --- a/reactive/webflux/java/oauth2/webclient/build.gradle +++ b/reactive/webflux/java/oauth2/webclient/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -26,4 +26,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/java-configuration/aspectj/build.gradle b/servlet/java-configuration/aspectj/build.gradle index c7545f1..3146d43 100644 --- a/servlet/java-configuration/aspectj/build.gradle +++ b/servlet/java-configuration/aspectj/build.gradle @@ -4,7 +4,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/preauth/build.gradle b/servlet/java-configuration/authentication/preauth/build.gradle index 7951bbf..c57463c 100644 --- a/servlet/java-configuration/authentication/preauth/build.gradle +++ b/servlet/java-configuration/authentication/preauth/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/remember-me/build.gradle b/servlet/java-configuration/authentication/remember-me/build.gradle index 7951bbf..c57463c 100644 --- a/servlet/java-configuration/authentication/remember-me/build.gradle +++ b/servlet/java-configuration/authentication/remember-me/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/form/build.gradle b/servlet/java-configuration/authentication/username-password/form/build.gradle index 7951bbf..c57463c 100644 --- a/servlet/java-configuration/authentication/username-password/form/build.gradle +++ b/servlet/java-configuration/authentication/username-password/form/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle index 7951bbf..c57463c 100644 --- a/servlet/java-configuration/authentication/username-password/in-memory/build.gradle +++ b/servlet/java-configuration/authentication/username-password/in-memory/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle index d86f41c..37c28f0 100644 --- a/servlet/java-configuration/authentication/username-password/jdbc/build.gradle +++ b/servlet/java-configuration/authentication/username-password/jdbc/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/username-password/ldap/build.gradle b/servlet/java-configuration/authentication/username-password/ldap/build.gradle index 93a00fd..a8a383f 100644 --- a/servlet/java-configuration/authentication/username-password/ldap/build.gradle +++ b/servlet/java-configuration/authentication/username-password/ldap/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/authentication/x509/build.gradle b/servlet/java-configuration/authentication/x509/build.gradle index 298d306..2095a17 100644 --- a/servlet/java-configuration/authentication/x509/build.gradle +++ b/servlet/java-configuration/authentication/x509/build.gradle @@ -8,7 +8,7 @@ plugins { //apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/data/build.gradle b/servlet/java-configuration/data/build.gradle index 0813bd4..b92f7db 100644 --- a/servlet/java-configuration/data/build.gradle +++ b/servlet/java-configuration/data/build.gradle @@ -4,7 +4,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/hello-mvc-security/build.gradle b/servlet/java-configuration/hello-mvc-security/build.gradle index 7951bbf..c57463c 100644 --- a/servlet/java-configuration/hello-mvc-security/build.gradle +++ b/servlet/java-configuration/hello-mvc-security/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/hello-security-explicit/build.gradle b/servlet/java-configuration/hello-security-explicit/build.gradle index 1bca326..ba08086 100644 --- a/servlet/java-configuration/hello-security-explicit/build.gradle +++ b/servlet/java-configuration/hello-security-explicit/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/hello-security/build.gradle b/servlet/java-configuration/hello-security/build.gradle index 1bca326..ba08086 100644 --- a/servlet/java-configuration/hello-security/build.gradle +++ b/servlet/java-configuration/hello-security/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/max-sessions/build.gradle b/servlet/java-configuration/max-sessions/build.gradle index 7951bbf..c57463c 100644 --- a/servlet/java-configuration/max-sessions/build.gradle +++ b/servlet/java-configuration/max-sessions/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/java-configuration/saml2/login/build.gradle b/servlet/java-configuration/saml2/login/build.gradle index 68f729f..3e34221 100644 --- a/servlet/java-configuration/saml2/login/build.gradle +++ b/servlet/java-configuration/saml2/login/build.gradle @@ -24,7 +24,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } diff --git a/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle b/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle index e31cf48..ac0000e 100644 --- a/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle +++ b/servlet/spring-boot/java/authentication/username-password/mfa/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle index cb31c44..b340e08 100644 --- a/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle +++ b/servlet/spring-boot/java/authentication/username-password/user-details-service/custom-user/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/hello-security-explicit/build.gradle b/servlet/spring-boot/java/hello-security-explicit/build.gradle index 516bea3..a35be68 100644 --- a/servlet/spring-boot/java/hello-security-explicit/build.gradle +++ b/servlet/spring-boot/java/hello-security-explicit/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/hello-security/build.gradle b/servlet/spring-boot/java/hello-security/build.gradle index 0dd6f9e..0040ffb 100644 --- a/servlet/spring-boot/java/hello-security/build.gradle +++ b/servlet/spring-boot/java/hello-security/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/hello/build.gradle b/servlet/spring-boot/java/hello/build.gradle index c57d291..a1baa10 100644 --- a/servlet/spring-boot/java/hello/build.gradle +++ b/servlet/spring-boot/java/hello/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/jwt/login/build.gradle b/servlet/spring-boot/java/jwt/login/build.gradle index 5b1e976..3f92f0e 100644 --- a/servlet/spring-boot/java/jwt/login/build.gradle +++ b/servlet/spring-boot/java/jwt/login/build.gradle @@ -5,7 +5,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/ldap/build.gradle b/servlet/spring-boot/java/ldap/build.gradle index 7ee8f9f..90aeaae 100644 --- a/servlet/spring-boot/java/ldap/build.gradle +++ b/servlet/spring-boot/java/ldap/build.gradle @@ -5,7 +5,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle index 24b0cf1..713ed53 100644 --- a/servlet/spring-boot/java/oauth2/authorization-server/build.gradle +++ b/servlet/spring-boot/java/oauth2/authorization-server/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/login/build.gradle b/servlet/spring-boot/java/oauth2/login/build.gradle index 3af5d5b..7afaf82 100644 --- a/servlet/spring-boot/java/oauth2/login/build.gradle +++ b/servlet/spring-boot/java/oauth2/login/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle index 22700ab..d64e474 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/hello-security/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -23,4 +23,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle index 06d5e7b..40f80e1 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/jwe/build.gradle @@ -22,7 +22,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -39,4 +39,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle index 13b802c..f34a102 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/multi-tenancy/build.gradle @@ -22,7 +22,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle index c0ff1b4..f34a102 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/opaque/build.gradle @@ -22,7 +22,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -40,4 +40,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle b/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle index 06d5e7b..40f80e1 100644 --- a/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle +++ b/servlet/spring-boot/java/oauth2/resource-server/static/build.gradle @@ -22,7 +22,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -39,4 +39,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/oauth2/webclient/build.gradle b/servlet/spring-boot/java/oauth2/webclient/build.gradle index 28cd9a0..6e282ac 100644 --- a/servlet/spring-boot/java/oauth2/webclient/build.gradle +++ b/servlet/spring-boot/java/oauth2/webclient/build.gradle @@ -22,7 +22,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } @@ -44,4 +44,4 @@ dependencies { tasks.withType(Test).configureEach { useJUnitPlatform() outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle index 0769f02..87351e1 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle +++ b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } diff --git a/servlet/spring-boot/java/saml2/login/build.gradle b/servlet/spring-boot/java/saml2/login/build.gradle index 0769f02..87351e1 100644 --- a/servlet/spring-boot/java/saml2/login/build.gradle +++ b/servlet/spring-boot/java/saml2/login/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle index b933682..961ac42 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle @@ -6,7 +6,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } diff --git a/servlet/xml/java/contacts/build.gradle b/servlet/xml/java/contacts/build.gradle index 9290adc..2883efa 100644 --- a/servlet/xml/java/contacts/build.gradle +++ b/servlet/xml/java/contacts/build.gradle @@ -8,7 +8,7 @@ plugins { apply from: "gradle/gretty.gradle" repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/xml/java/dms/build.gradle b/servlet/xml/java/dms/build.gradle index f9d78e1..0f06108 100644 --- a/servlet/xml/java/dms/build.gradle +++ b/servlet/xml/java/dms/build.gradle @@ -4,7 +4,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } diff --git a/servlet/xml/java/preauth/build.gradle b/servlet/xml/java/preauth/build.gradle index d518edf..857449c 100644 --- a/servlet/xml/java/preauth/build.gradle +++ b/servlet/xml/java/preauth/build.gradle @@ -4,7 +4,7 @@ plugins { } repositories { - jcenter() + mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } } From eb3ea3ded68f4c233a5df192af75487f7a483713 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 6 Jul 2022 09:56:39 -0500 Subject: [PATCH 20/35] Update aspectj Spring Security 5.8.0-SNAPSHOT --- servlet/java-configuration/aspectj/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet/java-configuration/aspectj/build.gradle b/servlet/java-configuration/aspectj/build.gradle index 3146d43..6519596 100644 --- a/servlet/java-configuration/aspectj/build.gradle +++ b/servlet/java-configuration/aspectj/build.gradle @@ -11,7 +11,7 @@ repositories { dependencies { aspect platform("org.springframework:spring-framework-bom:5.3.13") - aspect platform("org.springframework.security:spring-security-bom:5.4.0-SNAPSHOT") + aspect platform("org.springframework.security:spring-security-bom:5.8.0-SNAPSHOT") aspect "org.springframework.security:spring-security-aspects" implementation platform("org.springframework:spring-framework-bom:5.3.13") From 57a3de019e7aed647ecc78d88817465f5bee58da Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Wed, 27 Jul 2022 16:43:14 -0600 Subject: [PATCH 21/35] Update Dependencies for Saml2 Login Sample --- servlet/spring-boot/java/saml2/login/build.gradle | 10 +++++----- servlet/spring-boot/java/saml2/login/gradle.properties | 1 - .../java/example/Saml2LoginApplicationITests.java | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/servlet/spring-boot/java/saml2/login/build.gradle b/servlet/spring-boot/java/saml2/login/build.gradle index 87351e1..9dddc2d 100644 --- a/servlet/spring-boot/java/saml2/login/build.gradle +++ b/servlet/spring-boot/java/saml2/login/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.5.2' + id 'org.springframework.boot' version '2.7.1' id 'io.spring.dependency-management' version '1.0.10.RELEASE' id "nebula.integtest" version "8.2.0" id 'java' @@ -14,9 +14,9 @@ repositories { dependencies { constraints { - implementation "org.opensaml:opensaml-core:4.1.1" - implementation "org.opensaml:opensaml-saml-api:4.1.1" - implementation "org.opensaml:opensaml-saml-impl:4.1.1" + implementation "org.opensaml:opensaml-core:4.2.0" + implementation "org.opensaml:opensaml-saml-api:4.2.0" + implementation "org.opensaml:opensaml-saml-impl:4.2.0" } implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' @@ -24,7 +24,7 @@ dependencies { implementation 'org.springframework.security:spring-security-saml2-service-provider' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' + testImplementation 'net.sourceforge.htmlunit:htmlunit' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } diff --git a/servlet/spring-boot/java/saml2/login/gradle.properties b/servlet/spring-boot/java/saml2/login/gradle.properties index eeb7238..bd54891 100644 --- a/servlet/spring-boot/java/saml2/login/gradle.properties +++ b/servlet/spring-boot/java/saml2/login/gradle.properties @@ -1,3 +1,2 @@ version=5.8.0-SNAPSHOT spring-security.version=5.8.0-SNAPSHOT -selenium-htmlunit.version=2.44.0 diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index 00c98b9..261092b 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -89,7 +89,7 @@ public class Saml2LoginApplicationITests { void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { performLogin("one"); HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + assertThat(home.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); } @Test @@ -111,7 +111,7 @@ public class Saml2LoginApplicationITests { void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { performLogin("two"); HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + assertThat(home.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); } @Test From eb69904f4ddd2fbc63c5a66d48ca9dc14a48d072 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Wed, 27 Jul 2022 16:42:42 -0600 Subject: [PATCH 22/35] Simplify Saml2 Login Sample --- .../example/Saml2LoginApplicationITests.java | 25 +--- .../java/example/SecurityConfiguration.java | 109 ------------------ .../login/src/main/resources/application.yml | 14 +++ .../resources/credentials/idp-certificate.crt | 24 ---- 4 files changed, 15 insertions(+), 157 deletions(-) delete mode 100644 servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java delete mode 100644 servlet/spring-boot/java/saml2/login/src/main/resources/credentials/idp-certificate.crt diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index 261092b..a2cd2c3 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -52,8 +52,7 @@ public class Saml2LoginApplicationITests { } private void performLogin(String registrationId) throws Exception { - HtmlPage login = this.webClient.getPage("/"); - login.getAnchorByHref("/saml2/authenticate/" + registrationId).click(); + this.webClient.getPage("/"); this.webClient.waitForBackgroundJavaScript(10000); HtmlPage okta = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); this.webClient.waitForBackgroundJavaScript(10000); @@ -103,26 +102,4 @@ public class Saml2LoginApplicationITests { } - @DisplayName("Tenant two tests") - @Nested - class TenantTwoTests { - - @Test - void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { - performLogin("two"); - HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); - } - - @Test - void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - performLogin("two"); - HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); - HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); - HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); - } - - } - } diff --git a/servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java b/servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java deleted file mode 100644 index a58d88f..0000000 --- a/servlet/spring-boot/java/saml2/login/src/main/java/example/SecurityConfiguration.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2002-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import java.io.InputStream; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.security.interfaces.RSAPrivateKey; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.saml2.core.Saml2X509Credential; -import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver; -import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; -import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; -import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; -import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter; -import org.springframework.security.web.SecurityFilterChain; - -@Configuration -public class SecurityConfiguration { - - @Bean - SecurityFilterChain app(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .saml2Login(Customizer.withDefaults()) - .saml2Logout(Customizer.withDefaults()); - // @formatter:on - - return http.build(); - } - - @Bean - RelyingPartyRegistrationResolver relyingPartyRegistrationResolver( - RelyingPartyRegistrationRepository registrations) { - return new DefaultRelyingPartyRegistrationResolver(registrations); - } - - @Bean - FilterRegistrationBean metadata(RelyingPartyRegistrationResolver registrations) { - Saml2MetadataFilter metadata = new Saml2MetadataFilter(registrations, new OpenSamlMetadataResolver()); - FilterRegistrationBean filter = new FilterRegistrationBean<>(metadata); - filter.setOrder(-101); - return filter; - } - - @Bean - RelyingPartyRegistrationRepository repository( - @Value("classpath:credentials/rp-private.key") RSAPrivateKey privateKey) { - RelyingPartyRegistration one = RelyingPartyRegistrations - .fromMetadataLocation("https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata") - .registrationId("one") - .signingX509Credentials( - (c) -> c.add(Saml2X509Credential.signing(privateKey, relyingPartyCertificate()))) - .singleLogoutServiceLocation( - "https://dev-05937739.okta.com/app/dev-05937739_springgsecuritysaml2idp_1/exk46xofd8NZvFCpS5d7/slo/saml") - .singleLogoutServiceResponseLocation("http://localhost:8080/logout/saml2/slo") - .singleLogoutServiceBinding(Saml2MessageBinding.POST).build(); - RelyingPartyRegistration two = RelyingPartyRegistrations - .fromMetadataLocation("https://dev-05937739.okta.com/app/exk4842vmapcMkohr5d7/sso/saml/metadata") - .registrationId("two") - .signingX509Credentials( - (c) -> c.add(Saml2X509Credential.signing(privateKey, relyingPartyCertificate()))) - .singleLogoutServiceLocation( - "https://dev-05937739.okta.com/app/dev-05937739_springsecuritysaml2idptwo_1/exk4842vmapcMkohr5d7/slo/saml") - .singleLogoutServiceResponseLocation("http://localhost:8080/logout/saml2/slo") - .singleLogoutServiceBinding(Saml2MessageBinding.POST).build(); - return new InMemoryRelyingPartyRegistrationRepository(one, two); - } - - X509Certificate relyingPartyCertificate() { - Resource resource = new ClassPathResource("credentials/rp-certificate.crt"); - try (InputStream is = resource.getInputStream()) { - return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is); - } - catch (Exception ex) { - throw new UnsupportedOperationException(ex); - } - } - -} diff --git a/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml b/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml index e112d19..e618ee2 100644 --- a/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml +++ b/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml @@ -1,2 +1,16 @@ logging.level: org.springframework.security: TRACE + +spring: + security: + saml2: + relyingparty: + registration: + one: + signing.credentials: + - private-key-location: classpath:credentials/rp-private.key + certificate-location: classpath:credentials/rp-certificate.crt + singlelogout: + binding: POST + response-url: "{baseUrl}/logout/saml2/slo" + assertingparty.metadata-uri: https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata diff --git a/servlet/spring-boot/java/saml2/login/src/main/resources/credentials/idp-certificate.crt b/servlet/spring-boot/java/saml2/login/src/main/resources/credentials/idp-certificate.crt deleted file mode 100644 index 9c4ee07..0000000 --- a/servlet/spring-boot/java/saml2/login/src/main/resources/credentials/idp-certificate.crt +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD -VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD -VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX -c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw -aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ -BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa -BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD -DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr -QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62 -E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz -2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW -RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ -nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5 -cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph -iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5 -ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD -AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO -nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v -ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu -xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z -V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3 -lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk ------END CERTIFICATE----- From 91ccd91eaa575714ef43970fde9863de89755806 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 28 Jul 2022 16:16:35 -0300 Subject: [PATCH 23/35] Fix command to run Spring Boot SAML2 samples --- servlet/spring-boot/java/saml2/login-single-tenant/README.adoc | 3 ++- servlet/spring-boot/java/saml2/login/README.adoc | 2 +- .../spring-boot/java/saml2/refreshable-metadata/README.adoc | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc index cda90b5..e4659db 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc +++ b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc @@ -33,7 +33,8 @@ You can refer to the https://docs.spring.io/spring-security/reference/servlet/sa === Start up the Sample Boot Application ``` - ./gradlew :spring-security-samples-boot-saml2login:bootRun + ./gradlew :servlet:spring-boot:java:saml2:login-single-tenant:bootRun + ``` === Open a Browser diff --git a/servlet/spring-boot/java/saml2/login/README.adoc b/servlet/spring-boot/java/saml2/login/README.adoc index cda90b5..a9be556 100644 --- a/servlet/spring-boot/java/saml2/login/README.adoc +++ b/servlet/spring-boot/java/saml2/login/README.adoc @@ -33,7 +33,7 @@ You can refer to the https://docs.spring.io/spring-security/reference/servlet/sa === Start up the Sample Boot Application ``` - ./gradlew :spring-security-samples-boot-saml2login:bootRun + ./gradlew :servlet:spring-boot:java:saml2:login:bootRun ``` === Open a Browser diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc index 9694ed0..8c8b3a6 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc @@ -38,7 +38,7 @@ This particular implementation uses a `@Scheduled` annotation to update its meta === Start up the Sample Boot Application ``` - ./gradlew :spring-security-samples-boot-saml2login:bootRun + ./gradlew :servlet:spring-boot:java:saml2:refreshable-metadata:bootRun ``` === Open a Browser From a75deea8337aa35bef83eb66915e42976daccb5a Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Thu, 28 Jul 2022 18:03:37 -0600 Subject: [PATCH 24/35] Use url instead of response-url --- .../java/saml2/login/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml b/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml index e618ee2..26d950f 100644 --- a/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml +++ b/servlet/spring-boot/java/saml2/login/src/main/resources/application.yml @@ -12,5 +12,5 @@ spring: certificate-location: classpath:credentials/rp-certificate.crt singlelogout: binding: POST - response-url: "{baseUrl}/logout/saml2/slo" + url: "{baseUrl}/logout/saml2/slo" assertingparty.metadata-uri: https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata From 991e26eff6d55fdefb787a4943701a3e4f89db4f Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Wed, 14 Sep 2022 14:12:01 -0300 Subject: [PATCH 25/35] Add new task that runs all subproject's tests --- build.gradle | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 54e050c..908b36b 100644 --- a/build.gradle +++ b/build.gradle @@ -20,4 +20,19 @@ allprojects { } } } -} \ No newline at end of file +} + +repositories { + mavenCentral() +} + +tasks.register('runAllTests') { + var allTasks = rootProject.getAllTasks(true) + var allTestsTasks = allTasks.values().collect { t -> + t.findAll { it.name == 'test' || it.name == 'integrationTest' } + }.flatten() + it.dependsOn { + allTestsTasks + } +} + From 5a7114519ccc0156677141e90aa359c0c23b3808 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 12 Sep 2022 14:58:18 -0300 Subject: [PATCH 26/35] Accept gradle Terms of Service Issue gh-94 --- build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build.gradle b/build.gradle index 908b36b..67c6920 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,13 @@ allprojects { } } +if (hasProperty('buildScan')) { + buildScan { + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' + } +} + repositories { mavenCentral() } From 2eb0b65eabec0ee6b5dc4c2844cd9e89d14838e6 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 12 Sep 2022 15:12:19 -0300 Subject: [PATCH 27/35] Add Gradle Enterprise plugin Issue gh-94 --- settings.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/settings.gradle b/settings.gradle index 7484ac5..6b079b6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,11 @@ pluginManagement { } } +plugins { + id "com.gradle.enterprise" version "3.10.2" + id "io.spring.ge.conventions" version "0.0.7" +} + include ":reactive:rsocket:hello-security" include ":reactive:webflux-fn:hello" include ":reactive:webflux-fn:hello-security" From 65d4b2ec4bcf216a693e92a566e2a47b1ce0d13e Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 19 Sep 2022 15:40:15 -0300 Subject: [PATCH 28/35] Polish --- .../example/Saml2LoginApplicationITests.java | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index a2cd2c3..38052f2 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -25,8 +25,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -51,7 +49,23 @@ public class Saml2LoginApplicationITests { this.webClient.getCookieManager().clearCookies(); } - private void performLogin(String registrationId) throws Exception { + @Test + void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { + performLogin(); + HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); + } + + @Test + void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { + performLogin(); + HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); + HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); + HtmlPage loginPage = rpLogoutButton.click(); + assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + } + + private void performLogin() throws Exception { this.webClient.getPage("/"); this.webClient.waitForBackgroundJavaScript(10000); HtmlPage okta = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); @@ -80,26 +94,4 @@ public class Saml2LoginApplicationITests { throw new IllegalStateException("Could not resolve login form"); } - @DisplayName("Tenant one tests") - @Nested - class TenantOneTests { - - @Test - void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { - performLogin("one"); - HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); - } - - @Test - void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { - performLogin("one"); - HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); - HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); - HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); - } - - } - } From 9f6b38bf33c9519e3a0ce974fbc934a0c634135f Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Mon, 19 Sep 2022 08:47:46 -0300 Subject: [PATCH 29/35] Polish SAML2 tests --- .../example/Saml2JavaConfigurationITests.java | 18 +++++++++++++++++- .../example/Saml2LoginApplicationITests.java | 18 +++++++++++++++++- .../example/Saml2LoginApplicationITests.java | 18 +++++++++++++++++- .../integTest/java/example/Saml2XmlITests.java | 18 +++++++++++++++++- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java index fbf0cc9..b664155 100644 --- a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java +++ b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java @@ -16,6 +16,9 @@ package example; +import java.util.ArrayList; +import java.util.List; + import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlElement; @@ -78,7 +81,20 @@ public class Saml2JavaConfigurationITests { HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + this.webClient.waitForBackgroundJavaScript(10000); + List urls = new ArrayList<>(); + urls.add(loginPage.getUrl().getFile()); + urls.add(((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).getUrl().getFile()); + assertThat(urls).withFailMessage(() -> { + // @formatter:off + String builder = loginPage.asXml() + + "\n\n\n" + + "Enclosing Page" + + "\n\n\n" + + ((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).asXml(); + // @formatter:on + return builder; + }).contains("/login?logout"); } private void performLogin() throws Exception { diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java index 5382a08..01369dd 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -16,6 +16,9 @@ package example; +import java.util.ArrayList; +import java.util.List; + import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlElement; @@ -62,7 +65,20 @@ public class Saml2LoginApplicationITests { HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + this.webClient.waitForBackgroundJavaScript(10000); + List urls = new ArrayList<>(); + urls.add(loginPage.getUrl().getFile()); + urls.add(((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).getUrl().getFile()); + assertThat(urls).withFailMessage(() -> { + // @formatter:off + String builder = loginPage.asXml() + + "\n\n\n" + + "Enclosing Page" + + "\n\n\n" + + ((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).asXml(); + // @formatter:on + return builder; + }).contains("/login?logout"); } private void performLogin() throws Exception { diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index 38052f2..f99961a 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -16,6 +16,9 @@ package example; +import java.util.ArrayList; +import java.util.List; + import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlElement; @@ -62,7 +65,20 @@ public class Saml2LoginApplicationITests { HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + this.webClient.waitForBackgroundJavaScript(10000); + List urls = new ArrayList<>(); + urls.add(loginPage.getUrl().getFile()); + urls.add(((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).getUrl().getFile()); + assertThat(urls).withFailMessage(() -> { + // @formatter:off + String builder = loginPage.asXml() + + "\n\n\n" + + "Enclosing Page" + + "\n\n\n" + + ((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).asXml(); + // @formatter:on + return builder; + }).contains("/login?logout"); } private void performLogin() throws Exception { diff --git a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java index fb72938..670af6a 100644 --- a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java +++ b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java @@ -16,6 +16,9 @@ package example; +import java.util.ArrayList; +import java.util.List; + import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlElement; @@ -79,7 +82,20 @@ public class Saml2XmlITests { HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); HtmlPage loginPage = rpLogoutButton.click(); - assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + this.webClient.waitForBackgroundJavaScript(10000); + List urls = new ArrayList<>(); + urls.add(loginPage.getUrl().getFile()); + urls.add(((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).getUrl().getFile()); + assertThat(urls).withFailMessage(() -> { + // @formatter:off + String builder = loginPage.asXml() + + "\n\n\n" + + "Enclosing Page" + + "\n\n\n" + + ((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).asXml(); + // @formatter:on + return builder; + }).contains("/login?logout"); } private void performLogin() throws Exception { From 9f0c92710d891aca04643ff4ddcb7f044972f006 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 15 Dec 2022 10:02:02 -0300 Subject: [PATCH 30/35] Disable SAML2 integration tests Issue gh-111 --- .../integTest/java/example/Saml2JavaConfigurationITests.java | 2 ++ .../src/integTest/java/example/Saml2LoginApplicationITests.java | 2 ++ .../src/integTest/java/example/Saml2LoginApplicationITests.java | 2 ++ .../src/integTest/java/example/Saml2LoginApplicationITests.java | 2 ++ .../login-logout/src/integTest/java/example/Saml2XmlITests.java | 2 ++ 5 files changed, 10 insertions(+) diff --git a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java index b664155..eb0511d 100644 --- a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java +++ b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java @@ -28,6 +28,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -44,6 +45,7 @@ import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; +@Disabled @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = ApplicationConfiguration.class) @WebAppConfiguration diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java index 01369dd..8091299 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -28,6 +28,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -37,6 +38,7 @@ import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; +@Disabled @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index f99961a..7e4b462 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -28,6 +28,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -37,6 +38,7 @@ import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; +@Disabled @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java index b73e69c..5013f8d 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -24,6 +24,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -33,6 +34,7 @@ import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; +@Disabled @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { diff --git a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java index 670af6a..c2ed1e3 100644 --- a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java +++ b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java @@ -28,6 +28,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -44,6 +45,7 @@ import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; +@Disabled @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring-servlet.xml", "file:src/main/webapp/WEB-INF/spring/security.xml" }) From 5f41c8e9ac46acb53a683255250bf8b29907ac46 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Tue, 17 Jan 2023 14:06:51 -0300 Subject: [PATCH 31/35] Add Session Management samples --- .../maximum-sessions-prevent-login/.gitignore | 37 +++ .../build.gradle | 26 ++ .../gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + .../maximum-sessions-prevent-login/gradlew | 240 ++++++++++++++++++ .../gradlew.bat | 91 +++++++ .../settings.gradle | 0 .../MaximumSessionsPreventLoginTests.java | 63 +++++ .../HelloController.java | 30 +++ ...aximumSessionsPreventLoginApplication.java | 29 +++ .../SecurityConfig.java | 60 +++++ .../src/main/resources/application.properties | 1 + .../maximum-sessions/.gitignore | 37 +++ .../maximum-sessions/build.gradle | 26 ++ .../maximum-sessions/gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + .../maximum-sessions/gradlew | 240 ++++++++++++++++++ .../maximum-sessions/gradlew.bat | 91 +++++++ .../maximum-sessions/settings.gradle | 0 .../maximumsessions/MaximumSessionsTests.java | 60 +++++ .../maximumsessions/HelloController.java | 30 +++ .../MaximumSessionsApplication.java | 29 +++ .../maximumsessions/SecurityConfig.java | 50 ++++ .../src/main/resources/application.properties | 1 + settings.gradle | 2 + 27 files changed, 1155 insertions(+) create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/.gitignore create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/build.gradle create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle.properties create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle/wrapper/gradle-wrapper.jar create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle/wrapper/gradle-wrapper.properties create mode 100755 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradlew create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradlew.bat create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/settings.gradle create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/integTest/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginTests.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/HelloController.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginApplication.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/SecurityConfig.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/resources/application.properties create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/.gitignore create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/build.gradle create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/gradle.properties create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/gradle/wrapper/gradle-wrapper.jar create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/gradle/wrapper/gradle-wrapper.properties create mode 100755 servlet/spring-boot/java/session-management/maximum-sessions/gradlew create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/gradlew.bat create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/settings.gradle create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/src/integTest/java/com/example/maximumsessions/MaximumSessionsTests.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/HelloController.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/MaximumSessionsApplication.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/SecurityConfig.java create mode 100644 servlet/spring-boot/java/session-management/maximum-sessions/src/main/resources/application.properties diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/.gitignore b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/build.gradle b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/build.gradle new file mode 100644 index 0000000..b711a3d --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.7' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id "nebula.integtest" version "8.2.0" +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' +} + +tasks.withType(Test).configureEach { + useJUnitPlatform() + outputs.upToDateWhen { false } +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle.properties b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle.properties new file mode 100644 index 0000000..a4474f7 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle.properties @@ -0,0 +1 @@ +spring-security.version=5.8.1 diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle/wrapper/gradle-wrapper.jar b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradlew.bat b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradlew.bat new file mode 100644 index 0000000..53a6b23 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/settings.gradle b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/integTest/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginTests.java b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/integTest/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginTests.java new file mode 100644 index 0000000..22a759e --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/integTest/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessionspreventlogin; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@Import(SecurityConfig.class) +@AutoConfigureMockMvc +public class MaximumSessionsPreventLoginTests { + + @Autowired + private MockMvc mvc; + + @Test + void loginOnSecondLoginThenPreventLogin() throws Exception { + // @formatter:off + MvcResult mvcResult = this.mvc.perform(formLogin()) + .andExpect(authenticated()) + .andReturn(); + + MockHttpSession firstLoginSession = (MockHttpSession) mvcResult.getRequest().getSession(); + + this.mvc.perform(get("/").session(firstLoginSession)) + .andExpect(authenticated()); + + // second login is prevented + this.mvc.perform(formLogin()).andExpect(unauthenticated()); + + // first session is still valid + this.mvc.perform(get("/").session(firstLoginSession)) + .andExpect(authenticated()); + // @formatter:on + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/HelloController.java b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/HelloController.java new file mode 100644 index 0000000..447fd4f --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/HelloController.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessionspreventlogin; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping("/") + public String hello() { + return "hello"; + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginApplication.java b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginApplication.java new file mode 100644 index 0000000..fffa568 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/MaximumSessionsPreventLoginApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessionspreventlogin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MaximumSessionsPreventLoginApplication { + + public static void main(String[] args) { + SpringApplication.run(MaximumSessionsPreventLoginApplication.class, args); + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/SecurityConfig.java b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/SecurityConfig.java new file mode 100644 index 0000000..861edcc --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/java/com/example/maximumsessionspreventlogin/SecurityConfig.java @@ -0,0 +1,60 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessionspreventlogin; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import static org.springframework.security.config.Customizer.withDefaults; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .formLogin(withDefaults()) + .sessionManagement((sessions) -> sessions + .sessionConcurrency((concurrency) -> concurrency + .maximumSessions(1) + .maxSessionsPreventsLogin(true) + ) + ); + // @formatter:on + return http.build(); + } + + @Bean + public UserDetailsService userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER") + .build(); + return new InMemoryUserDetailsManager(user); + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/resources/application.properties b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/.gitignore b/servlet/spring-boot/java/session-management/maximum-sessions/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/build.gradle b/servlet/spring-boot/java/session-management/maximum-sessions/build.gradle new file mode 100644 index 0000000..b711a3d --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.7' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id "nebula.integtest" version "8.2.0" +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' +} + +tasks.withType(Test).configureEach { + useJUnitPlatform() + outputs.upToDateWhen { false } +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/gradle.properties b/servlet/spring-boot/java/session-management/maximum-sessions/gradle.properties new file mode 100644 index 0000000..a4474f7 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/gradle.properties @@ -0,0 +1 @@ +spring-security.version=5.8.1 diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/gradle/wrapper/gradle-wrapper.jar b/servlet/spring-boot/java/session-management/maximum-sessions/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/gradlew.bat b/servlet/spring-boot/java/session-management/maximum-sessions/gradlew.bat new file mode 100644 index 0000000..53a6b23 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/settings.gradle b/servlet/spring-boot/java/session-management/maximum-sessions/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/src/integTest/java/com/example/maximumsessions/MaximumSessionsTests.java b/servlet/spring-boot/java/session-management/maximum-sessions/src/integTest/java/com/example/maximumsessions/MaximumSessionsTests.java new file mode 100644 index 0000000..892ce3f --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/src/integTest/java/com/example/maximumsessions/MaximumSessionsTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessions; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +public class MaximumSessionsTests { + + @Autowired + private MockMvc mvc; + + @Test + void loginOnSecondLoginThenFirstSessionTerminated() throws Exception { + // @formatter:off + MvcResult mvcResult = this.mvc.perform(formLogin()) + .andExpect(authenticated()) + .andReturn(); + + MockHttpSession firstLoginSession = (MockHttpSession) mvcResult.getRequest().getSession(); + + this.mvc.perform(get("/").session(firstLoginSession)) + .andExpect(authenticated()); + + this.mvc.perform(formLogin()).andExpect(authenticated()); + + // first session is terminated by second login + this.mvc.perform(get("/").session(firstLoginSession)) + .andExpect(unauthenticated()); + // @formatter:on + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/HelloController.java b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/HelloController.java new file mode 100644 index 0000000..fb9c0c7 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/HelloController.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessions; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping("/") + public String hello() { + return "hello"; + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/MaximumSessionsApplication.java b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/MaximumSessionsApplication.java new file mode 100644 index 0000000..5f4b1b4 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/MaximumSessionsApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessions; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MaximumSessionsApplication { + + public static void main(String[] args) { + SpringApplication.run(MaximumSessionsApplication.class, args); + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/SecurityConfig.java b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/SecurityConfig.java new file mode 100644 index 0000000..40d26e1 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/java/com/example/maximumsessions/SecurityConfig.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.maximumsessions; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import static org.springframework.security.config.Customizer.withDefaults; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests.anyRequest().authenticated()) + .formLogin(withDefaults()) + .sessionManagement((sessionManagement) -> sessionManagement.maximumSessions(1)); + return http.build(); + } + + @Bean + public UserDetailsService userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER") + .build(); + return new InMemoryUserDetailsManager(user); + } + +} diff --git a/servlet/spring-boot/java/session-management/maximum-sessions/src/main/resources/application.properties b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/servlet/spring-boot/java/session-management/maximum-sessions/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/settings.gradle b/settings.gradle index 6b079b6..ce5b66a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -69,4 +69,6 @@ include ":servlet:xml:java:preauth" include ":servlet:xml:java:contacts" include ":servlet:xml:java:dms" include ":servlet:xml:java:saml2:login-logout" +include ":servlet:spring-boot:java:session-management:maximum-sessions" +include ":servlet:spring-boot:java:session-management:maximum-sessions-prevent-login" From 2b113118c30d2c0188ccfabb8fc556490c6d1b60 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 23 Feb 2023 09:26:30 -0300 Subject: [PATCH 32/35] Revert "Disable SAML2 integration tests" This reverts commit 9f0c92710d891aca04643ff4ddcb7f044972f006. --- .../integTest/java/example/Saml2JavaConfigurationITests.java | 2 -- .../src/integTest/java/example/Saml2LoginApplicationITests.java | 2 -- .../src/integTest/java/example/Saml2LoginApplicationITests.java | 2 -- .../src/integTest/java/example/Saml2LoginApplicationITests.java | 2 -- .../login-logout/src/integTest/java/example/Saml2XmlITests.java | 2 -- 5 files changed, 10 deletions(-) diff --git a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java index eb0511d..b664155 100644 --- a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java +++ b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java @@ -28,7 +28,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -45,7 +44,6 @@ import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; -@Disabled @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = ApplicationConfiguration.class) @WebAppConfiguration diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java index 8091299..01369dd 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -28,7 +28,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +37,6 @@ import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; -@Disabled @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index 7e4b462..f99961a 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -28,7 +28,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +37,6 @@ import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; -@Disabled @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java index 5013f8d..b73e69c 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -24,7 +24,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -34,7 +33,6 @@ import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; -@Disabled @SpringBootTest @AutoConfigureMockMvc public class Saml2LoginApplicationITests { diff --git a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java index c2ed1e3..670af6a 100644 --- a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java +++ b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java @@ -28,7 +28,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -45,7 +44,6 @@ import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; -@Disabled @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring-servlet.xml", "file:src/main/webapp/WEB-INF/spring/security.xml" }) From 1f26a2faeaf6c99cbd1e2e4b72a2a76afab92694 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 23 Feb 2023 11:13:33 -0300 Subject: [PATCH 33/35] Fix SAML2 samples --- servlet/java-configuration/saml2/login/README.adoc | 2 +- servlet/java-configuration/saml2/login/build.gradle | 2 +- .../java/example/Saml2JavaConfigurationITests.java | 4 ++-- .../java/saml2/login-single-tenant/README.adoc | 2 +- .../java/saml2/login-single-tenant/build.gradle | 10 +++++----- .../java/saml2/login-single-tenant/gradle.properties | 1 + .../java/example/Saml2LoginApplicationITests.java | 12 +++++++----- servlet/spring-boot/java/saml2/login/README.adoc | 2 +- .../spring-boot/java/saml2/login/gradle.properties | 1 + .../java/example/Saml2LoginApplicationITests.java | 8 ++++---- .../java/saml2/refreshable-metadata/README.adoc | 2 +- .../java/saml2/refreshable-metadata/build.gradle | 2 +- .../saml2/refreshable-metadata/gradle.properties | 1 + .../java/example/Saml2LoginApplicationITests.java | 4 ++-- servlet/xml/java/saml2/login-logout/build.gradle | 2 +- .../src/integTest/java/example/Saml2XmlITests.java | 4 ++-- 16 files changed, 32 insertions(+), 27 deletions(-) diff --git a/servlet/java-configuration/saml2/login/README.adoc b/servlet/java-configuration/saml2/login/README.adoc index cda90b5..6ff6b9b 100644 --- a/servlet/java-configuration/saml2/login/README.adoc +++ b/servlet/java-configuration/saml2/login/README.adoc @@ -45,7 +45,7 @@ You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: testuser@spring.security.saml +User: testuser2@spring.security.saml Password: 12345678 ``` diff --git a/servlet/java-configuration/saml2/login/build.gradle b/servlet/java-configuration/saml2/login/build.gradle index 3e34221..85c5641 100644 --- a/servlet/java-configuration/saml2/login/build.gradle +++ b/servlet/java-configuration/saml2/login/build.gradle @@ -56,7 +56,7 @@ dependencies { testImplementation "org.springframework:spring-test" testImplementation "org.springframework.security:spring-security-test" testImplementation("org.junit.jupiter:junit-jupiter-api") - testImplementation "org.seleniumhq.selenium:htmlunit-driver:2.44.0" + testImplementation "org.seleniumhq.selenium:htmlunit-driver:4.8.0" testImplementation 'org.hamcrest:hamcrest:2.2' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") diff --git a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java index b664155..d405ef4 100644 --- a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java +++ b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java @@ -72,7 +72,7 @@ public class Saml2JavaConfigurationITests { void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { performLogin(); HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + assertThat(home.asNormalizedText()).contains("You're email address is testuser2@spring.security.saml"); } @Test @@ -104,7 +104,7 @@ public class Saml2JavaConfigurationITests { HtmlInput username = form.getInputByName("username"); HtmlPasswordInput password = form.getInputByName("password"); HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); - username.type("testuser@spring.security.saml"); + username.type("testuser2@spring.security.saml"); password.type("12345678"); submit.click(); this.webClient.waitForBackgroundJavaScript(10000); diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc index e4659db..0daa32d 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc +++ b/servlet/spring-boot/java/saml2/login-single-tenant/README.adoc @@ -46,7 +46,7 @@ You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: testuser@spring.security.saml +User: testuser2@spring.security.saml Password: 12345678 ``` diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle index 87351e1..9dddc2d 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle +++ b/servlet/spring-boot/java/saml2/login-single-tenant/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.5.2' + id 'org.springframework.boot' version '2.7.1' id 'io.spring.dependency-management' version '1.0.10.RELEASE' id "nebula.integtest" version "8.2.0" id 'java' @@ -14,9 +14,9 @@ repositories { dependencies { constraints { - implementation "org.opensaml:opensaml-core:4.1.1" - implementation "org.opensaml:opensaml-saml-api:4.1.1" - implementation "org.opensaml:opensaml-saml-impl:4.1.1" + implementation "org.opensaml:opensaml-core:4.2.0" + implementation "org.opensaml:opensaml-saml-api:4.2.0" + implementation "org.opensaml:opensaml-saml-impl:4.2.0" } implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' @@ -24,7 +24,7 @@ dependencies { implementation 'org.springframework.security:spring-security-saml2-service-provider' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' + testImplementation 'net.sourceforge.htmlunit:htmlunit' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties b/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties index bd54891..5aba1dc 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties +++ b/servlet/spring-boot/java/saml2/login-single-tenant/gradle.properties @@ -1,2 +1,3 @@ version=5.8.0-SNAPSHOT spring-security.version=5.8.0-SNAPSHOT +htmlunit.version=2.70.0 diff --git a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java index 01369dd..8277c36 100644 --- a/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login-single-tenant/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -56,7 +56,7 @@ public class Saml2LoginApplicationITests { void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { performLogin(); HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + assertThat(home.asNormalizedText()).contains("You're email address is testuser2@spring.security.saml"); } @Test @@ -82,13 +82,15 @@ public class Saml2LoginApplicationITests { } private void performLogin() throws Exception { - HtmlPage login = this.webClient.getPage("/"); + this.webClient.getPage("/"); this.webClient.waitForBackgroundJavaScript(10000); - HtmlForm form = findForm(login); + HtmlPage okta = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + this.webClient.waitForBackgroundJavaScript(10000); + HtmlForm form = findForm(okta); HtmlInput username = form.getInputByName("username"); HtmlPasswordInput password = form.getInputByName("password"); - HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); - username.type("testuser@spring.security.saml"); + HtmlSubmitInput submit = okta.getHtmlElementById("okta-signin-submit"); + username.type("testuser2@spring.security.saml"); password.type("12345678"); submit.click(); this.webClient.waitForBackgroundJavaScript(10000); diff --git a/servlet/spring-boot/java/saml2/login/README.adoc b/servlet/spring-boot/java/saml2/login/README.adoc index a9be556..73bf4ad 100644 --- a/servlet/spring-boot/java/saml2/login/README.adoc +++ b/servlet/spring-boot/java/saml2/login/README.adoc @@ -45,7 +45,7 @@ You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: testuser@spring.security.saml +User: testuser2@spring.security.saml Password: 12345678 ``` diff --git a/servlet/spring-boot/java/saml2/login/gradle.properties b/servlet/spring-boot/java/saml2/login/gradle.properties index bd54891..5aba1dc 100644 --- a/servlet/spring-boot/java/saml2/login/gradle.properties +++ b/servlet/spring-boot/java/saml2/login/gradle.properties @@ -1,2 +1,3 @@ version=5.8.0-SNAPSHOT spring-security.version=5.8.0-SNAPSHOT +htmlunit.version=2.70.0 diff --git a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java index f99961a..8277c36 100644 --- a/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/login/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -55,14 +55,14 @@ public class Saml2LoginApplicationITests { @Test void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { performLogin(); - HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asNormalizedText()).contains("You're email address is testuser@spring.security.saml"); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); + assertThat(home.asNormalizedText()).contains("You're email address is testuser2@spring.security.saml"); } @Test void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { performLogin(); - HtmlPage home = (HtmlPage) Saml2LoginApplicationITests.this.webClient.getCurrentWindow().getEnclosedPage(); + HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button"); HtmlPage loginPage = rpLogoutButton.click(); this.webClient.waitForBackgroundJavaScript(10000); @@ -90,7 +90,7 @@ public class Saml2LoginApplicationITests { HtmlInput username = form.getInputByName("username"); HtmlPasswordInput password = form.getInputByName("password"); HtmlSubmitInput submit = okta.getHtmlElementById("okta-signin-submit"); - username.type("testuser@spring.security.saml"); + username.type("testuser2@spring.security.saml"); password.type("12345678"); submit.click(); this.webClient.waitForBackgroundJavaScript(10000); diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc index 8c8b3a6..c98a153 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/README.adoc @@ -50,7 +50,7 @@ You will be redirect to the Okta SAML 2.0 IDP === Type in your credentials ``` -User: testuser@spring.security.saml +User: testuser2@spring.security.saml Password: 12345678 ``` diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle index 961ac42..3fc5803 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle @@ -24,7 +24,7 @@ dependencies { implementation 'org.springframework.security:spring-security-saml2-service-provider' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' + testImplementation 'net.sourceforge.htmlunit:htmlunit' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties b/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties index bd54891..5aba1dc 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/gradle.properties @@ -1,2 +1,3 @@ version=5.8.0-SNAPSHOT spring-security.version=5.8.0-SNAPSHOT +htmlunit.version=2.70.0 diff --git a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java index b73e69c..0a7b593 100644 --- a/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java +++ b/servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java @@ -52,7 +52,7 @@ public class Saml2LoginApplicationITests { void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { performLogin(); HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + assertThat(home.asNormalizedText()).contains("You're email address is testuser2@spring.security.saml"); } private void performLogin() throws Exception { @@ -62,7 +62,7 @@ public class Saml2LoginApplicationITests { HtmlInput username = form.getInputByName("username"); HtmlPasswordInput password = form.getInputByName("password"); HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); - username.type("testuser@spring.security.saml"); + username.type("testuser2@spring.security.saml"); password.type("12345678"); submit.click(); this.webClient.waitForBackgroundJavaScript(10000); diff --git a/servlet/xml/java/saml2/login-logout/build.gradle b/servlet/xml/java/saml2/login-logout/build.gradle index c48e4ba..d884142 100644 --- a/servlet/xml/java/saml2/login-logout/build.gradle +++ b/servlet/xml/java/saml2/login-logout/build.gradle @@ -34,7 +34,7 @@ dependencies { testImplementation "org.springframework:spring-test" testImplementation "org.springframework.security:spring-security-test" testImplementation "org.junit.jupiter:junit-jupiter-api" - testImplementation 'net.sourceforge.htmlunit:htmlunit:2.44.0' + testImplementation 'net.sourceforge.htmlunit:htmlunit:2.70.0' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") diff --git a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java index 670af6a..160d655 100644 --- a/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java +++ b/servlet/xml/java/saml2/login-logout/src/integTest/java/example/Saml2XmlITests.java @@ -73,7 +73,7 @@ public class Saml2XmlITests { void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { performLogin(); HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage(); - assertThat(home.asText()).contains("You're email address is testuser@spring.security.saml"); + assertThat(home.asNormalizedText()).contains("You're email address is testuser2@spring.security.saml"); } @Test @@ -105,7 +105,7 @@ public class Saml2XmlITests { HtmlInput username = form.getInputByName("username"); HtmlPasswordInput password = form.getInputByName("password"); HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit"); - username.type("testuser@spring.security.saml"); + username.type("testuser2@spring.security.saml"); password.type("12345678"); submit.click(); this.webClient.waitForBackgroundJavaScript(10000); From a4a0cb7b732230edae8d822948fdb339d83b60c3 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 30 Mar 2023 10:36:25 -0300 Subject: [PATCH 34/35] Force Spring Security Version when built from Spring Security CI Closes gh-117 --- spring-security-ci.gradle | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spring-security-ci.gradle b/spring-security-ci.gradle index fa710e7..c4c6be4 100644 --- a/spring-security-ci.gradle +++ b/spring-security-ci.gradle @@ -1,4 +1,29 @@ allprojects { + configurations.all { + resolutionStrategy { + force "org.springframework.security:spring-security-acl:$springSecurityVersion" + force "org.springframework.security:spring-security-aspects:$springSecurityVersion" + force "org.springframework.security:spring-security-bom:$springSecurityVersion" + force "org.springframework.security:spring-security-cas:$springSecurityVersion" + force "org.springframework.security:spring-security-config:$springSecurityVersion" + force "org.springframework.security:spring-security-core:$springSecurityVersion" + force "org.springframework.security:spring-security-crypto:$springSecurityVersion" + force "org.springframework.security:spring-security-data:$springSecurityVersion" + force "org.springframework.security:spring-security-ldap:$springSecurityVersion" + force "org.springframework.security:spring-security-messaging:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-client:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-core:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-jose:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-resource-server:$springSecurityVersion" + force "org.springframework.security:spring-security-openid:$springSecurityVersion" + force "org.springframework.security:spring-security-remoting:$springSecurityVersion" + force "org.springframework.security:spring-security-rsocket:$springSecurityVersion" + force "org.springframework.security:spring-security-saml2-service-provider:$springSecurityVersion" + force "org.springframework.security:spring-security-taglibs:$springSecurityVersion" + force "org.springframework.security:spring-security-test:$springSecurityVersion" + force "org.springframework.security:spring-security-web:$springSecurityVersion" + } + } repositories { exclusiveContent { forRepository { From 37c2a72342ff740ce0dbf13aa7e6bfdae1f47eab Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Thu, 30 Mar 2023 10:36:25 -0300 Subject: [PATCH 35/35] Force Spring Security Version when built from Spring Security CI Closes gh-117 --- spring-security-ci.gradle | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spring-security-ci.gradle b/spring-security-ci.gradle index fa710e7..c4c6be4 100644 --- a/spring-security-ci.gradle +++ b/spring-security-ci.gradle @@ -1,4 +1,29 @@ allprojects { + configurations.all { + resolutionStrategy { + force "org.springframework.security:spring-security-acl:$springSecurityVersion" + force "org.springframework.security:spring-security-aspects:$springSecurityVersion" + force "org.springframework.security:spring-security-bom:$springSecurityVersion" + force "org.springframework.security:spring-security-cas:$springSecurityVersion" + force "org.springframework.security:spring-security-config:$springSecurityVersion" + force "org.springframework.security:spring-security-core:$springSecurityVersion" + force "org.springframework.security:spring-security-crypto:$springSecurityVersion" + force "org.springframework.security:spring-security-data:$springSecurityVersion" + force "org.springframework.security:spring-security-ldap:$springSecurityVersion" + force "org.springframework.security:spring-security-messaging:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-client:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-core:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-jose:$springSecurityVersion" + force "org.springframework.security:spring-security-oauth2-resource-server:$springSecurityVersion" + force "org.springframework.security:spring-security-openid:$springSecurityVersion" + force "org.springframework.security:spring-security-remoting:$springSecurityVersion" + force "org.springframework.security:spring-security-rsocket:$springSecurityVersion" + force "org.springframework.security:spring-security-saml2-service-provider:$springSecurityVersion" + force "org.springframework.security:spring-security-taglibs:$springSecurityVersion" + force "org.springframework.security:spring-security-test:$springSecurityVersion" + force "org.springframework.security:spring-security-web:$springSecurityVersion" + } + } repositories { exclusiveContent { forRepository {