diff --git a/.travis.yml b/.travis.yml index 9fc2c78e84b..981df90b0af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: java jdk: - - oraclejdk8 + - openjdk8 os: - linux diff --git a/Jenkinsfile b/Jenkinsfile index 88c32258ba0..4c6c3d9a194 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,7 +14,9 @@ try { node { checkout scm try { - sh "./gradlew clean check --refresh-dependencies --no-daemon --stacktrace" + withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) { + sh "./gradlew clean check --refresh-dependencies --no-daemon --stacktrace" + } } catch(Exception e) { currentBuild.result = 'FAILED: check' throw e @@ -30,10 +32,12 @@ try { checkout scm withCredentials([string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')]) { try { - if ("master" == env.BRANCH_NAME) { - sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace" - } else { - sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.projectKey='spring-security-${env.BRANCH_NAME}' -Dsonar.projectName='spring-security-${env.BRANCH_NAME}' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace" + withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) { + if ("master" == env.BRANCH_NAME) { + sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace" + } else { + sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.projectKey='spring-security-${env.BRANCH_NAME}' -Dsonar.projectName='spring-security-${env.BRANCH_NAME}' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace" + } } } catch(Exception e) { currentBuild.result = 'FAILED: sonar' @@ -48,7 +52,9 @@ try { node { checkout scm try { - sh "./gradlew clean test -PforceMavenRepositories=snapshot -PspringVersion='5.+' -PreactorVersion=Californium-BUILD-SNAPSHOT -PspringDataVersion=Lovelace-BUILD-SNAPSHOT --refresh-dependencies --no-daemon --stacktrace" + withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) { + sh "./gradlew clean test -PforceMavenRepositories=snapshot -PspringVersion='5.1.+' -PreactorVersion=Californium-BUILD-SNAPSHOT -PspringDataVersion=Lovelace-BUILD-SNAPSHOT --refresh-dependencies --no-daemon --stacktrace" + } } catch(Exception e) { currentBuild.result = 'FAILED: snapshots' throw e @@ -111,7 +117,9 @@ try { withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) { withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) { withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) { - sh "./gradlew deployArtifacts finalizeDeployArtifacts -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password='$SIGNING_PASSWORD' -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace" + withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) { + sh "./gradlew deployArtifacts finalizeDeployArtifacts -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password='$SIGNING_PASSWORD' -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace" + } } } } @@ -124,7 +132,9 @@ try { node { checkout scm withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) { - sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace" + withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) { + sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace" + } } } } @@ -134,7 +144,9 @@ try { node { checkout scm withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) { - sh "./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace" + withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) { + sh "./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace" + } } } } diff --git a/acl/src/main/java/org/springframework/security/acls/domain/AccessControlEntryImpl.java b/acl/src/main/java/org/springframework/security/acls/domain/AccessControlEntryImpl.java index f39dbd30b54..fb41f101d6c 100644 --- a/acl/src/main/java/org/springframework/security/acls/domain/AccessControlEntryImpl.java +++ b/acl/src/main/java/org/springframework/security/acls/domain/AccessControlEntryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited + * Copyright 2002-2016 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. @@ -131,10 +131,9 @@ public boolean equals(Object arg0) { @Override public int hashCode() { - int result = this.acl.hashCode(); - result = 31 * result + this.permission.hashCode(); + int result = this.permission.hashCode(); result = 31 * result + (this.id != null ? this.id.hashCode() : 0); - result = 31 * result + this.sid.hashCode(); + result = 31 * result + (this.sid.hashCode()); result = 31 * result + (this.auditFailure ? 1 : 0); result = 31 * result + (this.auditSuccess ? 1 : 0); result = 31 * result + (this.granting ? 1 : 0); diff --git a/acl/src/test/java/org/springframework/security/acls/domain/AclImplTests.java b/acl/src/test/java/org/springframework/security/acls/domain/AclImplTests.java index 1362a45f98e..c14292bb1f9 100644 --- a/acl/src/test/java/org/springframework/security/acls/domain/AclImplTests.java +++ b/acl/src/test/java/org/springframework/security/acls/domain/AclImplTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -560,6 +560,25 @@ public void changingParentIsSuccessful() throws Exception { childAcl.setParent(changeParentAcl); } + @Test + public void hashCodeWithoutStackOverFlow() throws Exception { + //given + Sid sid = new PrincipalSid("pSid"); + ObjectIdentity oid = new ObjectIdentityImpl("type", 1); + AclAuthorizationStrategy authStrategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("role")); + PermissionGrantingStrategy grantingStrategy = new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()); + + AclImpl acl = new AclImpl(oid, 1L, authStrategy, grantingStrategy, null, null, false, sid); + AccessControlEntryImpl ace = new AccessControlEntryImpl(1L, acl, sid, BasePermission.READ, true, true, true); + + Field fieldAces = FieldUtils.getField(AclImpl.class, "aces"); + fieldAces.setAccessible(true); + List aces = (List) fieldAces.get(acl); + aces.add(ace); + //when - then none StackOverFlowError been raised + ace.hashCode(); + } + // ~ Inner Classes // ================================================================================================== diff --git a/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java b/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java index 63613d8fe6c..c323bb491b7 100644 --- a/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java +++ b/cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java @@ -47,7 +47,7 @@ public class ServiceAuthenticationDetailsSource implements // =================================================================================================== /** - * Creates an implementation that uses the specified ServiceProperites and the default + * Creates an implementation that uses the specified ServiceProperties and the default * CAS artifactParameterName. * * @param serviceProperties The ServiceProperties to use to construct the serviceUrl. diff --git a/config/src/main/java/org/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.java index 1e681b1670e..cd7e8680b67 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.java @@ -16,8 +16,10 @@ package org.springframework.security.config.annotation.configuration; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -34,9 +36,11 @@ * @since 3.2 */ @Configuration +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class ObjectPostProcessorConfiguration { @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public ObjectPostProcessor objectPostProcessor( AutowireCapableBeanFactory beanFactory) { return new AutowireBeanFactoryObjectPostProcessor(beanFactory); diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java index 1639eeb4047..7c73ab55d3d 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java @@ -31,8 +31,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.Bean; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportAware; +import org.springframework.context.annotation.Role; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.type.AnnotationMetadata; @@ -83,6 +85,7 @@ * @see EnableGlobalMethodSecurity */ @Configuration +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInitializingSingleton, BeanFactoryAware { private static final Log logger = LogFactory diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MetadataSourceConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MetadataSourceConfiguration.java index e8786c27614..79e74eaf8fc 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MetadataSourceConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MetadataSourceConfiguration.java @@ -15,14 +15,18 @@ */ package org.springframework.security.config.annotation.method.configuration; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource; @Configuration +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) class Jsr250MetadataSourceConfiguration { @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource() { return new Jsr250MethodSecurityMetadataSource(); } diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java index f6d9aa6cce8..53a839f460f 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java @@ -49,6 +49,7 @@ public MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMet } @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public DelegatingMethodSecurityMetadataSource methodMetadataSource() { ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory( new DefaultMethodSecurityExpressionHandler()); @@ -69,6 +70,7 @@ public PrePostAdviceReactiveMethodInterceptor securityMethodInterceptor(Abstract } @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() { return new DefaultMethodSecurityExpressionHandler(); } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index db5151c521b..a29bf42c9b9 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java @@ -586,6 +586,7 @@ public RememberMeConfigurer rememberMe() throws Exception { /** * Allows restricting access based upon the {@link HttpServletRequest} using + * {@link RequestMatcher} implementations (i.e. via URL patterns). * *

Example Configurations

* @@ -723,7 +724,7 @@ public ServletApiConfigurer servletApi() throws Exception { * } * * - * @return the {@link ServletApiConfigurer} for further customizations + * @return the {@link CsrfConfigurer} for further customizations * @throws Exception */ public CsrfConfigurer csrf() throws Exception { diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java index 8598e71507c..f56bfdb9a61 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java @@ -171,7 +171,7 @@ public final T authenticationDetailsSource( /** * Specifies the {@link AuthenticationSuccessHandler} to be used. The default is - * {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properites + * {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties * set. * * @param successHandler the {@link AuthenticationSuccessHandler}. diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java index 83e88c5252f..6b2cc72d653 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -26,7 +26,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider; import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken; @@ -64,6 +63,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.NegatedRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -429,9 +429,6 @@ public void init(B http) throws Exception { this.loginProcessingUrl); this.setAuthenticationFilter(authenticationFilter); super.loginProcessingUrl(this.loginProcessingUrl); - RequestMatcher authenticationNullMatcher = request -> SecurityContextHolder.getContext().getAuthentication() == null; - authenticationFilter.setRequiresAuthenticationRequestMatcher(new AndRequestMatcher(createLoginProcessingUrlMatcher(this.loginProcessingUrl), - authenticationNullMatcher)); if (this.loginPage != null) { // Set custom login page @@ -604,8 +601,11 @@ private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLogin RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher( new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher); + RequestMatcher notXRequestedWith = new NegatedRequestMatcher( + new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); + LinkedHashMap entryPoints = new LinkedHashMap<>(); - entryPoints.put(new NegatedRequestMatcher(defaultLoginPageMatcher), + entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)), new LoginUrlAuthenticationEntryPoint(providerLoginPage)); DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints); diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java index 490dc7f597d..e203194ed5d 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java @@ -160,6 +160,25 @@ public JwtConfigurer jwt() { @Override public void init(H http) throws Exception { + if ( this.jwtConfigurer == null ) { + throw new IllegalStateException("Jwt is the only supported format for bearer tokens " + + "in Spring Security and no Jwt configuration was found. Make sure to specify " + + "a jwk set uri by doing http.oauth2ResourceServer().jwt().jwkSetUri(uri), or wire a " + + "JwtDecoder instance by doing http.oauth2ResourceServer().jwt().decoder(decoder), or " + + "expose a JwtDecoder instance as a bean and do http.oauth2ResourceServer().jwt()."); + } + + JwtDecoder decoder = this.jwtConfigurer.getJwtDecoder(); + Converter jwtAuthenticationConverter = + this.jwtConfigurer.getJwtAuthenticationConverter(); + + JwtAuthenticationProvider provider = + new JwtAuthenticationProvider(decoder); + provider.setJwtAuthenticationConverter(jwtAuthenticationConverter); + provider = postProcess(provider); + + http.authenticationProvider(provider); + registerDefaultAccessDeniedHandler(http); registerDefaultEntryPoint(http); registerDefaultCsrfOverride(http); @@ -179,25 +198,6 @@ public void configure(H http) throws Exception { filter = postProcess(filter); http.addFilter(filter); - - if ( this.jwtConfigurer == null ) { - throw new IllegalStateException("Jwt is the only supported format for bearer tokens " + - "in Spring Security and no Jwt configuration was found. Make sure to specify " + - "a jwk set uri by doing http.oauth2ResourceServer().jwt().jwkSetUri(uri), or wire a " + - "JwtDecoder instance by doing http.oauth2ResourceServer().jwt().decoder(decoder), or " + - "expose a JwtDecoder instance as a bean and do http.oauth2ResourceServer().jwt()."); - } - - JwtDecoder decoder = this.jwtConfigurer.getJwtDecoder(); - Converter jwtAuthenticationConverter = - this.jwtConfigurer.getJwtAuthenticationConverter(); - - JwtAuthenticationProvider provider = - new JwtAuthenticationProvider(decoder); - provider.setJwtAuthenticationConverter(jwtAuthenticationConverter); - provider = postProcess(provider); - - http.authenticationProvider(provider); } public class JwtConfigurer { diff --git a/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java index 7755c85a51b..062d0815edc 100644 --- a/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java @@ -208,7 +208,7 @@ public BeanDefinition parse(Element element, ParserContext pc) { pc.registerBeanComponent(new BeanComponentDefinition( expressionHandler, expressionHandlerRef)); logger.info("Expressions were enabled for method security but no SecurityExpressionHandler was configured. " - + "All hasPermision() expressions will evaluate to false."); + + "All hasPermission() expressions will evaluate to false."); } BeanDefinitionBuilder expressionPreAdviceBldr = BeanDefinitionBuilder diff --git a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java index ec5f578ca66..95db5745dfd 100644 --- a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -53,8 +53,6 @@ import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeReactiveAuthenticationManager; @@ -90,7 +88,6 @@ import org.springframework.security.web.server.MatcherSecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.ServerAuthenticationEntryPoint; -import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.authentication.AuthenticationWebFilter; import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint; import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint; @@ -620,38 +617,62 @@ protected void configure(ServerHttpSecurity http) { AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager, authorizedClientRepository); authenticationFilter.setRequiresAuthenticationMatcher(createAttemptAuthenticationRequestMatcher()); authenticationFilter.setServerAuthenticationConverter(getAuthenticationConverter(clientRegistrationRepository)); - RedirectServerAuthenticationSuccessHandler redirectHandler = new RedirectServerAuthenticationSuccessHandler(); - - authenticationFilter.setAuthenticationSuccessHandler(redirectHandler); - authenticationFilter.setAuthenticationFailureHandler(new ServerAuthenticationFailureHandler() { - @Override - public Mono onAuthenticationFailure(WebFilterExchange webFilterExchange, - AuthenticationException exception) { - return Mono.error(exception); - } - }); + authenticationFilter.setAuthenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler()); + authenticationFilter.setAuthenticationFailureHandler(new RedirectServerAuthenticationFailureHandler("/login?error")); authenticationFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository()); - MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher( - MediaType.TEXT_HTML); - htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL)); + setDefaultEntryPoints(http); + + http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC); + http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION); + } + + private void setDefaultEntryPoints(ServerHttpSecurity http) { + String defaultLoginPage = "/login"; Map urlToText = http.oauth2Login.getLinks(); + String providerLoginPage = null; if (urlToText.size() == 1) { - http.defaultEntryPoints.add(new DelegateEntry(htmlMatcher, new RedirectServerAuthenticationEntryPoint(urlToText.keySet().iterator().next()))); - } else { - http.defaultEntryPoints.add(new DelegateEntry(htmlMatcher, new RedirectServerAuthenticationEntryPoint("/login"))); + providerLoginPage = urlToText.keySet().iterator().next(); } - http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC); - http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION); + MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher( + MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), + MediaType.TEXT_HTML, MediaType.TEXT_PLAIN); + htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL)); + + ServerWebExchangeMatcher xhrMatcher = exchange -> { + if (exchange.getRequest().getHeaders().getOrDefault("X-Requested-With", Collections.emptyList()).contains("XMLHttpRequest")) { + return ServerWebExchangeMatcher.MatchResult.match(); + } + return ServerWebExchangeMatcher.MatchResult.notMatch(); + }; + ServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher); + + ServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher( + notXhrMatcher, htmlMatcher); + + if (providerLoginPage != null) { + ServerWebExchangeMatcher loginPageMatcher = new PathPatternParserServerWebExchangeMatcher(defaultLoginPage); + ServerWebExchangeMatcher faviconMatcher = new PathPatternParserServerWebExchangeMatcher("/favicon.ico"); + ServerWebExchangeMatcher defaultLoginPageMatcher = new AndServerWebExchangeMatcher( + new OrServerWebExchangeMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher); + + ServerWebExchangeMatcher matcher = new AndServerWebExchangeMatcher( + notXhrMatcher, new NegatedServerWebExchangeMatcher(defaultLoginPageMatcher)); + RedirectServerAuthenticationEntryPoint entryPoint = + new RedirectServerAuthenticationEntryPoint(providerLoginPage); + entryPoint.setRequestCache(http.requestCache.requestCache); + http.defaultEntryPoints.add(new DelegateEntry(matcher, entryPoint)); + } + + RedirectServerAuthenticationEntryPoint defaultEntryPoint = + new RedirectServerAuthenticationEntryPoint(defaultLoginPage); + defaultEntryPoint.setRequestCache(http.requestCache.requestCache); + http.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint)); } private ServerWebExchangeMatcher createAttemptAuthenticationRequestMatcher() { - PathPatternParserServerWebExchangeMatcher loginPathMatcher = new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/{registrationId}"); - ServerWebExchangeMatcher notAuthenticatedMatcher = e -> ReactiveSecurityContextHolder.getContext() - .flatMap(p -> ServerWebExchangeMatcher.MatchResult.notMatch()) - .switchIfEmpty(ServerWebExchangeMatcher.MatchResult.match()); - return new AndServerWebExchangeMatcher(loginPathMatcher, notAuthenticatedMatcher); + return new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/{registrationId}"); } private ReactiveOAuth2UserService getOidcUserService() { @@ -1282,11 +1303,15 @@ public SecurityWebFilterChain build() { this.cors.configure(this); } if (this.httpBasic != null) { - this.httpBasic.authenticationManager(this.authenticationManager); + if (this.httpBasic.authenticationManager == null) { + this.httpBasic.authenticationManager(this.authenticationManager); + } this.httpBasic.configure(this); } if (this.formLogin != null) { - this.formLogin.authenticationManager(this.authenticationManager); + if (this.formLogin.authenticationManager == null) { + this.formLogin.authenticationManager(this.authenticationManager); + } if (this.securityContextRepository != null) { this.formLogin.securityContextRepository(this.securityContextRepository); } @@ -1478,7 +1503,7 @@ public AuthorizeExchangeSpec hasRole(String role) { /** * Require a specific authority. - * @param authority the authority to require (i.e. "USER" woudl require authority of "USER"). + * @param authority the authority to require (i.e. "USER" would require authority of "USER"). * @return the {@link AuthorizeExchangeSpec} to configure */ public AuthorizeExchangeSpec hasAuthority(String authority) { @@ -2387,7 +2412,9 @@ private HeaderSpec() { */ public final class LogoutSpec { private LogoutWebFilter logoutWebFilter = new LogoutWebFilter(); - private List logoutHandlers = new ArrayList<>(Arrays.asList(new SecurityContextServerLogoutHandler())); + private final SecurityContextServerLogoutHandler DEFAULT_LOGOUT_HANDLER = new SecurityContextServerLogoutHandler(); + private List logoutHandlers = new ArrayList<>(Arrays.asList(this.DEFAULT_LOGOUT_HANDLER)); + /** * Configures the logout handler. Default is {@code SecurityContextServerLogoutHandler} @@ -2451,6 +2478,10 @@ public ServerHttpSecurity disable() { } private Optional createLogoutHandler() { + ServerSecurityContextRepository securityContextRepository = ServerHttpSecurity.this.securityContextRepository; + if (securityContextRepository != null) { + this.DEFAULT_LOGOUT_HANDLER.setSecurityContextRepository(securityContextRepository); + } if (this.logoutHandlers.isEmpty()) { return Optional.empty(); } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java index 3031dde48ce..e8b6741d438 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java @@ -28,7 +28,6 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -38,7 +37,6 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; @@ -182,31 +180,6 @@ public void oauth2LoginWhenSuccessThenAuthenticationSuccessEventPublished() thro assertThat(OAuth2LoginConfig.EVENTS.get(0)).isInstanceOf(AuthenticationSuccessEvent.class); } - @Test - public void oauth2LoginWhenAuthenticatedThenIgnored() throws Exception { - // setup application context - loadConfig(OAuth2LoginConfig.class); - - // authenticate - TestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken("a", - "b", "ROLE_TEST"); - - this.request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, new SecurityContextImpl(expectedAuthentication)); - - // setup authentication parameters - this.request.setParameter("code", "code123"); - this.request.setParameter("state", "state"); - - // perform test - this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - - // assertions - Authentication authentication = this.securityContextRepository - .loadContext(new HttpRequestResponseHolder(this.request, this.response)) - .getAuthentication(); - assertThat(authentication).isEqualTo(expectedAuthentication); - } - @Test public void oauth2LoginCustomWithConfigurer() throws Exception { // setup application context @@ -353,6 +326,21 @@ public void oauth2LoginWithMultipleClientsConfiguredThenRedirectDefaultLoginPage assertThat(this.response.getRedirectedUrl()).matches("http://localhost/login"); } + // gh-6812 + @Test + public void oauth2LoginWithOneClientConfiguredAndRequestXHRNotAuthenticatedThenDoesNotRedirectForAuthorization() throws Exception { + loadConfig(OAuth2LoginConfig.class); + + String requestUri = "/"; + this.request = new MockHttpServletRequest("GET", requestUri); + this.request.setServletPath(requestUri); + this.request.addHeader("X-Requested-With", "XMLHttpRequest"); + + this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); + + assertThat(this.response.getRedirectedUrl()).doesNotMatch("http://localhost/oauth2/authorization/google"); + } + @Test public void oauth2LoginWithCustomLoginPageThenRedirectCustomLoginPage() throws Exception { loadConfig(OAuth2LoginConfigCustomLoginPage.class); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index 99cde97018a..3d7558467ec 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -298,6 +298,18 @@ public void postWhenCsrfDisabledWithBearerTokenAsFormParameterThenIgnoresToken() .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, "Bearer")); } + // gh-8031 + @Test + public void getWhenAnonymousDisabledThenAllows() throws Exception { + this.spring.register(JwtDecoderConfig.class, AnonymousDisabledConfig.class).autowire(); + JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class); + when(decoder.decode(anyString())).thenReturn(JWT); + + this.mvc.perform(get("/authenticated") + .with(bearerToken("token"))) + .andExpect(status().isNotFound()); + } + @Test public void getWhenUsingDefaultsWithNoBearerTokenThenUnauthorized() throws Exception { @@ -652,7 +664,8 @@ public void getBearerTokenResolverWhenDuplicateResolverBeansAndAnotherOnTheDslTh @Test public void getBearerTokenResolverWhenDuplicateResolverBeansThenWiringException() { - assertThatCode(() -> this.spring.register(MultipleBearerTokenResolverBeansConfig.class).autowire()) + assertThatCode(() -> this.spring + .register(JwtDecoderConfig.class, MultipleBearerTokenResolverBeansConfig.class).autowire()) .isInstanceOf(BeanCreationException.class) .hasRootCauseInstanceOf(NoUniqueBeanDefinitionException.class); } @@ -1097,6 +1110,22 @@ protected void configure(HttpSecurity http) throws Exception { } } + @EnableWebSecurity + static class AnonymousDisabledConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .anonymous().disable() + .oauth2ResourceServer() + .jwt(); + // @formatter:on + } + } + @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) static class MethodSecurityConfig extends WebSecurityConfigurerAdapter { diff --git a/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java b/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java index cbf1a2782b0..96a174f3b69 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 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. @@ -23,6 +23,8 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder; import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder; import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; @@ -40,6 +42,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; /** * @author Rob Winch @@ -152,6 +158,42 @@ public void authenticationSuccess() { assertThat(driver.getCurrentUrl()).endsWith("/custom"); } + @Test + public void customAuthenticationManager() { + ReactiveAuthenticationManager defaultAuthenticationManager = mock(ReactiveAuthenticationManager.class); + ReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class); + + given(defaultAuthenticationManager.authenticate(any())).willThrow(new RuntimeException("should not interact with default auth manager")); + given(customAuthenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("user", "password", "ROLE_USER", "ROLE_ADMIN"))); + + SecurityWebFilterChain securityWebFilter = this.http + .authenticationManager(defaultAuthenticationManager) + .formLogin() + .authenticationManager(customAuthenticationManager) + .and() + .build(); + + WebTestClient webTestClient = WebTestClientBuilder + .bindToWebFilters(securityWebFilter) + .build(); + + WebDriver driver = WebTestClientHtmlUnitDriverBuilder + .webTestClientSetup(webTestClient) + .build(); + + DefaultLoginPage loginPage = DefaultLoginPage.to(driver) + .assertAt(); + + HomePage homePage = loginPage.loginForm() + .username("user") + .password("password") + .submit(HomePage.class); + + homePage.assertAt(); + + verifyZeroInteractions(defaultAuthenticationManager); + } + public static class CustomLoginPage { private WebDriver driver; diff --git a/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java index a8dbc2ac264..8cf2b588b19 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java @@ -21,6 +21,7 @@ import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder; import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder; import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; @@ -117,4 +118,45 @@ public void customLogout() { .assertAt() .assertLogout(); } + + @Test + public void logoutWhenCustomSecurityContextRepositoryThenLogsOut() { + WebSessionServerSecurityContextRepository repository = new WebSessionServerSecurityContextRepository(); + repository.setSpringSecurityContextAttrName("CUSTOM_CONTEXT_ATTR"); + SecurityWebFilterChain securityWebFilter = this.http + .securityContextRepository(repository) + .authorizeExchange() + .anyExchange().authenticated() + .and() + .formLogin() + .and() + .logout() + .and() + .build(); + + WebTestClient webTestClient = WebTestClientBuilder + .bindToWebFilters(securityWebFilter) + .build(); + + WebDriver driver = WebTestClientHtmlUnitDriverBuilder + .webTestClientSetup(webTestClient) + .build(); + + FormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class) + .assertAt(); + + FormLoginTests.HomePage homePage = loginPage.loginForm() + .username("user") + .password("password") + .submit(FormLoginTests.HomePage.class); + + homePage.assertAt(); + + FormLoginTests.DefaultLogoutPage.to(driver) + .assertAt() + .logout(); + + FormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class) + .assertAt(); + } } diff --git a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java index 9982ca608a0..1755d8e4279 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -34,11 +34,17 @@ import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.TestClientRegistrations; +import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository; import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.TestOAuth2AccessTokens; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; -import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests; +import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses; import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.security.web.server.SecurityWebFilterChain; @@ -69,8 +75,11 @@ public class OAuth2ClientSpecTests { private ClientRegistration registration = TestClientRegistrations.clientRegistration().build(); + private ApplicationContext context; + @Autowired public void setApplicationContext(ApplicationContext context) { + this.context = context; this.client = WebTestClient.bindToApplicationContext(context).build(); } @@ -140,19 +149,40 @@ public void oauth2ClientWhenCustomObjectsThenUsed() { ServerAuthenticationConverter converter = config.authenticationConverter; ReactiveAuthenticationManager manager = config.manager; + ServerAuthorizationRequestRepository authorizationRequestRepository = + new WebSessionOAuth2ServerAuthorizationRequestRepository(); - OAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success(); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .redirectUri("/authorize/oauth2/code/registration-id") + .build(); + OAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success() + .redirectUri("/authorize/oauth2/code/registration-id") + .build(); + OAuth2AuthorizationExchange authorizationExchange = + new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse); OAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes(); - OAuth2AuthorizationCodeAuthenticationToken result = new OAuth2AuthorizationCodeAuthenticationToken(this.registration, exchange, accessToken); + OAuth2AuthorizationCodeAuthenticationToken result = new OAuth2AuthorizationCodeAuthenticationToken( + this.registration, authorizationExchange, accessToken); when(converter.convert(any())).thenReturn(Mono.just(new TestingAuthenticationToken("a", "b", "c"))); when(manager.authenticate(any())).thenReturn(Mono.just(result)); - this.client.get() - .uri("/authorize/oauth2/code/registration-id") - .exchange() - .expectStatus().is3xxRedirection(); + WebTestClient client = WebTestClient.bindToApplicationContext(this.context) + .webFilter((exchange, chain) -> + authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, exchange) + .then(chain.filter(exchange).then(Mono.empty())) + ) + .build(); + + client.get() + .uri(uriBuilder -> + uriBuilder.path("/authorize/oauth2/code/registration-id") + .queryParam(OAuth2ParameterNames.CODE, "code") + .queryParam(OAuth2ParameterNames.STATE, "state") + .build()) + .exchange() + .expectStatus().is3xxRedirection(); verify(converter).convert(any()); verify(manager).authenticate(any()); diff --git a/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java b/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java index 16d0fb43d53..090fa7ac2e7 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -26,21 +26,36 @@ import org.junit.Test; import org.openqa.selenium.WebDriver; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken; +import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; +import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient; +import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager; +import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository; +import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService; import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.TestOAuth2AccessTokens; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges; +import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests; +import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.TestOAuth2Users; import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; @@ -48,12 +63,17 @@ import org.springframework.security.web.server.WebFilterChainProxy; import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.config.EnableWebFlux; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; +import org.springframework.web.server.WebHandler; import reactor.core.publisher.Mono; +import java.time.Duration; +import java.time.Instant; + /** * @author Rob Winch * @since 5.1 @@ -63,6 +83,8 @@ public class OAuth2LoginTests { @Rule public final SpringTestRule spring = new SpringTestRule(); + private WebTestClient client; + @Autowired private WebFilterChainProxy springSecurity; @@ -72,6 +94,20 @@ public class OAuth2LoginTests { .clientSecret("secret") .build(); + private static ClientRegistration google = CommonOAuth2Provider.GOOGLE + .getBuilder("google") + .clientId("client") + .clientSecret("secret") + .build(); + + @Autowired + public void setApplicationContext(ApplicationContext context) { + if (context.getBeanNamesForType(WebHandler.class).length > 0) { + this.client = WebTestClient.bindToApplicationContext(context) + .build(); + } + } + @Test public void defaultLoginPageWithMultipleClientRegistrationsThenLinks() { this.spring.register(OAuth2LoginWithMulitpleClientRegistrations.class).autowire(); @@ -97,11 +133,6 @@ public void defaultLoginPageWithMultipleClientRegistrationsThenLinks() { static class OAuth2LoginWithMulitpleClientRegistrations { @Bean InMemoryReactiveClientRegistrationRepository clientRegistrationRepository() { - ClientRegistration google = CommonOAuth2Provider.GOOGLE - .getBuilder("google") - .clientId("client") - .clientSecret("secret") - .build(); return new InMemoryReactiveClientRegistrationRepository(github, google); } } @@ -123,6 +154,22 @@ public void defaultLoginPageWithSingleClientRegistrationThenRedirect() { assertThat(driver.getCurrentUrl()).startsWith("https://github.com/login/oauth/authorize"); } + // gh-8118 + @Test + public void defaultLoginPageWithSingleClientRegistrationAndXhrRequestThenDoesNotRedirectForAuthorization() { + this.spring.register(OAuth2LoginWithSingleClientRegistrations.class, WebFluxConfig.class).autowire(); + + this.client.get() + .uri("/") + .header("X-Requested-With", "XMLHttpRequest") + .exchange() + .expectStatus().is3xxRedirection() + .expectHeader().valueEquals(HttpHeaders.LOCATION, "/login"); + } + + @EnableWebFlux + static class WebFluxConfig { } + @EnableWebFluxSecurity static class OAuth2LoginWithSingleClientRegistrations { @Bean @@ -182,6 +229,78 @@ public SecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) { } } + // gh-5562 + @Test + public void oauth2LoginWhenAccessTokenRequestFailsThenDefaultRedirectToLogin() { + this.spring.register(OAuth2LoginWithMulitpleClientRegistrations.class, + OAuth2LoginWithCustomBeansConfig.class).autowire(); + + WebTestClient webTestClient = WebTestClientBuilder + .bindToWebFilters(this.springSecurity) + .build(); + + OAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().scope("openid").build(); + OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build(); + OAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response); + OAuth2AccessToken accessToken = new OAuth2AccessToken( + OAuth2AccessToken.TokenType.BEARER, "openid", Instant.now(), Instant.now().plus(Duration.ofDays(1))); + OAuth2AuthorizationCodeAuthenticationToken authenticationToken = + new OAuth2AuthorizationCodeAuthenticationToken(google, exchange, accessToken); + + OAuth2LoginWithCustomBeansConfig config = this.spring.getContext().getBean(OAuth2LoginWithCustomBeansConfig.class); + + ServerAuthenticationConverter converter = config.authenticationConverter; + when(converter.convert(any())).thenReturn(Mono.just(authenticationToken)); + + ReactiveOAuth2AccessTokenResponseClient tokenResponseClient = config.tokenResponseClient; + OAuth2Error oauth2Error = new OAuth2Error("invalid_request", "Invalid request", null); + when(tokenResponseClient.getTokenResponse(any())).thenThrow(new OAuth2AuthenticationException(oauth2Error)); + + webTestClient.get() + .uri("/login/oauth2/code/google") + .exchange() + .expectStatus() + .is3xxRedirection() + .expectHeader() + .valueEquals("Location", "/login?error"); + } + + @Configuration + static class OAuth2LoginWithCustomBeansConfig { + + ServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class); + + ReactiveOAuth2AccessTokenResponseClient tokenResponseClient = + mock(ReactiveOAuth2AccessTokenResponseClient.class); + + ReactiveOAuth2UserService userService = mock(ReactiveOAuth2UserService.class); + + @Bean + public SecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) { + // @formatter:off + http + .authorizeExchange() + .anyExchange().authenticated() + .and() + .oauth2Login() + .authenticationConverter(authenticationConverter) + .authenticationManager(authenticationManager()); + return http.build(); + // @formatter:on + } + + private ReactiveAuthenticationManager authenticationManager() { + OidcAuthorizationCodeReactiveAuthenticationManager oidc = + new OidcAuthorizationCodeReactiveAuthenticationManager(tokenResponseClient, userService); + return oidc; + } + + @Bean + public ReactiveOAuth2AccessTokenResponseClient accessTokenResponseClient() { + return tokenResponseClient; + } + } + static class GitHubWebFilter implements WebFilter { @Override diff --git a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java index 58516f14baa..ff538356b29 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java @@ -19,6 +19,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import java.util.Arrays; @@ -190,6 +192,25 @@ public void csrfServerLogoutHandlerAppliedIfCsrfIsEnabled() { .isEqualTo(Arrays.asList(SecurityContextServerLogoutHandler.class, CsrfServerLogoutHandler.class)); } + @Test + public void basicWithCustomAuthenticationManager() { + ReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class); + given(customAuthenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN"))); + + SecurityWebFilterChain securityFilterChain = this.http.httpBasic().authenticationManager(customAuthenticationManager).and().build(); + WebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain); + WebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build(); + + client.get() + .uri("/") + .headers(headers -> headers.setBasicAuth("rob", "rob")) + .exchange() + .expectStatus().isOk() + .expectBody(String.class).consumeWith(b -> assertThat(b.getResponseBody()).isEqualTo("ok")); + + verifyZeroInteractions(this.authenticationManager); + } + private Optional getWebFilter(SecurityWebFilterChain filterChain, Class filterClass) { return (Optional) filterChain.getWebFilters() .filter(Objects::nonNull) diff --git a/core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java b/core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java index 563f726a42f..97b201d0855 100644 --- a/core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java +++ b/core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java @@ -17,7 +17,6 @@ import java.io.Serializable; import java.util.Collection; -import java.util.HashSet; import java.util.Set; import org.springframework.security.access.PermissionEvaluator; @@ -158,7 +157,6 @@ public void setDefaultRolePrefix(String defaultRolePrefix) { private Set getAuthoritySet() { if (roles == null) { - roles = new HashSet<>(); Collection userAuthorities = authentication .getAuthorities(); diff --git a/core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationProvider.java b/core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationProvider.java index 2b38352461e..34a3f231eef 100644 --- a/core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationProvider.java +++ b/core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationProvider.java @@ -129,7 +129,7 @@ * </property> * * - * A configuration note: The JaasAuthenticationProvider uses the security properites + * A configuration note: The JaasAuthenticationProvider uses the security properties * "login.config.url.X" to configure jaas. If you would like to customize the way Jaas * gets configured, create a subclass of this and override the * {@link #configureJaas(Resource)} method. diff --git a/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java index b476ece2c86..b34f942e1c4 100644 --- a/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java +++ b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java @@ -132,13 +132,18 @@ public void registerNewSession(String sessionId, Object principal) { sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date())); - Set sessionsUsedByPrincipal = principals.computeIfAbsent(principal, key -> new CopyOnWriteArraySet<>()); - sessionsUsedByPrincipal.add(sessionId); + principals.compute(principal, (key, sessionsUsedByPrincipal) -> { + if (sessionsUsedByPrincipal == null) { + sessionsUsedByPrincipal = new CopyOnWriteArraySet<>(); + } + sessionsUsedByPrincipal.add(sessionId); - if (logger.isTraceEnabled()) { - logger.trace("Sessions used by '" + principal + "' : " - + sessionsUsedByPrincipal); - } + if (logger.isTraceEnabled()) { + logger.trace("Sessions used by '" + principal + "' : " + + sessionsUsedByPrincipal); + } + return sessionsUsedByPrincipal; + }); } public void removeSessionInformation(String sessionId) { @@ -157,32 +162,29 @@ public void removeSessionInformation(String sessionId) { sessionIds.remove(sessionId); - Set sessionsUsedByPrincipal = principals.get(info.getPrincipal()); - - if (sessionsUsedByPrincipal == null) { - return; - } - - if (logger.isDebugEnabled()) { - logger.debug("Removing session " + sessionId - + " from principal's set of registered sessions"); - } + principals.computeIfPresent(info.getPrincipal(), (key, sessionsUsedByPrincipal) -> { + if (logger.isDebugEnabled()) { + logger.debug("Removing session " + sessionId + + " from principal's set of registered sessions"); + } - sessionsUsedByPrincipal.remove(sessionId); + sessionsUsedByPrincipal.remove(sessionId); - if (sessionsUsedByPrincipal.isEmpty()) { - // No need to keep object in principals Map anymore - if (logger.isDebugEnabled()) { - logger.debug("Removing principal " + info.getPrincipal() - + " from registry"); + if (sessionsUsedByPrincipal.isEmpty()) { + // No need to keep object in principals Map anymore + if (logger.isDebugEnabled()) { + logger.debug("Removing principal " + info.getPrincipal() + + " from registry"); + } + sessionsUsedByPrincipal = null; } - principals.remove(info.getPrincipal()); - } - if (logger.isTraceEnabled()) { - logger.trace("Sessions used by '" + info.getPrincipal() + "' : " - + sessionsUsedByPrincipal); - } + if (logger.isTraceEnabled()) { + logger.trace("Sessions used by '" + info.getPrincipal() + "' : " + + sessionsUsedByPrincipal); + } + return sessionsUsedByPrincipal; + }); } } diff --git a/core/src/main/resources/org/springframework/security/messages.properties b/core/src/main/resources/org/springframework/security/messages.properties index 665a742002e..a84f259753f 100644 --- a/core/src/main/resources/org/springframework/security/messages.properties +++ b/core/src/main/resources/org/springframework/security/messages.properties @@ -31,6 +31,7 @@ DigestAuthenticationFilter.usernameNotFound=Username {0} not found JdbcDaoImpl.noAuthority=User {0} has no GrantedAuthority JdbcDaoImpl.notFound=User {0} not found LdapAuthenticationProvider.badCredentials=Bad credentials +LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed LdapAuthenticationProvider.credentialsExpired=User credentials have expired LdapAuthenticationProvider.disabled=User is disabled LdapAuthenticationProvider.expired=User account has expired diff --git a/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java index e0b80e5dd04..c1ad734aeba 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java @@ -65,6 +65,10 @@ public BCryptPasswordEncoder(int strength, SecureRandom random) { } public String encode(CharSequence rawPassword) { + if (rawPassword == null) { + throw new IllegalArgumentException("rawPassword cannot be null"); + } + String salt; if (strength > 0) { if (random != null) { @@ -81,6 +85,10 @@ public String encode(CharSequence rawPassword) { } public boolean matches(CharSequence rawPassword, String encodedPassword) { + if (rawPassword == null) { + throw new IllegalArgumentException("rawPassword cannot be null"); + } + if (encodedPassword == null || encodedPassword.length() == 0) { logger.warn("Empty encoded password"); return false; diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java index 7e0c5e8295e..7ebfb5a356d 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2016 the original author or authors. + * Copyright 2011-2020 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. @@ -32,16 +32,13 @@ public class Encryptors { * (Password-Based Key Derivation Function #2). Salts the password to prevent * dictionary attacks against the key. The provided salt is expected to be * hex-encoded; it should be random and at least 8 bytes in length. Also applies a - * random 16 byte initialization vector to ensure each encrypted message will be + * random 16-byte initialization vector to ensure each encrypted message will be * unique. Requires Java 6. * * @param password the password used to generate the encryptor's secret key; should * not be shared * @param salt a hex-encoded, random, site-global salt value to use to generate the * key - * - * @see #standard(CharSequence, CharSequence) which uses the slightly weaker CBC mode - * (instead of GCM) */ public static BytesEncryptor stronger(CharSequence password, CharSequence salt) { return new AesBytesEncryptor(password.toString(), salt, @@ -53,13 +50,21 @@ public static BytesEncryptor stronger(CharSequence password, CharSequence salt) * Derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation * Function #2). Salts the password to prevent dictionary attacks against the key. The * provided salt is expected to be hex-encoded; it should be random and at least 8 - * bytes in length. Also applies a random 16 byte initialization vector to ensure each + * bytes in length. Also applies a random 16-byte initialization vector to ensure each * encrypted message will be unique. Requires Java 6. + * NOTE: This mode is not + * authenticated + * and does not provide any guarantees about the authenticity of the data. + * For a more secure alternative, users should prefer + * {@link #stronger(CharSequence, CharSequence)}. * * @param password the password used to generate the encryptor's secret key; should * not be shared * @param salt a hex-encoded, random, site-global salt value to use to generate the * key + * + * @see #stronger(CharSequence, CharSequence), which uses the significatly more secure + * GCM (instead of CBC) */ public static BytesEncryptor standard(CharSequence password, CharSequence salt) { return new AesBytesEncryptor(password.toString(), salt, @@ -100,7 +105,10 @@ public static TextEncryptor text(CharSequence password, CharSequence salt) { * not be shared * @param salt a hex-encoded, random, site-global salt value to use to generate the * secret key + * @deprecated This encryptor is not secure. Instead, look to your data store for a + * mechanism to query encrypted data. */ + @Deprecated public static TextEncryptor queryableText(CharSequence password, CharSequence salt) { return new HexEncodingTextEncryptor(new AesBytesEncryptor(password.toString(), salt)); diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/Md4PasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/Md4PasswordEncoder.java index a0110e86124..7fe06a979de 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/Md4PasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/Md4PasswordEncoder.java @@ -83,8 +83,6 @@ public class Md4PasswordEncoder implements PasswordEncoder { private StringKeyGenerator saltGenerator = new Base64StringKeyGenerator(); private boolean encodeHashAsBase64; - private Digester digester; - public void setEncodeHashAsBase64(boolean encodeHashAsBase64) { this.encodeHashAsBase64 = encodeHashAsBase64; diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/NoOpPasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/NoOpPasswordEncoder.java index 907eb7c718e..3c28e167419 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/NoOpPasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/NoOpPasswordEncoder.java @@ -26,7 +26,8 @@ * @deprecated This PasswordEncoder is not secure. Instead use an * adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or * SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports - * password upgrades. + * password upgrades. There are no plans to remove this support. It is deprecated to indicate that + * this is a legacy implementation and using it is considered insecure. */ @Deprecated public final class NoOpPasswordEncoder implements PasswordEncoder { diff --git a/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java b/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java index d637c085f44..ec34e95c5b5 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java @@ -92,4 +92,15 @@ public void doesntMatchBogusEncodedValue() { assertThat(encoder.matches("password", "012345678901234567890123456789")).isFalse(); } + @Test(expected = IllegalArgumentException.class) + public void encodeNullRawPassword() { + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + encoder.encode(null); + } + + @Test(expected = IllegalArgumentException.class) + public void matchNullRawPassword() { + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + encoder.matches(null, "does-not-matter"); + } } diff --git a/dep-updates.txt b/dep-updates.txt new file mode 100644 index 00000000000..9da3481c8cf --- /dev/null +++ b/dep-updates.txt @@ -0,0 +1,2873 @@ + +------------------------------------------------------------ +:spring-security-config Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - ldapsdk:ldapsdk:4.1 + - org.eclipse.persistence:javax.persistence:2.2.1 + - org.openid4java:openid4java-nodeps:0.9.6 + - org.springframework.ldap:spring-ldap-core:2.3.2.RELEASE + +The following dependencies have later release versions: + - cglib:cglib-nodep [3.2.10 -> 3.2.12] + https://github.com/cglib/cglib + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - com.squareup.okhttp3:mockwebserver [3.12.2 -> 4.0.1] + https://github.com/square/okhttp + - io.projectreactor:reactor-core [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor.netty:reactor-netty [0.8.6.RELEASE -> 0.9.0.M3] + https://github.com/reactor/reactor-netty + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.annotation:jsr250-api [1.0 -> 1.0-20050927.133100] + https://jax-ws.dev.java.net/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - net.sourceforge.htmlunit:htmlunit [2.33 -> 2.35.0] + http://htmlunit.sourceforge.net + - org.apache.directory.server:apacheds-core [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-core-entry [1.5.5 -> 1.5.7] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-ldap [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-shared [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-server-jndi [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.shared:shared-ldap [0.9.15 -> 1.0.0-M1] + http://directory.apache.org/ + - org.aspectj:aspectjweaver [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.codehaus.groovy:groovy-all [2.4.14 -> 3.0.0-beta-2] + https://groovy-lang.org + - org.hibernate:hibernate-entitymanager [5.3.9.Final -> 5.4.4.Final] + http://hibernate.org/orm + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-java [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.spockframework:spock-core [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.spockframework:spock-spring [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-expression [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-orm [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-websocket [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.data:spring-data-jpa [2.1.6.RELEASE -> 2.2.0.RC2] + https://projects.spring.io/spring-data-jpa + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-taglibs Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.codehaus.groovy:groovy-all [2.4.14 -> 3.0.0-beta-2] + https://groovy-lang.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.spockframework:spock-core [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.spockframework:spock-spring [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-expression [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-cas Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + - net.sf.ehcache:ehcache:2.10.6 + - org.jasig.cas.client:cas-client-core:3.5.1 + - org.skyscreamer:jsonassert:1.5.0 + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-crypto Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.bouncycastle:bcpkix-jdk15on [1.61 -> 1.62] + http://www.bouncycastle.org/java.html + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-jcl [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-oauth2-jose Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - com.nimbusds:nimbus-jose-jwt [6.0.2 -> 7.7] + https://bitbucket.org/connect2id/nimbus-jose-jwt + - com.squareup.okhttp3:mockwebserver [3.12.2 -> 4.0.1] + https://github.com/square/okhttp + - io.projectreactor:reactor-core [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor.netty:reactor-netty [0.8.6.RELEASE -> 0.9.0.M3] + https://github.com/reactor/reactor-netty + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-oauth2-resource-server Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - com.squareup.okhttp3:mockwebserver [3.12.2 -> 4.0.1] + https://github.com/square/okhttp + - io.projectreactor:reactor-core [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor:reactor-test [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor.netty:reactor-netty [0.8.6.RELEASE -> 0.9.0.M3] + https://github.com/reactor/reactor-netty + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-oauth2-core Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - com.nimbusds:oauth2-oidc-sdk [6.0 -> 6.14] + https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-oauth2-client Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - com.nimbusds:oauth2-oidc-sdk [6.0 -> 6.14] + https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions + - com.squareup.okhttp3:mockwebserver [3.12.2 -> 4.0.1] + https://github.com/square/okhttp + - io.projectreactor:reactor-core [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor:reactor-test [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor.netty:reactor-netty [0.8.6.RELEASE -> 0.9.0.M3] + https://github.com/reactor/reactor-netty + - io.projectreactor.tools:blockhound [1.0.0.M4 -> 1.0.0.M5] + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-test Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.skyscreamer:jsonassert:1.5.0 + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - io.projectreactor:reactor-core [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor:reactor-test [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-remoting Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-openid Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + - net.sourceforge.nekohtml:nekohtml:1.9.22 + - org.openid4java:openid4java-nodeps:0.9.6 + +The following dependencies have later release versions: + - com.google.inject:guice [3.0 -> 4.2.2] + https://github.com/google/guice + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.apache.httpcomponents:httpclient [4.5.7 -> 4.5.9] + http://hc.apache.org/httpcomponents-client + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-web Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.skyscreamer:jsonassert:1.5.0 + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - commons-codec:commons-codec [1.11 -> 1.13] + https://commons.apache.org/proper/commons-codec/ + - io.projectreactor:reactor-core [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor:reactor-test [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.codehaus.groovy:groovy-all [2.4.14 -> 3.0.0-beta-2] + https://groovy-lang.org + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.spockframework:spock-core [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.spockframework:spock-spring [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-expression [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-data Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.data:spring-data-commons [2.1.6.RELEASE -> 2.2.0.RC2] + https://www.spring.io/spring-data + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-core Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - net.sf.ehcache:ehcache:2.10.6 + - org.skyscreamer:jsonassert:1.5.0 + +The following dependencies have later release versions: + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - commons-collections:commons-collections [3.2.2 -> 20040616] + - io.projectreactor:reactor-core [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.projectreactor:reactor-test [3.2.8.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.annotation:jsr250-api [1.0 -> 1.0-20050927.133100] + https://jax-ws.dev.java.net/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.aspectj:aspectjrt [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-expression [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-messaging Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - commons-codec:commons-codec [1.11 -> 1.13] + https://commons.apache.org/proper/commons-codec/ + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.powermock:powermock-api-mockito2 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-api-support [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-core [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4 [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-module-junit4-common [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.powermock:powermock-reflect [2.0.0 -> 2.0.2] + http://www.powermock.org + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.spockframework:spock-core [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.spockframework:spock-spring [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-expression [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-messaging [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-websocket [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-acl Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - net.sf.ehcache:ehcache:2.10.6 + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context-support [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-ldap Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - ldapsdk:ldapsdk:4.1 + - org.springframework.ldap:spring-ldap-core:2.3.2.RELEASE + +The following dependencies have later release versions: + - com.unboundid:unboundid-ldapsdk [4.0.10 -> 4.0.11] + https://github.com/pingidentity/ldapsdk + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.apache.directory.server:apacheds-core [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-core-entry [1.5.5 -> 1.5.7] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-ldap [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-shared [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-server-jndi [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.shared:shared-ldap [0.9.15 -> 1.0.0-M1] + http://directory.apache.org/ + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-itest-context Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.aspectj:aspectjweaver [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.python:jython [2.5.0 -> 2.7.1b3] + http://www.jython.org/ + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-itest-web Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet:javax.servlet-api:4.0.1 + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-inmemory Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-hellojs Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - com.fasterxml.jackson.core:jackson-databind [2.9.8 -> 2.10.0.pr1] + http://github.com/FasterXML/jackson + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-helloworld Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-openid Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - net.sourceforge.nekohtml:nekohtml:1.9.22 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-preauth Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-hellomvc Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-concurrency Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-form Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - com.sun.xml.bind:jaxb-core:2.3.0.1 + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - com.sun.xml.bind:jaxb-impl [2.3.0.1 -> 2.4.0-b180830.0438] + http://jaxb.java.net + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-data Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.validation:validation-api:2.0.1.Final + - org.eclipse.persistence:javax.persistence:2.2.1 + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-entitymanager [5.3.9.Final -> 5.4.4.Final] + http://hibernate.org/orm + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.data:spring-data-jpa [2.1.6.RELEASE -> 2.2.0.RC2] + https://projects.spring.io/spring-data-jpa + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-aspectj Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.aspectj:aspectjrt [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.aspectj:aspectjtools [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-jdbc Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - com.sun.xml.bind:jaxb-core:2.3.0.1 + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - com.sun.xml.bind:jaxb-impl [2.3.0.1 -> 2.4.0-b180830.0438] + http://jaxb.java.net + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-x509 Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:jul-to-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-ldap Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.apache.directory.server:apacheds-core [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-core-entry [1.5.5 -> 1.5.7] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-ldap [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-shared [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-server-jndi [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.shared:shared-ldap [0.9.15 -> 1.0.0-M1] + http://directory.apache.org/ + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-rememberme Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-javaconfig-messages Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.validation:validation-api:2.0.1.Final + - org.eclipse.persistence:javax.persistence:2.2.1 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + - org.thymeleaf:thymeleaf-spring5:3.0.11.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect [2.3.0 -> 2.4.1] + https://github.com/ultraq/thymeleaf-layout-dialect/ + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-entitymanager [5.3.9.Final -> 5.4.4.Final] + http://hibernate.org/orm + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-aspects [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-instrument [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-orm [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.data:spring-data-jpa [2.1.6.RELEASE -> 2.2.0.RC2] + https://projects.spring.io/spring-data-jpa + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-oauth2webclient-webflux Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE + +The following dependencies have later release versions: + - io.projectreactor.netty:reactor-netty [0.8.5.RELEASE -> 0.9.0.M3] + https://github.com/reactor/reactor-netty + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-thymeleaf [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-thymeleaf + - org.springframework.boot:spring-boot-starter-webflux [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-webflux + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-hellowebflux-method Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.projectreactor:reactor-test [3.2.6.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-webflux [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-webflux + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-webflux-form Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.skyscreamer:jsonassert:1.5.0 + - org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE + +The following dependencies have later release versions: + - io.projectreactor:reactor-test [3.2.6.RELEASE -> 3.3.0.M3] + https://github.com/reactor/reactor-core + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-thymeleaf [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-thymeleaf + - org.springframework.boot:spring-boot-starter-webflux [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-webflux + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-oauth2resourceserver-webflux Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - com.squareup.okhttp3:mockwebserver [3.12.2 -> 4.0.1] + https://github.com/square/okhttp + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-webflux [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-webflux + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-oauth2authorizationserver Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - com.sun.xml.bind:jaxb-core:2.3.0.1 + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + +The following dependencies have later release versions: + - com.nimbusds:nimbus-jose-jwt [6.0.2 -> 7.7] + https://bitbucket.org/connect2id/nimbus-jose-jwt + - com.sun.xml.bind:jaxb-impl [2.3.0.1 -> 2.4.0-b180830.0438] + http://jaxb.java.net + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-security [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-security + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-web [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web + - org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure [2.1.3.RELEASE -> 2.2.0.M4] + https://spring.io/spring-security + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-helloworld Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-thymeleaf [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-thymeleaf + - org.springframework.boot:spring-boot-starter-web [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-hellowebfluxfn Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-webflux [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-webflux + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-insecure Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-thymeleaf [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-thymeleaf + - org.springframework.boot:spring-boot-starter-web [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-oauth2webclient Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE + +The following dependencies have later release versions: + - io.projectreactor.netty:reactor-netty [0.8.5.RELEASE -> 0.9.0.M3] + https://github.com/reactor/reactor-netty + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webflux [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-thymeleaf [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-thymeleaf + - org.springframework.boot:spring-boot-starter-web [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-oauth2resourceserver Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - com.squareup.okhttp3:mockwebserver [3.12.2 -> 4.0.1] + https://github.com/square/okhttp + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-web [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-oauth2login-webflux Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - net.sourceforge.htmlunit:htmlunit [2.33 -> 2.35.0] + http://htmlunit.sourceforge.net + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-thymeleaf [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-thymeleaf + - org.springframework.boot:spring-boot-starter-webflux [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-webflux + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-oauth2login Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - net.sourceforge.htmlunit:htmlunit [2.33 -> 2.35.0] + http://htmlunit.sourceforge.net + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-thymeleaf [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-thymeleaf + - org.springframework.boot:spring-boot-starter-web [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-boot-hellowebflux Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.5.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework.boot:spring-boot-starter-test [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-test + - org.springframework.boot:spring-boot-starter-webflux [2.1.3.RELEASE -> 2.2.0.M4] + https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-webflux + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-cassample Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - commons-httpclient:commons-httpclient:3.1 + - net.sf.ehcache:ehcache:2.10.6 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.jasig.cas.client:cas-client-core:3.5.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.codehaus.groovy:groovy [2.4.14 -> 3.0.0-beta-2] + https://groovy-lang.org + - org.codehaus.groovy:groovy-all [2.4.14 -> 3.0.0-beta-2] + https://groovy-lang.org + - org.eclipse.jetty:jetty-server [9.4.12.v20180830 -> 10.0.0-alpha0] + http://www.eclipse.org/jetty + - org.eclipse.jetty:jetty-servlet [9.4.12.v20180830 -> 10.0.0-alpha0] + http://www.eclipse.org/jetty + - org.gebish:geb-spock [0.10.0 -> 3.0.1] + http://www.gebish.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.spockframework:spock-core [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.spockframework:spock-spring [1.0-groovy-2.4 -> 1.3-RC1-groovy-2.5] + http://spockframework.org + - org.springframework:spring-context-support [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-casserver Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.aspectj:aspectjrt [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.aspectj:aspectjtools [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.aspectj:aspectjweaver [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.jasig.cas:cas-server-webapp [4.0.0 -> 4.2.7] + http://www.apereo.org/cas + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-cas Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-tutorial Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-helloworld Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.skyscreamer:jsonassert:1.5.0 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-openid Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-preauth Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-insecuremvc Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - opensymphony:sitemesh [2.4.2 -> 2.5-atlassian-11] + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-insecure Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.xml.bind:jaxb-api:2.4.0-b180830.0359 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-gae Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - javax.validation:validation-api:2.0.1.Final + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - com.google.appengine:appengine [1.9.71 -> 1.9.76] + http://code.google.com/appengine/ + - com.google.appengine:appengine-api-1.0-sdk [1.9.71 -> 1.9.76] + http://code.google.com/appengine/ + - com.google.appengine:appengine-api-labs [1.9.71 -> 1.9.76] + http://code.google.com/appengine/ + - com.google.appengine:appengine-api-stubs [1.9.71 -> 1.9.76] + http://code.google.com/appengine/ + - com.google.appengine:appengine-testing [1.9.71 -> 1.9.76] + http://code.google.com/appengine/ + - com.google.cloud.tools:appengine-gradle-plugin [1.3.5 -> 2.1.0] + https://github.com/GoogleCloudPlatform/app-gradle-plugin + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hibernate:hibernate-validator [6.0.16.Final -> 6.1.0.Alpha6] + http://hibernate.org/validator + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context-support [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-aspectj Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.aspectj:aspectjrt [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.aspectj:aspectjtools [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-dms Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - net.sf.ehcache:ehcache:2.10.6 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context-support [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-ldap Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.apache.directory.server:apacheds-core [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-core-entry [1.5.5 -> 1.5.7] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-ldap [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-protocol-shared [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.server:apacheds-server-jndi [1.5.5 -> 2.0.0-M24] + http://directory.apache.org/apacheds/1.5 + - org.apache.directory.shared:shared-ldap [0.9.15 -> 1.0.0-M1] + http://directory.apache.org/ + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-jaas Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-contacts Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - net.sf.ehcache:ehcache:2.10.6 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.hsqldb:hsqldb [2.4.1 -> 2.5.0] + http://hsqldb.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.seleniumhq.selenium:htmlunit-driver [2.33.3 -> 2.35.1] + https://github.com/SeleniumHQ/htmlunit-driver + - org.seleniumhq.selenium:selenium-support [3.141.59 -> 4.0.0-alpha-2] + http://www.seleniumhq.org/ + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context-support [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-jdbc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-tx [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-samples-xml-servletapi Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies are using the latest release version: + - javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2 + - org.apache.taglibs:taglibs-standard-jstlel:1.2.5 + - org.gretty:gretty-runner-jetty7:2.3.1 + - org.gretty:gretty-runner-jetty8:2.3.1 + - org.gretty:gretty-runner-jetty9:2.3.1 + - org.gretty:gretty-runner-jetty93:2.3.1 + - org.gretty:gretty-runner-jetty94:2.3.1 + - org.gretty:gretty-runner-tomcat7:2.3.1 + - org.gretty:gretty-runner-tomcat8:2.3.1 + - org.gretty:gretty-runner-tomcat85:2.3.1 + - org.gretty:gretty-runner-tomcat9:2.3.1 + - org.gretty:gretty-starter:2.3.1 + - org.springframework:springloaded:1.2.8.RELEASE + +The following dependencies have later release versions: + - ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha4] + http://logback.qos.ch + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - javax.servlet:javax.servlet-api [3.1.0 -> 4.0.1] + https://javaee.github.io/servlet-spec/ + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.slf4j:jcl-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:log4j-over-slf4j [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.slf4j:slf4j-api [1.7.26 -> 2.0.0-alpha0] + http://www.slf4j.org + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context-support [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-web [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-webmvc [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] + +------------------------------------------------------------ +:spring-security-aspects Project Dependency Updates (report to plain text file) +------------------------------------------------------------ + +The following dependencies have later release versions: + - io.spring.javaformat:spring-javaformat-checkstyle [0.0.7 -> 0.0.15] + https://github.com/spring-io/spring-javaformat/#/spring-javaformat/spring-javaformat-checkstyle + - junit:junit [4.12 -> 4.13-beta-3] + http://junit.org + - org.aspectj:aspectjrt [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.aspectj:aspectjtools [1.9.2 -> 1.9.4] + http://www.aspectj.org + - org.assertj:assertj-core [3.11.1 -> 3.13.2] + http://assertj.org + - org.mockito:mockito-core [2.23.4 -> 3.0.0] + https://github.com/mockito/mockito + - org.springframework:spring-aop [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-beans [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-context [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-core [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + - org.springframework:spring-test [5.1.6.RELEASE -> 5.2.0.RC1] + https://github.com/spring-projects/spring-framework + +Gradle release-candidate updates: + - Gradle: [4.10.2 -> 5.5.1 -> 5.6-rc-1] diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc b/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc index 0508f8adeea..c3c2c353b53 100644 --- a/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc @@ -17,7 +17,7 @@ Below are the highlights of the release. * <> support for selecting an `AccessDeniedHandler` by `RequestMatcher` * <> support for excluding certain requests * Added Support for <> -* Added {security-api-url}core/src/main/java/org/springframework/security/core/Transient.java[@Transient] authentication tokens +* Added {security-api-url}org/springframework/security/core/Transient.html[@Transient] authentication tokens * A modern look-and-feel for the default log in page === WebFlux diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc index 56a6d88ad3f..648fe7e3662 100644 --- a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc @@ -341,7 +341,7 @@ Resource Server uses `JwtTimestampValidator` to verify a token's validity window @Bean ReactiveJwtDecoder jwtDecoder() { NimbusReactiveJwtDecoder jwtDecoder = (NimbusReactiveJwtDecoder) - ReactiveJwtDecoders.withOidcIssuerLocation(issuerUri); + ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri); OAuth2TokenValidator withClockSkew = new DelegatingOAuth2TokenValidator<>( new JwtTimestampValidator(Duration.ofSeconds(60)), @@ -383,7 +383,7 @@ Then, to add into a resource server, it's a matter of specifying the `ReactiveJw @Bean ReactiveJwtDecoder jwtDecoder() { NimbusReactiveJwtDecoder jwtDecoder = (NimbusReactiveJwtDecoder) - ReactiveJwtDecoders.withOidcIssuerLocation(issuerUri); + ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri); OAuth2TokenValidator audienceValidator = new AudienceValidator(); OAuth2TokenValidator withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri); diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/crypto.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/crypto.adoc index 8ed89233d9f..e2ec18ad03a 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/crypto.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/crypto.adoc @@ -17,14 +17,16 @@ Encryptors are thread-safe. [[spring-security-crypto-encryption-bytes]] ==== BytesEncryptor -Use the Encryptors.standard factory method to construct a "standard" BytesEncryptor: +Use the `Encryptors.stronger` factory method to construct a BytesEncryptor: [source,java] ---- -Encryptors.standard("password", "salt"); +Encryptors.stronger("password", "salt"); ---- -The "standard" encryption method is 256-bit AES using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2). +The "stronger" encryption method creates an encryptor using 256 bit AES encryption with +Galois Counter Mode (GCM). +It derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2). This method requires Java 6. The password used to generate the SecretKey should be kept in a secure place and not be shared. The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised. @@ -38,6 +40,11 @@ Such a salt may be generated using a KeyGenerator: String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded ---- +Users may also use the `standard` encryption method, which is 256-bit AES in Cipher Block Chaining (CBC) Mode. +This mode is not https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any +guarantees about the authenticity of the data. +For a more secure alternative, users should prefer `Encryptors.stronger`. + [[spring-security-crypto-encryption-text]] ==== TextEncryptor Use the Encryptors.text factory method to construct a standard TextEncryptor: diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/technical-overview.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/technical-overview.adoc index 6dfb93bece7..de0cedb91ee 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/technical-overview.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/technical-overview.adoc @@ -156,7 +156,7 @@ Let's consider a standard authentication scenario that everyone is familiar with . The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information. -The first three items constitute the authentication process so we'll take a look at how these take place within Spring Security. +The first four items constitute the authentication process so we'll take a look at how these take place within Spring Security. . The username and password are obtained and combined into an instance of `UsernamePasswordAuthenticationToken` (an instance of the `Authentication` interface, which we saw earlier). . The token is passed to an instance of `AuthenticationManager` for validation. @@ -258,7 +258,7 @@ Or you might work for a company that has a legacy proprietary authentication sys In situations like this it's quite easy to get Spring Security to work, and still provide authorization capabilities. All you need to do is write a filter (or equivalent) that reads the third-party user information from a location, build a Spring Security-specific `Authentication` object, and put it into the `SecurityContextHolder`. In this case you also need to think about things which are normally taken care of automatically by the built-in authentication infrastructure. -For example, you might need to pre-emptively create an HTTP session to <>, before you write the response to the client footnote:[It isn't possible to create a session once the response has been committed. +For example, you might need to pre-emptively create an HTTP session to <>, before you write the response to the client footnote:[It isn't possible to create a session once the response has been committed.]. If you're wondering how the `AuthenticationManager` is implemented in a real world example, we'll look at that in the <>. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/architecture.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/architecture.adoc index b33d622e0b2..c2bcddb0593 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/architecture.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/architecture.adoc @@ -41,7 +41,7 @@ A pre-invocation decision on whether the invocation is allowed to proceed is mad [[authz-access-decision-manager]] ==== The AccessDecisionManager The `AccessDecisionManager` is called by the `AbstractSecurityInterceptor` and is responsible for making final access control decisions. -the `AccessDecisionManager` interface contains three methods: +The `AccessDecisionManager` interface contains three methods: [source,java] ---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc index e5a34e66f24..f2b603bc2da 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc @@ -25,10 +25,10 @@ import org.springframework.security.config.annotation.authentication.builders.*; import org.springframework.security.config.annotation.web.configuration.*; @EnableWebSecurity -public class WebSecurityConfig implements WebMvcConfigurer { +public class WebSecurityConfig { @Bean - public UserDetailsService userDetailsService() throws Exception { + public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build()); return manager; @@ -131,7 +131,10 @@ public class MvcWebApplicationInitializer extends == HttpSecurity Thus far our <> only contains information about how to authenticate our users. -How does Spring Security know that we want to require all users to be authenticated? How does Spring Security know we want to support form based authentication? The reason for this is that the `WebSecurityConfigurerAdapter` provides a default configuration in the `configure(HttpSecurity http)` method that looks like: +How does Spring Security know that we want to require all users to be authenticated? +How does Spring Security know we want to support form based authentication? +Actually, there is an configuration class that is being invoked behind the scenes called `WebSecurityConfigurerAdapter`. +It has a method called `configure` with the following default implementation: [source,java] ---- @@ -172,9 +175,17 @@ I want to configure authorized requests __and__ configure form login __and__ con You might be wondering where the login form came from when you were prompted to log in, since we made no mention of any HTML files or JSPs. Since Spring Security's default configuration does not explicitly set a URL for the login page, Spring Security generates one automatically, based on the features that are enabled and using standard values for the URL which processes the submitted login, the default target URL the user will be sent to after logging in and so on. -While the automatically generated log in page is convenient to get up and running quickly, most applications will want to provide their own log in page. -To do so we can update our configuration as seen below: +While the automatically generated log in page is convenient to get up and running quickly, most applications will want to provide their own login page. +When we want to change the default configuration, we can customize the `WebSecurityConfigurerAdapter` that we mentioned earlier by extending it like so: + +[source,java] +---- +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + // ... +} +---- +And then override the `configure` method as seen below: [source,java] ---- @@ -719,8 +730,8 @@ Resource Server uses `JwtTimestampValidator` to verify a token's validity window ```java @Bean JwtDecoder jwtDecoder() { - NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport) - JwtDecoders.withOidcIssuerLocation(issuerUri); + NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) + JwtDecoders.fromOidcIssuerLocation(issuerUri); OAuth2TokenValidator withClockSkew = new DelegatingOAuth2TokenValidator<>( new JwtTimestampValidator(Duration.ofSeconds(60)), @@ -759,8 +770,8 @@ Then, to add into a resource server, it's a matter of specifying the `JwtDecoder ```java @Bean JwtDecoder jwtDecoder() { - NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport) - JwtDecoders.withOidcIssuerLocation(issuerUri); + NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) + JwtDecoders.fromOidcIssuerLocation(issuerUri); OAuth2TokenValidator audienceValidator = new AudienceValidator(); OAuth2TokenValidator withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri); @@ -1055,7 +1066,7 @@ public BCryptPasswordEncoder passwordEncoder() { == Multiple HttpSecurity We can configure multiple HttpSecurity instances just as we can have multiple `` blocks. -The key is to extend the `WebSecurityConfigurationAdapter` multiple times. +The key is to extend the `WebSecurityConfigurerAdapter` multiple times. For example, the following is an example of having a different configuration for URL's that start with `/api/`. [source,java] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc index ff49f69556d..4891a18eb11 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc @@ -133,7 +133,7 @@ With the default configuration, this is typically a comma-separated list of role The prefix "ROLE_" is a marker which indicates that a simple comparison with the user's authorities should be made. In other words, a normal role-based check should be used. Access-control in Spring Security is not limited to the use of simple roles (hence the use of the prefix to differentiate between different types of security attributes). -We'll see later how the interpretation can vary footnote:[The interpretation of the comma-separated values in the `access` attribute depends on the implementation of the pass:specialcharacters,macros[<>] which is used. +We'll see later how the interpretation can vary footnote:[The interpretation of the comma-separated values in the `access` attribute depends on the implementation of the <> which is used.]. In Spring Security 3.0, the attribute can also be populated with an pass:specialcharacters,macros[<>]. diff --git a/gradle.properties b/gradle.properties index e2e850a9900..add72a99b14 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -gaeVersion=1.9.71 -springBootVersion=2.1.3.RELEASE -version=5.1.5.RELEASE +gaeVersion=1.9.80 +springBootVersion=2.1.14.RELEASE +version=5.1.11.RELEASE diff --git a/gradle/dependency-management.gradle b/gradle/dependency-management.gradle index ac5c5a3576c..784f5905a8b 100644 --- a/gradle/dependency-management.gradle +++ b/gradle/dependency-management.gradle @@ -1,13 +1,13 @@ if (!project.hasProperty('reactorVersion')) { - ext.reactorVersion = 'Californium-SR6' + ext.reactorVersion = 'Californium-SR18' } if (!project.hasProperty('springVersion')) { - ext.springVersion = '5.1.6.RELEASE' + ext.springVersion = '5.1.15.RELEASE' } if (!project.hasProperty('springDataVersion')) { - ext.springDataVersion = 'Lovelace-SR6' + ext.springDataVersion = 'Lovelace-SR17' } dependencyManagement { @@ -17,18 +17,18 @@ dependencyManagement { mavenBom "org.springframework.data:spring-data-releasetrain:${springDataVersion}" } dependencies { - dependency 'cglib:cglib-nodep:3.2.10' - dependency 'com.squareup.okhttp3:mockwebserver:3.12.2' + dependency 'cglib:cglib-nodep:3.2.12' + dependency 'com.squareup.okhttp3:mockwebserver:3.12.12' dependency 'opensymphony:sitemesh:2.4.2' dependency 'org.gebish:geb-spock:0.10.0' dependency 'org.jasig.cas:cas-server-webapp:4.2.7' - dependency 'org.powermock:powermock-api-mockito2:2.0.0' - dependency 'org.powermock:powermock-api-support:2.0.0' - dependency 'org.powermock:powermock-core:2.0.0' - dependency 'org.powermock:powermock-module-junit4-common:2.0.0' - dependency 'org.powermock:powermock-module-junit4:2.0.0' - dependency 'org.powermock:powermock-reflect:2.0.0' - dependency 'org.python:jython:2.5.0' + dependency 'org.powermock:powermock-api-mockito2:2.0.7' + dependency 'org.powermock:powermock-api-support:2.0.7' + dependency 'org.powermock:powermock-core:2.0.7' + dependency 'org.powermock:powermock-module-junit4-common:2.0.7' + dependency 'org.powermock:powermock-module-junit4:2.0.7' + dependency 'org.powermock:powermock-reflect:2.0.7' + dependency 'org.python:jython:2.5.3' dependency 'org.spockframework:spock-core:1.0-groovy-2.4' dependency 'org.spockframework:spock-spring:1.0-groovy-2.4' } @@ -40,27 +40,27 @@ dependencyManagement { dependency 'asm:asm:3.1' dependency 'ch.qos.logback:logback-classic:1.2.3' dependency 'ch.qos.logback:logback-core:1.2.3' - dependency 'com.fasterxml.jackson.core:jackson-annotations:2.9.8' - dependency 'com.fasterxml.jackson.core:jackson-core:2.9.8' - dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.8' + dependency 'com.fasterxml.jackson.core:jackson-annotations:2.9.10' + dependency 'com.fasterxml.jackson.core:jackson-core:2.9.10' + dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.10.2' dependency 'com.fasterxml:classmate:1.3.4' dependency 'com.github.stephenc.jcip:jcip-annotations:1.0-1' - dependency 'com.google.appengine:appengine-api-1.0-sdk:1.9.64' - dependency 'com.google.appengine:appengine-api-labs:1.9.64' - dependency 'com.google.appengine:appengine-api-stubs:1.9.64' - dependency 'com.google.appengine:appengine-testing:1.9.64' - dependency 'com.google.appengine:appengine:1.9.63' + dependency 'com.google.appengine:appengine-api-1.0-sdk:1.9.80' + dependency 'com.google.appengine:appengine-api-labs:1.9.80' + dependency 'com.google.appengine:appengine-api-stubs:1.9.80' + dependency 'com.google.appengine:appengine-testing:1.9.80' + dependency 'com.google.appengine:appengine:1.9.80' dependency 'com.google.code.gson:gson:2.8.2' dependency 'com.google.guava:guava:20.0' dependency 'com.google.inject:guice:3.0' dependency 'com.nimbusds:lang-tag:1.4.3' dependency 'com.nimbusds:nimbus-jose-jwt:6.0.2' dependency 'com.nimbusds:oauth2-oidc-sdk:6.0' - dependency 'com.squareup.okhttp3:okhttp:3.12.2' + dependency 'com.squareup.okhttp3:okhttp:3.12.12' dependency 'com.squareup.okio:okio:1.13.0' dependency 'com.sun.xml.bind:jaxb-core:2.3.0.1' - dependency 'com.sun.xml.bind:jaxb-impl:2.3.0.1' - dependency 'com.unboundid:unboundid-ldapsdk:4.0.10' + dependency 'com.sun.xml.bind:jaxb-impl:2.3.3' + dependency 'com.unboundid:unboundid-ldapsdk:4.0.14' dependency 'com.vaadin.external.google:android-json:0.0.20131108.vaadin1' dependency 'commons-cli:commons-cli:1.4' dependency 'commons-codec:commons-codec:1.11' @@ -127,7 +127,7 @@ dependencyManagement { dependency 'org.apache.directory.shared:shared-cursor:0.9.15' dependency 'org.apache.directory.shared:shared-ldap-constants:0.9.15' dependency 'org.apache.directory.shared:shared-ldap:0.9.15' - dependency 'org.apache.httpcomponents:httpclient:4.5.7' + dependency 'org.apache.httpcomponents:httpclient:4.5.12' dependency 'org.apache.httpcomponents:httpcore:4.4.8' dependency 'org.apache.httpcomponents:httpmime:4.5.3' dependency 'org.apache.mina:mina-core:2.0.0-M6' @@ -140,28 +140,28 @@ dependencyManagement { dependency 'org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.0.44' dependency 'org.apache.tomcat.embed:tomcat-embed-websocket:8.5.23' dependency 'org.apache.tomcat:tomcat-annotations-api:8.5.23' - dependency 'org.aspectj:aspectjrt:1.9.2' - dependency 'org.aspectj:aspectjtools:1.9.2' - dependency 'org.aspectj:aspectjweaver:1.9.2' + dependency 'org.aspectj:aspectjrt:1.9.5' + dependency 'org.aspectj:aspectjtools:1.9.5' + dependency 'org.aspectj:aspectjweaver:1.9.5' dependency 'org.assertj:assertj-core:3.11.1' dependency 'org.attoparser:attoparser:2.0.4.RELEASE' - dependency 'org.bouncycastle:bcpkix-jdk15on:1.61' + dependency 'org.bouncycastle:bcpkix-jdk15on:1.64' dependency 'org.bouncycastle:bcprov-jdk15on:1.58' - dependency 'org.codehaus.groovy:groovy-all:2.4.14' - dependency 'org.codehaus.groovy:groovy-json:2.4.14' - dependency 'org.codehaus.groovy:groovy:2.4.14' + dependency 'org.codehaus.groovy:groovy-all:2.4.19' + dependency 'org.codehaus.groovy:groovy-json:2.4.19' + dependency 'org.codehaus.groovy:groovy:2.4.19' dependency 'org.eclipse.jdt:ecj:3.12.3' - dependency 'org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830' - dependency 'org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830' - dependency 'org.eclipse.jetty.websocket:websocket-common:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-client:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-http:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-io:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-security:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-server:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-servlet:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-util:9.4.12.v20180830' - dependency 'org.eclipse.jetty:jetty-xml:9.4.12.v20180830' + dependency 'org.eclipse.jetty.websocket:websocket-api:9.4.27.v20200227' + dependency 'org.eclipse.jetty.websocket:websocket-client:9.4.27.v20200227' + dependency 'org.eclipse.jetty.websocket:websocket-common:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-client:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-http:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-io:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-security:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-server:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-servlet:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-util:9.4.27.v20200227' + dependency 'org.eclipse.jetty:jetty-xml:9.4.27.v20200227' dependency 'org.eclipse.persistence:javax.persistence:2.2.1' dependency 'org.gebish:geb-ast:0.10.0' dependency 'org.gebish:geb-core:0.10.0' @@ -170,9 +170,9 @@ dependencyManagement { dependency 'org.hamcrest:hamcrest-core:1.3' dependency 'org.hibernate.common:hibernate-commons-annotations:5.0.1.Final' dependency 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final' - dependency 'org.hibernate:hibernate-core:5.2.17.Final' - dependency 'org.hibernate:hibernate-entitymanager:5.3.9.Final' - dependency 'org.hibernate:hibernate-validator:6.0.16.Final' + dependency 'org.hibernate:hibernate-core:5.2.18.Final' + dependency 'org.hibernate:hibernate-entitymanager:5.3.15.Final' + dependency 'org.hibernate:hibernate-validator:6.0.19.Final' dependency 'org.hsqldb:hsqldb:2.4.1' dependency 'org.jasig.cas.client:cas-client-core:3.5.1' dependency 'org.javassist:javassist:3.22.0-CR2' @@ -183,17 +183,17 @@ dependencyManagement { dependency 'org.objenesis:objenesis:2.6' dependency 'org.openid4java:openid4java-nodeps:0.9.6' dependency 'org.ow2.asm:asm:6.2.1' - dependency 'org.reactivestreams:reactive-streams:1.0.1' + dependency 'org.reactivestreams:reactive-streams:1.0.3' dependency 'org.seleniumhq.selenium:htmlunit-driver:2.33.3' dependency 'org.seleniumhq.selenium:selenium-api:3.141.59' dependency 'org.seleniumhq.selenium:selenium-java:3.141.59' dependency 'org.seleniumhq.selenium:selenium-support:3.141.59' dependency 'org.skyscreamer:jsonassert:1.5.0' - dependency 'org.slf4j:jcl-over-slf4j:1.7.26' - dependency 'org.slf4j:jul-to-slf4j:1.7.26' - dependency 'org.slf4j:log4j-over-slf4j:1.7.26' - dependency 'org.slf4j:slf4j-api:1.7.26' - dependency 'org.slf4j:slf4j-nop:1.7.26' + dependency 'org.slf4j:jcl-over-slf4j:1.7.30' + dependency 'org.slf4j:jul-to-slf4j:1.7.30' + dependency 'org.slf4j:log4j-over-slf4j:1.7.30' + dependency 'org.slf4j:slf4j-api:1.7.30' + dependency 'org.slf4j:slf4j-nop:1.7.30' dependency 'org.sonatype.sisu.inject:cglib:2.2.1-v20090111' dependency 'org.springframework.ldap:spring-ldap-core:2.3.2.RELEASE' dependency 'org.thymeleaf:thymeleaf-spring5:3.0.11.RELEASE' diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java new file mode 100644 index 00000000000..a407a244e80 --- /dev/null +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2020 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 org.springframework.security.ldap.server; + +import org.junit.After; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ldap.core.ContextSource; +import org.springframework.security.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.SpringSecurityLdapTemplate; + +import javax.annotation.PreDestroy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; + +/** + * Tests for {@link UnboundIdContainer}, specifically relating to LDIF file detection. + * + * @author Eleftheria Stein + */ +public class UnboundIdContainerLdifTests { + + AnnotationConfigApplicationContext appCtx; + + @After + public void closeAppContext() { + if (appCtx != null) { + appCtx.close(); + appCtx = null; + } + } + + @Test + public void unboundIdContainerWhenCustomLdifNameThenLdifLoaded() { + appCtx = new AnnotationConfigApplicationContext(CustomLdifConfig.class); + + DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx + .getBean(ContextSource.class); + + SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource); + assertThat(template.compare("uid=bob,ou=people", "uid", "bob")).isTrue(); + } + + @Configuration + static class CustomLdifConfig { + private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", + "classpath:test-server.ldif"); + + @Bean + UnboundIdContainer ldapContainer() { + this.container.setPort(0); + return this.container; + } + + @Bean + ContextSource contextSource(UnboundIdContainer container) { + return new DefaultSpringSecurityContextSource("ldap://127.0.0.1:" + + container.getPort() + "/dc=springframework,dc=org"); + } + + @PreDestroy + void shutdown() { + this.container.stop(); + } + } + + @Test + public void unboundIdContainerWhenWildcardLdifNameThenLdifLoaded() { + appCtx = new AnnotationConfigApplicationContext(WildcardLdifConfig.class); + + DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx + .getBean(ContextSource.class); + + SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource); + assertThat(template.compare("uid=bob,ou=people", "uid", "bob")).isTrue(); + } + + @Configuration + static class WildcardLdifConfig { + private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", + "classpath*:test-server.ldif"); + + @Bean + UnboundIdContainer ldapContainer() { + this.container.setPort(0); + return this.container; + } + + @Bean + ContextSource contextSource(UnboundIdContainer container) { + return new DefaultSpringSecurityContextSource("ldap://127.0.0.1:" + + container.getPort() + "/dc=springframework,dc=org"); + } + + @PreDestroy + void shutdown() { + this.container.stop(); + } + } + + @Test + public void unboundIdContainerWhenMalformedLdifThenException() { + try { + appCtx = new AnnotationConfigApplicationContext(MalformedLdifConfig.class); + failBecauseExceptionWasNotThrown(IllegalStateException.class); + } catch (Exception e) { + assertThat(e.getCause()).isInstanceOf(IllegalStateException.class); + assertThat(e.getMessage()).contains("Unable to load LDIF classpath:test-server-malformed.txt"); + } + } + + @Configuration + static class MalformedLdifConfig { + private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", + "classpath:test-server-malformed.txt"); + + @Bean + UnboundIdContainer ldapContainer() { + this.container.setPort(0); + return this.container; + } + + @PreDestroy + void shutdown() { + this.container.stop(); + } + } +} diff --git a/ldap/src/integration-test/resources/test-server-malformed.txt b/ldap/src/integration-test/resources/test-server-malformed.txt new file mode 100644 index 00000000000..e24b511f763 --- /dev/null +++ b/ldap/src/integration-test/resources/test-server-malformed.txt @@ -0,0 +1,9 @@ +dn: ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn ou=subgroups,ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: organizationalUnit +ou: subgroups diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java index 863de6bb295..334e89b739e 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java @@ -16,6 +16,7 @@ package org.springframework.security.ldap.authentication.ad; import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.ldap.CommunicationException; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.support.DefaultDirObjectFactory; @@ -24,6 +25,7 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; @@ -142,12 +144,15 @@ protected DirContextOperations doAuthentication( UsernamePasswordAuthenticationToken auth) { String username = auth.getName(); String password = (String) auth.getCredentials(); - - DirContext ctx = bindAsUser(username, password); + DirContext ctx = null; try { + ctx = bindAsUser(username, password); return searchForUser(ctx, username); } + catch (CommunicationException e) { + throw badLdapConnection(e); + } catch (NamingException e) { logger.error("Failed to locate directory entry for authenticated user: " + username, e); @@ -210,8 +215,7 @@ private DirContext bindAsUser(String username, String password) { || (e instanceof OperationNotSupportedException)) { handleBindException(bindPrincipal, e); throw badCredentials(e); - } - else { + } else { throw LdapUtils.convertLdapException(e); } } @@ -313,6 +317,12 @@ private BadCredentialsException badCredentials(Throwable cause) { return (BadCredentialsException) badCredentials().initCause(cause); } + private InternalAuthenticationServiceException badLdapConnection(Throwable cause) { + return new InternalAuthenticationServiceException(messages.getMessage( + "LdapAuthenticationProvider.badLdapConnection", + "Connection to LDAP server failed."), cause); + } + private DirContextOperations searchForUser(DirContext context, String username) throws NamingException { SearchControls searchControls = new SearchControls(); @@ -327,6 +337,9 @@ private DirContextOperations searchForUser(DirContext context, String username) searchControls, searchRoot, searchFilter, new Object[] { bindPrincipal, username }); } + catch (CommunicationException ldapCommunicationException) { + throw badLdapConnection(ldapCommunicationException); + } catch (IncorrectResultSizeDataAccessException incorrectResults) { // Search should never return multiple results if properly configured - just // rethrow diff --git a/ldap/src/main/java/org/springframework/security/ldap/server/UnboundIdContainer.java b/ldap/src/main/java/org/springframework/security/ldap/server/UnboundIdContainer.java index bf9b7b4820a..d620c5e986f 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/server/UnboundIdContainer.java +++ b/ldap/src/main/java/org/springframework/security/ldap/server/UnboundIdContainer.java @@ -114,10 +114,10 @@ public void start() { private void importLdif(InMemoryDirectoryServer directoryServer) { if (StringUtils.hasText(this.ldif)) { - Resource resource = this.context.getResource(this.ldif); try { - if (resource.exists()) { - try (InputStream inputStream = resource.getInputStream()) { + Resource[] resources = this.context.getResources(this.ldif); + if (resources.length > 0 && resources[0].exists()) { + try (InputStream inputStream = resources[0].getInputStream()) { directoryServer.importFromLDIF(false, new LDIFReader(inputStream)); } } diff --git a/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java index 5152bdbf11d..ba9e0c03bbc 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java @@ -32,6 +32,7 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -58,6 +59,9 @@ * @author Rob Winch */ public class ActiveDirectoryLdapAuthenticationProviderTests { + public static final String EXISTING_LDAP_PROVIDER = "ldap://192.168.1.200/"; + public static final String NON_EXISTING_LDAP_PROVIDER = "ldap://192.168.1.201/"; + @Rule public ExpectedException thrown = ExpectedException.none(); @@ -378,17 +382,29 @@ public void errorWithNoSubcodeIsHandledCleanly() throws Exception { } @Test(expected = org.springframework.ldap.CommunicationException.class) - public void nonAuthenticationExceptionIsConvertedToSpringLdapException() - throws Exception { - provider.contextFactory = createContextFactoryThrowing(new CommunicationException( - msg)); - provider.authenticate(joe); + public void nonAuthenticationExceptionIsConvertedToSpringLdapException() throws Throwable { + try { + provider.contextFactory = createContextFactoryThrowing(new CommunicationException( + msg)); + provider.authenticate(joe); + } catch (InternalAuthenticationServiceException e) { + // Since GH-8418 ldap communication exception is wrapped into InternalAuthenticationServiceException. + // This test is about the wrapped exception, so we throw it. + throw e.getCause(); + } + } + + @Test(expected = org.springframework.security.authentication.InternalAuthenticationServiceException.class ) + public void connectionExceptionIsWrappedInInternalException() throws Exception { + ActiveDirectoryLdapAuthenticationProvider noneReachableProvider = new ActiveDirectoryLdapAuthenticationProvider( + "mydomain.eu", NON_EXISTING_LDAP_PROVIDER, "dc=ad,dc=eu,dc=mydomain"); + noneReachableProvider.doAuthentication(joe); } @Test public void rootDnProvidedSeparatelyFromDomainAlsoWorks() throws Exception { ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider( - "mydomain.eu", "ldap://192.168.1.200/", "dc=ad,dc=eu,dc=mydomain"); + "mydomain.eu", EXISTING_LDAP_PROVIDER, "dc=ad,dc=eu,dc=mydomain"); checkAuthentication("dc=ad,dc=eu,dc=mydomain", provider); } @@ -414,8 +430,11 @@ public void contextEnvironmentPropertiesUsed() throws Exception { provider.authenticate(joe); fail("CommunicationException was expected with a root cause of ClassNotFoundException"); } - catch (org.springframework.ldap.CommunicationException expected) { - assertThat(expected.getRootCause()).isInstanceOf(ClassNotFoundException.class); + catch (InternalAuthenticationServiceException expected) { + assertThat(expected.getCause()).isInstanceOf(org.springframework.ldap.CommunicationException.class); + org.springframework.ldap.CommunicationException cause = + (org.springframework.ldap.CommunicationException) expected.getCause(); + assertThat(cause.getRootCause()).isInstanceOf(ClassNotFoundException.class); } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java index 938fd9c2155..fc5fb598b37 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -73,7 +73,8 @@ public Authentication authenticate(Authentication authentication) throws Authent authorizationCodeAuthentication.getClientRegistration(), authorizationCodeAuthentication.getAuthorizationExchange(), accessTokenResponse.getAccessToken(), - accessTokenResponse.getRefreshToken()); + accessTokenResponse.getRefreshToken(), + accessTokenResponse.getAdditionalParameters()); authenticationResult.setDetails(authorizationCodeAuthentication.getDetails()); return authenticationResult; diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationExchangeValidator.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationExchangeValidator.java index 37d6d8115b4..a240e0521a8 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationExchangeValidator.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationExchangeValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -30,7 +30,6 @@ */ final class OAuth2AuthorizationExchangeValidator { private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter"; - private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter"; static void validate(OAuth2AuthorizationExchange authorizationExchange) { OAuth2AuthorizationRequest authorizationRequest = authorizationExchange.getAuthorizationRequest(); @@ -44,10 +43,5 @@ static void validate(OAuth2AuthorizationExchange authorizationExchange) { OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE); throw new OAuth2AuthorizationException(oauth2Error); } - - if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) { - OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE); - throw new OAuth2AuthorizationException(oauth2Error); - } } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java index 02d4b5e7b1f..8d3b70234d8 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -28,7 +28,6 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2AuthorizationException; import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.util.Assert; @@ -60,7 +59,7 @@ * @see Section 4.1.4 Access Token Response */ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider { - private final OAuth2AccessTokenResponseClient accessTokenResponseClient; + private final OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider; private final OAuth2UserService userService; private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities); @@ -74,59 +73,54 @@ public OAuth2LoginAuthenticationProvider( OAuth2AccessTokenResponseClient accessTokenResponseClient, OAuth2UserService userService) { - Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null"); Assert.notNull(userService, "userService cannot be null"); - this.accessTokenResponseClient = accessTokenResponseClient; + this.authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(accessTokenResponseClient); this.userService = userService; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2LoginAuthenticationToken authorizationCodeAuthentication = + OAuth2LoginAuthenticationToken loginAuthenticationToken = (OAuth2LoginAuthenticationToken) authentication; // Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest // scope // REQUIRED. OpenID Connect requests MUST contain the "openid" scope value. - if (authorizationCodeAuthentication.getAuthorizationExchange() + if (loginAuthenticationToken.getAuthorizationExchange() .getAuthorizationRequest().getScopes().contains("openid")) { // This is an OpenID Connect Authentication Request so return null // and let OidcAuthorizationCodeAuthenticationProvider handle it instead return null; } - OAuth2AccessTokenResponse accessTokenResponse; + OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken; try { - OAuth2AuthorizationExchangeValidator.validate( - authorizationCodeAuthentication.getAuthorizationExchange()); - - accessTokenResponse = this.accessTokenResponseClient.getTokenResponse( - new OAuth2AuthorizationCodeGrantRequest( - authorizationCodeAuthentication.getClientRegistration(), - authorizationCodeAuthentication.getAuthorizationExchange())); - + authorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) this.authorizationCodeAuthenticationProvider + .authenticate(new OAuth2AuthorizationCodeAuthenticationToken( + loginAuthenticationToken.getClientRegistration(), + loginAuthenticationToken.getAuthorizationExchange())); } catch (OAuth2AuthorizationException ex) { OAuth2Error oauth2Error = ex.getError(); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } - OAuth2AccessToken accessToken = accessTokenResponse.getAccessToken(); - Map additionalParameters = accessTokenResponse.getAdditionalParameters(); + OAuth2AccessToken accessToken = authorizationCodeAuthenticationToken.getAccessToken(); + Map additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters(); OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest( - authorizationCodeAuthentication.getClientRegistration(), accessToken, additionalParameters)); + loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters)); Collection mappedAuthorities = this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken( - authorizationCodeAuthentication.getClientRegistration(), - authorizationCodeAuthentication.getAuthorizationExchange(), + loginAuthenticationToken.getClientRegistration(), + loginAuthenticationToken.getAuthorizationExchange(), oauth2User, mappedAuthorities, accessToken, - accessTokenResponse.getRefreshToken()); - authenticationResult.setDetails(authorizationCodeAuthentication.getDetails()); + authorizationCodeAuthenticationToken.getRefreshToken()); + authenticationResult.setDetails(loginAuthenticationToken.getDetails()); return authenticationResult; } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClient.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClient.java index 9a64a9248dc..14c87ae0cf9 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClient.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -15,7 +15,10 @@ */ package org.springframework.security.oauth2.client.endpoint; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; @@ -65,15 +68,18 @@ public Mono getTokenResponse(OAuth2ClientCredentialsG .headers(headers(clientRegistration)) .body(body) .exchange() - .flatMap(response ->{ - if (!response.statusCode().is2xxSuccessful()){ + .flatMap(response -> { + HttpStatus status = HttpStatus.resolve(response.rawStatusCode()); + if (status == null || !status.is2xxSuccessful()) { // extract the contents of this into a method named oauth2AccessTokenResponse but has an argument for the response - throw WebClientResponseException.create(response.rawStatusCode(), + return response.bodyToFlux(DataBuffer.class) + .map(DataBufferUtils::release) + .then(Mono.error(WebClientResponseException.create(response.rawStatusCode(), "Cannot get token, expected 2xx HTTP Status code", null, null, null - ); + ))); } return response.body(oauth2AccessTokenResponse()); }) .map(response -> { @@ -90,7 +96,6 @@ public Mono getTokenResponse(OAuth2ClientCredentialsG private Consumer headers(ClientRegistration clientRegistration) { return headers -> { headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret()); if (ClientAuthenticationMethod.BASIC.equals(clientRegistration.getClientAuthenticationMethod())) { headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret()); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java index de044d34216..7af5319acbd 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java @@ -73,7 +73,6 @@ */ public class OidcAuthorizationCodeAuthenticationProvider implements AuthenticationProvider { private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter"; - private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter"; private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token"; private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier"; private final OAuth2AccessTokenResponseClient accessTokenResponseClient; @@ -127,11 +126,6 @@ public Authentication authenticate(Authentication authentication) throws Authent throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } - if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) { - OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); - } - OAuth2AccessTokenResponse accessTokenResponse; try { accessTokenResponse = this.accessTokenResponseClient.getTokenResponse( diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeReactiveAuthenticationManager.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeReactiveAuthenticationManager.java index ca8d16ad3d7..8d0c15bb956 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeReactiveAuthenticationManager.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeReactiveAuthenticationManager.java @@ -76,7 +76,6 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements ReactiveAuthenticationManager { private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter"; - private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter"; private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token"; private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier"; @@ -127,11 +126,6 @@ public Mono authenticate(Authentication authentication) { throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } - if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) { - OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); - } - OAuth2AuthorizationCodeGrantRequest authzRequest = new OAuth2AuthorizationCodeGrantRequest( authorizationCodeAuthentication.getClientRegistration(), authorizationCodeAuthentication.getAuthorizationExchange()); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java index f6170fad0b9..b17510ece7f 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java @@ -486,7 +486,7 @@ public ClientRegistration build() { this.validateClientCredentialsGrantType(); } else if (AuthorizationGrantType.IMPLICIT.equals(this.authorizationGrantType)) { this.validateImplicitGrantType(); - } else { + } else if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType)) { this.validateAuthorizationCodeGrantType(); } return this.create(); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java index 4d2db9d0a6e..98ba597c41c 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -15,15 +15,22 @@ */ package org.springframework.security.oauth2.client.registration; +import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import reactor.core.publisher.Mono; +import org.springframework.util.Assert; + /** * A Reactive {@link ClientRegistrationRepository} that stores {@link ClientRegistration}(s) in-memory. * * @author Rob Winch + * @author Ebert Toribio * @since 5.1 * @see ClientRegistrationRepository * @see ClientRegistration @@ -31,7 +38,7 @@ public final class InMemoryReactiveClientRegistrationRepository implements ReactiveClientRegistrationRepository, Iterable { - private final InMemoryClientRegistrationRepository delegate; + private final Map clientIdToClientRegistration; /** * Constructs an {@code InMemoryReactiveClientRegistrationRepository} using the provided parameters. @@ -39,7 +46,12 @@ public final class InMemoryReactiveClientRegistrationRepository * @param registrations the client registration(s) */ public InMemoryReactiveClientRegistrationRepository(ClientRegistration... registrations) { - this.delegate = new InMemoryClientRegistrationRepository(registrations); + this(toList(registrations)); + } + + private static List toList(ClientRegistration... registrations) { + Assert.notEmpty(registrations, "registrations cannot be null or empty"); + return Arrays.asList(registrations); } /** @@ -48,12 +60,12 @@ public InMemoryReactiveClientRegistrationRepository(ClientRegistration... regist * @param registrations the client registration(s) */ public InMemoryReactiveClientRegistrationRepository(List registrations) { - this.delegate = new InMemoryClientRegistrationRepository(registrations); + this.clientIdToClientRegistration = toUnmodifiableConcurrentMap(registrations); } @Override public Mono findByRegistrationId(String registrationId) { - return Mono.justOrEmpty(this.delegate.findByRegistrationId(registrationId)); + return Mono.justOrEmpty(this.clientIdToClientRegistration.get(registrationId)); } /** @@ -63,6 +75,20 @@ public Mono findByRegistrationId(String registrationId) { */ @Override public Iterator iterator() { - return delegate.iterator(); + return this.clientIdToClientRegistration.values().iterator(); + } + + private static Map toUnmodifiableConcurrentMap(List registrations) { + Assert.notEmpty(registrations, "registrations cannot be null or empty"); + ConcurrentHashMap result = new ConcurrentHashMap<>(); + for (ClientRegistration registration : registrations) { + Assert.notNull(registration, "no registration can be null"); + if (result.containsKey(registration.getRegistrationId())) { + throw new IllegalStateException(String.format("Duplicate key %s", + registration.getRegistrationId())); + } + result.put(registration.getRegistrationId(), registration); + } + return Collections.unmodifiableMap(result); } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java index 36f235a9f1e..3cd0467d989 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -41,6 +41,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import javax.servlet.FilterChain; @@ -48,6 +49,11 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; /** * A {@code Filter} for the OAuth 2.0 Authorization Code Grant, @@ -132,24 +138,39 @@ public final void setAuthorizationRequestRepository(AuthorizationRequestReposito protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - if (this.shouldProcessAuthorizationResponse(request)) { - this.processAuthorizationResponse(request, response); + if (matchesAuthorizationResponse(request)) { + processAuthorizationResponse(request, response); return; } filterChain.doFilter(request, response); } - private boolean shouldProcessAuthorizationResponse(HttpServletRequest request) { + private boolean matchesAuthorizationResponse(HttpServletRequest request) { + MultiValueMap params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap()); + if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) { + return false; + } OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.loadAuthorizationRequest(request); if (authorizationRequest == null) { return false; } - String requestUrl = UrlUtils.buildFullRequestUrl(request.getScheme(), request.getServerName(), - request.getServerPort(), request.getRequestURI(), null); - MultiValueMap params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap()); - if (requestUrl.equals(authorizationRequest.getRedirectUri()) && - OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) { + + // Compare redirect_uri + UriComponents requestUri = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request)).build(); + UriComponents redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri()).build(); + Set>> requestUriParameters = new LinkedHashSet<>(requestUri.getQueryParams().entrySet()); + Set>> redirectUriParameters = new LinkedHashSet<>(redirectUri.getQueryParams().entrySet()); + // Remove the additional request parameters (if any) from the authorization response (request) + // before doing an exact comparison with the authorizationRequest.getRedirectUri() parameters (if any) + requestUriParameters.retainAll(redirectUriParameters); + + if (Objects.equals(requestUri.getScheme(), redirectUri.getScheme()) && + Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo()) && + Objects.equals(requestUri.getHost(), redirectUri.getHost()) && + Objects.equals(requestUri.getPort(), redirectUri.getPort()) && + Objects.equals(requestUri.getPath(), redirectUri.getPath()) && + Objects.equals(requestUriParameters.toString(), redirectUriParameters.toString())) { return true; } return false; @@ -165,10 +186,7 @@ private void processAuthorizationResponse(HttpServletRequest request, HttpServle ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); MultiValueMap params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap()); - String redirectUri = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)) - .replaceQuery(null) - .build() - .toUriString(); + String redirectUri = UrlUtils.buildFullRequestUrl(request); OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri); OAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken( @@ -183,7 +201,7 @@ private void processAuthorizationResponse(HttpServletRequest request, HttpServle } catch (OAuth2AuthorizationException ex) { OAuth2Error error = ex.getError(); UriComponentsBuilder uriBuilder = UriComponentsBuilder - .fromUriString(authorizationResponse.getRedirectUri()) + .fromUriString(authorizationRequest.getRedirectUri()) .queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode()); if (!StringUtils.isEmpty(error.getDescription())) { uriBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription()); @@ -206,7 +224,7 @@ private void processAuthorizationResponse(HttpServletRequest request, HttpServle this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, currentAuthentication, request, response); - String redirectUrl = authorizationResponse.getRedirectUri(); + String redirectUrl = authorizationRequest.getRedirectUri(); SavedRequest savedRequest = this.requestCache.getRequest(request, response); if (savedRequest != null) { redirectUrl = savedRequest.getRedirectUrl(); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientResolver.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientResolver.java index 77179560f55..bf13f5ca0dc 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientResolver.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/OAuth2AuthorizedClientResolver.java @@ -133,7 +133,7 @@ private Mono authorizedClientNotLoaded(String clientRegi }); } - private Mono clientCredentials( + Mono clientCredentials( ClientRegistration clientRegistration, Authentication authentication, ServerWebExchange exchange) { OAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration); return this.clientCredentialsTokenResponseClient.getTokenResponse(grantRequest) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java index 9366d7ea6e3..f223822955c 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java @@ -85,8 +85,12 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements private final OAuth2AuthorizedClientResolver authorizedClientResolver; public ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveClientRegistrationRepository clientRegistrationRepository, ServerOAuth2AuthorizedClientRepository authorizedClientRepository) { + this(authorizedClientRepository, new OAuth2AuthorizedClientResolver(clientRegistrationRepository, authorizedClientRepository)); + } + + ServerOAuth2AuthorizedClientExchangeFilterFunction(ServerOAuth2AuthorizedClientRepository authorizedClientRepository, OAuth2AuthorizedClientResolver authorizedClientResolver) { this.authorizedClientRepository = authorizedClientRepository; - this.authorizedClientResolver = new OAuth2AuthorizedClientResolver(clientRegistrationRepository, authorizedClientRepository); + this.authorizedClientResolver = authorizedClientResolver; } /** @@ -246,13 +250,30 @@ private Mono createRequest(ClientRequest } private Mono refreshIfNecessary(ClientRequest request, ExchangeFunction next, OAuth2AuthorizedClient authorizedClient) { - if (shouldRefresh(authorizedClient)) { + ClientRegistration clientRegistration = authorizedClient.getClientRegistration(); + if (isClientCredentialsGrantType(clientRegistration) && hasTokenExpired(authorizedClient)) { + return createRequest(request) + .flatMap(r -> authorizeWithClientCredentials(clientRegistration, r)); + } else if (shouldRefresh(authorizedClient)) { return createRequest(request) .flatMap(r -> refreshAuthorizedClient(next, authorizedClient, r)); } return Mono.just(authorizedClient); } + private boolean isClientCredentialsGrantType(ClientRegistration clientRegistration) { + return AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType()); + } + + private Mono authorizeWithClientCredentials(ClientRegistration clientRegistration, OAuth2AuthorizedClientResolver.Request request) { + Authentication authentication = request.getAuthentication(); + ServerWebExchange exchange = request.getExchange(); + + return this.authorizedClientResolver.clientCredentials(clientRegistration, authentication, exchange). + flatMap(result -> this.authorizedClientRepository.saveAuthorizedClient(result, authentication, exchange) + .thenReturn(result)); + } + private Mono refreshAuthorizedClient(ExchangeFunction next, OAuth2AuthorizedClient authorizedClient, OAuth2AuthorizedClientResolver.Request r) { ServerWebExchange exchange = r.getExchange(); @@ -285,6 +306,10 @@ private boolean shouldRefresh(OAuth2AuthorizedClient authorizedClient) { if (refreshToken == null) { return false; } + return hasTokenExpired(authorizedClient); + } + + private boolean hasTokenExpired(OAuth2AuthorizedClient authorizedClient) { Instant now = this.clock.instant(); Instant expiresAt = authorizedClient.getAccessToken().getExpiresAt(); if (now.isAfter(expiresAt.minus(this.accessTokenExpiresSkew))) { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java index 8045e52e4cd..d3a5afa9731 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java @@ -22,6 +22,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.lang.Nullable; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.ReactiveSecurityContextHolder; @@ -103,6 +104,7 @@ * * * @author Rob Winch + * @author Roman Matiushchenko * @since 5.1 */ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction @@ -146,7 +148,7 @@ public ServletOAuth2AuthorizedClientExchangeFilterFunction( @Override public void afterPropertiesSet() throws Exception { - Hooks.onLastOperator(REQUEST_CONTEXT_OPERATOR_KEY, Operators.lift((s, sub) -> createRequestContextSubscriber(sub))); + Hooks.onLastOperator(REQUEST_CONTEXT_OPERATOR_KEY, Operators.liftPublisher((s, sub) -> createRequestContextSubscriberIfNecessary(sub))); } @Override @@ -288,32 +290,53 @@ public void setAccessTokenExpiresSkew(Duration accessTokenExpiresSkew) { @Override public Mono filter(ClientRequest request, ExchangeFunction next) { - return Mono.just(request) - .filter(req -> req.attribute(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME).isPresent()) - .switchIfEmpty(mergeRequestAttributesFromContext(request)) + return mergeRequestAttributesIfNecessary(request) .filter(req -> req.attribute(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME).isPresent()) .flatMap(req -> authorizedClient(req, next, getOAuth2AuthorizedClient(req.attributes()))) + .switchIfEmpty(Mono.defer(() -> + mergeRequestAttributesIfNecessary(request) + .filter(req -> resolveClientRegistrationId(req.attributes()) != null) + .flatMap(this::authorizeClient) + )) .map(authorizedClient -> bearer(request, authorizedClient)) .flatMap(next::exchange) - .switchIfEmpty(next.exchange(request)); + .switchIfEmpty(Mono.defer(() -> next.exchange(request))); + } + + private Mono mergeRequestAttributesIfNecessary(ClientRequest request) { + if (!request.attribute(HTTP_SERVLET_REQUEST_ATTR_NAME).isPresent() || + !request.attribute(HTTP_SERVLET_RESPONSE_ATTR_NAME).isPresent() || + !request.attribute(AUTHENTICATION_ATTR_NAME).isPresent()) { + return mergeRequestAttributesFromContext(request); + } else { + return Mono.just(request); + } } private Mono mergeRequestAttributesFromContext(ClientRequest request) { - return Mono.just(ClientRequest.from(request)) - .flatMap(builder -> Mono.subscriberContext() - .map(ctx -> builder.attributes(attrs -> populateRequestAttributes(attrs, ctx)))) + ClientRequest.Builder builder = ClientRequest.from(request); + return Mono.subscriberContext() + .map(ctx -> builder.attributes(attrs -> populateRequestAttributes(attrs, ctx))) .map(ClientRequest.Builder::build); } private void populateRequestAttributes(Map attrs, Context ctx) { - if (ctx.hasKey(HTTP_SERVLET_REQUEST_ATTR_NAME)) { - attrs.putIfAbsent(HTTP_SERVLET_REQUEST_ATTR_NAME, ctx.get(HTTP_SERVLET_REQUEST_ATTR_NAME)); - } - if (ctx.hasKey(HTTP_SERVLET_RESPONSE_ATTR_NAME)) { - attrs.putIfAbsent(HTTP_SERVLET_RESPONSE_ATTR_NAME, ctx.get(HTTP_SERVLET_RESPONSE_ATTR_NAME)); - } - if (ctx.hasKey(AUTHENTICATION_ATTR_NAME)) { - attrs.putIfAbsent(AUTHENTICATION_ATTR_NAME, ctx.get(AUTHENTICATION_ATTR_NAME)); + RequestContextDataHolder holder = RequestContextSubscriber.getRequestContext(ctx); + if (holder != null) { + HttpServletRequest request = holder.getRequest(); + if (request != null) { + attrs.putIfAbsent(HTTP_SERVLET_REQUEST_ATTR_NAME, request); + } + + HttpServletResponse response = holder.getResponse(); + if (response != null) { + attrs.putIfAbsent(HTTP_SERVLET_RESPONSE_ATTR_NAME, response); + } + + Authentication authentication = holder.getAuthentication(); + if (authentication != null) { + attrs.putIfAbsent(AUTHENTICATION_ATTR_NAME, authentication); + } } populateDefaultOAuth2AuthorizedClient(attrs); } @@ -348,39 +371,50 @@ private void populateDefaultOAuth2AuthorizedClient(Map attrs) { return; } + String clientRegistrationId = resolveClientRegistrationId(attrs); Authentication authentication = getAuthentication(attrs); + HttpServletRequest request = getRequest(attrs); + if (clientRegistrationId != null && authentication != null && request != null) { + OAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository.loadAuthorizedClient( + clientRegistrationId, authentication, request); + if (authorizedClient != null) { + oauth2AuthorizedClient(authorizedClient).accept(attrs); + } + } + } + + private String resolveClientRegistrationId(Map attrs) { String clientRegistrationId = getClientRegistrationId(attrs); if (clientRegistrationId == null) { clientRegistrationId = this.defaultClientRegistrationId; } + Authentication authentication = getAuthentication(attrs); if (clientRegistrationId == null && this.defaultOAuth2AuthorizedClient && authentication instanceof OAuth2AuthenticationToken) { clientRegistrationId = ((OAuth2AuthenticationToken) authentication).getAuthorizedClientRegistrationId(); } - if (clientRegistrationId != null) { - HttpServletRequest request = getRequest(attrs); - OAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository - .loadAuthorizedClient(clientRegistrationId, authentication, - request); - if (authorizedClient == null) { - authorizedClient = getAuthorizedClient(clientRegistrationId, attrs); - } - oauth2AuthorizedClient(authorizedClient).accept(attrs); - } + return clientRegistrationId; } - private OAuth2AuthorizedClient getAuthorizedClient(String clientRegistrationId, Map attrs) { + private Mono authorizeClient(ClientRequest request) { + Map attrs = request.attributes(); + String clientRegistrationId = resolveClientRegistrationId(attrs); ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId); if (clientRegistration == null) { throw new IllegalArgumentException("Could not find ClientRegistration with id " + clientRegistrationId); } if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType())) { - return getAuthorizedClient(clientRegistration, attrs); + // NOTE: 'getAuthorizedClient()' needs to be executed on a dedicated thread via subscribeOn(Schedulers.elastic()) + // since it performs a blocking I/O operation using RestTemplate internally + return Mono.fromSupplier(() -> getAuthorizedClient(clientRegistration, attrs)).subscribeOn(Schedulers.elastic()); } throw new ClientAuthorizationRequiredException(clientRegistrationId); } + private boolean isClientCredentialsGrantType(ClientRegistration clientRegistration) { + return AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType()); + } private OAuth2AuthorizedClient getAuthorizedClient(ClientRegistration clientRegistration, Map attrs) { @@ -409,7 +443,11 @@ private OAuth2AuthorizedClient getAuthorizedClient(ClientRegistration clientRegi } private Mono authorizedClient(ClientRequest request, ExchangeFunction next, OAuth2AuthorizedClient authorizedClient) { - if (shouldRefresh(authorizedClient)) { + ClientRegistration clientRegistration = authorizedClient.getClientRegistration(); + if (isClientCredentialsGrantType(clientRegistration) && hasTokenExpired(authorizedClient)) { + //Client credentials grant do not have refresh tokens but can expire so we need to get another one + return Mono.fromSupplier(() -> getAuthorizedClient(clientRegistration, request.attributes())); + } else if (shouldRefresh(authorizedClient)) { return refreshAuthorizedClient(request, next, authorizedClient); } return Mono.just(authorizedClient); @@ -454,6 +492,10 @@ private boolean shouldRefresh(OAuth2AuthorizedClient authorizedClient) { if (refreshToken == null) { return false; } + return hasTokenExpired(authorizedClient); + } + + private boolean hasTokenExpired(OAuth2AuthorizedClient authorizedClient) { Instant now = this.clock.instant(); Instant expiresAt = authorizedClient.getAccessToken().getExpiresAt(); if (now.isAfter(expiresAt.minus(this.accessTokenExpiresSkew))) { @@ -468,7 +510,7 @@ private ClientRequest bearer(ClientRequest request, OAuth2AuthorizedClient autho .build(); } - private CoreSubscriber createRequestContextSubscriber(CoreSubscriber delegate) { + CoreSubscriber createRequestContextSubscriberIfNecessary(CoreSubscriber delegate) { HttpServletRequest request = null; HttpServletResponse response = null; ServletRequestAttributes requestAttributes = @@ -478,6 +520,10 @@ private CoreSubscriber createRequestContextSubscriber(CoreSubscriber d response = requestAttributes.getResponse(); } Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null && request == null && response == null) { + //do not need to create RequestContextSubscriber with empty data + return delegate; + } return new RequestContextSubscriber<>(delegate, request, response, authentication); } @@ -555,34 +601,37 @@ private UnsupportedOperationException unsupported() { } } - private static class RequestContextSubscriber implements CoreSubscriber { - private static final String CONTEXT_DEFAULTED_ATTR_NAME = RequestContextSubscriber.class.getName().concat(".CONTEXT_DEFAULTED_ATTR_NAME"); + static class RequestContextSubscriber implements CoreSubscriber { + static final String REQUEST_CONTEXT_DATA_HOLDER = + RequestContextSubscriber.class.getName().concat(".REQUEST_CONTEXT_DATA_HOLDER"); private final CoreSubscriber delegate; - private final HttpServletRequest request; - private final HttpServletResponse response; - private final Authentication authentication; + private final Context context; - private RequestContextSubscriber(CoreSubscriber delegate, - HttpServletRequest request, - HttpServletResponse response, - Authentication authentication) { + RequestContextSubscriber(CoreSubscriber delegate, + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) { this.delegate = delegate; - this.request = request; - this.response = response; - this.authentication = authentication; + + Context parentContext = this.delegate.currentContext(); + Context context; + if (parentContext.hasKey(REQUEST_CONTEXT_DATA_HOLDER)) { + context = parentContext; + } else { + context = parentContext.put(REQUEST_CONTEXT_DATA_HOLDER, new RequestContextDataHolder(request, response, authentication)); + } + + this.context = context; + } + + @Nullable + private static RequestContextDataHolder getRequestContext(Context ctx) { + return ctx.getOrDefault(REQUEST_CONTEXT_DATA_HOLDER, null); } @Override public Context currentContext() { - Context context = this.delegate.currentContext(); - if (context.hasKey(CONTEXT_DEFAULTED_ATTR_NAME)) { - return context; - } - return Context.of( - CONTEXT_DEFAULTED_ATTR_NAME, Boolean.TRUE, - HTTP_SERVLET_REQUEST_ATTR_NAME, this.request, - HTTP_SERVLET_RESPONSE_ATTR_NAME, this.response, - AUTHENTICATION_ATTR_NAME, this.authentication); + return this.context; } @Override @@ -605,4 +654,33 @@ public void onComplete() { this.delegate.onComplete(); } } + + static class RequestContextDataHolder { + private final HttpServletRequest request; + private final HttpServletResponse response; + private final Authentication authentication; + + RequestContextDataHolder(@Nullable HttpServletRequest request, + @Nullable HttpServletResponse response, + @Nullable Authentication authentication) { + this.request = request; + this.response = response; + this.authentication = authentication; + } + + @Nullable + private HttpServletRequest getRequest() { + return this.request; + } + + @Nullable + private HttpServletResponse getResponse() { + return this.response; + } + + @Nullable + private Authentication getAuthentication() { + return this.authentication; + } + } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolver.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolver.java index ff9c0cd03d4..d52a7bd7856 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolver.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolver.java @@ -18,7 +18,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; import org.springframework.security.crypto.keygen.StringKeyGenerator; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -149,7 +148,7 @@ private String expandRedirectUri(ServerHttpRequest request, ClientRegistration c Map uriVariables = new HashMap<>(); uriVariables.put("registrationId", clientRegistration.getRegistrationId()); - String baseUrl = UriComponentsBuilder.fromHttpRequest(new ServerHttpRequestDecorator(request)) + String baseUrl = UriComponentsBuilder.fromUri(request.getURI()) .replacePath(request.getPath().contextPath().value()) .replaceQuery(null) .build() diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilter.java index 02e76b1a1a6..ecd9084744a 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -35,14 +35,22 @@ import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler; import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler; -import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.util.Assert; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Mono; +import java.net.URI; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + /** * A {@code Filter} for the OAuth 2.0 Authorization Code Grant, * which handles the processing of the OAuth 2.0 Authorization Response. @@ -71,6 +79,7 @@ * * * @author Rob Winch + * @author Joe Grandja * @since 5.1 * @see OAuth2AuthorizationCodeAuthenticationToken * @see org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeReactiveAuthenticationManager @@ -89,6 +98,9 @@ public class OAuth2AuthorizationCodeGrantWebFilter implements WebFilter { private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository; + private ServerAuthorizationRequestRepository authorizationRequestRepository = + new WebSessionOAuth2ServerAuthorizationRequestRepository(); + private ServerAuthenticationSuccessHandler authenticationSuccessHandler; private ServerAuthenticationConverter authenticationConverter; @@ -109,7 +121,7 @@ public OAuth2AuthorizationCodeGrantWebFilter( Assert.notNull(authorizedClientRepository, "authorizedClientRepository cannot be null"); this.authenticationManager = authenticationManager; this.authorizedClientRepository = authorizedClientRepository; - this.requiresAuthenticationMatcher = new PathPatternParserServerWebExchangeMatcher("/{action}/oauth2/code/{registrationId}"); + this.requiresAuthenticationMatcher = this::matchesAuthorizationResponse; this.authenticationConverter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository); this.authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler(); this.authenticationFailureHandler = (webFilterExchange, exception) -> Mono.error(exception); @@ -124,7 +136,7 @@ public OAuth2AuthorizationCodeGrantWebFilter( Assert.notNull(authorizedClientRepository, "authorizedClientRepository cannot be null"); this.authenticationManager = authenticationManager; this.authorizedClientRepository = authorizedClientRepository; - this.requiresAuthenticationMatcher = new PathPatternParserServerWebExchangeMatcher("/{action}/oauth2/code/{registrationId}"); + this.requiresAuthenticationMatcher = this::matchesAuthorizationResponse; this.authenticationConverter = authenticationConverter; this.authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler(); this.authenticationFailureHandler = (webFilterExchange, exception) -> Mono.error(exception); @@ -133,10 +145,10 @@ public OAuth2AuthorizationCodeGrantWebFilter( @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { return this.requiresAuthenticationMatcher.matches(exchange) - .filter( matchResult -> matchResult.isMatch()) - .flatMap( matchResult -> this.authenticationConverter.convert(exchange)) + .filter(ServerWebExchangeMatcher.MatchResult::isMatch) + .flatMap(matchResult -> this.authenticationConverter.convert(exchange)) .switchIfEmpty(chain.filter(exchange).then(Mono.empty())) - .flatMap( token -> authenticate(exchange, chain, token)); + .flatMap(token -> authenticate(exchange, chain, token)); } private Mono authenticate(ServerWebExchange exchange, @@ -164,4 +176,36 @@ private Mono onAuthenticationSuccess(Authentication authentication, WebFil .flatMap(principal -> this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, webFilterExchange.getExchange())) ); } + + private Mono matchesAuthorizationResponse(ServerWebExchange exchange) { + return Mono.just(exchange) + .filter(exch -> OAuth2AuthorizationResponseUtils.isAuthorizationResponse(exch.getRequest().getQueryParams())) + .flatMap(exch -> this.authorizationRequestRepository.loadAuthorizationRequest(exchange) + .flatMap(authorizationRequest -> + matchesRedirectUri(exch.getRequest().getURI(), authorizationRequest.getRedirectUri()))) + .switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch()); + } + + private static Mono matchesRedirectUri( + URI authorizationResponseUri, String authorizationRequestRedirectUri) { + UriComponents requestUri = UriComponentsBuilder.fromUri(authorizationResponseUri).build(); + UriComponents redirectUri = UriComponentsBuilder.fromUriString(authorizationRequestRedirectUri).build(); + Set>> requestUriParameters = + new LinkedHashSet<>(requestUri.getQueryParams().entrySet()); + Set>> redirectUriParameters = + new LinkedHashSet<>(redirectUri.getQueryParams().entrySet()); + // Remove the additional request parameters (if any) from the authorization response (request) + // before doing an exact comparison with the authorizationRequest.getRedirectUri() parameters (if any) + requestUriParameters.retainAll(redirectUriParameters); + + if (Objects.equals(requestUri.getScheme(), redirectUri.getScheme()) && + Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo()) && + Objects.equals(requestUri.getHost(), redirectUri.getHost()) && + Objects.equals(requestUri.getPort(), redirectUri.getPort()) && + Objects.equals(requestUri.getPath(), redirectUri.getPath()) && + Objects.equals(requestUriParameters.toString(), redirectUriParameters.toString())) { + return ServerWebExchangeMatcher.MatchResult.match(); + } + return ServerWebExchangeMatcher.MatchResult.notMatch(); + } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizationCodeAuthenticationTokenConverter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizationCodeAuthenticationTokenConverter.java index 4d5c04488bd..3d76bc390e6 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizationCodeAuthenticationTokenConverter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizationCodeAuthenticationTokenConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -28,7 +28,6 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; import org.springframework.util.Assert; -import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Mono; @@ -103,14 +102,10 @@ private Mono authenticationRequest(S } private static OAuth2AuthorizationResponse convertResponse(ServerWebExchange exchange) { - MultiValueMap queryParams = exchange.getRequest() - .getQueryParams(); String redirectUri = UriComponentsBuilder.fromUri(exchange.getRequest().getURI()) - .query(null) .build() .toUriString(); - return OAuth2AuthorizationResponseUtils - .convert(queryParams, redirectUri); + .convert(exchange.getRequest().getQueryParams(), redirectUri); } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepository.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepository.java index c40a12234fd..2ad3026ca40 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepository.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -85,6 +85,9 @@ public Mono removeAuthorizationRequest( OAuth2AuthorizationRequest removedValue = stateToAuthzRequest.remove(state); if (stateToAuthzRequest.isEmpty()) { sessionAttrs.remove(this.sessionAttributeName); + } else if (removedValue != null) { + // gh-7327 Overwrite the existing Map to ensure the state is saved for distributed sessions + sessionAttrs.put(this.sessionAttributeName, stateToAuthzRequest); } if (removedValue == null) { sink.complete(); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java index 1888c9b429b..74019c7baa1 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -15,6 +15,10 @@ */ package org.springframework.security.oauth2.client.authentication; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,13 +37,12 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses.accessTokenResponse; /** * Tests for {@link OAuth2AuthorizationCodeAuthenticationProvider}. @@ -108,18 +111,6 @@ public void authenticateWhenAuthorizationResponseStateNotEqualAuthorizationReque }).isInstanceOf(OAuth2AuthorizationException.class).hasMessageContaining("invalid_state_parameter"); } - @Test - public void authenticateWhenAuthorizationResponseRedirectUriNotEqualAuthorizationRequestRedirectUriThenThrowOAuth2AuthorizationException() { - when(this.authorizationRequest.getRedirectUri()).thenReturn("https://example.com"); - when(this.authorizationResponse.getRedirectUri()).thenReturn("https://example2.com"); - - assertThatThrownBy(() -> { - this.authenticationProvider.authenticate( - new OAuth2AuthorizationCodeAuthenticationToken( - this.clientRegistration, this.authorizationExchange)); - }).isInstanceOf(OAuth2AuthorizationException.class).hasMessageContaining("invalid_redirect_uri_parameter"); - } - @Test public void authenticateWhenAuthorizationSuccessResponseThenExchangedForAccessToken() { OAuth2AccessToken accessToken = mock(OAuth2AccessToken.class); @@ -142,4 +133,26 @@ public void authenticateWhenAuthorizationSuccessResponseThenExchangedForAccessTo assertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken); assertThat(authenticationResult.getRefreshToken()).isEqualTo(refreshToken); } + + // gh-5368 + @Test + public void authenticateWhenAuthorizationSuccessResponseThenAdditionalParametersIncluded() { + Map additionalParameters = new HashMap<>(); + additionalParameters.put("param1", "value1"); + additionalParameters.put("param2", "value2"); + + OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().additionalParameters(additionalParameters) + .build(); + when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); + + OAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest, + this.authorizationResponse); + + OAuth2AuthorizationCodeAuthenticationToken authentication = (OAuth2AuthorizationCodeAuthenticationToken) this.authenticationProvider + .authenticate( + new OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration, authorizationExchange)); + + assertThat(authentication.getAdditionalParameters()) + .containsAllEntriesOf(accessTokenResponse.getAdditionalParameters()); + } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java index adb28dca3d4..8c3cc2e19e3 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -80,13 +80,6 @@ public void authenticateWhenStateNotEqualThenOAuth2AuthorizationException() { .isInstanceOf(OAuth2AuthorizationException.class); } - @Test - public void authenticateWhenRedirectUriNotEqualThenOAuth2AuthorizationException() { - this.authorizationRequest.redirectUri("https://example.org/notequal"); - assertThatCode(() -> authenticate()) - .isInstanceOf(OAuth2AuthorizationException.class); - } - @Test public void authenticateWhenValidThenSuccess() { when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(Mono.just(this.tokenResponse.build())); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java index 98a2e241f2e..f8d15290079 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -154,18 +154,6 @@ public void authenticateWhenAuthorizationResponseStateNotEqualAuthorizationReque new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange)); } - @Test - public void authenticateWhenAuthorizationResponseRedirectUriNotEqualAuthorizationRequestRedirectUriThenThrowOAuth2AuthenticationException() { - this.exception.expect(OAuth2AuthenticationException.class); - this.exception.expectMessage(containsString("invalid_redirect_uri_parameter")); - - when(this.authorizationRequest.getRedirectUri()).thenReturn("https://example.com"); - when(this.authorizationResponse.getRedirectUri()).thenReturn("https://example2.com"); - - this.authenticationProvider.authenticate( - new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange)); - } - @Test public void authenticateWhenLoginSuccessThenReturnAuthentication() { OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenSuccessResponse(); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java index 8bcd8164a8c..096f9d8145a 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -95,9 +95,11 @@ public void getTokenResponseWhenPostThenSuccess() throws Exception { OAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(registration); OAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block(); - String body = this.server.takeRequest().getUtf8Body(); + RecordedRequest actualRequest = this.server.takeRequest(); + String body = actualRequest.getUtf8Body(); assertThat(response.getAccessToken()).isNotNull(); + assertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); assertThat(body).isEqualTo("grant_type=client_credentials&scope=read%3Auser&client_id=client-id&client_secret=client-secret"); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProviderTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProviderTests.java index 083247b76d7..d53f731a93c 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProviderTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -169,18 +169,6 @@ public void authenticateWhenAuthorizationResponseStateNotEqualAuthorizationReque new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange)); } - @Test - public void authenticateWhenAuthorizationResponseRedirectUriNotEqualAuthorizationRequestRedirectUriThenThrowOAuth2AuthenticationException() { - this.exception.expect(OAuth2AuthenticationException.class); - this.exception.expectMessage(containsString("invalid_redirect_uri_parameter")); - - when(this.authorizationRequest.getRedirectUri()).thenReturn("https://example1.com"); - when(this.authorizationResponse.getRedirectUri()).thenReturn("https://example2.com"); - - this.authenticationProvider.authenticate( - new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange)); - } - @Test public void authenticateWhenTokenResponseDoesNotContainIdTokenThenThrowOAuth2AuthenticationException() { this.exception.expect(OAuth2AuthenticationException.class); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java index 23059d595b4..208a6e63fa0 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java @@ -535,4 +535,27 @@ public void buildWhenClientCredentialsGrantTokenUriIsNullThenThrowIllegalArgumen .build() ).isInstanceOf(IllegalArgumentException.class); } + + @Test + public void buildWhenCustomGrantAllAttributesProvidedThenAllAttributesAreSet() { + AuthorizationGrantType customGrantType = new AuthorizationGrantType("CUSTOM"); + ClientRegistration registration = ClientRegistration.withRegistrationId(REGISTRATION_ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) + .authorizationGrantType(customGrantType) + .scope(SCOPES.toArray(new String[0])) + .tokenUri(TOKEN_URI) + .clientName(CLIENT_NAME) + .build(); + + assertThat(registration.getRegistrationId()).isEqualTo(REGISTRATION_ID); + assertThat(registration.getClientId()).isEqualTo(CLIENT_ID); + assertThat(registration.getClientSecret()).isEqualTo(CLIENT_SECRET); + assertThat(registration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); + assertThat(registration.getAuthorizationGrantType()).isEqualTo(customGrantType); + assertThat(registration.getScopes()).isEqualTo(SCOPES); + assertThat(registration.getProviderDetails().getTokenUri()).isEqualTo(TOKEN_URI); + assertThat(registration.getClientName()).isEqualTo(CLIENT_NAME); + } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilterTests.java index 4dd9e2fc0d3..3eec2d00515 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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,10 +18,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -39,36 +35,44 @@ import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.TestClientRegistrations; -import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthorizationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.OAuth2RefreshToken; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.RequestCache; +import org.springframework.security.web.util.UrlUtils; +import org.springframework.util.CollectionUtils; import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes; +import static org.springframework.security.oauth2.core.TestOAuth2RefreshTokens.refreshToken; +import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges.success; +import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests.request; /** * Tests for {@link OAuth2AuthorizationCodeGrantFilter}. * * @author Joe Grandja */ -@PowerMockIgnore("javax.security.*") -@PrepareForTest({OAuth2AuthorizationRequest.class, OAuth2AuthorizationExchange.class, OAuth2AuthorizationCodeGrantFilter.class}) -@RunWith(PowerMockRunner.class) public class OAuth2AuthorizationCodeGrantFilterTests { private ClientRegistration registration1; private String principalName1 = "principal-1"; @@ -132,8 +136,7 @@ public void doFilterWhenNotAuthorizationResponseThenNotProcessed() throws Except MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); // NOTE: A valid Authorization Response contains either a 'code' or 'error' parameter. - - HttpServletResponse response = mock(HttpServletResponse.class); + MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); this.filter.doFilter(request, response, filterChain); @@ -143,94 +146,142 @@ public void doFilterWhenNotAuthorizationResponseThenNotProcessed() throws Except @Test public void doFilterWhenAuthorizationRequestNotFoundThenNotProcessed() throws Exception { - String requestUri = "/path"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); - - HttpServletResponse response = mock(HttpServletResponse.class); + MockHttpServletRequest authorizationRequest = createAuthorizationRequest("/path"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); + MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } @Test - public void doFilterWhenAuthorizationResponseUrlDoesNotMatchAuthorizationRequestRedirectUriThenNotProcessed() throws Exception { + public void doFilterWhenAuthorizationRequestRedirectUriDoesNotMatchThenNotProcessed() throws Exception { String requestUri = "/callback/client-1"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); - - HttpServletResponse response = mock(HttpServletResponse.class); + MockHttpServletRequest authorizationRequest = createAuthorizationRequest(requestUri); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); + MockHttpServletResponse response = new MockHttpServletResponse(); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); + authorizationResponse.setRequestURI(requestUri + "-no-match"); FilterChain filterChain = mock(FilterChain.class); - this.setUpAuthorizationRequest(request, response, this.registration1); - request.setRequestURI(requestUri + "-no-match"); - - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } + // gh-7963 @Test - public void doFilterWhenAuthorizationResponseValidThenAuthorizationRequestRemoved() throws Exception { + public void doFilterWhenAuthorizationRequestRedirectUriParametersMatchThenProcessed() throws Exception { + // 1) redirect_uri with query parameters String requestUri = "/callback/client-1"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); + Map parameters = new LinkedHashMap<>(); + parameters.put("param1", "value1"); + parameters.put("param2", "value2"); + MockHttpServletRequest authorizationRequest = createAuthorizationRequest(requestUri, parameters); + MockHttpServletResponse response = new MockHttpServletResponse(); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); + this.setUpAuthenticationResult(this.registration1); + FilterChain filterChain = mock(FilterChain.class); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); + this.filter.doFilter(authorizationResponse, response, filterChain); + verifyZeroInteractions(filterChain); + + // 2) redirect_uri with query parameters AND authorization response additional parameters + Map additionalParameters = new LinkedHashMap<>(); + additionalParameters.put("auth-param1", "value1"); + additionalParameters.put("auth-param2", "value2"); + response = new MockHttpServletResponse(); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); + authorizationResponse = createAuthorizationResponse(authorizationRequest, additionalParameters); + this.filter.doFilter(authorizationResponse, response, filterChain); + verifyZeroInteractions(filterChain); + } + // gh-7963 + @Test + public void doFilterWhenAuthorizationRequestRedirectUriParametersDoesNotMatchThenNotProcessed() throws Exception { + String requestUri = "/callback/client-1"; + Map parameters = new LinkedHashMap<>(); + parameters.put("param1", "value1"); + parameters.put("param2", "value2"); + MockHttpServletRequest authorizationRequest = createAuthorizationRequest(requestUri, parameters); MockHttpServletResponse response = new MockHttpServletResponse(); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); + this.setUpAuthenticationResult(this.registration1); FilterChain filterChain = mock(FilterChain.class); - this.setUpAuthorizationRequest(request, response, this.registration1); + // 1) Parameter value + Map parametersNotMatch = new LinkedHashMap<>(parameters); + parametersNotMatch.put("param2", "value8"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse( + createAuthorizationRequest(requestUri, parametersNotMatch)); + authorizationResponse.setSession(authorizationRequest.getSession()); + this.filter.doFilter(authorizationResponse, response, filterChain); + verify(filterChain, times(1)).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); + + // 2) Parameter order + parametersNotMatch = new LinkedHashMap<>(); + parametersNotMatch.put("param2", "value2"); + parametersNotMatch.put("param1", "value1"); + authorizationResponse = createAuthorizationResponse( + createAuthorizationRequest(requestUri, parametersNotMatch)); + authorizationResponse.setSession(authorizationRequest.getSession()); + this.filter.doFilter(authorizationResponse, response, filterChain); + verify(filterChain, times(2)).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); + + // 3) Parameter missing + parametersNotMatch = new LinkedHashMap<>(parameters); + parametersNotMatch.remove("param2"); + authorizationResponse = createAuthorizationResponse( + createAuthorizationRequest(requestUri, parametersNotMatch)); + authorizationResponse.setSession(authorizationRequest.getSession()); + this.filter.doFilter(authorizationResponse, response, filterChain); + verify(filterChain, times(3)).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); + } + + @Test + public void doFilterWhenAuthorizationRequestMatchThenAuthorizationRequestRemoved() throws Exception { + MockHttpServletRequest authorizationRequest = createAuthorizationRequest("/callback/client-1"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain filterChain = mock(FilterChain.class); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); this.setUpAuthenticationResult(this.registration1); - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); - assertThat(this.authorizationRequestRepository.loadAuthorizationRequest(request)).isNull(); + assertThat(this.authorizationRequestRepository.loadAuthorizationRequest(authorizationResponse)).isNull(); } @Test public void doFilterWhenAuthorizationFailsThenHandleOAuth2AuthorizationException() throws Exception { - String requestUri = "/callback/client-1"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); - + MockHttpServletRequest authorizationRequest = createAuthorizationRequest("/callback/client-1"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); - this.setUpAuthorizationRequest(request, response, this.registration1); OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT); when(this.authenticationManager.authenticate(any(Authentication.class))) .thenThrow(new OAuth2AuthorizationException(error)); - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/callback/client-1?error=invalid_grant"); } @Test - public void doFilterWhenAuthorizationResponseSuccessThenAuthorizedClientSavedToService() throws Exception { - String requestUri = "/callback/client-1"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); - + public void doFilterWhenAuthorizationSucceedsThenAuthorizedClientSavedToService() throws Exception { + MockHttpServletRequest authorizationRequest = createAuthorizationRequest("/callback/client-1"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); - - this.setUpAuthorizationRequest(request, response, this.registration1); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); this.setUpAuthenticationResult(this.registration1); - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient( this.registration1.getRegistrationId(), this.principalName1); @@ -242,40 +293,31 @@ public void doFilterWhenAuthorizationResponseSuccessThenAuthorizedClientSavedToS } @Test - public void doFilterWhenAuthorizationResponseSuccessThenRedirected() throws Exception { - String requestUri = "/callback/client-1"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); - + public void doFilterWhenAuthorizationSucceedsThenRedirected() throws Exception { + MockHttpServletRequest authorizationRequest = createAuthorizationRequest("/callback/client-1"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); - - this.setUpAuthorizationRequest(request, response, this.registration1); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); this.setUpAuthenticationResult(this.registration1); - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/callback/client-1"); } @Test - public void doFilterWhenAuthorizationResponseSuccessHasSavedRequestThenRedirectedToSavedRequest() throws Exception { + public void doFilterWhenAuthorizationSucceedsAndHasSavedRequestThenRedirectToSavedRequest() throws Exception { String requestUri = "/saved-request"; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); MockHttpServletResponse response = new MockHttpServletResponse(); RequestCache requestCache = new HttpSessionRequestCache(); requestCache.saveRequest(request, response); - - requestUri = "/callback/client-1"; - request.setRequestURI(requestUri); + request.setRequestURI("/callback/client-1"); request.addParameter(OAuth2ParameterNames.CODE, "code"); request.addParameter(OAuth2ParameterNames.STATE, "state"); - FilterChain filterChain = mock(FilterChain.class); - this.setUpAuthorizationRequest(request, response, this.registration1); this.setUpAuthenticationResult(this.registration1); @@ -285,36 +327,30 @@ public void doFilterWhenAuthorizationResponseSuccessHasSavedRequestThenRedirecte } @Test - public void doFilterWhenAuthorizationResponseSuccessAndAnonymousAccessThenAuthorizedClientSavedToHttpSession() throws Exception { + public void doFilterWhenAuthorizationSucceedsAndAnonymousAccessThenAuthorizedClientSavedToHttpSession() throws Exception { AnonymousAuthenticationToken anonymousPrincipal = new AnonymousAuthenticationToken("key-1234", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(anonymousPrincipal); SecurityContextHolder.setContext(securityContext); - String requestUri = "/callback/client-1"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); - + MockHttpServletRequest authorizationRequest = createAuthorizationRequest("/callback/client-1"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); - - this.setUpAuthorizationRequest(request, response, this.registration1); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); this.setUpAuthenticationResult(this.registration1); - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); OAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository.loadAuthorizedClient( - this.registration1.getRegistrationId(), anonymousPrincipal, request); + this.registration1.getRegistrationId(), anonymousPrincipal, authorizationResponse); assertThat(authorizedClient).isNotNull(); - assertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1); assertThat(authorizedClient.getPrincipalName()).isEqualTo(anonymousPrincipal.getName()); assertThat(authorizedClient.getAccessToken()).isNotNull(); - HttpSession session = request.getSession(false); + HttpSession session = authorizationResponse.getSession(false); assertThat(session).isNotNull(); @SuppressWarnings("unchecked") @@ -326,33 +362,27 @@ public void doFilterWhenAuthorizationResponseSuccessAndAnonymousAccessThenAuthor } @Test - public void doFilterWhenAuthorizationResponseSuccessAndAnonymousAccessNullAuthenticationThenAuthorizedClientSavedToHttpSession() throws Exception { + public void doFilterWhenAuthorizationSucceedsAndAnonymousAccessNullAuthenticationThenAuthorizedClientSavedToHttpSession() throws Exception { SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); SecurityContextHolder.setContext(securityContext); // null Authentication - String requestUri = "/callback/client-1"; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.addParameter(OAuth2ParameterNames.CODE, "code"); - request.addParameter(OAuth2ParameterNames.STATE, "state"); - + MockHttpServletRequest authorizationRequest = createAuthorizationRequest("/callback/client-1"); + MockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); - - this.setUpAuthorizationRequest(request, response, this.registration1); + this.setUpAuthorizationRequest(authorizationRequest, response, this.registration1); this.setUpAuthenticationResult(this.registration1); - this.filter.doFilter(request, response, filterChain); + this.filter.doFilter(authorizationResponse, response, filterChain); OAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository.loadAuthorizedClient( - this.registration1.getRegistrationId(), null, request); + this.registration1.getRegistrationId(), null, authorizationResponse); assertThat(authorizedClient).isNotNull(); - assertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1); assertThat(authorizedClient.getPrincipalName()).isEqualTo("anonymousUser"); assertThat(authorizedClient.getAccessToken()).isNotNull(); - HttpSession session = request.getSession(false); + HttpSession session = authorizationResponse.getSession(false); assertThat(session).isNotNull(); @SuppressWarnings("unchecked") @@ -363,23 +393,57 @@ public void doFilterWhenAuthorizationResponseSuccessAndAnonymousAccessNullAuthen assertThat(authorizedClients.values().iterator().next()).isSameAs(authorizedClient); } + private static MockHttpServletRequest createAuthorizationRequest(String requestUri) { + return createAuthorizationRequest(requestUri, new LinkedHashMap<>()); + } + + private static MockHttpServletRequest createAuthorizationRequest(String requestUri, Map parameters) { + MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); + request.setServletPath(requestUri); + if (!CollectionUtils.isEmpty(parameters)) { + parameters.forEach(request::addParameter); + request.setQueryString( + parameters.entrySet().stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining("&"))); + } + return request; + } + + private static MockHttpServletRequest createAuthorizationResponse(MockHttpServletRequest authorizationRequest) { + return createAuthorizationResponse(authorizationRequest, new LinkedHashMap<>()); + } + + private static MockHttpServletRequest createAuthorizationResponse( + MockHttpServletRequest authorizationRequest, Map additionalParameters) { + MockHttpServletRequest authorizationResponse = new MockHttpServletRequest( + authorizationRequest.getMethod(), authorizationRequest.getRequestURI()); + authorizationResponse.setServletPath(authorizationRequest.getRequestURI()); + authorizationRequest.getParameterMap().forEach(authorizationResponse::addParameter); + authorizationResponse.addParameter(OAuth2ParameterNames.CODE, "code"); + authorizationResponse.addParameter(OAuth2ParameterNames.STATE, "state"); + additionalParameters.forEach(authorizationResponse::addParameter); + authorizationResponse.setQueryString( + authorizationResponse.getParameterMap().entrySet().stream() + .map(e -> e.getKey() + "=" + e.getValue()[0]) + .collect(Collectors.joining("&"))); + authorizationResponse.setSession(authorizationRequest.getSession()); + return authorizationResponse; + } + private void setUpAuthorizationRequest(HttpServletRequest request, HttpServletResponse response, ClientRegistration registration) { Map additionalParameters = new HashMap<>(); additionalParameters.put(OAuth2ParameterNames.REGISTRATION_ID, registration.getRegistrationId()); - OAuth2AuthorizationRequest authorizationRequest = mock(OAuth2AuthorizationRequest.class); - when(authorizationRequest.getAdditionalParameters()).thenReturn(additionalParameters); - when(authorizationRequest.getRedirectUri()).thenReturn(request.getRequestURL().toString()); - when(authorizationRequest.getState()).thenReturn("state"); + OAuth2AuthorizationRequest authorizationRequest = request() + .additionalParameters(additionalParameters) + .redirectUri(UrlUtils.buildFullRequestUrl(request)).build(); this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response); } private void setUpAuthenticationResult(ClientRegistration registration) { - OAuth2AuthorizationCodeAuthenticationToken authentication = mock(OAuth2AuthorizationCodeAuthenticationToken.class); - when(authentication.getClientRegistration()).thenReturn(registration); - when(authentication.getAuthorizationExchange()).thenReturn(mock(OAuth2AuthorizationExchange.class)); - when(authentication.getAccessToken()).thenReturn(mock(OAuth2AccessToken.class)); - when(authentication.getRefreshToken()).thenReturn(mock(OAuth2RefreshToken.class)); + OAuth2AuthorizationCodeAuthenticationToken authentication = + new OAuth2AuthorizationCodeAuthenticationToken(registration, success(), noScopes(), refreshToken()); when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(authentication); } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java index a679a5996d5..a5c0e91f5b4 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java @@ -44,6 +44,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.TestClientRegistrations; +import org.springframework.security.oauth2.client.web.reactive.function.client.OAuth2AuthorizedClientResolver.Request; import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; @@ -69,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -88,6 +90,9 @@ public class ServerOAuth2AuthorizedClientExchangeFilterFunctionTests { @Mock private ReactiveClientRegistrationRepository clientRegistrationRepository; + @Mock + private OAuth2AuthorizedClientResolver oAuth2AuthorizedClientResolver; + @Mock private ServerWebExchange serverWebExchange; @@ -149,6 +154,88 @@ public void filterWhenExistingAuthorizationThenSingleAuthorizationHeader() { assertThat(headers.get(HttpHeaders.AUTHORIZATION)).containsOnly("Bearer " + this.accessToken.getTokenValue()); } + @Test + public void filterWhenClientCredentialsTokenExpiredThenGetNewToken() { + TestingAuthenticationToken authentication = new TestingAuthenticationToken("test", "this"); + ClientRegistration registration = TestClientRegistrations.clientCredentials().build(); + String clientRegistrationId = registration.getClientId(); + + this.function = new ServerOAuth2AuthorizedClientExchangeFilterFunction(this.authorizedClientRepository, this.oAuth2AuthorizedClientResolver); + + OAuth2AccessToken newAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, + "new-token", + Instant.now(), + Instant.now().plus(Duration.ofDays(1))); + OAuth2AuthorizedClient newAuthorizedClient = new OAuth2AuthorizedClient(registration, + "principalName", newAccessToken, null); + Request r = new Request(clientRegistrationId, authentication, null); + when(this.oAuth2AuthorizedClientResolver.clientCredentials(any(), any(), any())).thenReturn(Mono.just(newAuthorizedClient)); + when(this.oAuth2AuthorizedClientResolver.createDefaultedRequest(any(), any(), any())).thenReturn(Mono.just(r)); + + when(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).thenReturn(Mono.empty()); + + Instant issuedAt = Instant.now().minus(Duration.ofDays(1)); + Instant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1)); + + OAuth2AccessToken accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), + this.accessToken.getTokenValue(), + issuedAt, + accessTokenExpiresAt); + + + OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(registration, + "principalName", accessToken, null); + ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .build(); + + + this.function.filter(request, this.exchange) + .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication)) + .block(); + + verify(this.authorizedClientRepository).saveAuthorizedClient(any(), eq(authentication), any()); + verify(this.oAuth2AuthorizedClientResolver).clientCredentials(any(), any(), any()); + verify(this.oAuth2AuthorizedClientResolver).createDefaultedRequest(any(), any(), any()); + + List requests = this.exchange.getRequests(); + assertThat(requests).hasSize(1); + ClientRequest request1 = requests.get(0); + assertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer new-token"); + assertThat(request1.url().toASCIIString()).isEqualTo("https://example.com"); + assertThat(request1.method()).isEqualTo(HttpMethod.GET); + assertThat(getBody(request1)).isEmpty(); + } + + @Test + public void filterWhenClientCredentialsTokenNotExpiredThenUseCurrentToken() { + TestingAuthenticationToken authentication = new TestingAuthenticationToken("test", "this"); + ClientRegistration registration = TestClientRegistrations.clientCredentials().build(); + + this.function = new ServerOAuth2AuthorizedClientExchangeFilterFunction(this.authorizedClientRepository, this.oAuth2AuthorizedClientResolver); + + OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(registration, + "principalName", this.accessToken, null); + ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .build(); + + this.function.filter(request, this.exchange) + .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication)) + .block(); + + verify(this.oAuth2AuthorizedClientResolver, never()).clientCredentials(any(), any(), any()); + verify(this.oAuth2AuthorizedClientResolver, never()).createDefaultedRequest(any(), any(), any()); + + List requests = this.exchange.getRequests(); + assertThat(requests).hasSize(1); + ClientRequest request1 = requests.get(0); + assertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0"); + assertThat(request1.url().toASCIIString()).isEqualTo("https://example.com"); + assertThat(request1.method()).isEqualTo(HttpMethod.GET); + assertThat(getBody(request1)).isEmpty(); + } + @Test public void filterWhenRefreshRequiredThenRefresh() { when(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).thenReturn(Mono.empty()); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionITests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionITests.java new file mode 100644 index 00000000000..7f963e53c81 --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionITests.java @@ -0,0 +1,254 @@ +/* + * Copyright 2002-2019 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 org.springframework.security.oauth2.client.web.reactive.function.client; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; +import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2RefreshToken; +import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.reactive.function.client.WebClient; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.HashSet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; +import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId; + +/** + * @author Joe Grandja + */ +public class ServletOAuth2AuthorizedClientExchangeFilterFunctionITests { + private ClientRegistrationRepository clientRegistrationRepository; + private OAuth2AuthorizedClientRepository authorizedClientRepository; + private ServletOAuth2AuthorizedClientExchangeFilterFunction authorizedClientFilter; + private MockWebServer server; + private String serverUrl; + private WebClient webClient; + private Authentication authentication; + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + @Before + public void setUp() throws Exception { + this.clientRegistrationRepository = mock(ClientRegistrationRepository.class); + final OAuth2AuthorizedClientRepository delegate = new AuthenticatedPrincipalOAuth2AuthorizedClientRepository( + new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository)); + this.authorizedClientRepository = spy(new OAuth2AuthorizedClientRepository() { + @Override + public T loadAuthorizedClient(String clientRegistrationId, Authentication principal, HttpServletRequest request) { + return delegate.loadAuthorizedClient(clientRegistrationId, principal, request); + } + + @Override + public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal, HttpServletRequest request, HttpServletResponse response) { + delegate.saveAuthorizedClient(authorizedClient, principal, request, response); + } + + @Override + public void removeAuthorizedClient(String clientRegistrationId, Authentication principal, HttpServletRequest request, HttpServletResponse response) { + delegate.removeAuthorizedClient(clientRegistrationId, principal, request, response); + } + }); + this.authorizedClientFilter = new ServletOAuth2AuthorizedClientExchangeFilterFunction( + this.clientRegistrationRepository, this.authorizedClientRepository); + this.authorizedClientFilter.afterPropertiesSet(); + this.server = new MockWebServer(); + this.server.start(); + this.serverUrl = this.server.url("/").toString(); + this.webClient = WebClient.builder() + .apply(this.authorizedClientFilter.oauth2Configuration()) + .build(); + this.authentication = new TestingAuthenticationToken("principal", "password"); + SecurityContextHolder.getContext().setAuthentication(this.authentication); + this.request = new MockHttpServletRequest(); + this.response = new MockHttpServletResponse(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(this.request, this.response)); + } + + @After + public void cleanup() throws Exception { + this.authorizedClientFilter.destroy(); + this.server.shutdown(); + SecurityContextHolder.clearContext(); + RequestContextHolder.resetRequestAttributes(); + } + + @Test + public void requestWhenNotAuthorizedThenAuthorizeAndSendRequest() { + String accessTokenResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\",\n" + + " \"scope\": \"read write\"\n" + + "}\n"; + String clientResponse = "{\n" + + " \"attribute1\": \"value1\",\n" + + " \"attribute2\": \"value2\"\n" + + "}\n"; + + this.server.enqueue(jsonResponse(accessTokenResponse)); + this.server.enqueue(jsonResponse(clientResponse)); + + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().tokenUri(this.serverUrl).build(); + when(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId()))).thenReturn(clientRegistration); + + this.webClient + .get() + .uri(this.serverUrl) + .attributes(clientRegistrationId(clientRegistration.getRegistrationId())) + .retrieve() + .bodyToMono(String.class) + .block(); + + assertThat(this.server.getRequestCount()).isEqualTo(2); + + ArgumentCaptor authorizedClientCaptor = ArgumentCaptor.forClass(OAuth2AuthorizedClient.class); + verify(this.authorizedClientRepository).saveAuthorizedClient( + authorizedClientCaptor.capture(), eq(this.authentication), eq(this.request), eq(this.response)); + assertThat(authorizedClientCaptor.getValue().getClientRegistration()).isSameAs(clientRegistration); + } + + @Test + public void requestWhenAuthorizedButExpiredThenRefreshAndSendRequest() { + String accessTokenResponse = "{\n" + + " \"access_token\": \"refreshed-access-token\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + String clientResponse = "{\n" + + " \"attribute1\": \"value1\",\n" + + " \"attribute2\": \"value2\"\n" + + "}\n"; + + this.server.enqueue(jsonResponse(accessTokenResponse)); + this.server.enqueue(jsonResponse(clientResponse)); + + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().tokenUri(this.serverUrl).build(); + when(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId()))).thenReturn(clientRegistration); + + Instant issuedAt = Instant.now().minus(Duration.ofDays(1)); + Instant expiresAt = issuedAt.plus(Duration.ofHours(1)); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, + "expired-access-token", issuedAt, expiresAt, new HashSet<>(Arrays.asList("read", "write"))); + OAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken(); + OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient( + clientRegistration, this.authentication.getName(), accessToken, refreshToken); + doReturn(authorizedClient).when(this.authorizedClientRepository).loadAuthorizedClient( + eq(clientRegistration.getRegistrationId()), eq(this.authentication), eq(this.request)); + + this.webClient + .get() + .uri(this.serverUrl) + .attributes(clientRegistrationId(clientRegistration.getRegistrationId())) + .retrieve() + .bodyToMono(String.class) + .block(); + + assertThat(this.server.getRequestCount()).isEqualTo(2); + + ArgumentCaptor authorizedClientCaptor = ArgumentCaptor.forClass(OAuth2AuthorizedClient.class); + verify(this.authorizedClientRepository).saveAuthorizedClient( + authorizedClientCaptor.capture(), eq(this.authentication), eq(this.request), eq(this.response)); + OAuth2AuthorizedClient refreshedAuthorizedClient = authorizedClientCaptor.getValue(); + assertThat(refreshedAuthorizedClient.getClientRegistration()).isSameAs(clientRegistration); + assertThat(refreshedAuthorizedClient.getAccessToken().getTokenValue()).isEqualTo("refreshed-access-token"); + } + + @Test + public void requestMultipleWhenNoneAuthorizedThenAuthorizeAndSendRequest() { + String accessTokenResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\",\n" + + " \"scope\": \"read write\"\n" + + "}\n"; + String clientResponse = "{\n" + + " \"attribute1\": \"value1\",\n" + + " \"attribute2\": \"value2\"\n" + + "}\n"; + + // Client 1 + this.server.enqueue(jsonResponse(accessTokenResponse)); + this.server.enqueue(jsonResponse(clientResponse)); + + ClientRegistration clientRegistration1 = TestClientRegistrations.clientCredentials() + .registrationId("client-1").tokenUri(this.serverUrl).build(); + when(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration1.getRegistrationId()))).thenReturn(clientRegistration1); + + // Client 2 + this.server.enqueue(jsonResponse(accessTokenResponse)); + this.server.enqueue(jsonResponse(clientResponse)); + + ClientRegistration clientRegistration2 = TestClientRegistrations.clientCredentials() + .registrationId("client-2").tokenUri(this.serverUrl).build(); + when(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration2.getRegistrationId()))).thenReturn(clientRegistration2); + + this.webClient + .get() + .uri(this.serverUrl) + .attributes(clientRegistrationId(clientRegistration1.getRegistrationId())) + .retrieve() + .bodyToMono(String.class) + .flatMap(response -> this.webClient + .get() + .uri(this.serverUrl) + .attributes(clientRegistrationId(clientRegistration2.getRegistrationId())) + .retrieve() + .bodyToMono(String.class)) + .block(); + + assertThat(this.server.getRequestCount()).isEqualTo(4); + + ArgumentCaptor authorizedClientCaptor = ArgumentCaptor.forClass(OAuth2AuthorizedClient.class); + verify(this.authorizedClientRepository, times(2)).saveAuthorizedClient( + authorizedClientCaptor.capture(), eq(this.authentication), eq(this.request), eq(this.response)); + assertThat(authorizedClientCaptor.getAllValues().get(0).getClientRegistration()).isSameAs(clientRegistration1); + assertThat(authorizedClientCaptor.getAllValues().get(1).getClientRegistration()).isSameAs(clientRegistration2); + } + + private MockResponse jsonResponse(String json) { + return new MockResponse() + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(json); + } +} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java index 6b71657f626..8a6dec12955 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java @@ -62,7 +62,10 @@ import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.CoreSubscriber; +import reactor.core.publisher.BaseSubscriber; import reactor.core.publisher.Mono; +import reactor.util.context.Context; import java.net.URI; import java.time.Duration; @@ -78,7 +81,11 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; import static org.springframework.http.HttpMethod.GET; import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.*; @@ -126,9 +133,10 @@ public void setup() { } @After - public void cleanup() { + public void cleanup() throws Exception { SecurityContextHolder.clearContext(); RequestContextHolder.resetRequestAttributes(); + this.function.destroy(); } @Test @@ -207,7 +215,10 @@ public void defaultRequestOAuth2AuthorizedClientWhenRepositoryNullThenOAuth2Auth } @Test - public void defaultRequestOAuth2AuthorizedClientWhenDefaultTrueAndAuthenticationAndClientRegistrationIdNullThenOAuth2AuthorizedClient() { + public void defaultRequestOAuth2AuthorizedClientWhenDefaultTrueAndClientRegistrationIdNullThenOAuth2AuthorizedClient() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response)); this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, this.authorizedClientRepository); this.function.setDefaultOAuth2AuthorizedClient(true); @@ -241,6 +252,9 @@ public void defaultRequestOAuth2AuthorizedClientWhenDefaultFalseAndAuthenticatio @Test public void defaultRequestOAuth2AuthorizedClientWhenAuthenticationAndClientRegistrationIdThenIdIsExplicit() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response)); this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, this.authorizedClientRepository); OAuth2User user = mock(OAuth2User.class); @@ -259,7 +273,11 @@ public void defaultRequestOAuth2AuthorizedClientWhenAuthenticationAndClientRegis } @Test - public void defaultRequestOAuth2AuthorizedClientWhenAuthenticationNullAndClientRegistrationIdThenOAuth2AuthorizedClient() { + public void defaultRequestOAuth2AuthorizedClientWhenClientRegistrationIdThenOAuth2AuthorizedClient() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response)); + SecurityContextHolder.getContext().setAuthentication(this.authentication); this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, this.authorizedClientRepository); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, @@ -273,63 +291,6 @@ public void defaultRequestOAuth2AuthorizedClientWhenAuthenticationNullAndClientR verify(this.authorizedClientRepository).loadAuthorizedClient(eq("id"), any(), any()); } - @Test - public void defaultRequestWhenClientCredentialsThenAuthorizedClient() { - this.registration = TestClientRegistrations.clientCredentials().build(); - this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, - this.authorizedClientRepository); - this.function.setClientCredentialsTokenResponseClient(this.clientCredentialsTokenResponseClient); - when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(this.registration); - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses - .accessTokenResponse().build(); - when(this.clientCredentialsTokenResponseClient.getTokenResponse(any())).thenReturn( - accessTokenResponse); - - clientRegistrationId(this.registration.getRegistrationId()).accept(this.result); - - Map attrs = getDefaultRequestAttributes(); - OAuth2AuthorizedClient authorizedClient = getOAuth2AuthorizedClient(attrs); - - assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken()); - assertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration); - assertThat(authorizedClient.getPrincipalName()).isEqualTo("anonymousUser"); - assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken()); - } - - @Test - public void defaultRequestWhenDefaultClientRegistrationIdThenAuthorizedClient() { - this.registration = TestClientRegistrations.clientCredentials().build(); - this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, - this.authorizedClientRepository); - this.function.setDefaultClientRegistrationId(this.registration.getRegistrationId()); - this.function.setClientCredentialsTokenResponseClient(this.clientCredentialsTokenResponseClient); - when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(this.registration); - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses - .accessTokenResponse().build(); - when(this.clientCredentialsTokenResponseClient.getTokenResponse(any())).thenReturn( - accessTokenResponse); - - Map attrs = getDefaultRequestAttributes(); - OAuth2AuthorizedClient authorizedClient = getOAuth2AuthorizedClient(attrs); - - assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken()); - assertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration); - assertThat(authorizedClient.getPrincipalName()).isEqualTo("anonymousUser"); - assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken()); - } - - @Test - public void defaultRequestWhenClientIdNotFoundThenIllegalArgumentException() { - this.registration = TestClientRegistrations.clientCredentials().build(); - this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, - this.authorizedClientRepository); - - clientRegistrationId(this.registration.getRegistrationId()).accept(this.result); - - assertThatCode(() -> getDefaultRequestAttributes()) - .isInstanceOf(IllegalArgumentException.class); - } - private Map getDefaultRequestAttributes() { this.function.defaultRequest().accept(this.spec); verify(this.spec).attributes(this.attrs.capture()); @@ -477,6 +438,80 @@ public void filterWhenRefreshRequiredThenRefreshAndResponseDoesNotContainRefresh assertThat(getBody(request1)).isEmpty(); } + @Test + public void filterWhenClientCredentialsTokenNotExpiredThenUseCurrentToken() { + this.registration = TestClientRegistrations.clientCredentials().build(); + + this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, + this.authorizedClientRepository); + this.function.setClientCredentialsTokenResponseClient(this.clientCredentialsTokenResponseClient); + + OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, + "principalName", this.accessToken, null); + ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .attributes(authentication(this.authentication)) + .build(); + + this.function.filter(request, this.exchange).block(); + + verify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), eq(this.authentication), any(), any()); + + verify(clientCredentialsTokenResponseClient, never()).getTokenResponse(any()); + + List requests = this.exchange.getRequests(); + assertThat(requests).hasSize(1); + + ClientRequest request1 = requests.get(0); + assertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0"); + assertThat(request1.url().toASCIIString()).isEqualTo("https://example.com"); + assertThat(request1.method()).isEqualTo(HttpMethod.GET); + assertThat(getBody(request1)).isEmpty(); + } + + @Test + public void filterWhenClientCredentialsTokenExpiredThenGetNewToken() { + this.registration = TestClientRegistrations.clientCredentials().build(); + + OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses + .accessTokenResponse().build(); + when(this.clientCredentialsTokenResponseClient.getTokenResponse(any())).thenReturn( + accessTokenResponse); + + Instant issuedAt = Instant.now().minus(Duration.ofDays(1)); + Instant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1)); + + this.accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), + this.accessToken.getTokenValue(), + issuedAt, + accessTokenExpiresAt); + this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, + this.authorizedClientRepository); + this.function.setClientCredentialsTokenResponseClient(this.clientCredentialsTokenResponseClient); + + OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, + "principalName", this.accessToken, null); + ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .attributes(authentication(this.authentication)) + .build(); + + this.function.filter(request, this.exchange).block(); + + verify(this.authorizedClientRepository).saveAuthorizedClient(any(), eq(this.authentication), any(), any()); + + verify(clientCredentialsTokenResponseClient).getTokenResponse(any()); + + List requests = this.exchange.getRequests(); + assertThat(requests).hasSize(1); + + ClientRequest request1 = requests.get(0); + assertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token"); + assertThat(request1.url().toASCIIString()).isEqualTo("https://example.com"); + assertThat(request1.method()).isEqualTo(HttpMethod.GET); + assertThat(getBody(request1)).isEmpty(); + } + @Test public void filterWhenRefreshRequiredAndEmptyReactiveSecurityContextThenSaved() { OAuth2AccessTokenResponse response = OAuth2AccessTokenResponse.withToken("token-1") @@ -685,6 +720,90 @@ public void filterWhenRequestAttributesNotSetAndHooksInitHooksResetThenDefaultsN assertThat(getBody(request)).isEmpty(); } + // gh-7228 + @Test + public void afterPropertiesSetWhenHooksInitAndOutsideWebSecurityContextThenShouldNotThrowException() throws Exception { + this.function.afterPropertiesSet(); // Hooks.onLastOperator() initialized + assertThatCode(() -> Mono.subscriberContext().block()) + .as("RequestContext Hook brakes application outside of web/security context") + .doesNotThrowAnyException(); + } + + @Test + public void createRequestContextSubscriberIfNecessaryWhenOutsideWebSecurityContextThenReturnOriginalSubscriber() throws Exception { + BaseSubscriber originalSubscriber = new BaseSubscriber() {}; + CoreSubscriber resultSubscriber = this.function.createRequestContextSubscriberIfNecessary(originalSubscriber); + assertThat(resultSubscriber).isSameAs(originalSubscriber); + } + + // gh-7228 + @Test + public void createRequestContextSubscriberWhenRequestResponseProvidedThenCreateWithParentContext() throws Exception { + testRequestContextSubscriber(new MockHttpServletRequest(), new MockHttpServletResponse(), null); + } + + // gh-7228 + @Test + public void createRequestContextSubscriberWhenAuthenticationProvidedThenCreateWithParentContext() throws Exception { + testRequestContextSubscriber(null, null, this.authentication); + } + + @Test + public void createRequestContextSubscriberWhenParentContextHasDataHolderThenShouldReuseParentContext() throws Exception { + RequestContextDataHolder testValue = new RequestContextDataHolder(null, null, null); + final Context parentContext = Context.of(RequestContextSubscriber.REQUEST_CONTEXT_DATA_HOLDER, testValue); + BaseSubscriber parent = new BaseSubscriber() { + @Override + public Context currentContext() { + return parentContext; + } + }; + + RequestContextSubscriber requestContextSubscriber = + new RequestContextSubscriber<>(parent, null, null, authentication); + + Context resultContext = requestContextSubscriber.currentContext(); + + assertThat(resultContext) + .describedAs("parent context was replaced") + .isSameAs(parentContext); + } + + private void testRequestContextSubscriber(MockHttpServletRequest servletRequest, + MockHttpServletResponse servletResponse, + Authentication authentication) { + String testKey = "test_key"; + String testValue = "test_value"; + + BaseSubscriber parent = new BaseSubscriber() { + @Override + public Context currentContext() { + return Context.of(testKey, testValue); + } + }; + + RequestContextSubscriber requestContextSubscriber = + new RequestContextSubscriber<>(parent, servletRequest, servletResponse, authentication); + + Context resultContext = requestContextSubscriber.currentContext(); + + assertThat(resultContext) + .describedAs("result context is null") + .isNotNull(); + + assertThat(resultContext.getOrEmpty(testKey)) + .describedAs("context is replaced") + .hasValue(testValue); + + Object dataHolder = resultContext.getOrDefault(RequestContextSubscriber.REQUEST_CONTEXT_DATA_HOLDER, null); + assertThat(dataHolder) + .describedAs("context is not populated with REQUEST_CONTEXT_DATA_HOLDER") + .isNotNull() + .hasFieldOrPropertyWithValue("request", servletRequest) + .hasFieldOrPropertyWithValue("response", servletResponse) + .hasFieldOrPropertyWithValue("authentication", authentication); + } + private static String getBody(ClientRequest request) { final List> messageWriters = new ArrayList<>(); messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder())); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolverTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolverTests.java index 170b57620e9..21f98fcd471 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolverTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolverTests.java @@ -87,4 +87,18 @@ private OAuth2AuthorizationRequest resolve(String path) { ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(path)); return this.resolver.resolve(exchange).block(); } + + @Test + public void resolveWhenForwardedHeadersClientRegistrationFoundThenWorks() { + when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn( + Mono.just(this.registration)); + ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/oauth2/authorization/id").header("X-Forwarded-Host", "evil.com")); + + OAuth2AuthorizationRequest request = this.resolver.resolve(exchange).block(); + + assertThat(request.getAuthorizationRequestUri()).matches("https://example.com/login/oauth/authorize\\?" + + "response_type=code&client_id=client-id&" + + "scope=read:user&state=.*?&" + + "redirect_uri=/login/oauth2/code/registration-id"); + } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilterTests.java index 5727a26fa34..cbc08accd63 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -25,19 +25,28 @@ import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken; import org.springframework.security.oauth2.client.authentication.TestOAuth2AuthorizationCodeAuthenticationTokens; +import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; -import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.util.CollectionUtils; import org.springframework.web.server.handler.DefaultWebFilterChain; import reactor.core.publisher.Mono; -import static org.assertj.core.api.Assertions.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThatCode; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests.request; /** * @author Rob Winch @@ -53,6 +62,9 @@ public class OAuth2AuthorizationCodeGrantWebFilterTests { @Mock private ServerOAuth2AuthorizedClientRepository authorizedClientRepository; + private ServerAuthorizationRequestRepository authorizationRequestRepository = + new WebSessionOAuth2ServerAuthorizationRequestRepository(); + @Before public void setup() { this.filter = new OAuth2AuthorizationCodeGrantWebFilter( @@ -92,7 +104,7 @@ public void filterWhenNotMatchThenAuthenticationManagerNotCalled() { MockServerWebExchange exchange = MockServerWebExchange .from(MockServerHttpRequest.get("/")); DefaultWebFilterChain chain = new DefaultWebFilterChain( - e -> e.getResponse().setComplete()); + e -> e.getResponse().setComplete(), Collections.emptyList()); this.filter.filter(exchange, chain).block(); @@ -101,25 +113,154 @@ public void filterWhenNotMatchThenAuthenticationManagerNotCalled() { @Test public void filterWhenMatchThenAuthorizedClientSaved() { - Mono authentication = Mono - .just(TestOAuth2AuthorizationCodeAuthenticationTokens.unauthenticated()); - OAuth2AuthorizationCodeAuthenticationToken authenticated = TestOAuth2AuthorizationCodeAuthenticationTokens - .authenticated(); - ServerAuthenticationConverter converter = e -> authentication; - this.filter = new OAuth2AuthorizationCodeGrantWebFilter( - this.authenticationManager, converter, this.authorizedClientRepository); - MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest - .get("/authorize/oauth2/code/registration-id")); - DefaultWebFilterChain chain = new DefaultWebFilterChain( - e -> e.getResponse().setComplete()); - when(this.authenticationManager.authenticate(any())).thenReturn(Mono.just( - authenticated)); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + when(this.clientRegistrationRepository.findByRegistrationId(any())) + .thenReturn(Mono.just(clientRegistration)); when(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())) .thenReturn(Mono.empty()); + when(this.authenticationManager.authenticate(any())) + .thenReturn(Mono.just(TestOAuth2AuthorizationCodeAuthenticationTokens.authenticated())); + + MockServerHttpRequest authorizationRequest = + createAuthorizationRequest("/authorization/callback"); + OAuth2AuthorizationRequest oauth2AuthorizationRequest = + createOAuth2AuthorizationRequest(authorizationRequest, clientRegistration); + MockServerHttpRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); + MockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse); + this.authorizationRequestRepository.saveAuthorizationRequest(oauth2AuthorizationRequest, exchange).block(); + DefaultWebFilterChain chain = new DefaultWebFilterChain( + e -> e.getResponse().setComplete(), Collections.emptyList()); this.filter.filter(exchange, chain).block(); verify(this.authorizedClientRepository).saveAuthorizedClient(any(), any(AnonymousAuthenticationToken.class), any()); + } + + // gh-7966 + @Test + public void filterWhenAuthorizationRequestRedirectUriParametersMatchThenProcessed() { + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + when(this.clientRegistrationRepository.findByRegistrationId(any())) + .thenReturn(Mono.just(clientRegistration)); + when(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())) + .thenReturn(Mono.empty()); + when(this.authenticationManager.authenticate(any())) + .thenReturn(Mono.just(TestOAuth2AuthorizationCodeAuthenticationTokens.authenticated())); + + // 1) redirect_uri with query parameters + Map parameters = new LinkedHashMap<>(); + parameters.put("param1", "value1"); + parameters.put("param2", "value2"); + MockServerHttpRequest authorizationRequest = + createAuthorizationRequest("/authorization/callback", parameters); + OAuth2AuthorizationRequest oauth2AuthorizationRequest = + createOAuth2AuthorizationRequest(authorizationRequest, clientRegistration); + MockServerHttpRequest authorizationResponse = createAuthorizationResponse(authorizationRequest); + MockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse); + this.authorizationRequestRepository.saveAuthorizationRequest(oauth2AuthorizationRequest, exchange).block(); + DefaultWebFilterChain chain = new DefaultWebFilterChain( + e -> e.getResponse().setComplete(), Collections.emptyList()); + + this.filter.filter(exchange, chain).block(); + verify(this.authenticationManager, times(1)).authenticate(any()); + + // 2) redirect_uri with query parameters AND authorization response additional parameters + Map additionalParameters = new LinkedHashMap<>(); + additionalParameters.put("auth-param1", "value1"); + additionalParameters.put("auth-param2", "value2"); + authorizationResponse = createAuthorizationResponse(authorizationRequest, additionalParameters); + exchange = MockServerWebExchange.from(authorizationResponse); + this.authorizationRequestRepository.saveAuthorizationRequest(oauth2AuthorizationRequest, exchange).block(); + + this.filter.filter(exchange, chain).block(); + verify(this.authenticationManager, times(2)).authenticate(any()); + } + + // gh-7966 + @Test + public void filterWhenAuthorizationRequestRedirectUriParametersNotMatchThenNotProcessed() { + String requestUri = "/authorization/callback"; + Map parameters = new LinkedHashMap<>(); + parameters.put("param1", "value1"); + parameters.put("param2", "value2"); + MockServerHttpRequest authorizationRequest = + createAuthorizationRequest(requestUri, parameters); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + OAuth2AuthorizationRequest oauth2AuthorizationRequest = + createOAuth2AuthorizationRequest(authorizationRequest, clientRegistration); + + // 1) Parameter value + Map parametersNotMatch = new LinkedHashMap<>(parameters); + parametersNotMatch.put("param2", "value8"); + MockServerHttpRequest authorizationResponse = createAuthorizationResponse( + createAuthorizationRequest(requestUri, parametersNotMatch)); + MockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse); + this.authorizationRequestRepository.saveAuthorizationRequest(oauth2AuthorizationRequest, exchange).block(); + DefaultWebFilterChain chain = new DefaultWebFilterChain( + e -> e.getResponse().setComplete(), Collections.emptyList()); + + this.filter.filter(exchange, chain).block(); + verifyZeroInteractions(this.authenticationManager); + + // 2) Parameter order + parametersNotMatch = new LinkedHashMap<>(); + parametersNotMatch.put("param2", "value2"); + parametersNotMatch.put("param1", "value1"); + authorizationResponse = createAuthorizationResponse( + createAuthorizationRequest(requestUri, parametersNotMatch)); + exchange = MockServerWebExchange.from(authorizationResponse); + this.authorizationRequestRepository.saveAuthorizationRequest(oauth2AuthorizationRequest, exchange).block(); + + this.filter.filter(exchange, chain).block(); + verifyZeroInteractions(this.authenticationManager); + + // 3) Parameter missing + parametersNotMatch = new LinkedHashMap<>(parameters); + parametersNotMatch.remove("param2"); + authorizationResponse = createAuthorizationResponse( + createAuthorizationRequest(requestUri, parametersNotMatch)); + exchange = MockServerWebExchange.from(authorizationResponse); + this.authorizationRequestRepository.saveAuthorizationRequest(oauth2AuthorizationRequest, exchange).block(); + + this.filter.filter(exchange, chain).block(); + verifyZeroInteractions(this.authenticationManager); + } + + private static OAuth2AuthorizationRequest createOAuth2AuthorizationRequest( + MockServerHttpRequest authorizationRequest, ClientRegistration registration) { + Map additionalParameters = new HashMap<>(); + additionalParameters.put(OAuth2ParameterNames.REGISTRATION_ID, registration.getRegistrationId()); + return request() + .additionalParameters(additionalParameters) + .redirectUri(authorizationRequest.getURI().toString()) + .build(); + } + + private static MockServerHttpRequest createAuthorizationRequest(String requestUri) { + return createAuthorizationRequest(requestUri, new LinkedHashMap<>()); + } + + private static MockServerHttpRequest createAuthorizationRequest(String requestUri, Map parameters) { + MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest + .get(requestUri); + if (!CollectionUtils.isEmpty(parameters)) { + parameters.forEach(builder::queryParam); + } + return builder.build(); + } + + private static MockServerHttpRequest createAuthorizationResponse(MockServerHttpRequest authorizationRequest) { + return createAuthorizationResponse(authorizationRequest, new LinkedHashMap<>()); + } + private static MockServerHttpRequest createAuthorizationResponse( + MockServerHttpRequest authorizationRequest, Map additionalParameters) { + MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest + .get(authorizationRequest.getURI().toString()); + builder.queryParam(OAuth2ParameterNames.CODE, "code"); + builder.queryParam(OAuth2ParameterNames.STATE, "state"); + additionalParameters.forEach(builder::queryParam); + builder.cookies(authorizationRequest.getCookies()); + return builder.build(); } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepositoryTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepositoryTests.java index 79d17eabd1b..b4e11c05be4 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepositoryTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -63,7 +63,7 @@ public class WebSessionOAuth2ServerAuthorizationRequestRepositoryTests { .queryParam(OAuth2ParameterNames.STATE, "state")); @Test - public void loadAuthorizatioNRequestWhenNullExchangeThenIllegalArgumentException() { + public void loadAuthorizationRequestWhenNullExchangeThenIllegalArgumentException() { this.exchange = null; assertThatThrownBy(() -> this.repository.loadAuthorizationRequest(this.exchange)) .isInstanceOf(IllegalArgumentException.class); @@ -106,36 +106,6 @@ public void loadAuthorizationRequestWhenSavedThenAuthorizationRequest() { .verifyComplete(); } - @Test - public void multipleSavedAuthorizationRequestAndRedisCookie() { - String oldState = "state0"; - MockServerHttpRequest oldRequest = MockServerHttpRequest.get("/") - .queryParam(OAuth2ParameterNames.STATE, oldState).build(); - - OAuth2AuthorizationRequest oldAuthorizationRequest = OAuth2AuthorizationRequest.authorizationCode() - .authorizationUri("https://example.com/oauth2/authorize") - .clientId("client-id") - .redirectUri("http://localhost/client-1") - .state(oldState) - .build(); - - Map sessionAttrs = spy(new HashMap<>()); - WebSession session = mock(WebSession.class); - when(session.getAttributes()).thenReturn(sessionAttrs); - WebSessionManager sessionManager = e -> Mono.just(session); - - this.exchange = new DefaultServerWebExchange(this.exchange.getRequest(), new MockServerHttpResponse(), sessionManager, - ServerCodecConfigurer.create(), new AcceptHeaderLocaleContextResolver()); - ServerWebExchange oldExchange = new DefaultServerWebExchange(oldRequest, new MockServerHttpResponse(), sessionManager, - ServerCodecConfigurer.create(), new AcceptHeaderLocaleContextResolver()); - - Mono saveAndSave = this.repository.saveAuthorizationRequest(oldAuthorizationRequest, oldExchange) - .then(this.repository.saveAuthorizationRequest(this.authorizationRequest, this.exchange)); - - StepVerifier.create(saveAndSave).verifyComplete(); - verify(sessionAttrs, times(2)).put(any(), any()); - } - @Test public void loadAuthorizationRequestWhenMultipleSavedThenAuthorizationRequest() { String oldState = "state0"; @@ -269,6 +239,44 @@ public void removeAuthorizationRequestWhenMultipleThenOnlyOneRemoved() { .verifyComplete(); } + // gh-7327 + @Test + public void removeAuthorizationRequestWhenMultipleThenRemovedAndSessionAttributeUpdated() { + String oldState = "state0"; + MockServerHttpRequest oldRequest = MockServerHttpRequest.get("/") + .queryParam(OAuth2ParameterNames.STATE, oldState).build(); + + OAuth2AuthorizationRequest oldAuthorizationRequest = OAuth2AuthorizationRequest.authorizationCode() + .authorizationUri("https://example.com/oauth2/authorize") + .clientId("client-id") + .redirectUri("http://localhost/client-1") + .state(oldState) + .build(); + + Map sessionAttrs = spy(new HashMap<>()); + WebSession session = mock(WebSession.class); + when(session.getAttributes()).thenReturn(sessionAttrs); + WebSessionManager sessionManager = e -> Mono.just(session); + + this.exchange = new DefaultServerWebExchange(this.exchange.getRequest(), new MockServerHttpResponse(), sessionManager, + ServerCodecConfigurer.create(), new AcceptHeaderLocaleContextResolver()); + ServerWebExchange oldExchange = new DefaultServerWebExchange(oldRequest, new MockServerHttpResponse(), sessionManager, + ServerCodecConfigurer.create(), new AcceptHeaderLocaleContextResolver()); + + Mono saveAndSaveAndRemove = this.repository.saveAuthorizationRequest(oldAuthorizationRequest, oldExchange) + .then(this.repository.saveAuthorizationRequest(this.authorizationRequest, this.exchange)) + .then(this.repository.removeAuthorizationRequest(this.exchange)); + + StepVerifier.create(saveAndSaveAndRemove) + .expectNext(this.authorizationRequest) + .verifyComplete(); + + StepVerifier.create(this.repository.loadAuthorizationRequest(this.exchange)) + .verifyComplete(); + + verify(sessionAttrs, times(3)).put(any(), any()); + } + private void assertSessionStartedIs(boolean expected) { Mono isStarted = this.exchange.getSession().map(WebSession::isStarted); StepVerifier.create(isStarted) diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java index 75a6718ba3a..115e36260b1 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -57,8 +57,8 @@ public class OAuth2AccessTokenResponseHttpMessageConverter extends AbstractHttpMessageConverter { private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; - private static final ParameterizedTypeReference> PARAMETERIZED_RESPONSE_TYPE = - new ParameterizedTypeReference>() {}; + private static final ParameterizedTypeReference> PARAMETERIZED_RESPONSE_TYPE = + new ParameterizedTypeReference>() {}; private GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); @@ -82,10 +82,16 @@ protected OAuth2AccessTokenResponse readInternal(Class tokenResponseParameters = (Map) this.jsonMessageConverter.read( + Map tokenResponseParameters = (Map) this.jsonMessageConverter.read( PARAMETERIZED_RESPONSE_TYPE.getType(), null, inputMessage); - return this.tokenResponseConverter.convert(tokenResponseParameters); + return this.tokenResponseConverter.convert( + tokenResponseParameters.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().toString()))); } catch (Exception ex) { throw new HttpMessageNotReadableException("An error occurred reading the OAuth 2.0 Access Token Response: " + ex.getMessage(), ex, inputMessage); diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverter.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverter.java index b5f192b0d74..89ad73730e1 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverter.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -35,6 +35,7 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; /** * A {@link HttpMessageConverter} for an {@link OAuth2Error OAuth 2.0 Error}. @@ -47,8 +48,8 @@ public class OAuth2ErrorHttpMessageConverter extends AbstractHttpMessageConverter { private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; - private static final ParameterizedTypeReference> PARAMETERIZED_RESPONSE_TYPE = - new ParameterizedTypeReference>() {}; + private static final ParameterizedTypeReference> PARAMETERIZED_RESPONSE_TYPE = + new ParameterizedTypeReference>() {}; private GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); @@ -70,10 +71,16 @@ protected OAuth2Error readInternal(Class clazz, HttpInput throws IOException, HttpMessageNotReadableException { try { + // gh-8157 + // Parse parameter values as Object in order to handle potential JSON Object and then convert values to String @SuppressWarnings("unchecked") - Map errorParameters = (Map) this.jsonMessageConverter.read( + Map errorParameters = (Map) this.jsonMessageConverter.read( PARAMETERIZED_RESPONSE_TYPE.getType(), null, inputMessage); - return this.errorConverter.convert(errorParameters); + return this.errorConverter.convert( + errorParameters.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> String.valueOf(entry.getValue())))); } catch (Exception ex) { throw new HttpMessageNotReadableException("An error occurred reading the OAuth 2.0 Error: " + ex.getMessage(), ex, inputMessage); diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverterTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverterTests.java index bf74c947303..e96e85d1098 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverterTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -96,6 +96,39 @@ public void readInternalWhenSuccessfulTokenResponseThenReadOAuth2AccessTokenResp } + // gh-6463 + @Test + public void readInternalWhenSuccessfulTokenResponseWithObjectThenReadOAuth2AccessTokenResponse() throws Exception { + String tokenResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": 3600,\n" + + " \"scope\": \"read write\",\n" + + " \"refresh_token\": \"refresh-token-1234\",\n" + + " \"custom_object_1\": {\"name1\": \"value1\"},\n" + + " \"custom_object_2\": [\"value1\", \"value2\"],\n" + + " \"custom_parameter_1\": \"custom-value-1\",\n" + + " \"custom_parameter_2\": \"custom-value-2\"\n" + + "}\n"; + + MockClientHttpResponse response = new MockClientHttpResponse( + tokenResponse.getBytes(), HttpStatus.OK); + + OAuth2AccessTokenResponse accessTokenResponse = this.messageConverter.readInternal( + OAuth2AccessTokenResponse.class, response); + + assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo("access-token-1234"); + assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); + assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBeforeOrEqualTo(Instant.now().plusSeconds(3600)); + assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); + assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234"); + assertThat(accessTokenResponse.getAdditionalParameters()).containsExactly( + entry("custom_object_1", "{name1=value1}"), + entry("custom_object_2", "[value1, value2]"), + entry("custom_parameter_1", "custom-value-1"), + entry("custom_parameter_2", "custom-value-2")); + } + @Test public void readInternalWhenConversionFailsThenThrowHttpMessageNotReadableException() { Converter tokenResponseConverter = mock(Converter.class); diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverterTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverterTests.java index a57b1df1b83..11211aad561 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverterTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -78,6 +78,25 @@ public void readInternalWhenErrorResponseThenReadOAuth2Error() throws Exception assertThat(oauth2Error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6749#section-5.2"); } + // gh-8157 + @Test + public void readInternalWhenErrorResponseWithObjectThenReadOAuth2Error() throws Exception { + String errorResponse = "{\n" + + " \"error\": \"unauthorized_client\",\n" + + " \"error_description\": \"The client is not authorized\",\n" + + " \"error_codes\": [65001],\n" + + " \"error_uri\": \"https://tools.ietf.org/html/rfc6749#section-5.2\"\n" + + "}\n"; + + MockClientHttpResponse response = new MockClientHttpResponse( + errorResponse.getBytes(), HttpStatus.BAD_REQUEST); + + OAuth2Error oauth2Error = this.messageConverter.readInternal(OAuth2Error.class, response); + assertThat(oauth2Error.getErrorCode()).isEqualTo("unauthorized_client"); + assertThat(oauth2Error.getDescription()).isEqualTo("The client is not authorized"); + assertThat(oauth2Error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6749#section-5.2"); + } + @Test public void readInternalWhenConversionFailsThenThrowHttpMessageNotReadableException() { Converter errorConverter = mock(Converter.class); diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java index 26a39a48666..54d4754fea7 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupport.java @@ -20,6 +20,7 @@ import java.net.URL; import java.text.ParseException; import java.time.Instant; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -210,12 +211,13 @@ public final void setRestOperations(RestOperations restOperations) { } private static class RestOperationsResourceRetriever implements ResourceRetriever { + private static final MediaType APPLICATION_JWK_SET_JSON = new MediaType("application", "jwk-set+json"); private RestOperations restOperations = new RestTemplate(); @Override public Resource retrieveResource(URL url) throws IOException { HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8)); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON)); ResponseEntity response; try { diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupportTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupportTests.java index 5307471f283..ca1e9d75618 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupportTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderJwkSupportTests.java @@ -17,6 +17,7 @@ import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Map; import com.nimbusds.jose.JWSAlgorithm; @@ -31,16 +32,21 @@ import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.core.convert.converter.Converter; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; import org.springframework.security.oauth2.jose.jws.JwsAlgorithms; +import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -76,6 +82,8 @@ public class NimbusJwtDecoderJwkSupportTests { private static final String MALFORMED_JWT = "eyJhbGciOiJSUzI1NiJ9.eyJuYmYiOnt9LCJleHAiOjQ2ODQyMjUwODd9.guoQvujdWvd3xw7FYQEn4D6-gzM_WqFvXdmvAUNSLbxG7fv2_LLCNujPdrBHJoYPbOwS1BGNxIKQWS1tylvqzmr1RohQ-RZ2iAM1HYQzboUlkoMkcd8ENM__ELqho8aNYBfqwkNdUOyBFoy7Syu_w2SoJADw2RTjnesKO6CVVa05bW118pDS4xWxqC4s7fnBjmZoTn4uQ-Kt9YSQZQk8YQxkJSiyanozzgyfgXULA6mPu1pTNU3FVFaK1i1av_xtH_zAPgb647ZeaNe4nahgqC5h8nhOlm8W2dndXbwAt29nd2ZWBsru_QwZz83XSKLhTPFz-mPBByZZDsyBbIHf9A"; private static final String UNSIGNED_JWT = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOi0yMDMzMjI0OTcsImp0aSI6IjEyMyIsInR5cCI6IkpXVCJ9."; + private static final MediaType APPLICATION_JWK_SET_JSON = new MediaType("application", "jwk-set+json"); + private NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(JWK_SET_URL, JWS_ALGORITHM); @Test @@ -256,4 +264,19 @@ public void setClaimSetConverterWhenIsNullThenThrowsIllegalArgumentException() { assertThatCode(() -> jwtDecoder.setClaimSetConverter(null)) .isInstanceOf(IllegalArgumentException.class); } + + // gh-7290 + @Test + public void decodeWhenJwkSetRequestedThenAcceptHeaderJsonAndJwkSetJson() { + RestOperations restOperations = mock(RestOperations.class); + when(restOperations.exchange(any(RequestEntity.class), eq(String.class))) + .thenReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK)); + NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(JWK_SET_URL); + jwtDecoder.setRestOperations(restOperations); + jwtDecoder.decode(SIGNED_JWT); + ArgumentCaptor requestEntityCaptor = ArgumentCaptor.forClass(RequestEntity.class); + verify(restOperations).exchange(requestEntityCaptor.capture(), eq(String.class)); + List acceptHeader = requestEntityCaptor.getValue().getHeaders().getAccept(); + assertThat(acceptHeader).contains(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON); + } } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java index 068077aa2cb..8908360f196 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java @@ -48,8 +48,14 @@ public class ServerBearerTokenAuthenticationConverter private boolean allowUriQueryParameter = false; public Mono convert(ServerWebExchange exchange) { - return Mono.justOrEmpty(this.token(exchange.getRequest())) - .map(BearerTokenAuthenticationToken::new); + return Mono.justOrEmpty(token(exchange.getRequest())) + .map(token -> { + if (token.isEmpty()) { + BearerTokenError error = invalidTokenError(); + throw new OAuth2AuthenticationException(error); + } + return new BearerTokenAuthenticationToken(token); + }); } private String token(ServerHttpRequest request) { @@ -88,11 +94,8 @@ private static String resolveFromAuthorizationHeader(HttpHeaders headers) { if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer")) { Matcher matcher = authorizationPattern.matcher(authorization); - if ( !matcher.matches() ) { - BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, - HttpStatus.BAD_REQUEST, - "Bearer token is malformed", - "https://tools.ietf.org/html/rfc6750#section-3.1"); + if (!matcher.matches() ) { + BearerTokenError error = invalidTokenError(); throw new OAuth2AuthenticationException(error); } @@ -101,6 +104,13 @@ private static String resolveFromAuthorizationHeader(HttpHeaders headers) { return null; } + private static BearerTokenError invalidTokenError() { + return new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, + HttpStatus.UNAUTHORIZED, + "Bearer token is malformed", + "https://tools.ietf.org/html/rfc6750#section-3.1"); + } + private boolean isParameterTokenSupportedForRequest(ServerHttpRequest request) { return this.allowUriQueryParameter && HttpMethod.GET.equals(request.getMethod()); } diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverterTests.java index 5a08ee85c9f..a1a88255aac 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverterTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverterTests.java @@ -19,15 +19,19 @@ import org.junit.Before; import org.junit.Test; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.BearerTokenError; +import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes; import java.util.Base64; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.catchThrowableOfType; /** * @author Rob Winch @@ -52,6 +56,21 @@ public void resolveWhenValidHeaderIsPresentThenTokenIsResolved() { assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN); } + // gh-7011 + @Test + public void resolveWhenValidHeaderIsEmptyStringThenTokenIsResolved() { + MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest + .get("/") + .header(HttpHeaders.AUTHORIZATION, "Bearer "); + + OAuth2AuthenticationException expected = catchThrowableOfType(() -> convertToToken(request), + OAuth2AuthenticationException.class); + BearerTokenError error = (BearerTokenError) expected.getError(); + assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN); + assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1"); + assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED); + } + @Test public void resolveWhenNoHeaderIsPresentThenTokenIsNotResolved() { MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest @@ -114,6 +133,23 @@ public void resolveWhenQueryParameterIsPresentAndSupportedThenTokenIsResolved() assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN); } + // gh-7011 + @Test + public void resolveWhenQueryParameterIsEmptyAndSupportedThenOAuth2AuthenticationException() { + this.converter.setAllowUriQueryParameter(true); + + MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest + .get("/") + .queryParam("access_token", ""); + + OAuth2AuthenticationException expected = catchThrowableOfType(() -> convertToToken(request), + OAuth2AuthenticationException.class); + BearerTokenError error = (BearerTokenError) expected.getError(); + assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN); + assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1"); + assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED); + } + @Test public void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() { MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest diff --git a/samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java b/samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java index d176bd694ca..022aa7dd1ee 100644 --- a/samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java +++ b/samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -255,42 +255,6 @@ public void requestAuthorizationCodeGrantWhenInvalidStateParamThenDisplayLoginPa assertThat(errorElement.asText()).contains("authorization_request_not_found"); } - @Test - public void requestAuthorizationCodeGrantWhenInvalidRedirectUriThenDisplayLoginPageWithError() throws Exception { - HtmlPage page = this.webClient.getPage("/"); - URL loginPageUrl = page.getBaseURL(); - URL loginErrorPageUrl = new URL(loginPageUrl.toString() + "?error"); - - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); - - HtmlAnchor clientAnchorElement = this.getClientAnchorElement(page, clientRegistration); - assertThat(clientAnchorElement).isNotNull(); - - WebResponse response = this.followLinkDisableRedirects(clientAnchorElement); - - UriComponents authorizeRequestUriComponents = UriComponentsBuilder.fromUri( - URI.create(response.getResponseHeaderValue("Location"))).build(); - - Map params = authorizeRequestUriComponents.getQueryParams().toSingleValueMap(); - String code = "auth-code"; - String state = URLDecoder.decode(params.get(OAuth2ParameterNames.STATE), "UTF-8"); - String redirectUri = URLDecoder.decode(params.get(OAuth2ParameterNames.REDIRECT_URI), "UTF-8"); - redirectUri += "-invalid"; - - String authorizationResponseUri = - UriComponentsBuilder.fromHttpUrl(redirectUri) - .queryParam(OAuth2ParameterNames.CODE, code) - .queryParam(OAuth2ParameterNames.STATE, state) - .build().encode().toUriString(); - - page = this.webClient.getPage(new URL(authorizationResponseUri)); - assertThat(page.getBaseURL()).isEqualTo(loginErrorPageUrl); - - HtmlElement errorElement = page.getBody().getFirstByXPath("div"); - assertThat(errorElement).isNotNull(); - assertThat(errorElement.asText()).contains("invalid_redirect_uri_parameter"); - } - private void assertLoginPage(HtmlPage page) throws Exception { assertThat(page.getTitleText()).isEqualTo("Please sign in"); diff --git a/samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy b/samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy index b5b8f2feb35..8be5860fb20 100644 --- a/samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy +++ b/samples/xml/cas/cassample/src/integration-test/groovy/org/springframework/security/samples/cas/JettyCasService.groovy @@ -66,7 +66,7 @@ class JettyCasService extends Server { String password = System.getProperty('javax.net.ssl.trustStorePassword','password') - SslContextFactory sslContextFactory = new SslContextFactory(); + SslContextFactory sslContextFactory = new SslContextFactory.Server(); sslContextFactory.setKeyStorePath(getTrustStore()); sslContextFactory.setKeyStorePassword(password); sslContextFactory.setKeyManagerPassword(password); diff --git a/scripts/release/release-notes-sections.yml b/scripts/release/release-notes-sections.yml new file mode 100644 index 00000000000..574c8cfbec6 --- /dev/null +++ b/scripts/release/release-notes-sections.yml @@ -0,0 +1,14 @@ +releasenotes: + sections: + - title: "New Features" + emoji: ":star:" + labels: ["enhancement"] + - title: "Bug Fixes" + emoji: ":beetle:" + labels: ["bug", "regression"] + - title: "Dependency Upgrades" + emoji: ":hammer:" + labels: ["dependency-upgrade"] + - title: "Non-passive" + emoji: ":rewind:" + labels: ["breaks-passivity"] diff --git a/scripts/release/wait-for-done b/scripts/release/wait-for-done new file mode 100755 index 00000000000..7bd50205ed0 --- /dev/null +++ b/scripts/release/wait-for-done @@ -0,0 +1,4 @@ +#!/bin/bash + +VERSION=$1 +until http -h --check-status --ignore-stdin https://repo1.maven.org/maven2/org/springframework/security/spring-security-core/$VERSION/; do sleep 10; clear; done; spd-say "It is now uploaded" diff --git a/test/spring-security-test.gradle b/test/spring-security-test.gradle index 35174aa77cd..6474f133054 100644 --- a/test/spring-security-test.gradle +++ b/test/spring-security-test.gradle @@ -12,6 +12,7 @@ dependencies { provided 'javax.servlet:javax.servlet-api' + testCompile project(path : ':spring-security-config', configuration : 'tests') testCompile 'com.fasterxml.jackson.core:jackson-databind' testCompile 'io.projectreactor:reactor-test' testCompile 'javax.xml.bind:jaxb-api' diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java b/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java index 8c11be54dbf..d2cc86d9660 100644 --- a/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java +++ b/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java @@ -43,7 +43,7 @@ public SecurityContext createSecurityContext(WithMockUser withUser) { .username() : withUser.value(); if (username == null) { throw new IllegalArgumentException(withUser - + " cannot have null username on both username and value properites"); + + " cannot have null username on both username and value properties"); } List grantedAuthorities = new ArrayList<>(); diff --git a/test/src/main/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurer.java b/test/src/main/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurer.java index 909a92fee9e..f654fcd7558 100644 --- a/test/src/main/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurer.java +++ b/test/src/main/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurer.java @@ -16,6 +16,11 @@ package org.springframework.security.test.web.servlet.setup; import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import org.springframework.security.config.BeanIds; import org.springframework.test.web.servlet.request.RequestPostProcessor; @@ -23,6 +28,8 @@ import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter; import org.springframework.web.context.WebApplicationContext; +import java.io.IOException; + import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.testSecurityContext; /** @@ -34,12 +41,13 @@ * @since 4.0 */ final class SecurityMockMvcConfigurer extends MockMvcConfigurerAdapter { - private Filter springSecurityFilterChain; + private final DelegateFilter delegateFilter; /** * Creates a new instance */ SecurityMockMvcConfigurer() { + this.delegateFilter = new DelegateFilter(); } /** @@ -47,30 +55,101 @@ final class SecurityMockMvcConfigurer extends MockMvcConfigurerAdapter { * @param springSecurityFilterChain the {@link javax.servlet.Filter} to use */ SecurityMockMvcConfigurer(Filter springSecurityFilterChain) { - this.springSecurityFilterChain = springSecurityFilterChain; + this.delegateFilter = new DelegateFilter(springSecurityFilterChain); + } + + @Override + public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { + builder.addFilters(this.delegateFilter); } @Override public RequestPostProcessor beforeMockMvcCreated( ConfigurableMockMvcBuilder builder, WebApplicationContext context) { String securityBeanId = BeanIds.SPRING_SECURITY_FILTER_CHAIN; - if (this.springSecurityFilterChain == null + if (getSpringSecurityFilterChain() == null && context.containsBean(securityBeanId)) { - this.springSecurityFilterChain = context.getBean(securityBeanId, - Filter.class); + setSpringSecurityFitlerChain(context.getBean(securityBeanId, + Filter.class)); } - if (this.springSecurityFilterChain == null) { + if (getSpringSecurityFilterChain() == null) { throw new IllegalStateException( "springSecurityFilterChain cannot be null. Ensure a Bean with the name " + securityBeanId + " implementing Filter is present or inject the Filter to be used."); } - builder.addFilters(this.springSecurityFilterChain); + // This is used by other test support to obtain the FilterChainProxy context.getServletContext().setAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN, - this.springSecurityFilterChain); + getSpringSecurityFilterChain()); return testSecurityContext(); } + + private void setSpringSecurityFitlerChain(Filter filter) { + this.delegateFilter.setDelegate(filter); + } + + private Filter getSpringSecurityFilterChain() { + return this.delegateFilter.delegate; + } + + /** + * Allows adding in {@link #afterConfigurerAdded(ConfigurableMockMvcBuilder)} to preserve Filter order and then + * lazily set the delegate in {@link #beforeMockMvcCreated(ConfigurableMockMvcBuilder, WebApplicationContext)}. + * + * {@link org.springframework.web.filter.DelegatingFilterProxy} is not used because it is not easy to lazily set + * the delegate or get the delegate which is necessary for the test infrastructure. + */ + static class DelegateFilter implements Filter { + + private Filter delegate; + + DelegateFilter() { + } + + DelegateFilter(Filter delegate) { + this.delegate = delegate; + } + + void setDelegate(Filter delegate) { + this.delegate = delegate; + } + + Filter getDelegate() { + return this.delegate; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + this.delegate.init(filterConfig); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + this.delegate.doFilter(request, response, chain); + } + + @Override + public void destroy() { + this.delegate.destroy(); + } + + @Override + public int hashCode() { + return this.delegate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this.delegate.equals(obj); + } + + @Override + public String toString() { + return this.delegate.toString(); + } + } } diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java index 77ba524777e..40289bd9b4b 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java @@ -44,7 +44,7 @@ @RunWith(PowerMockRunner.class) @PrepareOnlyThisForTest(WebTestUtils.class) -@PowerMockIgnore("javax.security.auth.*") +@PowerMockIgnore({"javax.security.auth.*", "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*"}) public class SecurityMockMvcRequestPostProcessorsAuthenticationTests { @Captor private ArgumentCaptor contextCaptor; diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java index 4242971683c..005599649e2 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java @@ -32,6 +32,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.mock.web.MockHttpServletRequest; @@ -42,6 +43,7 @@ @RunWith(PowerMockRunner.class) @PrepareOnlyThisForTest(WebTestUtils.class) +@PowerMockIgnore({"javax.security.auth.*", "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*"}) public class SecurityMockMvcRequestPostProcessorsSecurityContextTests { @Captor private ArgumentCaptor contextCaptor; diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java index b87d3efd000..65ed0f969b4 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java @@ -29,6 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.mock.web.MockHttpServletRequest; @@ -39,6 +40,7 @@ @RunWith(PowerMockRunner.class) @PrepareOnlyThisForTest(WebTestUtils.class) +@PowerMockIgnore({"javax.security.auth.*", "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*"}) public class SecurityMockMvcRequestPostProcessorsTestSecurityContextTests { @Mock private SecurityContext context; @@ -81,4 +83,4 @@ private void mockWebTestUtils() { spy(WebTestUtils.class); when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository); } -} \ No newline at end of file +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java index 0b1a391b630..20bd7caed7f 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java @@ -32,6 +32,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.mock.web.MockHttpServletRequest; @@ -44,6 +45,7 @@ @RunWith(PowerMockRunner.class) @PrepareOnlyThisForTest(WebTestUtils.class) +@PowerMockIgnore({"javax.security.auth.*", "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*"}) public class SecurityMockMvcRequestPostProcessorsUserDetailsTests { @Captor private ArgumentCaptor contextCaptor; diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java index f29f3fb78a3..23bfa8715fa 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java @@ -35,6 +35,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.mock.web.MockHttpServletRequest; @@ -47,6 +48,7 @@ @RunWith(PowerMockRunner.class) @PrepareOnlyThisForTest(WebTestUtils.class) +@PowerMockIgnore({"javax.security.auth.*", "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*"}) public class SecurityMockMvcRequestPostProcessorsUserTests { @Captor private ArgumentCaptor contextCaptor; diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurerTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurerTests.java index 41df811a1fe..eb4c41567f5 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurerTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurerTests.java @@ -15,21 +15,24 @@ */ package org.springframework.security.test.web.servlet.setup; -import javax.servlet.Filter; -import javax.servlet.ServletContext; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; - import org.springframework.security.config.BeanIds; import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; import org.springframework.web.context.WebApplicationContext; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import javax.servlet.Filter; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,9 +59,10 @@ public void beforeMockMvcCreatedOverrideBean() throws Exception { returnFilterBean(); SecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer(this.filter); + configurer.afterConfigurerAdded(this.builder); configurer.beforeMockMvcCreated(this.builder, this.context); - verify(this.builder).addFilters(this.filter); + assertFilterAdded(this.filter); verify(this.servletContext).setAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN, this.filter); } @@ -68,27 +72,37 @@ public void beforeMockMvcCreatedBean() throws Exception { returnFilterBean(); SecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer(); + configurer.afterConfigurerAdded(this.builder); configurer.beforeMockMvcCreated(this.builder, this.context); - verify(this.builder).addFilters(this.beanFilter); + assertFilterAdded(this.beanFilter); } @Test public void beforeMockMvcCreatedNoBean() throws Exception { SecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer(this.filter); + configurer.afterConfigurerAdded(this.builder); configurer.beforeMockMvcCreated(this.builder, this.context); - verify(this.builder).addFilters(this.filter); + assertFilterAdded(this.filter); } @Test(expected = IllegalStateException.class) public void beforeMockMvcCreatedNoFilter() throws Exception { SecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer(); + configurer.afterConfigurerAdded(this.builder); configurer.beforeMockMvcCreated(this.builder, this.context); } + private void assertFilterAdded(Filter filter) throws IOException, ServletException { + ArgumentCaptor filterArg = ArgumentCaptor.forClass( + SecurityMockMvcConfigurer.DelegateFilter.class); + verify(this.builder).addFilters(filterArg.capture()); + assertThat(filterArg.getValue().getDelegate()).isEqualTo(filter); + } + private void returnFilterBean() { when(this.context.containsBean(anyString())).thenReturn(true); when(this.context.getBean(anyString(), eq(Filter.class))) diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurersTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurersTests.java new file mode 100644 index 00000000000..162114d6ee8 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurersTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2019 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 org.springframework.security.test.web.servlet.setup; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.users.AuthenticationTestConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import javax.servlet.Filter; + +import static org.mockito.Mockito.mock; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Rob Winch + */ +@RunWith(SpringRunner.class) +@WebAppConfiguration +public class SecurityMockMvcConfigurersTests { + @Autowired + WebApplicationContext wac; + + Filter noOpFilter = mock(Filter.class); + + /** + * Since noOpFilter is first does not continue the chain, security will not be invoked and the status should be OK + * + * @throws Exception + */ + @Test + public void applySpringSecurityWhenAddFilterFirstThenFilterFirst() throws Exception { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac) + .addFilters(this.noOpFilter) + .apply(springSecurity()) + .build(); + + mockMvc.perform(get("/")) + .andExpect(status().isOk()); + } + + /** + * Since noOpFilter is second security will be invoked and the status will be not OK. We know this because if noOpFilter + * were first security would not be invoked sincet noOpFilter does not continue the FilterChain + * @throws Exception + */ + @Test + public void applySpringSecurityWhenAddFilterSecondThenSecurityFirst() throws Exception { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac) + .apply(springSecurity()) + .addFilters(this.noOpFilter) + .build(); + + mockMvc.perform(get("/")) + .andExpect(status().is4xxClientError()); + } + + @Configuration + @EnableWebMvc + @EnableWebSecurity + @Import(AuthenticationTestConfiguration.class) + static class Config {} +} diff --git a/web/src/main/java/org/springframework/security/web/FilterChainProxy.java b/web/src/main/java/org/springframework/security/web/FilterChainProxy.java index b4157c8921c..d0d348b7517 100644 --- a/web/src/main/java/org/springframework/security/web/FilterChainProxy.java +++ b/web/src/main/java/org/springframework/security/web/FilterChainProxy.java @@ -60,7 +60,7 @@ * requests which match the pattern. An example configuration might look like this: * *
- *  <bean id="myfilterChainProxy" class="org.springframework.security.util.FilterChainProxy">
+ *  <bean id="myfilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
  *      <constructor-arg>
  *          <util:list>
  *              <security:filter-chain pattern="/do/not/filter*" filters="none"/>
diff --git a/web/src/main/java/org/springframework/security/web/FilterInvocation.java b/web/src/main/java/org/springframework/security/web/FilterInvocation.java
index 5e762118d8d..e85ce6fe390 100644
--- a/web/src/main/java/org/springframework/security/web/FilterInvocation.java
+++ b/web/src/main/java/org/springframework/security/web/FilterInvocation.java
@@ -228,10 +228,15 @@ public String getQueryString() {
 	public void setQueryString(String queryString) {
 		this.queryString = queryString;
 	}
+
+	@Override
+	public String getServerName() {
+		return null;
+	}
 }
 
 final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {
 	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 		throw new UnsupportedOperationException(method + " is not supported");
 	}
-}
\ No newline at end of file
+}
diff --git a/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java b/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java
index 68df38f9025..2e7401207c6 100644
--- a/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java
+++ b/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -15,6 +15,7 @@
  */
 package org.springframework.security.web.authentication.session;
 
+import java.util.Comparator;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
@@ -45,8 +46,9 @@
  * 

*

* If a user has reached the maximum number of permitted sessions, the behaviour depends - * on the exceptionIfMaxExceeded property. The default behaviour is to expired - * the least recently used session, which will be invalidated by the + * on the exceptionIfMaxExceeded property. The default behaviour is to expire + * any sessions that exceed the maximum number of permitted sessions, starting with the + * least recently used sessions. The expired sessions will be invalidated by the * {@link ConcurrentSessionFilter} if accessed again. If exceptionIfMaxExceeded * is set to true, however, the user will be prevented from starting a new * authenticated session. @@ -156,18 +158,13 @@ protected void allowableSessionsExceeded(List sessions, "Maximum sessions of {0} for this principal exceeded")); } - // Determine least recently used session, and mark it for invalidation - SessionInformation leastRecentlyUsed = null; - - for (SessionInformation session : sessions) { - if ((leastRecentlyUsed == null) - || session.getLastRequest() - .before(leastRecentlyUsed.getLastRequest())) { - leastRecentlyUsed = session; - } + // Determine least recently used sessions, and mark them for invalidation + sessions.sort(Comparator.comparing(SessionInformation::getLastRequest)); + int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1; + List sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy); + for (SessionInformation session: sessionsToBeExpired) { + session.expireNow(); } - - leastRecentlyUsed.expireNow(); } /** diff --git a/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java b/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java index c460f5a5220..e7863b90acf 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java @@ -563,6 +563,6 @@ public void setSwitchAuthorityRole(String switchAuthorityRole) { } private static RequestMatcher createMatcher(String pattern) { - return new AntPathRequestMatcher(pattern, null, true, new UrlPathHelper()); + return new AntPathRequestMatcher(pattern, "POST", true, new UrlPathHelper()); } } diff --git a/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java b/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java index 643b1e7b588..6e7afc73e0a 100644 --- a/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java +++ b/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2020 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,16 +16,17 @@ package org.springframework.security.web.firewall; -import org.springframework.http.HttpMethod; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Predicate; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.HttpMethod; /** *

@@ -66,10 +67,15 @@ * Rejects URLs that contain a URL encoded percent. See * {@link #setAllowUrlEncodedPercent(boolean)} * + *

  • + * Rejects hosts that are not allowed. See + * {@link #setAllowedHostnames(Predicate)} + *
  • * * * @see DefaultHttpFirewall * @author Rob Winch + * @author Eddú Meléndez * @since 4.2.4 */ public class StrictHttpFirewall implements HttpFirewall { @@ -96,6 +102,8 @@ public class StrictHttpFirewall implements HttpFirewall { private Set allowedHttpMethods = createDefaultAllowedHttpMethods(); + private Predicate allowedHostnames = hostname -> true; + public StrictHttpFirewall() { urlBlacklistsAddAll(FORBIDDEN_SEMICOLON); urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH); @@ -277,6 +285,21 @@ public void setAllowUrlEncodedPercent(boolean allowUrlEncodedPercent) { } } + /** + *

    + * Determines which hostnames should be allowed. The default is to allow any hostname. + *

    + * + * @param allowedHostnames the predicate for testing hostnames + * @since 5.1.11 + */ + public void setAllowedHostnames(Predicate allowedHostnames) { + if (allowedHostnames == null) { + throw new IllegalArgumentException("allowedHostnames cannot be null"); + } + this.allowedHostnames = allowedHostnames; + } + private void urlBlacklistsAddAll(Collection values) { this.encodedUrlBlacklist.addAll(values); this.decodedUrlBlacklist.addAll(values); @@ -291,6 +314,7 @@ private void urlBlacklistsRemoveAll(Collection values) { public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException { rejectForbiddenHttpMethod(request); rejectedBlacklistedUrls(request); + rejectedUntrustedHosts(request); if (!isNormalized(request)) { throw new RequestRejectedException("The request was rejected because the URL was not normalized."); @@ -332,6 +356,13 @@ private void rejectedBlacklistedUrls(HttpServletRequest request) { } } + private void rejectedUntrustedHosts(HttpServletRequest request) { + String serverName = request.getServerName(); + if (serverName != null && !this.allowedHostnames.test(serverName)) { + throw new RequestRejectedException("The request was rejected because the domain " + serverName + " is untrusted."); + } + } + @Override public HttpServletResponse getFirewalledResponse(HttpServletResponse response) { return new FirewalledResponse(response); diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java b/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java index 2fafd24becf..019442b5af3 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java @@ -19,12 +19,11 @@ import org.springframework.security.core.Authentication; import org.springframework.security.web.server.WebFilterExchange; import org.springframework.util.Assert; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Delegates to a collection of {@link ServerAuthenticationSuccessHandler} implementations. @@ -43,7 +42,8 @@ public DelegatingServerAuthenticationSuccessHandler(ServerAuthenticationSuccessH @Override public Mono onAuthenticationSuccess(WebFilterExchange exchange, Authentication authentication) { - Stream> results = this.delegates.stream().map(delegate -> delegate.onAuthenticationSuccess(exchange, authentication)); - return Mono.when(results.collect(Collectors.toList())); + return Flux.fromIterable(this.delegates) + .concatMap(delegate -> delegate.onAuthenticationSuccess(exchange, authentication)) + .then(); } } diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java b/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java index 197f3565357..9b6567227d1 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java @@ -20,9 +20,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.security.core.Authentication; @@ -50,10 +49,8 @@ public DelegatingServerLogoutHandler(Collection delegates) @Override public Mono logout(WebFilterExchange exchange, Authentication authentication) { - return Mono.when(this.delegates.stream() - .filter(Objects::nonNull) - .map(delegate -> delegate.logout(exchange, authentication)) - .collect(Collectors.toList()) - ); + return Flux.fromIterable(this.delegates) + .concatMap(delegate -> delegate.logout(exchange, authentication)) + .then(); } } diff --git a/web/src/main/java/org/springframework/security/web/server/csrf/CsrfWebFilter.java b/web/src/main/java/org/springframework/security/web/server/csrf/CsrfWebFilter.java index a74fc3384d3..d434ebe673f 100644 --- a/web/src/main/java/org/springframework/security/web/server/csrf/CsrfWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/csrf/CsrfWebFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 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. @@ -55,6 +55,7 @@ *

    * * @author Rob Winch + * @author Parikshit Dutta * @since 5.0 */ public class CsrfWebFilter implements WebFilter { @@ -136,7 +137,7 @@ private static class DefaultRequireCsrfProtectionMatcher implements ServerWebExc @Override public Mono matches(ServerWebExchange exchange) { return Mono.just(exchange.getRequest()) - .map(r -> r.getMethod()) + .flatMap(r -> Mono.justOrEmpty(r.getMethod())) .filter(m -> ALLOWED_METHODS.contains(m)) .flatMap(m -> MatchResult.notMatch()) .switchIfEmpty(MatchResult.match()); diff --git a/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java b/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java index bb28c4b2c8c..f5fcf0f9d62 100644 --- a/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java +++ b/web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java @@ -15,15 +15,13 @@ */ package org.springframework.security.web.server.header; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.springframework.web.server.ServerWebExchange; - +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.Arrays; +import java.util.List; + /** * Combines multiple {@link ServerHttpHeadersWriter} instances into a single instance. * @@ -43,8 +41,9 @@ public CompositeServerHttpHeadersWriter(List writers) { @Override public Mono writeHttpHeaders(ServerWebExchange exchange) { - Stream> results = writers.stream().map( writer -> writer.writeHttpHeaders(exchange)); - return Mono.when(results.collect(Collectors.toList())); + return Flux.fromIterable(this.writers) + .concatMap(w -> w.writeHttpHeaders(exchange)) + .then(); } } diff --git a/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java b/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java index 2c760654c21..5daa3f00127 100644 --- a/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java @@ -106,7 +106,7 @@ private byte[] createPage(ServerWebExchange exchange, String csrfTokenHtmlInput) + " \n" + "
    \n" + formLogin(queryParams, csrfTokenHtmlInput) - + oauth2LoginLinks(contextPath, this.oauth2AuthenticationUrlToClientName) + + oauth2LoginLinks(queryParams, contextPath, this.oauth2AuthenticationUrlToClientName) + "
    \n" + " \n" + ""; @@ -135,12 +135,14 @@ private String formLogin(MultiValueMap queryParams, String csrfT + " \n"; } - private static String oauth2LoginLinks(String contextPath, Map oauth2AuthenticationUrlToClientName) { + private static String oauth2LoginLinks(MultiValueMap queryParams, String contextPath, Map oauth2AuthenticationUrlToClientName) { if (oauth2AuthenticationUrlToClientName.isEmpty()) { return ""; } + boolean isError = queryParams.containsKey("error"); StringBuilder sb = new StringBuilder(); sb.append("
    "); + sb.append(createError(isError)); sb.append("\n"); for (Map.Entry clientAuthenticationUrlToClientName : oauth2AuthenticationUrlToClientName.entrySet()) { sb.append("
    "); diff --git a/web/src/main/java/org/springframework/security/web/servletapi/HttpServlet3RequestFactory.java b/web/src/main/java/org/springframework/security/web/servletapi/HttpServlet3RequestFactory.java index d76b289e5ad..46a124471b4 100644 --- a/web/src/main/java/org/springframework/security/web/servletapi/HttpServlet3RequestFactory.java +++ b/web/src/main/java/org/springframework/security/web/servletapi/HttpServlet3RequestFactory.java @@ -42,7 +42,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.logout.CompositeLogoutHandler; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -82,7 +81,7 @@ final class HttpServlet3RequestFactory implements HttpServletRequestFactory { private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); private AuthenticationEntryPoint authenticationEntryPoint; private AuthenticationManager authenticationManager; - private LogoutHandler logoutHandler; + private List logoutHandlers; HttpServlet3RequestFactory(String rolePrefix) { this.rolePrefix = rolePrefix; @@ -146,7 +145,7 @@ public void setAuthenticationManager(AuthenticationManager authenticationManager * {@link HttpServletRequest#logout()}. */ public void setLogoutHandlers(List logoutHandlers) { - this.logoutHandler = CollectionUtils.isEmpty(logoutHandlers) ? null : new CompositeLogoutHandler(logoutHandlers); + this.logoutHandlers = logoutHandlers; } /** @@ -246,8 +245,8 @@ public void login(String username, String password) throws ServletException { @Override public void logout() throws ServletException { - LogoutHandler handler = HttpServlet3RequestFactory.this.logoutHandler; - if (handler == null) { + List handlers = HttpServlet3RequestFactory.this.logoutHandlers; + if (CollectionUtils.isEmpty(handlers)) { HttpServlet3RequestFactory.this.logger.debug( "logoutHandlers is null, so allowing original HttpServletRequest to handle logout"); super.logout(); @@ -255,7 +254,9 @@ public void logout() throws ServletException { } Authentication authentication = SecurityContextHolder.getContext() .getAuthentication(); - handler.logout(this, this.response, authentication); + for (LogoutHandler handler : handlers) { + handler.logout(this, this.response, authentication); + } } private boolean isAuthenticated() { diff --git a/web/src/main/java/org/springframework/security/web/util/OnCommittedResponseWrapper.java b/web/src/main/java/org/springframework/security/web/util/OnCommittedResponseWrapper.java index 2002f786570..405206c85f0 100644 --- a/web/src/main/java/org/springframework/security/web/util/OnCommittedResponseWrapper.java +++ b/web/src/main/java/org/springframework/security/web/util/OnCommittedResponseWrapper.java @@ -69,6 +69,12 @@ public void setContentLength(int len) { super.setContentLength(len); } + @Override + public void setContentLengthLong(long len) { + setContentLength(len); + super.setContentLengthLong(len); + } + private void setContentLength(long len) { this.contentLength = len; checkContentLength(0); diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java index 6630d1a4d66..59810dc5805 100644 --- a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java @@ -67,7 +67,7 @@ public final class AntPathRequestMatcher /** * Creates a matcher with the specific pattern which will match all HTTP methods in a - * case insensitive manner. + * case sensitive manner. * * @param pattern the ant pattern to use for matching */ @@ -76,7 +76,7 @@ public AntPathRequestMatcher(String pattern) { } /** - * Creates a matcher with the supplied pattern and HTTP method in a case insensitive + * Creates a matcher with the supplied pattern and HTTP method in a case sensitive * manner. * * @param pattern the ant pattern to use for matching @@ -107,7 +107,7 @@ public AntPathRequestMatcher(String pattern, String httpMethod, * * @param pattern the ant pattern to use for matching * @param httpMethod the HTTP method. The {@code matches} method will return false if - * the incoming request doesn't doesn't have the same method. + * the incoming request doesn't have the same method. * @param caseSensitive true if the matcher should consider case, else false * @param urlPathHelper if non-null, will be used for extracting the path from the HttpServletRequest */ diff --git a/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java b/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java index 9bf67714b32..1d7037940cf 100644 --- a/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java +++ b/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java @@ -16,10 +16,6 @@ package org.springframework.security.web.server.authentication; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,9 +23,19 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.security.core.Authentication; import org.springframework.security.web.server.WebFilterExchange; - +import reactor.core.publisher.Mono; import reactor.test.publisher.PublisherProbe; +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + /** * @author Rob Winch * @since 5.1 @@ -88,4 +94,26 @@ public void onAuthenticationSuccessWhenMultipleThenExecuted() { this.delegate1Result.assertWasSubscribed(); this.delegate2Result.assertWasSubscribed(); } + + @Test + public void onAuthenticationSuccessSequential() throws Exception { + AtomicBoolean slowDone = new AtomicBoolean(); + CountDownLatch latch = new CountDownLatch(1); + ServerAuthenticationSuccessHandler slow = (exchange, authentication) -> + Mono.delay(Duration.ofMillis(100)) + .doOnSuccess(__ -> slowDone.set(true)) + .then(); + ServerAuthenticationSuccessHandler second = (exchange, authentication) -> + Mono.fromRunnable(() -> { + latch.countDown(); + assertThat(slowDone.get()) + .describedAs("ServerAuthenticationSuccessHandler should be executed sequentially") + .isTrue(); + }); + DelegatingServerAuthenticationSuccessHandler handler = new DelegatingServerAuthenticationSuccessHandler(slow, second); + + handler.onAuthenticationSuccess(this.exchange, this.authentication).block(); + + assertThat(latch.await(3, TimeUnit.SECONDS)).isTrue(); + } } diff --git a/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java b/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java index 10828683561..409f11af786 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2019 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. @@ -134,6 +134,25 @@ Arrays. asList(moreRecentSessionInfo, assertThat(sessionInformation.isExpired()).isTrue(); } + @Test + public void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpired() { + SessionInformation oldestSessionInfo = new SessionInformation( + authentication.getPrincipal(), "unique1", new Date(1374766134214L)); + SessionInformation secondOldestSessionInfo = new SessionInformation( + authentication.getPrincipal(), "unique2", new Date(1374766134215L)); + when(sessionRegistry.getAllSessions(any(), anyBoolean())).thenReturn( + Arrays. asList(oldestSessionInfo, + secondOldestSessionInfo, + sessionInformation)); + strategy.setMaximumSessions(2); + + strategy.onAuthentication(authentication, request, response); + + assertThat(oldestSessionInfo.isExpired()).isTrue(); + assertThat(secondOldestSessionInfo.isExpired()).isTrue(); + assertThat(sessionInformation.isExpired()).isFalse(); + } + @Test(expected = IllegalArgumentException.class) public void setMessageSourceNull() { strategy.setMessageSource(null); diff --git a/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java index 0cf3acd5c35..c79def87dfc 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java @@ -16,11 +16,17 @@ package org.springframework.security.web.authentication.switchuser; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.servlet.FilterChain; -import org.junit.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; import org.junit.rules.ExpectedException; + import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.AccountExpiredException; @@ -42,8 +48,10 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.security.web.util.matcher.AnyRequestMatcher; -import javax.servlet.FilterChain; -import java.util.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; /** * Tests @@ -75,6 +83,7 @@ private MockHttpServletRequest createMockSwitchRequest() { request.setScheme("http"); request.setServerName("localhost"); request.setRequestURI("/login/impersonate"); + request.setMethod("POST"); return request; } @@ -125,6 +134,20 @@ public void requiresExitUserWhenEndsWithThenDoesNotMatch() { assertThat(filter.requiresExitUser(request)).isFalse(); } + @Test + // gh-4183 + public void requiresExitUserWhenGetThenDoesNotMatch() { + SwitchUserFilter filter = new SwitchUserFilter(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setRequestURI("/login/impersonate"); + request.setMethod("GET"); + + assertThat(filter.requiresExitUser(request)).isFalse(); + } + @Test public void requiresExitUserWhenMatcherThenWorks() { SwitchUserFilter filter = new SwitchUserFilter(); @@ -159,6 +182,20 @@ public void requiresSwitchUserWhenEndsWithThenDoesNotMatch() { assertThat(filter.requiresSwitchUser(request)).isFalse(); } + @Test + // gh-4183 + public void requiresSwitchUserWhenGetThenDoesNotMatch() { + SwitchUserFilter filter = new SwitchUserFilter(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setRequestURI("/login/impersonate"); + request.setMethod("GET"); + + assertThat(filter.requiresSwitchUser(request)).isFalse(); + } + @Test public void requiresSwitchUserWhenMatcherThenWorks() { SwitchUserFilter filter = new SwitchUserFilter(); diff --git a/web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java b/web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java index 71603f584e0..27d6bf1dbc4 100644 --- a/web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java +++ b/web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2020 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. @@ -29,6 +29,7 @@ /** * @author Rob Winch + * @author Eddú Meléndez */ public class StrictHttpFirewallTests { public String[] unnormalizedPaths = { "/..", "/./path/", "/path/path/.", "/path/path//.", "./path/../path//.", @@ -428,4 +429,20 @@ public void getFirewalledRequestWhenAllowUrlEncodedSlashAndUppercaseEncodedPathT this.firewall.getFirewalledRequest(request); } + + @Test + public void getFirewalledRequestWhenTrustedDomainThenNoException() { + this.request.addHeader("Host", "example.org"); + this.firewall.setAllowedHostnames(hostname -> hostname.equals("example.org")); + + assertThatCode(() -> this.firewall.getFirewalledRequest(this.request)).doesNotThrowAnyException(); + } + + @Test(expected = RequestRejectedException.class) + public void getFirewalledRequestWhenUntrustedDomainThenException() { + this.request.addHeader("Host", "example.org"); + this.firewall.setAllowedHostnames(hostname -> hostname.equals("myexample.org")); + + this.firewall.getFirewalledRequest(this.request); + } } diff --git a/web/src/test/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandlerTests.java b/web/src/test/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandlerTests.java index 76c790c42fc..01a304c0fed 100644 --- a/web/src/test/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandlerTests.java +++ b/web/src/test/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandlerTests.java @@ -16,6 +16,7 @@ package org.springframework.security.web.server.authentication.logout; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.*; @@ -28,9 +29,14 @@ import org.springframework.security.core.Authentication; import org.springframework.security.web.server.WebFilterExchange; +import reactor.core.publisher.Mono; import reactor.test.publisher.PublisherProbe; +import java.time.Duration; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author Eric Deandrea @@ -98,4 +104,26 @@ public void logoutWhenMultipleThenExecuted() { this.delegate1Result.assertWasSubscribed(); this.delegate2Result.assertWasSubscribed(); } + + @Test + public void logoutSequential() throws Exception { + AtomicBoolean slowDone = new AtomicBoolean(); + CountDownLatch latch = new CountDownLatch(1); + ServerLogoutHandler slow = (exchange, authentication) -> + Mono.delay(Duration.ofMillis(100)) + .doOnSuccess(__ -> slowDone.set(true)) + .then(); + ServerLogoutHandler second = (exchange, authentication) -> + Mono.fromRunnable(() -> { + latch.countDown(); + assertThat(slowDone.get()) + .describedAs("ServerLogoutHandler should be executed sequentially") + .isTrue(); + }); + DelegatingServerLogoutHandler handler = new DelegatingServerLogoutHandler(slow, second); + + handler.logout(this.exchange, this.authentication).block(); + + assertThat(latch.await(3, TimeUnit.SECONDS)).isTrue(); + } } diff --git a/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java b/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java index 8acc3862e0f..f413678f91c 100644 --- a/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java +++ b/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java @@ -16,8 +16,6 @@ package org.springframework.security.web.server.csrf; -import static org.assertj.core.api.Assertions.assertThat; - import java.time.Duration; import org.junit.Test; @@ -28,6 +26,8 @@ import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.util.StringUtils; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Eric Deandrea * @since 5.1 @@ -111,7 +111,7 @@ public void saveTokenWhenHttpOnlyFalseThenHttpOnlyFalse() { @Test public void saveTokenWhenCustomPropertiesThenCustomProperties() { - setExpectedDomain(".spring.io"); + setExpectedDomain("spring.io"); setExpectedCookieName("csrfCookie"); setExpectedPath("/some/path"); setExpectedHeaderName("headerName"); diff --git a/web/src/test/java/org/springframework/security/web/server/csrf/CsrfWebFilterTests.java b/web/src/test/java/org/springframework/security/web/server/csrf/CsrfWebFilterTests.java index 52759f507b7..9ce86bb62c0 100644 --- a/web/src/test/java/org/springframework/security/web/server/csrf/CsrfWebFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/server/csrf/CsrfWebFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 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. @@ -20,10 +20,14 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebSession; import reactor.core.publisher.Mono; @@ -33,9 +37,11 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import static org.springframework.mock.web.server.MockServerWebExchange.from; /** * @author Rob Winch + * @author Parikshit Dutta * @since 5.0 */ @RunWith(MockitoJUnitRunner.class) @@ -49,10 +55,10 @@ public class CsrfWebFilterTests { private CsrfWebFilter csrfFilter = new CsrfWebFilter(); - private MockServerWebExchange get = MockServerWebExchange.from( + private MockServerWebExchange get = from( MockServerHttpRequest.get("/")); - private MockServerWebExchange post = MockServerWebExchange.from( + private MockServerWebExchange post = from( MockServerHttpRequest.post("/")); @Test @@ -104,7 +110,7 @@ public void filterWhenPostAndEstablishedCsrfTokenAndRequestParamInvalidTokenThen this.csrfFilter.setCsrfTokenRepository(this.repository); when(this.repository.loadToken(any())) .thenReturn(Mono.just(this.token)); - this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/") + this.post = from(MockServerHttpRequest.post("/") .body(this.token.getParameterName() + "="+this.token.getToken()+"INVALID")); Mono result = this.csrfFilter.filter(this.post, this.chain); @@ -125,7 +131,7 @@ public void filterWhenPostAndEstablishedCsrfTokenAndRequestParamValidTokenThenCo .thenReturn(Mono.just(this.token)); when(this.repository.generateToken(any())) .thenReturn(Mono.just(this.token)); - this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/") + this.post = from(MockServerHttpRequest.post("/") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body(this.token.getParameterName() + "="+this.token.getToken())); @@ -142,7 +148,7 @@ public void filterWhenPostAndEstablishedCsrfTokenAndHeaderInvalidTokenThenCsrfEx this.csrfFilter.setCsrfTokenRepository(this.repository); when(this.repository.loadToken(any())) .thenReturn(Mono.just(this.token)); - this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/") + this.post = from(MockServerHttpRequest.post("/") .header(this.token.getHeaderName(), this.token.getToken()+"INVALID")); Mono result = this.csrfFilter.filter(this.post, this.chain); @@ -163,7 +169,7 @@ public void filterWhenPostAndEstablishedCsrfTokenAndHeaderValidTokenThenContinue .thenReturn(Mono.just(this.token)); when(this.repository.generateToken(any())) .thenReturn(Mono.just(this.token)); - this.post = MockServerWebExchange.from(MockServerHttpRequest.post("/") + this.post = from(MockServerHttpRequest.post("/") .header(this.token.getHeaderName(), this.token.getToken())); Mono result = this.csrfFilter.filter(this.post, this.chain); @@ -173,4 +179,14 @@ public void filterWhenPostAndEstablishedCsrfTokenAndHeaderValidTokenThenContinue chainResult.assertWasSubscribed(); } + + @Test + // gh-8452 + public void matchesRequireCsrfProtectionWhenNonStandardHTTPMethodIsUsed() { + HttpMethod customHttpMethod = HttpMethod.resolve("non-standard-http-method"); + MockServerWebExchange nonStandardHttpRequest = from(MockServerHttpRequest.method(customHttpMethod, "/")); + + ServerWebExchangeMatcher serverWebExchangeMatcher = CsrfWebFilter.DEFAULT_CSRF_MATCHER; + assertThat(serverWebExchangeMatcher.matches(nonStandardHttpRequest).map(MatchResult::isMatch).block()).isTrue(); + } } diff --git a/web/src/test/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriterTests.java b/web/src/test/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriterTests.java index 1b4c169ff40..9ab68a53754 100644 --- a/web/src/test/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriterTests.java +++ b/web/src/test/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriterTests.java @@ -15,11 +15,6 @@ */ package org.springframework.security.web.server.header; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,10 +23,19 @@ import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; - import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import java.time.Duration; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + /** * * @author Rob Winch @@ -55,7 +59,6 @@ public void setup() { @Test public void writeHttpHeadersWhenErrorNoErrorThenError() { when(writer1.writeHttpHeaders(exchange)).thenReturn(Mono.error(new RuntimeException())); - when(writer2.writeHttpHeaders(exchange)).thenReturn(Mono.empty()); Mono result = writer.writeHttpHeaders(exchange); @@ -64,13 +67,11 @@ public void writeHttpHeadersWhenErrorNoErrorThenError() { .verify(); verify(writer1).writeHttpHeaders(exchange); - verify(writer2).writeHttpHeaders(exchange); } @Test public void writeHttpHeadersWhenErrorErrorThenError() { when(writer1.writeHttpHeaders(exchange)).thenReturn(Mono.error(new RuntimeException())); - when(writer2.writeHttpHeaders(exchange)).thenReturn(Mono.error(new RuntimeException())); Mono result = writer.writeHttpHeaders(exchange); @@ -79,7 +80,6 @@ public void writeHttpHeadersWhenErrorErrorThenError() { .verify(); verify(writer1).writeHttpHeaders(exchange); - verify(writer2).writeHttpHeaders(exchange); } @Test @@ -96,4 +96,26 @@ public void writeHttpHeadersWhenNoErrorThenNoError() { verify(writer1).writeHttpHeaders(exchange); verify(writer2).writeHttpHeaders(exchange); } + + @Test + public void writeHttpHeadersSequential() throws Exception { + AtomicBoolean slowDone = new AtomicBoolean(); + CountDownLatch latch = new CountDownLatch(1); + ServerHttpHeadersWriter slow = exchange -> + Mono.delay(Duration.ofMillis(100)) + .doOnSuccess(__ -> slowDone.set(true)) + .then(); + ServerHttpHeadersWriter second = exchange -> + Mono.fromRunnable(() -> { + latch.countDown(); + assertThat(slowDone.get()) + .describedAs("ServerLogoutHandler should be executed sequentially") + .isTrue(); + }); + CompositeServerHttpHeadersWriter writer = new CompositeServerHttpHeadersWriter(slow, second); + + writer.writeHttpHeaders(this.exchange).block(); + + assertThat(latch.await(3, TimeUnit.SECONDS)).isTrue(); + } } diff --git a/web/src/test/java/org/springframework/security/web/util/OnCommittedResponseWrapperTests.java b/web/src/test/java/org/springframework/security/web/util/OnCommittedResponseWrapperTests.java index f679f413ecc..3f4f94d1496 100644 --- a/web/src/test/java/org/springframework/security/web/util/OnCommittedResponseWrapperTests.java +++ b/web/src/test/java/org/springframework/security/web/util/OnCommittedResponseWrapperTests.java @@ -1101,6 +1101,17 @@ public void contentLengthOutputStreamWriteStringCommits() throws IOException { assertThat(committed).isTrue(); } + // gh-7261 + @Test + public void contentLengthLongOutputStreamWriteStringCommits() throws IOException { + String body = "something"; + response.setContentLengthLong(body.length()); + + response.getOutputStream().print(body); + + assertThat(committed).isTrue(); + } + @Test public void addHeaderContentLengthPrintWriterWriteStringCommits() throws Exception { int expected = 1234;