88 changed files with 9773 additions and 2 deletions
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
|
||||
abstract class AbstractRequestMatcherDsl { |
||||
|
||||
/** |
||||
* Matches any request. |
||||
*/ |
||||
val anyRequest: RequestMatcher = AnyRequestMatcher.INSTANCE |
||||
|
||||
protected data class MatcherAuthorizationRule(val matcher: RequestMatcher, |
||||
override val rule: String) : AuthorizationRule(rule) |
||||
|
||||
protected data class PatternAuthorizationRule(val pattern: String, |
||||
val patternType: PatternType, |
||||
val servletPath: String?, |
||||
override val rule: String) : AuthorizationRule(rule) |
||||
|
||||
protected abstract class AuthorizationRule(open val rule: String) |
||||
|
||||
protected enum class PatternType { |
||||
ANT, MVC |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer |
||||
import org.springframework.security.core.Authentication |
||||
import org.springframework.security.core.GrantedAuthority |
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] anonymous authentication using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property key the key to identify tokens created for anonymous authentication |
||||
* @property principal the principal for [Authentication] objects of anonymous users |
||||
* @property authorities the [Authentication.getAuthorities] for anonymous users |
||||
* @property authenticationProvider the [AuthenticationProvider] used to validate an |
||||
* anonymous user |
||||
* @property authenticationFilter the [AnonymousAuthenticationFilter] used to populate |
||||
* an anonymous user. |
||||
*/ |
||||
class AnonymousDsl { |
||||
var key: String? = null |
||||
var principal: Any? = null |
||||
var authorities: List<GrantedAuthority>? = null |
||||
var authenticationProvider: AuthenticationProvider? = null |
||||
var authenticationFilter: AnonymousAuthenticationFilter? = null |
||||
|
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disable anonymous authentication |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (AnonymousConfigurer<HttpSecurity>) -> Unit { |
||||
return { anonymous -> |
||||
key?.also { anonymous.key(key) } |
||||
principal?.also { anonymous.principal(principal) } |
||||
authorities?.also { anonymous.authorities(authorities) } |
||||
authenticationProvider?.also { anonymous.authenticationProvider(authenticationProvider) } |
||||
authenticationFilter?.also { anonymous.authenticationFilter(authenticationFilter) } |
||||
if (disabled) { |
||||
anonymous.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer |
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
import org.springframework.util.ClassUtils |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] request authorization using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
*/ |
||||
class AuthorizeRequestsDsl : AbstractRequestMatcherDsl() { |
||||
private val authorizationRules = mutableListOf<AuthorizationRule>() |
||||
|
||||
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector" |
||||
private val MVC_PRESENT = ClassUtils.isPresent( |
||||
HANDLER_MAPPING_INTROSPECTOR, |
||||
AuthorizeRequestsDsl::class.java.classLoader) |
||||
|
||||
/** |
||||
* Adds a request authorization rule. |
||||
* |
||||
* @param matches the [RequestMatcher] to match incoming requests against |
||||
* @param access the SpEL expression to secure the matching request |
||||
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')") |
||||
*/ |
||||
fun authorize(matches: RequestMatcher = AnyRequestMatcher.INSTANCE, |
||||
access: String = "authenticated") { |
||||
authorizationRules.add(MatcherAuthorizationRule(matches, access)) |
||||
} |
||||
|
||||
/** |
||||
* Adds a request authorization rule for an endpoint matching the provided |
||||
* pattern. |
||||
* If Spring MVC is on the classpath, it will use an MVC matcher. |
||||
* If Spring MVC is not an the classpath, it will use an ant matcher. |
||||
* The MVC will use the same rules that Spring MVC uses for matching. |
||||
* For example, often times a mapping of the path "/path" will match on |
||||
* "/path", "/path/", "/path.html", etc. |
||||
* If the current request will not be processed by Spring MVC, a reasonable default |
||||
* using the pattern as an ant pattern will be used. |
||||
* |
||||
* @param pattern the pattern to match incoming requests against. |
||||
* @param access the SpEL expression to secure the matching request |
||||
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')") |
||||
*/ |
||||
fun authorize(pattern: String, access: String = "authenticated") { |
||||
if (MVC_PRESENT) { |
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, null, access)) |
||||
} else { |
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, null, access)) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Adds a request authorization rule for an endpoint matching the provided |
||||
* pattern. |
||||
* If Spring MVC is on the classpath, it will use an MVC matcher. |
||||
* If Spring MVC is not an the classpath, it will use an ant matcher. |
||||
* The MVC will use the same rules that Spring MVC uses for matching. |
||||
* For example, often times a mapping of the path "/path" will match on |
||||
* "/path", "/path/", "/path.html", etc. |
||||
* If the current request will not be processed by Spring MVC, a reasonable default |
||||
* using the pattern as an ant pattern will be used. |
||||
* |
||||
* @param pattern the pattern to match incoming requests against. |
||||
* @param servletPath the servlet path to match incoming requests against. This |
||||
* only applies when using an MVC pattern matcher. |
||||
* @param access the SpEL expression to secure the matching request |
||||
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')") |
||||
*/ |
||||
fun authorize(pattern: String, servletPath: String, access: String = "authenticated") { |
||||
if (MVC_PRESENT) { |
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, servletPath, access)) |
||||
} else { |
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, servletPath, access)) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Specify that URLs require a particular authority. |
||||
* |
||||
* @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc). |
||||
* @return the SpEL expression "hasAuthority" with the given authority as a |
||||
* parameter |
||||
*/ |
||||
fun hasAuthority(authority: String) = "hasAuthority('$authority')" |
||||
|
||||
/** |
||||
* Specify that URLs are allowed by anyone. |
||||
*/ |
||||
val permitAll = "permitAll" |
||||
|
||||
/** |
||||
* Specify that URLs are allowed by anonymous users. |
||||
*/ |
||||
val anonymous = "anonymous" |
||||
|
||||
/** |
||||
* Specify that URLs are allowed by users that have been remembered. |
||||
*/ |
||||
val rememberMe = "rememberMe" |
||||
|
||||
/** |
||||
* Specify that URLs are not allowed by anyone. |
||||
*/ |
||||
val denyAll = "denyAll" |
||||
|
||||
/** |
||||
* Specify that URLs are allowed by any authenticated user. |
||||
*/ |
||||
val authenticated = "authenticated" |
||||
|
||||
/** |
||||
* Specify that URLs are allowed by users who have authenticated and were not |
||||
* "remembered". |
||||
*/ |
||||
val fullyAuthenticated = "fullyAuthenticated" |
||||
|
||||
internal fun get(): (ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry) -> Unit { |
||||
return { requests -> |
||||
authorizationRules.forEach { rule -> |
||||
when (rule) { |
||||
is MatcherAuthorizationRule -> requests.requestMatchers(rule.matcher).access(rule.rule) |
||||
is PatternAuthorizationRule -> { |
||||
when (rule.patternType) { |
||||
PatternType.ANT -> requests.antMatchers(rule.pattern).access(rule.rule) |
||||
PatternType.MVC -> { |
||||
val mvcMatchersAuthorizeUrl = requests.mvcMatchers(rule.pattern) |
||||
rule.servletPath?.also { mvcMatchersAuthorizeUrl.servletPath(rule.servletPath) } |
||||
mvcMatchersAuthorizeUrl.access(rule.rule) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] CORS using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
*/ |
||||
class CorsDsl { |
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disable CORS. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (CorsConfigurer<HttpSecurity>) -> Unit { |
||||
return { cors -> |
||||
if (disabled) { |
||||
cors.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer |
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy |
||||
import org.springframework.security.web.csrf.CsrfTokenRepository |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
import javax.servlet.http.HttpServletRequest |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] CSRF protection |
||||
* using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property csrfTokenRepository the [CsrfTokenRepository] to use. |
||||
* @property requireCsrfProtectionMatcher specify the [RequestMatcher] to use for |
||||
* determining when CSRF should be applied. |
||||
* @property sessionAuthenticationStrategy the [SessionAuthenticationStrategy] to use. |
||||
*/ |
||||
class CsrfDsl { |
||||
var csrfTokenRepository: CsrfTokenRepository? = null |
||||
var requireCsrfProtectionMatcher: RequestMatcher? = null |
||||
var sessionAuthenticationStrategy: SessionAuthenticationStrategy? = null |
||||
|
||||
private var ignoringAntMatchers: Array<out String>? = null |
||||
private var ignoringRequestMatchers: Array<out RequestMatcher>? = null |
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Allows specifying [HttpServletRequest]s that should not use CSRF Protection |
||||
* even if they match the [requireCsrfProtectionMatcher]. |
||||
* |
||||
* @param antMatchers the ANT pattern matchers that should not use CSRF |
||||
* protection |
||||
*/ |
||||
fun ignoringAntMatchers(vararg antMatchers: String) { |
||||
ignoringAntMatchers = antMatchers |
||||
} |
||||
|
||||
/** |
||||
* Allows specifying [HttpServletRequest]s that should not use CSRF Protection |
||||
* even if they match the [requireCsrfProtectionMatcher]. |
||||
* |
||||
* @param requestMatchers the request matchers that should not use CSRF |
||||
* protection |
||||
*/ |
||||
fun ignoringRequestMatchers(vararg requestMatchers: RequestMatcher) { |
||||
ignoringRequestMatchers = requestMatchers |
||||
} |
||||
|
||||
/** |
||||
* Disable CSRF protection |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (CsrfConfigurer<HttpSecurity>) -> Unit { |
||||
return { csrf -> |
||||
csrfTokenRepository?.also { csrf.csrfTokenRepository(csrfTokenRepository) } |
||||
requireCsrfProtectionMatcher?.also { csrf.requireCsrfProtectionMatcher(requireCsrfProtectionMatcher) } |
||||
sessionAuthenticationStrategy?.also { csrf.sessionAuthenticationStrategy(sessionAuthenticationStrategy) } |
||||
ignoringAntMatchers?.also { csrf.ignoringAntMatchers(*ignoringAntMatchers!!) } |
||||
ignoringRequestMatchers?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchers!!) } |
||||
if (disabled) { |
||||
csrf.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer |
||||
import org.springframework.security.web.AuthenticationEntryPoint |
||||
import org.springframework.security.web.access.AccessDeniedHandler |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
import java.util.* |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] exception handling using idiomatic Kotlin |
||||
* code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property accessDeniedPage the URL to the access denied page |
||||
* @property accessDeniedHandler the [AccessDeniedHandler] to use |
||||
* @property authenticationEntryPoint the [AuthenticationEntryPoint] to use |
||||
*/ |
||||
class ExceptionHandlingDsl { |
||||
var accessDeniedPage: String? = null |
||||
var accessDeniedHandler: AccessDeniedHandler? = null |
||||
var authenticationEntryPoint: AuthenticationEntryPoint? = null |
||||
|
||||
private var defaultDeniedHandlerMappings: LinkedHashMap<RequestMatcher, AccessDeniedHandler> = linkedMapOf() |
||||
private var defaultEntryPointMappings: LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> = linkedMapOf() |
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Sets a default [AccessDeniedHandler] to be used which prefers being |
||||
* invoked for the provided [RequestMatcher]. |
||||
* |
||||
* @param deniedHandler the [AccessDeniedHandler] to use |
||||
* @param preferredMatcher the [RequestMatcher] for this default |
||||
* [AccessDeniedHandler] |
||||
*/ |
||||
fun defaultAccessDeniedHandlerFor(deniedHandler: AccessDeniedHandler, preferredMatcher: RequestMatcher) { |
||||
defaultDeniedHandlerMappings[preferredMatcher] = deniedHandler |
||||
} |
||||
|
||||
/** |
||||
* Sets a default [AuthenticationEntryPoint] to be used which prefers being |
||||
* invoked for the provided [RequestMatcher]. |
||||
* |
||||
* @param entryPoint the [AuthenticationEntryPoint] to use |
||||
* @param preferredMatcher the [RequestMatcher] for this default |
||||
* [AccessDeniedHandler] |
||||
*/ |
||||
fun defaultAuthenticationEntryPointFor(entryPoint: AuthenticationEntryPoint, preferredMatcher: RequestMatcher) { |
||||
defaultEntryPointMappings[preferredMatcher] = entryPoint |
||||
} |
||||
|
||||
/** |
||||
* Disable exception handling. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (ExceptionHandlingConfigurer<HttpSecurity>) -> Unit { |
||||
return { exceptionHandling -> |
||||
accessDeniedPage?.also { exceptionHandling.accessDeniedPage(accessDeniedPage) } |
||||
accessDeniedHandler?.also { exceptionHandling.accessDeniedHandler(accessDeniedHandler) } |
||||
authenticationEntryPoint?.also { exceptionHandling.authenticationEntryPoint(authenticationEntryPoint) } |
||||
defaultDeniedHandlerMappings.forEach { (matcher, handler) -> |
||||
exceptionHandling.defaultAccessDeniedHandlerFor(handler, matcher) |
||||
} |
||||
defaultEntryPointMappings.forEach { (matcher, entryPoint) -> |
||||
exceptionHandling.defaultAuthenticationEntryPointFor(entryPoint, matcher) |
||||
} |
||||
if (disabled) { |
||||
exceptionHandling.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer |
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler |
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] form login using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property loginPage the login page to redirect to if authentication is required (i.e. |
||||
* "/login") |
||||
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after |
||||
* authentication success |
||||
* @property authenticationFailureHandler the [AuthenticationFailureHandler] used after |
||||
* authentication success |
||||
* @property failureUrl the URL to send users if authentication fails |
||||
* @property loginProcessingUrl the URL to validate the credentials |
||||
* @property permitAll whether to grant access to the urls for [failureUrl] as well as |
||||
* for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user |
||||
*/ |
||||
class FormLoginDsl { |
||||
var loginPage: String? = null |
||||
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null |
||||
var authenticationFailureHandler: AuthenticationFailureHandler? = null |
||||
var failureUrl: String? = null |
||||
var loginProcessingUrl: String? = null |
||||
var permitAll: Boolean? = null |
||||
|
||||
private var defaultSuccessUrlOption: Pair<String, Boolean>? = null |
||||
|
||||
/** |
||||
* Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the |
||||
* [loginPage] and [loginProcessingUrl] for every user. |
||||
*/ |
||||
fun permitAll() { |
||||
permitAll = true |
||||
} |
||||
|
||||
/** |
||||
* Specifies where users will be redirected after authenticating successfully if |
||||
* they have not visited a secured page prior to authenticating or [alwaysUse] |
||||
* is true. |
||||
* |
||||
* @param defaultSuccessUrl the default success url |
||||
* @param alwaysUse true if the [defaultSuccessUrl] should be used after |
||||
* authentication despite if a protected page had been previously visited |
||||
*/ |
||||
fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) { |
||||
defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse) |
||||
} |
||||
|
||||
internal fun get(): (FormLoginConfigurer<HttpSecurity>) -> Unit { |
||||
return { login -> |
||||
loginPage?.also { login.loginPage(loginPage) } |
||||
failureUrl?.also { login.failureUrl(failureUrl) } |
||||
loginProcessingUrl?.also { login.loginProcessingUrl(loginProcessingUrl) } |
||||
permitAll?.also { login.permitAll(permitAll!!) } |
||||
defaultSuccessUrlOption?.also { |
||||
login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second) |
||||
} |
||||
authenticationSuccessHandler?.also { login.successHandler(authenticationSuccessHandler) } |
||||
authenticationFailureHandler?.also { login.failureHandler(authenticationFailureHandler) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,199 @@
@@ -0,0 +1,199 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
import org.springframework.security.config.web.servlet.headers.* |
||||
import org.springframework.security.web.header.writers.* |
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] headers using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property defaultsDisabled whether all of the default headers should be included in the response |
||||
*/ |
||||
class HeadersDsl { |
||||
private var contentTypeOptions: ((HeadersConfigurer<HttpSecurity>.ContentTypeOptionsConfig) -> Unit)? = null |
||||
private var xssProtection: ((HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit)? = null |
||||
private var cacheControl: ((HeadersConfigurer<HttpSecurity>.CacheControlConfig) -> Unit)? = null |
||||
private var hsts: ((HeadersConfigurer<HttpSecurity>.HstsConfig) -> Unit)? = null |
||||
private var frameOptions: ((HeadersConfigurer<HttpSecurity>.FrameOptionsConfig) -> Unit)? = null |
||||
private var hpkp: ((HeadersConfigurer<HttpSecurity>.HpkpConfig) -> Unit)? = null |
||||
private var contentSecurityPolicy: ((HeadersConfigurer<HttpSecurity>.ContentSecurityPolicyConfig) -> Unit)? = null |
||||
private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null |
||||
private var featurePolicyDirectives: String? = null |
||||
|
||||
var defaultsDisabled: Boolean? = null |
||||
|
||||
/** |
||||
* Configures the [XContentTypeOptionsHeaderWriter] which inserts the <a href= |
||||
* "https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx" |
||||
* >X-Content-Type-Options header</a> |
||||
* |
||||
* @param contentTypeOptionsConfig the customization to apply to the header |
||||
*/ |
||||
fun contentTypeOptions(contentTypeOptionsConfig: ContentTypeOptionsDsl.() -> Unit) { |
||||
this.contentTypeOptions = ContentTypeOptionsDsl().apply(contentTypeOptionsConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* <strong>Note this is not comprehensive XSS protection!</strong> |
||||
* |
||||
* <p> |
||||
* Allows customizing the [XXssProtectionHeaderWriter] which adds the <a href= |
||||
* "https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx" |
||||
* >X-XSS-Protection header</a> |
||||
* </p> |
||||
* |
||||
* @param xssProtectionConfig the customization to apply to the header |
||||
*/ |
||||
fun xssProtection(xssProtectionConfig: XssProtectionConfigDsl.() -> Unit) { |
||||
this.xssProtection = XssProtectionConfigDsl().apply(xssProtectionConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Allows customizing the [CacheControlHeadersWriter]. Specifically it adds the |
||||
* following headers: |
||||
* <ul> |
||||
* <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li> |
||||
* <li>Pragma: no-cache</li> |
||||
* <li>Expires: 0</li> |
||||
* </ul> |
||||
* |
||||
* @param cacheControlConfig the customization to apply to the header |
||||
*/ |
||||
fun cacheControl(cacheControlConfig: CacheControlDsl.() -> Unit) { |
||||
this.cacheControl = CacheControlDsl().apply(cacheControlConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Allows customizing the [HstsHeaderWriter] which provides support for <a |
||||
* href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security |
||||
* (HSTS)</a>. |
||||
* |
||||
* @param hstsConfig the customization to apply to the header |
||||
*/ |
||||
fun httpStrictTransportSecurity(hstsConfig: HttpStrictTransportSecurityDsl.() -> Unit) { |
||||
this.hsts = HttpStrictTransportSecurityDsl().apply(hstsConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Allows customizing the [XFrameOptionsHeaderWriter] which add the X-Frame-Options |
||||
* header. |
||||
* |
||||
* @param frameOptionsConfig the customization to apply to the header |
||||
*/ |
||||
fun frameOptions(frameOptionsConfig: FrameOptionsDsl.() -> Unit) { |
||||
this.frameOptions = FrameOptionsDsl().apply(frameOptionsConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Allows customizing the [HpkpHeaderWriter] which provides support for <a |
||||
* href="https://tools.ietf.org/html/rfc7469">HTTP Public Key Pinning (HPKP)</a>. |
||||
* |
||||
* @param hpkpConfig the customization to apply to the header |
||||
*/ |
||||
fun httpPublicKeyPinning(hpkpConfig: HttpPublicKeyPinningDsl.() -> Unit) { |
||||
this.hpkp = HttpPublicKeyPinningDsl().apply(hpkpConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Allows configuration for <a href="https://www.w3.org/TR/CSP2/">Content Security Policy (CSP) Level 2</a>. |
||||
* |
||||
* <p> |
||||
* Calling this method automatically enables (includes) the Content-Security-Policy header in the response |
||||
* using the supplied security policy directive(s). |
||||
* </p> |
||||
* |
||||
* @param contentSecurityPolicyConfig the customization to apply to the header |
||||
*/ |
||||
fun contentSecurityPolicy(contentSecurityPolicyConfig: ContentSecurityPolicyDsl.() -> Unit) { |
||||
this.contentSecurityPolicy = ContentSecurityPolicyDsl().apply(contentSecurityPolicyConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer Policy</a>. |
||||
* |
||||
* <p> |
||||
* Configuration is provided to the [ReferrerPolicyHeaderWriter] which support the writing |
||||
* of the header as detailed in the W3C Technical Report: |
||||
* </p> |
||||
* <ul> |
||||
* <li>Referrer-Policy</li> |
||||
* </ul> |
||||
* |
||||
* @param referrerPolicyConfig the customization to apply to the header |
||||
*/ |
||||
fun referrerPolicy(referrerPolicyConfig: ReferrerPolicyDsl.() -> Unit) { |
||||
this.referrerPolicy = ReferrerPolicyDsl().apply(referrerPolicyConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Allows configuration for <a href="https://wicg.github.io/feature-policy/">Feature |
||||
* Policy</a>. |
||||
* |
||||
* <p> |
||||
* Calling this method automatically enables (includes) the Feature-Policy |
||||
* header in the response using the supplied policy directive(s). |
||||
* <p> |
||||
* |
||||
* @param policyDirectives policyDirectives the security policy directive(s) |
||||
*/ |
||||
fun featurePolicy(policyDirectives: String) { |
||||
this.featurePolicyDirectives = policyDirectives |
||||
} |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>) -> Unit { |
||||
return { headers -> |
||||
defaultsDisabled?.also { |
||||
if (defaultsDisabled!!) { |
||||
headers.defaultsDisabled() |
||||
} |
||||
} |
||||
contentTypeOptions?.also { |
||||
headers.contentTypeOptions(contentTypeOptions) |
||||
} |
||||
xssProtection?.also { |
||||
headers.xssProtection(xssProtection) |
||||
} |
||||
cacheControl?.also { |
||||
headers.cacheControl(cacheControl) |
||||
} |
||||
hsts?.also { |
||||
headers.httpStrictTransportSecurity(hsts) |
||||
} |
||||
frameOptions?.also { |
||||
headers.frameOptions(frameOptions) |
||||
} |
||||
hpkp?.also { |
||||
headers.httpPublicKeyPinning(hpkp) |
||||
} |
||||
contentSecurityPolicy?.also { |
||||
headers.contentSecurityPolicy(contentSecurityPolicy) |
||||
} |
||||
referrerPolicy?.also { |
||||
headers.referrerPolicy(referrerPolicy) |
||||
} |
||||
featurePolicyDirectives?.also { |
||||
headers.featurePolicy(featurePolicyDirectives) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer |
||||
import org.springframework.security.web.AuthenticationEntryPoint |
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter |
||||
import javax.servlet.http.HttpServletRequest |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] basic authentication using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property realmName the HTTP Basic realm to use. If [authenticationEntryPoint] |
||||
* has been invoked, invoking this method will result in an error. |
||||
* @property authenticationEntryPoint the [AuthenticationEntryPoint] to be populated on |
||||
* [BasicAuthenticationFilter] in the event that authentication fails. |
||||
* @property authenticationDetailsSource the custom [AuthenticationDetailsSource] to use for |
||||
* basic authentication. |
||||
*/ |
||||
class HttpBasicDsl { |
||||
var realmName: String? = null |
||||
var authenticationEntryPoint: AuthenticationEntryPoint? = null |
||||
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>? = null |
||||
|
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disables HTTP basic authentication |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (HttpBasicConfigurer<HttpSecurity>) -> Unit { |
||||
return { httpBasic -> |
||||
realmName?.also { httpBasic.realmName(realmName) } |
||||
authenticationEntryPoint?.also { httpBasic.authenticationEntryPoint(authenticationEntryPoint) } |
||||
authenticationDetailsSource?.also { httpBasic.authenticationDetailsSource(authenticationDetailsSource) } |
||||
if (disabled) { |
||||
httpBasic.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,651 @@
@@ -0,0 +1,651 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.context.ApplicationContext |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
import org.springframework.util.ClassUtils |
||||
import javax.servlet.http.HttpServletRequest |
||||
|
||||
/** |
||||
* Configures [HttpSecurity] using a [HttpSecurity Kotlin DSL][HttpSecurityDsl]. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* authorizeRequests { |
||||
* request("/public", permitAll) |
||||
* request(anyRequest, authenticated) |
||||
* } |
||||
* formLogin { |
||||
* loginPage = "/log-in" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @param httpConfiguration the configurations to apply to [HttpSecurity] |
||||
*/ |
||||
operator fun HttpSecurity.invoke(httpConfiguration: HttpSecurityDsl.() -> Unit) = |
||||
HttpSecurityDsl(this, httpConfiguration).build() |
||||
|
||||
/** |
||||
* An [HttpSecurity] Kotlin DSL created by [`http { }`][invoke] |
||||
* in order to configure [HttpSecurity] using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @param http the [HttpSecurity] which all configurations will be applied to |
||||
* @param init the configurations to apply to the provided [HttpSecurity] |
||||
*/ |
||||
class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecurityDsl.() -> Unit) { |
||||
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector" |
||||
|
||||
/** |
||||
* Allows configuring the [HttpSecurity] to only be invoked when matching the |
||||
* provided pattern. |
||||
* If Spring MVC is on the classpath, it will use an MVC matcher. |
||||
* If Spring MVC is not an the classpath, it will use an ant matcher. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* securityMatcher("/private/**") |
||||
* formLogin { |
||||
* loginPage = "/log-in" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param pattern one or more patterns used to determine whether this |
||||
* configuration should be invoked. |
||||
*/ |
||||
fun securityMatcher(vararg pattern: String) { |
||||
val mvcPresent = ClassUtils.isPresent( |
||||
HANDLER_MAPPING_INTROSPECTOR, |
||||
AuthorizeRequestsDsl::class.java.classLoader) |
||||
this.http.requestMatchers { |
||||
if (mvcPresent) { |
||||
it.mvcMatchers(*pattern) |
||||
} else { |
||||
it.antMatchers(*pattern) |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Allows configuring the [HttpSecurity] to only be invoked when matching the |
||||
* provided [RequestMatcher]. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* securityMatcher(AntPathRequestMatcher("/private/**")) |
||||
* formLogin { |
||||
* loginPage = "/log-in" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param requestMatcher one or more [RequestMatcher] used to determine whether |
||||
* this configuration should be invoked. |
||||
*/ |
||||
fun securityMatcher(vararg requestMatcher: RequestMatcher) { |
||||
this.http.requestMatchers { |
||||
it.requestMatchers(*requestMatcher) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Enables form based authentication. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* formLogin { |
||||
* loginPage = "/log-in" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param formLoginConfiguration custom configurations to be applied |
||||
* to the form based authentication |
||||
* @see [FormLoginDsl] |
||||
*/ |
||||
fun formLogin(formLoginConfiguration: FormLoginDsl.() -> Unit) { |
||||
val loginCustomizer = FormLoginDsl().apply(formLoginConfiguration).get() |
||||
this.http.formLogin(loginCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Allows restricting access based upon the [HttpServletRequest] |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* authorizeRequests { |
||||
* request("/public", permitAll) |
||||
* request(anyRequest, authenticated) |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param authorizeRequestsConfiguration custom configuration that specifies |
||||
* access for requests |
||||
* @see [AuthorizeRequestsDsl] |
||||
*/ |
||||
fun authorizeRequests(authorizeRequestsConfiguration: AuthorizeRequestsDsl.() -> Unit) { |
||||
val authorizeRequestsCustomizer = AuthorizeRequestsDsl().apply(authorizeRequestsConfiguration).get() |
||||
this.http.authorizeRequests(authorizeRequestsCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Enables HTTP basic authentication. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* httpBasic { |
||||
* realmName = "Custom Realm" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param httpBasicConfiguration custom configurations to be applied to the |
||||
* HTTP basic authentication |
||||
* @see [HttpBasicDsl] |
||||
*/ |
||||
fun httpBasic(httpBasicConfiguration: HttpBasicDsl.() -> Unit) { |
||||
val httpBasicCustomizer = HttpBasicDsl().apply(httpBasicConfiguration).get() |
||||
this.http.httpBasic(httpBasicCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Allows configuring response headers. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* headers { |
||||
* referrerPolicy { |
||||
* policy = ReferrerPolicy.SAME_ORIGIN |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param headersConfiguration custom configurations to configure the |
||||
* response headers |
||||
* @see [HeadersDsl] |
||||
*/ |
||||
fun headers(headersConfiguration: HeadersDsl.() -> Unit) { |
||||
val headersCustomizer = HeadersDsl().apply(headersConfiguration).get() |
||||
this.http.headers(headersCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Enables CORS. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* cors { |
||||
* disable() |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param corsConfiguration custom configurations to configure the |
||||
* response headers |
||||
* @see [CorsDsl] |
||||
*/ |
||||
fun cors(corsConfiguration: CorsDsl.() -> Unit) { |
||||
val corsCustomizer = CorsDsl().apply(corsConfiguration).get() |
||||
this.http.cors(corsCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Allows configuring session management. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* sessionManagement { |
||||
* invalidSessionUrl = "/invalid-session" |
||||
* sessionConcurrency { |
||||
* maximumSessions = 1 |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param sessionManagementConfiguration custom configurations to configure |
||||
* session management |
||||
* @see [SessionManagementDsl] |
||||
*/ |
||||
fun sessionManagement(sessionManagementConfiguration: SessionManagementDsl.() -> Unit) { |
||||
val sessionManagementCustomizer = SessionManagementDsl().apply(sessionManagementConfiguration).get() |
||||
this.http.sessionManagement(sessionManagementCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Allows configuring a port mapper. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* portMapper { |
||||
* map(80, 443) |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param portMapperConfiguration custom configurations to configure |
||||
* the port mapper |
||||
* @see [PortMapperDsl] |
||||
*/ |
||||
fun portMapper(portMapperConfiguration: PortMapperDsl.() -> Unit) { |
||||
val portMapperCustomizer = PortMapperDsl().apply(portMapperConfiguration).get() |
||||
this.http.portMapper(portMapperCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Allows configuring channel security based upon the [HttpServletRequest] |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* requiresChannel { |
||||
* secure("/public", requiresInsecure) |
||||
* secure(anyRequest, requiresSecure) |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param requiresChannelConfiguration custom configuration that specifies |
||||
* channel security |
||||
* @see [RequiresChannelDsl] |
||||
*/ |
||||
fun requiresChannel(requiresChannelConfiguration: RequiresChannelDsl.() -> Unit) { |
||||
val requiresChannelCustomizer = RequiresChannelDsl().apply(requiresChannelConfiguration).get() |
||||
this.http.requiresChannel(requiresChannelCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Adds X509 based pre authentication to an application |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* x509 { } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param x509Configuration custom configuration to apply to the |
||||
* X509 based pre authentication |
||||
* @see [X509Dsl] |
||||
*/ |
||||
fun x509(x509Configuration: X509Dsl.() -> Unit) { |
||||
val x509Customizer = X509Dsl().apply(x509Configuration).get() |
||||
this.http.x509(x509Customizer) |
||||
} |
||||
|
||||
/** |
||||
* Enables request caching. Specifically this ensures that requests that |
||||
* are saved (i.e. after authentication is required) are later replayed. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* requestCache { } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param requestCacheConfiguration custom configuration to apply to the |
||||
* request cache |
||||
* @see [RequestCacheDsl] |
||||
*/ |
||||
fun requestCache(requestCacheConfiguration: RequestCacheDsl.() -> Unit) { |
||||
val requestCacheCustomizer = RequestCacheDsl().apply(requestCacheConfiguration).get() |
||||
this.http.requestCache(requestCacheCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Allows configuring exception handling. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* exceptionHandling { |
||||
* accessDeniedPage = "/access-denied" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param exceptionHandlingConfiguration custom configuration to apply to the |
||||
* exception handling |
||||
* @see [ExceptionHandlingDsl] |
||||
*/ |
||||
fun exceptionHandling(exceptionHandlingConfiguration: ExceptionHandlingDsl.() -> Unit) { |
||||
val exceptionHandlingCustomizer = ExceptionHandlingDsl().apply(exceptionHandlingConfiguration).get() |
||||
this.http.exceptionHandling(exceptionHandlingCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Enables CSRF protection. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* csrf { } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param csrfConfiguration custom configuration to apply to CSRF |
||||
* @see [CsrfDsl] |
||||
*/ |
||||
fun csrf(csrfConfiguration: CsrfDsl.() -> Unit) { |
||||
val csrfCustomizer = CsrfDsl().apply(csrfConfiguration).get() |
||||
this.http.csrf(csrfCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Provides logout support. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* logout { |
||||
* logoutUrl = "/log-out" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param logoutConfiguration custom configuration to apply to logout |
||||
* @see [LogoutDsl] |
||||
*/ |
||||
fun logout(logoutConfiguration: LogoutDsl.() -> Unit) { |
||||
val logoutCustomizer = LogoutDsl().apply(logoutConfiguration).get() |
||||
this.http.logout(logoutCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Configures authentication support using a SAML 2.0 Service Provider. |
||||
* A [RelyingPartyRegistrationRepository] is required and must be registered with |
||||
* the [ApplicationContext] or configured via |
||||
* [Saml2Dsl.relyingPartyRegistrationRepository] |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* saml2Login { |
||||
* relyingPartyRegistration = getSaml2RelyingPartyRegistration() |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param saml2LoginConfiguration custom configuration to configure the |
||||
* SAML2 service provider |
||||
* @see [Saml2Dsl] |
||||
*/ |
||||
fun saml2Login(saml2LoginConfiguration: Saml2Dsl.() -> Unit) { |
||||
val saml2LoginCustomizer = Saml2Dsl().apply(saml2LoginConfiguration).get() |
||||
this.http.saml2Login(saml2LoginCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Allows configuring how an anonymous user is represented. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* anonymous { |
||||
* authorities = listOf(SimpleGrantedAuthority("ROLE_ANON")) |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param anonymousConfiguration custom configuration to configure the |
||||
* anonymous user |
||||
* @see [AnonymousDsl] |
||||
*/ |
||||
fun anonymous(anonymousConfiguration: AnonymousDsl.() -> Unit) { |
||||
val anonymousCustomizer = AnonymousDsl().apply(anonymousConfiguration).get() |
||||
this.http.anonymous(anonymousCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider. |
||||
* A [ClientRegistrationRepository] is required and must be registered as a Bean or |
||||
* configured via [OAuth2LoginDsl.clientRegistrationRepository] |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* oauth2Login { |
||||
* clientRegistrationRepository = getClientRegistrationRepository() |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param oauth2LoginConfiguration custom configuration to configure the |
||||
* OAuth 2.0 Login |
||||
* @see [OAuth2LoginDsl] |
||||
*/ |
||||
fun oauth2Login(oauth2LoginConfiguration: OAuth2LoginDsl.() -> Unit) { |
||||
val oauth2LoginCustomizer = OAuth2LoginDsl().apply(oauth2LoginConfiguration).get() |
||||
this.http.oauth2Login(oauth2LoginCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Configures OAuth 2.0 client support. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* oauth2Client { } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param oauth2ClientConfiguration custom configuration to configure the |
||||
* OAuth 2.0 client support |
||||
* @see [OAuth2ClientDsl] |
||||
*/ |
||||
fun oauth2Client(oauth2ClientConfiguration: OAuth2ClientDsl.() -> Unit) { |
||||
val oauth2ClientCustomizer = OAuth2ClientDsl().apply(oauth2ClientConfiguration).get() |
||||
this.http.oauth2Client(oauth2ClientCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Configures OAuth 2.0 resource server support. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* http { |
||||
* oauth2ResourceServer { |
||||
* jwt { } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param oauth2ResourceServerConfiguration custom configuration to configure the |
||||
* OAuth 2.0 resource server support |
||||
* @see [OAuth2ResourceServerDsl] |
||||
*/ |
||||
fun oauth2ResourceServer(oauth2ResourceServerConfiguration: OAuth2ResourceServerDsl.() -> Unit) { |
||||
val oauth2ResourceServerCustomizer = OAuth2ResourceServerDsl().apply(oauth2ResourceServerConfiguration).get() |
||||
this.http.oauth2ResourceServer(oauth2ResourceServerCustomizer) |
||||
} |
||||
|
||||
/** |
||||
* Apply all configurations to the provided [HttpSecurity] |
||||
*/ |
||||
internal fun build() { |
||||
init() |
||||
} |
||||
} |
||||
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer |
||||
import org.springframework.security.core.Authentication |
||||
import org.springframework.security.web.access.AccessDeniedHandler |
||||
import org.springframework.security.web.authentication.logout.LogoutHandler |
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler |
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
import java.util.* |
||||
import javax.servlet.http.HttpSession |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] logout support |
||||
* using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property clearAuthentication whether the [SecurityContextLogoutHandler] should clear |
||||
* the [Authentication] at the time of logout. |
||||
* @property clearAuthentication whether to invalidate the [HttpSession] at the time of logout. |
||||
* @property logoutUrl the URL that triggers log out to occur. |
||||
* @property logoutRequestMatcher the [RequestMatcher] that triggers log out to occur. |
||||
* @property logoutSuccessUrl the URL to redirect to after logout has occurred. |
||||
* @property logoutSuccessHandler the [LogoutSuccessHandler] to use after logout has occurred. |
||||
* If this is specified, [logoutSuccessUrl] is ignored. |
||||
*/ |
||||
class LogoutDsl { |
||||
var clearAuthentication: Boolean? = null |
||||
var invalidateHttpSession: Boolean? = null |
||||
var logoutUrl: String? = null |
||||
var logoutRequestMatcher: RequestMatcher? = null |
||||
var logoutSuccessUrl: String? = null |
||||
var logoutSuccessHandler: LogoutSuccessHandler? = null |
||||
var permitAll: Boolean? = null |
||||
|
||||
private var logoutHandlers = mutableListOf<LogoutHandler>() |
||||
private var deleteCookies: Array<out String>? = null |
||||
private var defaultLogoutSuccessHandlerMappings: LinkedHashMap<RequestMatcher, LogoutSuccessHandler> = linkedMapOf() |
||||
private var disabled = false |
||||
|
||||
|
||||
/** |
||||
* Adds a [LogoutHandler]. The [SecurityContextLogoutHandler] is added as |
||||
* the last [LogoutHandler] by default. |
||||
* |
||||
* @param logoutHandler the [LogoutHandler] to add |
||||
*/ |
||||
fun addLogoutHandler(logoutHandler: LogoutHandler) { |
||||
this.logoutHandlers.add(logoutHandler) |
||||
} |
||||
|
||||
/** |
||||
* Allows specifying the names of cookies to be removed on logout success. |
||||
* |
||||
* @param cookieNamesToClear the names of cookies to be removed on logout success. |
||||
*/ |
||||
fun deleteCookies(vararg cookieNamesToClear: String) { |
||||
this.deleteCookies = cookieNamesToClear |
||||
} |
||||
|
||||
/** |
||||
* Sets a default [LogoutSuccessHandler] to be used which prefers being |
||||
* invoked for the provided [RequestMatcher]. |
||||
* |
||||
* @param logoutHandler the [LogoutSuccessHandler] to use |
||||
* @param preferredMatcher the [RequestMatcher] for this default |
||||
* [AccessDeniedHandler] |
||||
*/ |
||||
fun defaultLogoutSuccessHandlerFor(logoutHandler: LogoutSuccessHandler, preferredMatcher: RequestMatcher) { |
||||
defaultLogoutSuccessHandlerMappings[preferredMatcher] = logoutHandler |
||||
} |
||||
|
||||
/** |
||||
* Disables logout |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
/** |
||||
* Grants access to the [logoutSuccessUrl] and the [logoutUrl] for every user. |
||||
*/ |
||||
fun permitAll() { |
||||
permitAll = true |
||||
} |
||||
|
||||
internal fun get(): (LogoutConfigurer<HttpSecurity>) -> Unit { |
||||
return { logout -> |
||||
clearAuthentication?.also { logout.clearAuthentication(clearAuthentication!!) } |
||||
invalidateHttpSession?.also { logout.invalidateHttpSession(invalidateHttpSession!!) } |
||||
logoutUrl?.also { logout.logoutUrl(logoutUrl) } |
||||
logoutRequestMatcher?.also { logout.logoutRequestMatcher(logoutRequestMatcher) } |
||||
logoutSuccessUrl?.also { logout.logoutSuccessUrl(logoutSuccessUrl) } |
||||
logoutSuccessHandler?.also { logout.logoutSuccessHandler(logoutSuccessHandler) } |
||||
deleteCookies?.also { logout.deleteCookies(*deleteCookies!!) } |
||||
permitAll?.also { logout.permitAll(permitAll!!) } |
||||
defaultLogoutSuccessHandlerMappings.forEach { (matcher, handler) -> |
||||
logout.defaultLogoutSuccessHandlerFor(handler, matcher) |
||||
} |
||||
logoutHandlers.forEach { logoutHandler -> |
||||
logout.addLogoutHandler(logoutHandler) |
||||
} |
||||
if (disabled) { |
||||
logout.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.oauth2.client.AuthorizationCodeGrantDsl |
||||
import org.springframework.security.config.web.servlet.oauth2.login.AuthorizationEndpointDsl |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer |
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 client support using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property clientRegistrationRepository the repository of client registrations. |
||||
* @property authorizedClientRepository the repository for authorized client(s). |
||||
* @property authorizedClientService the service for authorized client(s). |
||||
*/ |
||||
class OAuth2ClientDsl { |
||||
var clientRegistrationRepository: ClientRegistrationRepository? = null |
||||
var authorizedClientRepository: OAuth2AuthorizedClientRepository? = null |
||||
var authorizedClientService: OAuth2AuthorizedClientService? = null |
||||
|
||||
private var authorizationCodeGrant: ((OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit)? = null |
||||
|
||||
/** |
||||
* Configures the OAuth 2.0 Authorization Code Grant. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* oauth2Client { |
||||
* authorizationCodeGrant { |
||||
* authorizationRequestResolver = getAuthorizationRequestResolver() |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param authorizationCodeGrantConfig custom configurations to configure the authorization |
||||
* code grant |
||||
* @see [AuthorizationEndpointDsl] |
||||
*/ |
||||
fun authorizationCodeGrant(authorizationCodeGrantConfig: AuthorizationCodeGrantDsl.() -> Unit) { |
||||
this.authorizationCodeGrant = AuthorizationCodeGrantDsl().apply(authorizationCodeGrantConfig).get() |
||||
} |
||||
|
||||
internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>) -> Unit { |
||||
return { oauth2Client -> |
||||
clientRegistrationRepository?.also { oauth2Client.clientRegistrationRepository(clientRegistrationRepository) } |
||||
authorizedClientRepository?.also { oauth2Client.authorizedClientRepository(authorizedClientRepository) } |
||||
authorizedClientService?.also { oauth2Client.authorizedClientService(authorizedClientService) } |
||||
authorizationCodeGrant?.also { oauth2Client.authorizationCodeGrant(authorizationCodeGrant) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,225 @@
@@ -0,0 +1,225 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.oauth2.login.AuthorizationEndpointDsl |
||||
import org.springframework.security.config.web.servlet.oauth2.login.RedirectionEndpointDsl |
||||
import org.springframework.security.config.web.servlet.oauth2.login.TokenEndpointDsl |
||||
import org.springframework.security.config.web.servlet.oauth2.login.UserInfoEndpointDsl |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer |
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository |
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler |
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 login using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property clientRegistrationRepository the repository of client registrations. |
||||
* @property authorizedClientRepository the repository for authorized client(s). |
||||
* @property authorizedClientService the service for authorized client(s). |
||||
* @property loginPage the login page to redirect to if authentication is required (i.e. |
||||
* "/login") |
||||
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after |
||||
* authentication success |
||||
* @property authenticationFailureHandler the [AuthenticationFailureHandler] used after |
||||
* authentication success |
||||
* @property failureUrl the URL to send users if authentication fails |
||||
* @property loginProcessingUrl the URL to validate the credentials |
||||
* @property permitAll whether to grant access to the urls for [failureUrl] as well as |
||||
* for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user |
||||
*/ |
||||
class OAuth2LoginDsl { |
||||
var clientRegistrationRepository: ClientRegistrationRepository? = null |
||||
var authorizedClientRepository: OAuth2AuthorizedClientRepository? = null |
||||
var authorizedClientService: OAuth2AuthorizedClientService? = null |
||||
var loginPage: String? = null |
||||
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null |
||||
var authenticationFailureHandler: AuthenticationFailureHandler? = null |
||||
var failureUrl: String? = null |
||||
var loginProcessingUrl: String? = null |
||||
var permitAll: Boolean? = null |
||||
|
||||
private var defaultSuccessUrlOption: Pair<String, Boolean>? = null |
||||
private var authorizationEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit)? = null |
||||
private var tokenEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.TokenEndpointConfig) -> Unit)? = null |
||||
private var redirectionEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.RedirectionEndpointConfig) -> Unit)? = null |
||||
private var userInfoEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.UserInfoEndpointConfig) -> Unit)? = null |
||||
|
||||
/** |
||||
* Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the |
||||
* [loginPage] and [loginProcessingUrl] for every user. |
||||
*/ |
||||
fun permitAll() { |
||||
permitAll = true |
||||
} |
||||
|
||||
/** |
||||
* Specifies where users will be redirected after authenticating successfully if |
||||
* they have not visited a secured page prior to authenticating or [alwaysUse] |
||||
* is true. |
||||
* |
||||
* @param defaultSuccessUrl the default success url |
||||
* @param alwaysUse true if the [defaultSuccessUrl] should be used after |
||||
* authentication despite if a protected page had been previously visited |
||||
*/ |
||||
fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) { |
||||
defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse) |
||||
} |
||||
|
||||
/** |
||||
* Configures the Authorization Server's Authorization Endpoint. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* oauth2Login { |
||||
* authorizationEndpoint { |
||||
* baseUri = "/auth" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param authorizationEndpointConfig custom configurations to configure the authorization |
||||
* endpoint |
||||
* @see [AuthorizationEndpointDsl] |
||||
*/ |
||||
fun authorizationEndpoint(authorizationEndpointConfig: AuthorizationEndpointDsl.() -> Unit) { |
||||
this.authorizationEndpoint = AuthorizationEndpointDsl().apply(authorizationEndpointConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Configures the Authorization Server's Token Endpoint. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* oauth2Login { |
||||
* tokenEndpoint { |
||||
* accessTokenResponseClient = getAccessTokenResponseClient() |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param tokenEndpointConfig custom configurations to configure the token |
||||
* endpoint |
||||
* @see [TokenEndpointDsl] |
||||
*/ |
||||
fun tokenEndpoint(tokenEndpointConfig: TokenEndpointDsl.() -> Unit) { |
||||
this.tokenEndpoint = TokenEndpointDsl().apply(tokenEndpointConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Configures the Authorization Server's Redirection Endpoint. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* oauth2Login { |
||||
* redirectionEndpoint { |
||||
* baseUri = "/home" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param redirectionEndpointConfig custom configurations to configure the redirection |
||||
* endpoint |
||||
* @see [RedirectionEndpointDsl] |
||||
*/ |
||||
fun redirectionEndpoint(redirectionEndpointConfig: RedirectionEndpointDsl.() -> Unit) { |
||||
this.redirectionEndpoint = RedirectionEndpointDsl().apply(redirectionEndpointConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Configures the Authorization Server's UserInfo Endpoint. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* oauth2Login { |
||||
* userInfoEndpoint { |
||||
* userService = getUserService() |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param userInfoEndpointConfig custom configurations to configure the user info |
||||
* endpoint |
||||
* @see [UserInfoEndpointDsl] |
||||
*/ |
||||
fun userInfoEndpoint(userInfoEndpointConfig: UserInfoEndpointDsl.() -> Unit) { |
||||
this.userInfoEndpoint = UserInfoEndpointDsl().apply(userInfoEndpointConfig).get() |
||||
} |
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>) -> Unit { |
||||
return { oauth2Login -> |
||||
clientRegistrationRepository?.also { oauth2Login.clientRegistrationRepository(clientRegistrationRepository) } |
||||
authorizedClientRepository?.also { oauth2Login.authorizedClientRepository(authorizedClientRepository) } |
||||
authorizedClientService?.also { oauth2Login.authorizedClientService(authorizedClientService) } |
||||
loginPage?.also { oauth2Login.loginPage(loginPage) } |
||||
failureUrl?.also { oauth2Login.failureUrl(failureUrl) } |
||||
loginProcessingUrl?.also { oauth2Login.loginProcessingUrl(loginProcessingUrl) } |
||||
permitAll?.also { oauth2Login.permitAll(permitAll!!) } |
||||
defaultSuccessUrlOption?.also { |
||||
oauth2Login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second) |
||||
} |
||||
authenticationSuccessHandler?.also { oauth2Login.successHandler(authenticationSuccessHandler) } |
||||
authenticationFailureHandler?.also { oauth2Login.failureHandler(authenticationFailureHandler) } |
||||
authorizationEndpoint?.also { oauth2Login.authorizationEndpoint(authorizationEndpoint) } |
||||
tokenEndpoint?.also { oauth2Login.tokenEndpoint(tokenEndpoint) } |
||||
redirectionEndpoint?.also { oauth2Login.redirectionEndpoint(redirectionEndpoint) } |
||||
userInfoEndpoint?.also { oauth2Login.userInfoEndpoint(userInfoEndpoint) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.oauth2.resourceserver.JwtDsl |
||||
import org.springframework.security.config.web.servlet.oauth2.resourceserver.OpaqueTokenDsl |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer |
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver |
||||
import org.springframework.security.web.AuthenticationEntryPoint |
||||
import org.springframework.security.web.access.AccessDeniedHandler |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 resource server support using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property accessDeniedHandler the [AccessDeniedHandler] to use for requests authenticating |
||||
* with <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s. |
||||
* @property authenticationEntryPoint the [AuthenticationEntryPoint] to use for requests authenticating |
||||
* with <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s. |
||||
* @property bearerTokenResolver the [BearerTokenResolver] to use for requests authenticating |
||||
* with <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s. |
||||
*/ |
||||
class OAuth2ResourceServerDsl { |
||||
var accessDeniedHandler: AccessDeniedHandler? = null |
||||
var authenticationEntryPoint: AuthenticationEntryPoint? = null |
||||
var bearerTokenResolver: BearerTokenResolver? = null |
||||
|
||||
private var jwt: ((OAuth2ResourceServerConfigurer<HttpSecurity>.JwtConfigurer) -> Unit)? = null |
||||
private var opaqueToken: ((OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer) -> Unit)? = null |
||||
|
||||
/** |
||||
* Enables JWT-encoded bearer token support. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* oauth2ResourceServer { |
||||
* jwt { |
||||
* jwkSetUri = "https://example.com/oauth2/jwk" |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param jwtConfig custom configurations to configure JWT resource server support |
||||
* @see [JwtDsl] |
||||
*/ |
||||
fun jwt(jwtConfig: JwtDsl.() -> Unit) { |
||||
this.jwt = JwtDsl().apply(jwtConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Enables opaque token support. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* oauth2ResourceServer { |
||||
* opaqueToken { } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param opaqueTokenConfig custom configurations to configure opaque token resource server support |
||||
* @see [OpaqueTokenDsl] |
||||
*/ |
||||
fun opaqueToken(opaqueTokenConfig: OpaqueTokenDsl.() -> Unit) { |
||||
this.opaqueToken = OpaqueTokenDsl().apply(opaqueTokenConfig).get() |
||||
} |
||||
|
||||
internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>) -> Unit { |
||||
return { oauth2ResourceServer -> |
||||
accessDeniedHandler?.also { oauth2ResourceServer.accessDeniedHandler(accessDeniedHandler) } |
||||
authenticationEntryPoint?.also { oauth2ResourceServer.authenticationEntryPoint(authenticationEntryPoint) } |
||||
bearerTokenResolver?.also { oauth2ResourceServer.bearerTokenResolver(bearerTokenResolver) } |
||||
jwt?.also { oauth2ResourceServer.jwt(jwt) } |
||||
opaqueToken?.also { oauth2ResourceServer.opaqueToken(opaqueToken) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.PortMapperConfigurer |
||||
import org.springframework.security.web.PortMapper |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure a [PortMapper] for [HttpSecurity] using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property portMapper allows specifying the [PortMapper] instance. |
||||
*/ |
||||
class PortMapperDsl { |
||||
private val mappings = mutableListOf<Pair<Int, Int>>() |
||||
|
||||
var portMapper: PortMapper? = null |
||||
|
||||
/** |
||||
* Adds a mapping to the port mapper. |
||||
* |
||||
* @param fromPort the HTTP port number to map from |
||||
* @param toPort the HTTPS port number to map to |
||||
*/ |
||||
fun map(fromPort: Int, toPort: Int) { |
||||
mappings.add(Pair(fromPort, toPort)) |
||||
} |
||||
|
||||
internal fun get(): (PortMapperConfigurer<HttpSecurity>) -> Unit { |
||||
return { portMapperConfig -> |
||||
portMapper?.also { |
||||
portMapperConfig.portMapper(portMapper) |
||||
} |
||||
this.mappings.forEach { |
||||
portMapperConfig.http(it.first).mapsTo(it.second) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer |
||||
import org.springframework.security.web.savedrequest.RequestCache |
||||
|
||||
/** |
||||
* A Kotlin DSL to enable request caching for [HttpSecurity] using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property requestCache allows explicit configuration of the [RequestCache] to be used |
||||
*/ |
||||
class RequestCacheDsl { |
||||
var requestCache: RequestCache? = null |
||||
|
||||
internal fun get(): (RequestCacheConfigurer<HttpSecurity>) -> Unit { |
||||
return { requestCacheConfig -> |
||||
requestCache?.also { |
||||
requestCacheConfig.requestCache(requestCache) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,136 @@
@@ -0,0 +1,136 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer |
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl |
||||
import org.springframework.security.web.access.channel.ChannelProcessor |
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
import org.springframework.util.ClassUtils |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] channel security using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property channelProcessors the [ChannelProcessor] instances to use in |
||||
* [ChannelDecisionManagerImpl] |
||||
*/ |
||||
class RequiresChannelDsl : AbstractRequestMatcherDsl() { |
||||
private val channelSecurityRules = mutableListOf<AuthorizationRule>() |
||||
|
||||
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector" |
||||
private val MVC_PRESENT = ClassUtils.isPresent( |
||||
HANDLER_MAPPING_INTROSPECTOR, |
||||
RequiresChannelDsl::class.java.classLoader) |
||||
|
||||
var channelProcessors: List<ChannelProcessor>? = null |
||||
|
||||
/** |
||||
* Adds a channel security rule. |
||||
* |
||||
* @param matches the [RequestMatcher] to match incoming requests against |
||||
* @param attribute the configuration attribute to secure the matching request |
||||
* (i.e. "REQUIRES_SECURE_CHANNEL") |
||||
*/ |
||||
fun secure(matches: RequestMatcher = AnyRequestMatcher.INSTANCE, |
||||
attribute: String = "REQUIRES_SECURE_CHANNEL") { |
||||
channelSecurityRules.add(MatcherAuthorizationRule(matches, attribute)) |
||||
} |
||||
|
||||
/** |
||||
* Adds a request authorization rule for an endpoint matching the provided |
||||
* pattern. |
||||
* If Spring MVC is not an the classpath, it will use an ant matcher. |
||||
* If Spring MVC is on the classpath, it will use an MVC matcher. |
||||
* The MVC will use the same rules that Spring MVC uses for matching. |
||||
* For example, often times a mapping of the path "/path" will match on |
||||
* "/path", "/path/", "/path.html", etc. |
||||
* If the current request will not be processed by Spring MVC, a reasonable default |
||||
* using the pattern as an ant pattern will be used. |
||||
* |
||||
* @param pattern the pattern to match incoming requests against. |
||||
* @param attribute the configuration attribute to secure the matching request |
||||
* (i.e. "REQUIRES_SECURE_CHANNEL") |
||||
*/ |
||||
fun secure(pattern: String, attribute: String = "REQUIRES_SECURE_CHANNEL") { |
||||
if (MVC_PRESENT) { |
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, null, attribute)) |
||||
} else { |
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, null, attribute)) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Adds a request authorization rule for an endpoint matching the provided |
||||
* pattern. |
||||
* If Spring MVC is not an the classpath, it will use an ant matcher. |
||||
* If Spring MVC is on the classpath, it will use an MVC matcher. |
||||
* The MVC will use the same rules that Spring MVC uses for matching. |
||||
* For example, often times a mapping of the path "/path" will match on |
||||
* "/path", "/path/", "/path.html", etc. |
||||
* If the current request will not be processed by Spring MVC, a reasonable default |
||||
* using the pattern as an ant pattern will be used. |
||||
* |
||||
* @param pattern the pattern to match incoming requests against. |
||||
* @param servletPath the servlet path to match incoming requests against. This |
||||
* only applies when using an MVC pattern matcher. |
||||
* @param attribute the configuration attribute to secure the matching request |
||||
* (i.e. "REQUIRES_SECURE_CHANNEL") |
||||
*/ |
||||
fun secure(pattern: String, servletPath: String, attribute: String = "REQUIRES_SECURE_CHANNEL") { |
||||
if (MVC_PRESENT) { |
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, servletPath, attribute)) |
||||
} else { |
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, servletPath, attribute)) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Specify channel security is active. |
||||
*/ |
||||
val requiresSecure = "REQUIRES_SECURE_CHANNEL" |
||||
|
||||
/** |
||||
* Specify channel security is inactive. |
||||
*/ |
||||
val requiresInsecure = "REQUIRES_INSECURE_CHANNEL" |
||||
|
||||
internal fun get(): (ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry) -> Unit { |
||||
return { channelSecurity -> |
||||
channelProcessors?.also { channelSecurity.channelProcessors(channelProcessors) } |
||||
channelSecurityRules.forEach { rule -> |
||||
when (rule) { |
||||
is MatcherAuthorizationRule -> channelSecurity.requestMatchers(rule.matcher).requires(rule.rule) |
||||
is PatternAuthorizationRule -> { |
||||
when (rule.patternType) { |
||||
PatternType.ANT -> channelSecurity.antMatchers(rule.pattern).requires(rule.rule) |
||||
PatternType.MVC -> { |
||||
val mvcMatchersRequiresChannel = channelSecurity.mvcMatchers(rule.pattern) |
||||
rule.servletPath?.also { mvcMatchersRequiresChannel.servletPath(rule.servletPath) } |
||||
mvcMatchersRequiresChannel.requires(rule.rule) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,91 @@
@@ -0,0 +1,91 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer |
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository |
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler |
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] SAML2 login using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property relyingPartyRegistrationRepository the [RelyingPartyRegistrationRepository] of relying parties, |
||||
* each party representing a service provider, SP and this host, and identity provider, IDP pair that |
||||
* communicate with each other. |
||||
* @property loginPage the login page to redirect to if authentication is required (i.e. |
||||
* "/login") |
||||
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after |
||||
* authentication success |
||||
* @property authenticationFailureHandler the [AuthenticationFailureHandler] used after |
||||
* authentication success |
||||
* @property failureUrl the URL to send users if authentication fails |
||||
* @property loginProcessingUrl the URL to validate the credentials |
||||
* @property permitAll whether to grant access to the urls for [failureUrl] as well as |
||||
* for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user |
||||
*/ |
||||
class Saml2Dsl { |
||||
var relyingPartyRegistrationRepository: RelyingPartyRegistrationRepository? = null |
||||
var loginPage: String? = null |
||||
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null |
||||
var authenticationFailureHandler: AuthenticationFailureHandler? = null |
||||
var failureUrl: String? = null |
||||
var loginProcessingUrl: String? = null |
||||
var permitAll: Boolean? = null |
||||
|
||||
private var defaultSuccessUrlOption: Pair<String, Boolean>? = null |
||||
|
||||
/** |
||||
* Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the |
||||
* [loginPage] and [loginProcessingUrl] for every user. |
||||
*/ |
||||
fun permitAll() { |
||||
permitAll = true |
||||
} |
||||
|
||||
/** |
||||
* Specifies where users will be redirected after authenticating successfully if |
||||
* they have not visited a secured page prior to authenticating or [alwaysUse] |
||||
* is true. |
||||
* |
||||
* @param defaultSuccessUrl the default success url |
||||
* @param alwaysUse true if the [defaultSuccessUrl] should be used after |
||||
* authentication despite if a protected page had been previously visited |
||||
*/ |
||||
fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) { |
||||
defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse) |
||||
} |
||||
|
||||
internal fun get(): (Saml2LoginConfigurer<HttpSecurity>) -> Unit { |
||||
return { saml2Login -> |
||||
relyingPartyRegistrationRepository?.also { saml2Login.relyingPartyRegistrationRepository(relyingPartyRegistrationRepository) } |
||||
loginPage?.also { saml2Login.loginPage(loginPage) } |
||||
failureUrl?.also { saml2Login.failureUrl(failureUrl) } |
||||
loginProcessingUrl?.also { saml2Login.loginProcessingUrl(loginProcessingUrl) } |
||||
permitAll?.also { saml2Login.permitAll(permitAll!!) } |
||||
defaultSuccessUrlOption?.also { |
||||
saml2Login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second) |
||||
} |
||||
authenticationSuccessHandler?.also { saml2Login.successHandler(authenticationSuccessHandler) } |
||||
authenticationFailureHandler?.also { saml2Login.failureHandler(authenticationFailureHandler) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,117 @@
@@ -0,0 +1,117 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.session.SessionConcurrencyDsl |
||||
import org.springframework.security.config.web.servlet.session.SessionFixationDsl |
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer |
||||
import org.springframework.security.config.http.SessionCreationPolicy |
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler |
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy |
||||
import org.springframework.security.web.session.InvalidSessionStrategy |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] session management using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
*/ |
||||
class SessionManagementDsl { |
||||
var invalidSessionUrl: String? = null |
||||
var invalidSessionStrategy: InvalidSessionStrategy? = null |
||||
var sessionAuthenticationErrorUrl: String? = null |
||||
var sessionAuthenticationFailureHandler: AuthenticationFailureHandler? = null |
||||
var enableSessionUrlRewriting: Boolean? = null |
||||
var sessionCreationPolicy: SessionCreationPolicy? = null |
||||
var sessionAuthenticationStrategy: SessionAuthenticationStrategy? = null |
||||
private var sessionFixation: ((SessionManagementConfigurer<HttpSecurity>.SessionFixationConfigurer) -> Unit)? = null |
||||
private var sessionConcurrency: ((SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit)? = null |
||||
|
||||
/** |
||||
* Enables session fixation protection. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* sessionManagement { |
||||
* sessionFixation { } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param sessionFixationConfig custom configurations to configure session fixation |
||||
* protection |
||||
* @see [SessionFixationDsl] |
||||
*/ |
||||
fun sessionFixation(sessionFixationConfig: SessionFixationDsl.() -> Unit) { |
||||
this.sessionFixation = SessionFixationDsl().apply(sessionFixationConfig).get() |
||||
} |
||||
|
||||
/** |
||||
* Controls the behaviour of multiple sessions for a user. |
||||
* |
||||
* Example: |
||||
* |
||||
* ``` |
||||
* @EnableWebSecurity |
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
* |
||||
* override fun configure(http: HttpSecurity) { |
||||
* httpSecurity(http) { |
||||
* sessionManagement { |
||||
* sessionConcurrency { |
||||
* maximumSessions = 1 |
||||
* maxSessionsPreventsLogin = true |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* } |
||||
* ``` |
||||
* |
||||
* @param sessionConcurrencyConfig custom configurations to configure concurrency |
||||
* control |
||||
* @see [SessionConcurrencyDsl] |
||||
*/ |
||||
fun sessionConcurrency(sessionConcurrencyConfig: SessionConcurrencyDsl.() -> Unit) { |
||||
this.sessionConcurrency = SessionConcurrencyDsl().apply(sessionConcurrencyConfig).get() |
||||
} |
||||
|
||||
internal fun get(): (SessionManagementConfigurer<HttpSecurity>) -> Unit { |
||||
return { sessionManagement -> |
||||
invalidSessionUrl?.also { sessionManagement.invalidSessionUrl(invalidSessionUrl) } |
||||
invalidSessionStrategy?.also { sessionManagement.invalidSessionStrategy(invalidSessionStrategy) } |
||||
sessionAuthenticationErrorUrl?.also { sessionManagement.sessionAuthenticationErrorUrl(sessionAuthenticationErrorUrl) } |
||||
sessionAuthenticationFailureHandler?.also { sessionManagement.sessionAuthenticationFailureHandler(sessionAuthenticationFailureHandler) } |
||||
enableSessionUrlRewriting?.also { sessionManagement.enableSessionUrlRewriting(enableSessionUrlRewriting!!) } |
||||
sessionCreationPolicy?.also { sessionManagement.sessionCreationPolicy(sessionCreationPolicy) } |
||||
sessionAuthenticationStrategy?.also { sessionManagement.sessionAuthenticationStrategy(sessionAuthenticationStrategy) } |
||||
sessionFixation?.also { sessionManagement.sessionFixation(sessionFixation) } |
||||
sessionConcurrency?.also { sessionManagement.sessionConcurrency(sessionConcurrency) } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.X509Configurer |
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService |
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken |
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails |
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter |
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor |
||||
import javax.servlet.http.HttpServletRequest |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] X509 based pre authentication |
||||
* using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property x509AuthenticationFilter the entire [X509AuthenticationFilter]. If |
||||
* this is specified, the properties on [X509Configurer] will not be populated |
||||
* on the {@link X509AuthenticationFilter}. |
||||
* @property x509PrincipalExtractor the [X509PrincipalExtractor] |
||||
* @property authenticationDetailsSource the [X509PrincipalExtractor] |
||||
* @property userDetailsService shortcut for invoking |
||||
* [authenticationUserDetailsService] with a [UserDetailsByNameServiceWrapper] |
||||
* @property authenticationUserDetailsService the [AuthenticationUserDetailsService] to use |
||||
* @property subjectPrincipalRegex the regex to extract the principal from the certificate |
||||
*/ |
||||
class X509Dsl { |
||||
var x509AuthenticationFilter: X509AuthenticationFilter? = null |
||||
var x509PrincipalExtractor: X509PrincipalExtractor? = null |
||||
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>? = null |
||||
var userDetailsService: UserDetailsService? = null |
||||
var authenticationUserDetailsService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>? = null |
||||
var subjectPrincipalRegex: String? = null |
||||
|
||||
internal fun get(): (X509Configurer<HttpSecurity>) -> Unit { |
||||
return { x509 -> |
||||
x509AuthenticationFilter?.also { x509.x509AuthenticationFilter(x509AuthenticationFilter) } |
||||
x509PrincipalExtractor?.also { x509.x509PrincipalExtractor(x509PrincipalExtractor) } |
||||
authenticationDetailsSource?.also { x509.authenticationDetailsSource(authenticationDetailsSource) } |
||||
userDetailsService?.also { x509.userDetailsService(userDetailsService) } |
||||
authenticationUserDetailsService?.also { x509.authenticationUserDetailsService(authenticationUserDetailsService) } |
||||
subjectPrincipalRegex?.also { x509.subjectPrincipalRegex(subjectPrincipalRegex) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the [HttpSecurity] cache control headers using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
*/ |
||||
class CacheControlDsl { |
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disable cache control headers. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.CacheControlConfig) -> Unit { |
||||
return { cacheControl -> |
||||
if (disabled) { |
||||
cacheControl.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the [HttpSecurity] Content-Security-Policy header using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property policyDirectives the security policy directive(s) to be used in the response header. |
||||
* @property reportOnly includes the Content-Security-Policy-Report-Only header in the response. |
||||
*/ |
||||
class ContentSecurityPolicyDsl { |
||||
var policyDirectives: String? = null |
||||
var reportOnly: Boolean? = null |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.ContentSecurityPolicyConfig) -> Unit { |
||||
return { contentSecurityPolicy -> |
||||
policyDirectives?.also { |
||||
contentSecurityPolicy.policyDirectives(policyDirectives) |
||||
} |
||||
reportOnly?.also { |
||||
if (reportOnly!!) { |
||||
contentSecurityPolicy.reportOnly() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure [HttpSecurity] X-Content-Type-Options header using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
*/ |
||||
class ContentTypeOptionsDsl { |
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disable the X-Content-Type-Options header. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.ContentTypeOptionsConfig) -> Unit { |
||||
return { contentTypeOptions -> |
||||
if (disabled) { |
||||
contentTypeOptions.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the [HttpSecurity] X-Frame-Options header using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property sameOrigin allow any request that comes from the same origin to frame this |
||||
* application. |
||||
* @property deny deny framing any content from this application. |
||||
*/ |
||||
class FrameOptionsDsl { |
||||
var sameOrigin: Boolean? = null |
||||
var deny: Boolean? = null |
||||
|
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disable the X-Frame-Options header. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.FrameOptionsConfig) -> Unit { |
||||
return { frameOptions -> |
||||
sameOrigin?.also { |
||||
if (sameOrigin!!) { |
||||
frameOptions.sameOrigin() |
||||
} |
||||
} |
||||
deny?.also { |
||||
if (deny!!) { |
||||
frameOptions.deny() |
||||
} |
||||
} |
||||
if (disabled) { |
||||
frameOptions.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the [HttpSecurity] HTTP Public Key Pinning header using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property pins the value for the pin- directive of the Public-Key-Pins header. |
||||
* @property maxAgeInSeconds the value (in seconds) for the max-age directive of the |
||||
* Public-Key-Pins header. |
||||
* @property includeSubDomains if true, the pinning policy applies to this pinned host |
||||
* as well as any subdomains of the host's domain name. |
||||
* @property reportOnly if true, the browser should not terminate the connection with |
||||
* the server. |
||||
* @property reportUri the URI to which the browser should report pin validation failures. |
||||
*/ |
||||
class HttpPublicKeyPinningDsl { |
||||
var pins: Map<String, String>? = null |
||||
var maxAgeInSeconds: Long? = null |
||||
var includeSubDomains: Boolean? = null |
||||
var reportOnly: Boolean? = null |
||||
var reportUri: String? = null |
||||
|
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disable the HTTP Public Key Pinning header. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.HpkpConfig) -> Unit { |
||||
return { hpkp -> |
||||
pins?.also { |
||||
hpkp.withPins(pins) |
||||
} |
||||
maxAgeInSeconds?.also { |
||||
hpkp.maxAgeInSeconds(maxAgeInSeconds!!) |
||||
} |
||||
includeSubDomains?.also { |
||||
hpkp.includeSubDomains(includeSubDomains!!) |
||||
} |
||||
reportOnly?.also { |
||||
hpkp.reportOnly(reportOnly!!) |
||||
} |
||||
reportUri?.also { |
||||
hpkp.reportUri(reportUri) |
||||
} |
||||
if (disabled) { |
||||
hpkp.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
import org.springframework.security.web.util.matcher.RequestMatcher |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the [HttpSecurity] HTTP Strict Transport Security header using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property maxAgeInSeconds the value (in seconds) for the max-age directive of the |
||||
* Strict-Transport-Security header. |
||||
* @property requestMatcher the [RequestMatcher] used to determine if the |
||||
* "Strict-Transport-Security" header should be added. If true the header is added, |
||||
* else the header is not added. |
||||
* @property includeSubDomains if true, subdomains should be considered HSTS Hosts too. |
||||
* @property preload if true, preload will be included in HSTS Header. |
||||
*/ |
||||
class HttpStrictTransportSecurityDsl { |
||||
var maxAgeInSeconds: Long? = null |
||||
var requestMatcher: RequestMatcher? = null |
||||
var includeSubDomains: Boolean? = null |
||||
var preload: Boolean? = null |
||||
|
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Disable the HTTP Strict Transport Security header. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.HstsConfig) -> Unit { |
||||
return { hsts -> |
||||
maxAgeInSeconds?.also { hsts.maxAgeInSeconds(maxAgeInSeconds!!) } |
||||
requestMatcher?.also { hsts.requestMatcher(requestMatcher) } |
||||
includeSubDomains?.also { hsts.includeSubDomains(includeSubDomains!!) } |
||||
preload?.also { hsts.preload(preload!!) } |
||||
if (disabled) { |
||||
hsts.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the [HttpSecurity] referrer policy header using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property policy the policy to be used in the response header. |
||||
*/ |
||||
class ReferrerPolicyDsl { |
||||
var policy: ReferrerPolicyHeaderWriter.ReferrerPolicy? = null |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit { |
||||
return { referrerPolicy -> |
||||
policy?.also { |
||||
referrerPolicy.policy(policy) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the [HttpSecurity] XSS protection header using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property block whether to specify the mode as blocked |
||||
* @property xssProtectionEnabled if true, the header value will contain a value of 1. |
||||
* If false, will explicitly disable specify that X-XSS-Protection is disabled. |
||||
*/ |
||||
class XssProtectionConfigDsl { |
||||
var block: Boolean? = null |
||||
var xssProtectionEnabled: Boolean? = null |
||||
|
||||
private var disabled = false |
||||
|
||||
/** |
||||
* Do not include the X-XSS-Protection header in the response. |
||||
*/ |
||||
fun disable() { |
||||
disabled = true |
||||
} |
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit { |
||||
return { xssProtection -> |
||||
block?.also { xssProtection.block(block!!) } |
||||
xssProtectionEnabled?.also { xssProtection.xssProtectionEnabled(xssProtectionEnabled!!) } |
||||
|
||||
if (disabled) { |
||||
xssProtection.disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.client |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure OAuth 2.0 Authorization Code Grant. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s. |
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s. |
||||
* @property accessTokenResponseClient the client used for requesting the access token credential |
||||
* from the Token Endpoint. |
||||
*/ |
||||
class AuthorizationCodeGrantDsl { |
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null |
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null |
||||
var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null |
||||
|
||||
internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit { |
||||
return { authorizationCodeGrant -> |
||||
authorizationRequestResolver?.also { authorizationCodeGrant.authorizationRequestResolver(authorizationRequestResolver) } |
||||
authorizationRequestRepository?.also { authorizationCodeGrant.authorizationRequestRepository(authorizationRequestRepository) } |
||||
accessTokenResponseClient?.also { authorizationCodeGrant.accessTokenResponseClient(accessTokenResponseClient) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.login |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the Authorization Server's Authorization Endpoint using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property baseUri the base URI used for authorization requests. |
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s. |
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s. |
||||
*/ |
||||
class AuthorizationEndpointDsl { |
||||
var baseUri: String? = null |
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null |
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null |
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit { |
||||
return { authorizationEndpoint -> |
||||
baseUri?.also { authorizationEndpoint.baseUri(baseUri) } |
||||
authorizationRequestResolver?.also { authorizationEndpoint.authorizationRequestResolver(authorizationRequestResolver) } |
||||
authorizationRequestRepository?.also { authorizationEndpoint.authorizationRequestRepository(authorizationRequestRepository) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.login |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the Authorization Server's Redirection Endpoint using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property baseUri the URI where the authorization response will be processed. |
||||
*/ |
||||
class RedirectionEndpointDsl { |
||||
var baseUri: String? = null |
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.RedirectionEndpointConfig) -> Unit { |
||||
return { redirectionEndpoint -> |
||||
baseUri?.also { redirectionEndpoint.baseUri(baseUri) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.login |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the Authorization Server's Token Endpoint using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property accessTokenResponseClient the client used for requesting the access token credential |
||||
* from the Token Endpoint. |
||||
*/ |
||||
class TokenEndpointDsl { |
||||
var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null |
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.TokenEndpointConfig) -> Unit { |
||||
return { tokenEndpoint -> |
||||
accessTokenResponseClient?.also { tokenEndpoint.accessTokenResponseClient(accessTokenResponseClient) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.login |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer |
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper |
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration |
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest |
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService |
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser |
||||
import org.springframework.security.oauth2.core.user.OAuth2User |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the Authorization Server's UserInfo Endpoint using |
||||
* idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property userService the OAuth 2.0 service used for obtaining the user attributes of the End-User |
||||
* from the UserInfo Endpoint. |
||||
* @property oidcUserService the OpenID Connect 1.0 service used for obtaining the user attributes of the |
||||
* End-User from the UserInfo Endpoint. |
||||
* @property userAuthoritiesMapper the [GrantedAuthoritiesMapper] used for mapping [OAuth2User.getAuthorities] |
||||
*/ |
||||
class UserInfoEndpointDsl { |
||||
var userService: OAuth2UserService<OAuth2UserRequest, OAuth2User>? = null |
||||
var oidcUserService: OAuth2UserService<OidcUserRequest, OidcUser>? = null |
||||
var userAuthoritiesMapper: GrantedAuthoritiesMapper? = null |
||||
|
||||
private var customUserTypePair: Pair<Class<out OAuth2User>, String>? = null |
||||
|
||||
/** |
||||
* Sets a custom [OAuth2User] type and associates it to the provided |
||||
* client [ClientRegistration.getRegistrationId] registration identifier. |
||||
* |
||||
* @param customUserType a custom [OAuth2User] type |
||||
* @param clientRegistrationId the client registration identifier |
||||
*/ |
||||
fun customUserType(customUserType: Class<out OAuth2User>, clientRegistrationId: String) { |
||||
customUserTypePair = Pair(customUserType, clientRegistrationId) |
||||
} |
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.UserInfoEndpointConfig) -> Unit { |
||||
return { userInfoEndpoint -> |
||||
userService?.also { userInfoEndpoint.userService(userService) } |
||||
oidcUserService?.also { userInfoEndpoint.oidcUserService(oidcUserService) } |
||||
userAuthoritiesMapper?.also { userInfoEndpoint.userAuthoritiesMapper(userAuthoritiesMapper) } |
||||
customUserTypePair?.also { userInfoEndpoint.customUserType(customUserTypePair!!.first, customUserTypePair!!.second) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.resourceserver |
||||
|
||||
import org.springframework.core.convert.converter.Converter |
||||
import org.springframework.security.authentication.AbstractAuthenticationToken |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer |
||||
import org.springframework.security.oauth2.jwt.Jwt |
||||
import org.springframework.security.oauth2.jwt.JwtDecoder |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure JWT Resource Server Support using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property jwtAuthenticationConverter the [Converter] to use for converting a [Jwt] into |
||||
* an [AbstractAuthenticationToken]. |
||||
* @property jwtDecoder the [JwtDecoder] to use. |
||||
* @property jwkSetUri configures a [JwtDecoder] using a |
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc7517">JSON Web Key (JWK)</a> URL |
||||
*/ |
||||
class JwtDsl { |
||||
var jwtAuthenticationConverter: Converter<Jwt, out AbstractAuthenticationToken>? = null |
||||
var jwtDecoder: JwtDecoder? = null |
||||
var jwkSetUri: String? = null |
||||
|
||||
internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>.JwtConfigurer) -> Unit { |
||||
return { jwt -> |
||||
jwtAuthenticationConverter?.also { jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) } |
||||
jwtDecoder?.also { jwt.decoder(jwtDecoder) } |
||||
jwkSetUri?.also { jwt.jwkSetUri(jwkSetUri) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.resourceserver |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer |
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure JWT Resource Server Support using idiomatic Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property introspectionUri the URI of the Introspection endpoint. |
||||
* @property introspector the [OpaqueTokenIntrospector] to use. |
||||
*/ |
||||
class OpaqueTokenDsl { |
||||
var introspectionUri: String? = null |
||||
var introspector: OpaqueTokenIntrospector? = null |
||||
|
||||
private var clientCredentials: Pair<String, String>? = null |
||||
|
||||
/** |
||||
* Configures the credentials for Introspection endpoint. |
||||
* |
||||
* @param clientId the clientId part of the credentials. |
||||
* @param clientSecret the clientSecret part of the credentials. |
||||
*/ |
||||
fun introspectionClientCredentials(clientId: String, clientSecret: String) { |
||||
clientCredentials = Pair(clientId, clientSecret) |
||||
} |
||||
|
||||
internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer) -> Unit { |
||||
return { opaqueToken -> |
||||
introspectionUri?.also { opaqueToken.introspectionUri(introspectionUri) } |
||||
introspector?.also { opaqueToken.introspector(introspector) } |
||||
clientCredentials?.also { opaqueToken.introspectionClientCredentials(clientCredentials!!.first, clientCredentials!!.second) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
/* |
||||
* 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.config.web.servlet.session |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer |
||||
import org.springframework.security.core.session.SessionRegistry |
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure the behaviour of multiple sessions using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
* @property maximumSessions controls the maximum number of sessions for a user. |
||||
* @property expiredUrl the URL to redirect to if a user tries to access a resource and |
||||
* their session has been expired due to too many sessions for the current user. |
||||
* @property expiredSessionStrategy determines the behaviour when an expired session |
||||
* is detected. |
||||
* @property maxSessionsPreventsLogin if true, prevents a user from authenticating when the |
||||
* [maximumSessions] has been reached. Otherwise (default), the user who authenticates |
||||
* is allowed access and an existing user's session is expired. |
||||
* @property sessionRegistry the [SessionRegistry] implementation used. |
||||
* |
||||
*/ |
||||
class SessionConcurrencyDsl { |
||||
var maximumSessions: Int? = null |
||||
var expiredUrl: String? = null |
||||
var expiredSessionStrategy: SessionInformationExpiredStrategy? = null |
||||
var maxSessionsPreventsLogin: Boolean? = null |
||||
var sessionRegistry: SessionRegistry? = null |
||||
|
||||
internal fun get(): (SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit { |
||||
return { sessionConcurrencyControl -> |
||||
maximumSessions?.also { |
||||
sessionConcurrencyControl.maximumSessions(maximumSessions!!) |
||||
} |
||||
expiredUrl?.also { |
||||
sessionConcurrencyControl.expiredUrl(expiredUrl) |
||||
} |
||||
expiredSessionStrategy?.also { |
||||
sessionConcurrencyControl.expiredSessionStrategy(expiredSessionStrategy) |
||||
} |
||||
maxSessionsPreventsLogin?.also { |
||||
sessionConcurrencyControl.maxSessionsPreventsLogin(maxSessionsPreventsLogin!!) |
||||
} |
||||
sessionRegistry?.also { |
||||
sessionConcurrencyControl.sessionRegistry(sessionRegistry) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
/* |
||||
* 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.config.web.servlet.session |
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer |
||||
import javax.servlet.http.HttpServletRequest |
||||
import javax.servlet.http.HttpSession |
||||
|
||||
/** |
||||
* A Kotlin DSL to configure session fixation protection using idiomatic |
||||
* Kotlin code. |
||||
* |
||||
* @author Eleftheria Stein |
||||
* @since 5.3 |
||||
*/ |
||||
class SessionFixationDsl { |
||||
private var strategy: SessionFixationStrategy? = null |
||||
|
||||
/** |
||||
* Specifies that a new session should be created, but the session attributes from |
||||
* the original [HttpSession] should not be retained. |
||||
*/ |
||||
fun newSession() { |
||||
this.strategy = SessionFixationStrategy.NEW |
||||
} |
||||
|
||||
/** |
||||
* Specifies that a new session should be created and the session attributes from |
||||
* the original [HttpSession] should be retained. |
||||
*/ |
||||
fun migrateSession() { |
||||
this.strategy = SessionFixationStrategy.MIGRATE |
||||
} |
||||
|
||||
/** |
||||
* Specifies that the Servlet container-provided session fixation protection |
||||
* should be used. When a session authenticates, the Servlet method |
||||
* [HttpServletRequest.changeSessionId] is called to change the session ID |
||||
* and retain all session attributes. |
||||
*/ |
||||
fun changeSessionId() { |
||||
this.strategy = SessionFixationStrategy.CHANGE_ID |
||||
} |
||||
|
||||
/** |
||||
* Specifies that no session fixation protection should be enabled. |
||||
*/ |
||||
fun none() { |
||||
this.strategy = SessionFixationStrategy.NONE |
||||
} |
||||
|
||||
internal fun get(): (SessionManagementConfigurer<HttpSecurity>.SessionFixationConfigurer) -> Unit { |
||||
return { sessionFixation -> |
||||
strategy?.also { |
||||
when (strategy) { |
||||
SessionFixationStrategy.NEW -> sessionFixation.newSession() |
||||
SessionFixationStrategy.MIGRATE -> sessionFixation.migrateSession() |
||||
SessionFixationStrategy.CHANGE_ID -> sessionFixation.changeSessionId() |
||||
SessionFixationStrategy.NONE -> sessionFixation.none() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private enum class SessionFixationStrategy { |
||||
NEW, MIGRATE, CHANGE_ID, NONE |
||||
} |
||||
@ -0,0 +1,163 @@
@@ -0,0 +1,163 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal |
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority |
||||
import org.springframework.security.core.context.SecurityContextHolder |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.web.bind.annotation.GetMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc |
||||
import java.util.* |
||||
|
||||
/** |
||||
* Tests for [AnonymousDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class AnonymousDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `anonymous when custom principal then custom principal used`() { |
||||
this.spring.register(PrincipalConfig::class.java, PrincipalController::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/principal") |
||||
.andExpect { |
||||
content { string("principal") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class PrincipalConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
anonymous { |
||||
principal = "principal" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `anonymous when custom key then custom key used`() { |
||||
this.spring.register(KeyConfig::class.java, PrincipalController::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/key") |
||||
.andExpect { |
||||
content { string("key".hashCode().toString()) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class KeyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
anonymous { |
||||
key = "key" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `anonymous when disabled then responds with forbidden`() { |
||||
this.spring.register(AnonymousDisabledConfig::class.java, PrincipalController::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/principal") |
||||
.andExpect { |
||||
content { string("") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AnonymousDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
anonymous { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `anonymous when custom authorities then authorities used`() { |
||||
this.spring.register(AnonymousAuthoritiesConfig::class.java, PrincipalController::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/principal") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AnonymousAuthoritiesConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
anonymous { |
||||
authorities = listOf(SimpleGrantedAuthority("TEST")) |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, hasAuthority("TEST")) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
internal class PrincipalController { |
||||
@GetMapping("/principal") |
||||
fun principal(@AuthenticationPrincipal principal: String?): String? { |
||||
return principal |
||||
} |
||||
|
||||
@GetMapping("/key") |
||||
fun key(): String { |
||||
return anonymousToken() |
||||
.map { it.keyHash } |
||||
.map { it.toString() } |
||||
.orElse(null) |
||||
} |
||||
|
||||
private fun anonymousToken(): Optional<AnonymousAuthenticationToken> { |
||||
return Optional.of(SecurityContextHolder.getContext()) |
||||
.map { it.authentication } |
||||
.filter { it is AnonymousAuthenticationToken } |
||||
.map { AnonymousAuthenticationToken::class.java.cast(it) } |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,211 @@
@@ -0,0 +1,211 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status |
||||
import org.springframework.web.bind.annotation.PathVariable |
||||
import org.springframework.web.bind.annotation.RequestMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc |
||||
|
||||
/** |
||||
* Tests for [AuthorizeRequestsDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class AuthorizeRequestsDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `request when secured by regex matcher then responds with forbidden`() { |
||||
this.spring.register(AuthorizeRequestsByRegexConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/private") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when allowed by regex matcher then responds with ok`() { |
||||
this.spring.register(AuthorizeRequestsByRegexConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/path") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class AuthorizeRequestsByRegexConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(RegexRequestMatcher("/path", null), permitAll) |
||||
authorize(RegexRequestMatcher(".*", null), authenticated) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
internal class PathController { |
||||
@RequestMapping("/path") |
||||
fun path() { |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when secured by mvc then responds with forbidden`() { |
||||
this.spring.register(AuthorizeRequestsByMvcConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/private") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when allowed by mvc then responds with OK`() { |
||||
this.spring.register(AuthorizeRequestsByMvcConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/path") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
|
||||
this.mockMvc.get("/path.html") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
|
||||
this.mockMvc.get("/path/") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AuthorizeRequestsByMvcConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize("/path", permitAll) |
||||
authorize("/**", authenticated) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
internal class PathController { |
||||
@RequestMapping("/path") |
||||
fun path() { |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when secured by mvc path variables then responds based on path variable value`() { |
||||
this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/user/user") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
|
||||
this.mockMvc.get("/user/deny") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class MvcMatcherPathVariablesConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize("/user/{userName}", "#userName == 'user'") |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
internal class PathController { |
||||
@RequestMapping("/user/{user}") |
||||
fun path(@PathVariable user: String) { |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when secured by mvc with servlet path then responds based on servlet path`() { |
||||
this.spring.register(MvcMatcherServletPathConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/spring/path") |
||||
.with { request -> |
||||
request.servletPath = "/spring" |
||||
request |
||||
}) |
||||
.andExpect(status().isForbidden) |
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/other/path") |
||||
.with { request -> |
||||
request.servletPath = "/other" |
||||
request |
||||
}) |
||||
.andExpect(status().isOk) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class MvcMatcherServletPathConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize("/path", |
||||
"/spring", |
||||
denyAll) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
internal class PathController { |
||||
@RequestMapping("/path") |
||||
fun path() { |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,138 @@
@@ -0,0 +1,138 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.BeanCreationException |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.http.HttpHeaders |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.web.bind.annotation.RequestMethod |
||||
import org.springframework.web.cors.CorsConfiguration |
||||
import org.springframework.web.cors.CorsConfigurationSource |
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc |
||||
|
||||
/** |
||||
* Tests for [CorsDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class CorsDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `CORS when no MVC then exception`() { |
||||
assertThatThrownBy { this.spring.register(DefaultCorsConfig::class.java).autowire() } |
||||
.isInstanceOf(BeanCreationException::class.java) |
||||
.hasMessageContaining("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext") |
||||
|
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DefaultCorsConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
cors { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `CORS when CORS configuration source bean then responds with CORS header`() { |
||||
this.spring.register(CorsCrossOriginConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
{ |
||||
header(HttpHeaders.ORIGIN, "https://example.com") |
||||
}.andExpect { |
||||
header { exists("Access-Control-Allow-Origin") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebMvc |
||||
@EnableWebSecurity |
||||
open class CorsCrossOriginConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
cors { } |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun corsConfigurationSource(): CorsConfigurationSource { |
||||
val source = UrlBasedCorsConfigurationSource() |
||||
val corsConfiguration = CorsConfiguration() |
||||
corsConfiguration.allowedOrigins = listOf("*") |
||||
corsConfiguration.allowedMethods = listOf( |
||||
RequestMethod.GET.name, |
||||
RequestMethod.POST.name) |
||||
source.registerCorsConfiguration("/**", corsConfiguration) |
||||
return source |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `CORS when disabled then response does not include CORS header`() { |
||||
this.spring.register(CorsDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
{ |
||||
header(HttpHeaders.ORIGIN, "https://example.com") |
||||
}.andExpect { |
||||
header { doesNotExist("Access-Control-Allow-Origin") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebMvc |
||||
@EnableWebSecurity |
||||
open class CorsDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http.cors() |
||||
http { |
||||
cors { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun corsConfigurationSource(): CorsConfigurationSource { |
||||
val source = UrlBasedCorsConfigurationSource() |
||||
val corsConfiguration = CorsConfiguration() |
||||
corsConfiguration.allowedOrigins = listOf("*") |
||||
corsConfiguration.allowedMethods = listOf( |
||||
RequestMethod.GET.name, |
||||
RequestMethod.POST.name) |
||||
source.registerCorsConfiguration("/**", corsConfiguration) |
||||
return source |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,265 @@
@@ -0,0 +1,265 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.Authentication |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf |
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy |
||||
import org.springframework.security.web.csrf.CsrfTokenRepository |
||||
import org.springframework.security.web.csrf.DefaultCsrfToken |
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.test.web.servlet.post |
||||
import org.springframework.web.bind.annotation.PostMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
import javax.servlet.http.HttpServletRequest |
||||
import javax.servlet.http.HttpServletResponse |
||||
|
||||
/** |
||||
* Tests for [CsrfDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class CsrfDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `POST when CSRF enabled and no CSRF token then forbidden`() { |
||||
this.spring.register(DefaultCsrfConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/test1") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `POST when CSRF enabled and CSRF token then status OK`() { |
||||
this.spring.register(DefaultCsrfConfig::class.java, BasicController::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/test1") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isOk } |
||||
} |
||||
|
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DefaultCsrfConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
csrf { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `POST when CSRF disabled and no CSRF token then status OK`() { |
||||
this.spring.register(CsrfDisabledConfig::class.java, BasicController::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/test1") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CsrfDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
csrf { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `CSRF when custom CSRF token repository then repo used`() { |
||||
`when`(CustomRepositoryConfig.REPO.loadToken(any<HttpServletRequest>())) |
||||
.thenReturn(DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token")) |
||||
|
||||
this.spring.register(CustomRepositoryConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/test1") |
||||
|
||||
verify(CustomRepositoryConfig.REPO).loadToken(any<HttpServletRequest>()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomRepositoryConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REPO: CsrfTokenRepository = mock(CsrfTokenRepository::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
csrf { |
||||
csrfTokenRepository = REPO |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `CSRF when require CSRF protection matcher then CSRF protection on matching requests`() { |
||||
this.spring.register(RequireCsrfProtectionMatcherConfig::class.java, BasicController::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/test1") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
|
||||
this.mockMvc.post("/test2") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class RequireCsrfProtectionMatcherConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
csrf { |
||||
requireCsrfProtectionMatcher = AntPathRequestMatcher("/test1") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `CSRF when custom session authentication strategy then strategy used`() { |
||||
this.spring.register(CustomStrategyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin()) |
||||
|
||||
verify(CustomStrategyConfig.STRATEGY, atLeastOnce()) |
||||
.onAuthentication(any(Authentication::class.java), any(HttpServletRequest::class.java), any(HttpServletResponse::class.java)) |
||||
|
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomStrategyConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var STRATEGY: SessionAuthenticationStrategy = mock(SessionAuthenticationStrategy::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin { } |
||||
csrf { |
||||
sessionAuthenticationStrategy = STRATEGY |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
override fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `CSRF when ignoring request matchers then CSRF disabled on matching requests`() { |
||||
this.spring.register(IgnoringRequestMatchersConfig::class.java, BasicController::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/test1") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
|
||||
this.mockMvc.post("/test2") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class IgnoringRequestMatchersConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
csrf { |
||||
requireCsrfProtectionMatcher = AntPathRequestMatcher("/**") |
||||
ignoringRequestMatchers(AntPathRequestMatcher("/test2")) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `CSRF when ignoring ant matchers then CSRF disabled on matching requests`() { |
||||
this.spring.register(IgnoringAntMatchersConfig::class.java, BasicController::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/test1") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
|
||||
this.mockMvc.post("/test2") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class IgnoringAntMatchersConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
csrf { |
||||
requireCsrfProtectionMatcher = AntPathRequestMatcher("/**") |
||||
ignoringAntMatchers("/test2") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
internal class BasicController { |
||||
@PostMapping("/test1") |
||||
fun test1() { |
||||
} |
||||
|
||||
@PostMapping("/test2") |
||||
fun test2() { |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,252 @@
@@ -0,0 +1,252 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.access.AccessDeniedException |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.userdetails.User.withUsername |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user |
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl |
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint |
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc |
||||
|
||||
/** |
||||
* Tests for [ExceptionHandlingDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class ExceptionHandlingDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `request when exception handling enabled then returns forbidden`() { |
||||
this.spring.register(ExceptionHandlingConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class ExceptionHandlingConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
exceptionHandling { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test(expected = AccessDeniedException::class) |
||||
fun `request when exception handling disabled then throws exception`() { |
||||
this.spring.register(ExceptionHandlingDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ExceptionHandlingDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
exceptionHandling { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `exception handling when custom access denied page then redirects to custom page`() { |
||||
this.spring.register(AccessDeniedPageConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/admin") { |
||||
with(user(withUsername("user").password("password").roles("USER").build())) |
||||
}.andExpect { |
||||
status { isForbidden } |
||||
forwardedUrl("/access-denied") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AccessDeniedPageConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize("/admin", hasAuthority("ROLE_ADMIN")) |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
exceptionHandling { |
||||
accessDeniedPage = "/access-denied" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `exception handling when custom access denied handler then handler used`() { |
||||
this.spring.register(AccessDeniedHandlerConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/admin") { |
||||
with(user(withUsername("user").password("password").roles("USER").build())) |
||||
}.andExpect { |
||||
status { isForbidden } |
||||
forwardedUrl("/access-denied") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AccessDeniedHandlerConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
val customAccessDeniedHandler = AccessDeniedHandlerImpl() |
||||
customAccessDeniedHandler.setErrorPage("/access-denied") |
||||
http { |
||||
authorizeRequests { |
||||
authorize("/admin", hasAuthority("ROLE_ADMIN")) |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
exceptionHandling { |
||||
accessDeniedHandler = customAccessDeniedHandler |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `exception handling when default access denied handler for page then handlers used`() { |
||||
this.spring.register(AccessDeniedHandlerForConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/admin1") { |
||||
with(user(withUsername("user").password("password").roles("USER").build())) |
||||
}.andExpect { |
||||
status { isForbidden } |
||||
forwardedUrl("/access-denied1") |
||||
} |
||||
|
||||
this.mockMvc.get("/admin2") { |
||||
with(user(withUsername("user").password("password").roles("USER").build())) |
||||
}.andExpect { |
||||
status { isForbidden } |
||||
forwardedUrl("/access-denied2") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AccessDeniedHandlerForConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
val customAccessDeniedHandler1 = AccessDeniedHandlerImpl() |
||||
customAccessDeniedHandler1.setErrorPage("/access-denied1") |
||||
val customAccessDeniedHandler2 = AccessDeniedHandlerImpl() |
||||
customAccessDeniedHandler2.setErrorPage("/access-denied2") |
||||
http { |
||||
authorizeRequests { |
||||
authorize("/admin1", hasAuthority("ROLE_ADMIN")) |
||||
authorize("/admin2", hasAuthority("ROLE_ADMIN")) |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
exceptionHandling { |
||||
defaultAccessDeniedHandlerFor(customAccessDeniedHandler1, AntPathRequestMatcher("/admin1")) |
||||
defaultAccessDeniedHandlerFor(customAccessDeniedHandler2, AntPathRequestMatcher("/admin2")) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `exception handling when custom authentication entry point then entry point used`() { |
||||
this.spring.register(AuthenticationEntryPointConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("http://localhost/custom-login") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AuthenticationEntryPointConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
exceptionHandling { |
||||
authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/custom-login") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `exception handling when authentication entry point for page then entry points used`() { |
||||
this.spring.register(AuthenticationEntryPointForConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/secured1") |
||||
.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("http://localhost/custom-login1") |
||||
} |
||||
|
||||
this.mockMvc.get("/secured2") |
||||
.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("http://localhost/custom-login2") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class AuthenticationEntryPointForConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
val customAuthenticationEntryPoint1 = LoginUrlAuthenticationEntryPoint("/custom-login1") |
||||
val customAuthenticationEntryPoint2 = LoginUrlAuthenticationEntryPoint("/custom-login2") |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
exceptionHandling { |
||||
defaultAuthenticationEntryPointFor(customAuthenticationEntryPoint1, AntPathRequestMatcher("/secured1")) |
||||
defaultAuthenticationEntryPointFor(customAuthenticationEntryPoint2, AntPathRequestMatcher("/secured2")) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,291 @@
@@ -0,0 +1,291 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin |
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler |
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler |
||||
import org.springframework.stereotype.Controller |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status |
||||
import org.springframework.web.bind.annotation.GetMapping |
||||
|
||||
/** |
||||
* Tests for [FormLoginDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class FormLoginDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `login page when form login configured then default login page created`() { |
||||
this.spring.register(FormLoginConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/login") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when success then redirects to home`() { |
||||
this.spring.register(FormLoginConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin()) |
||||
.andExpect { |
||||
status().isFound |
||||
redirectedUrl("/") |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when failure then redirects to login page with error`() { |
||||
this.spring.register(FormLoginConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin().password("invalid")) |
||||
.andExpect { |
||||
status().isFound |
||||
redirectedUrl("/login?error") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FormLoginConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin {} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when secure then redirects to default login page`() { |
||||
this.spring.register(AllSecuredConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("http://localhost/login") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class AllSecuredConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin {} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when secure and custom login page then redirects to custom login page`() { |
||||
this.spring.register(LoginPageConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("http://localhost/log-in") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class LoginPageConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin { |
||||
loginPage = "/log-in" |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when custom success handler then used`() { |
||||
this.spring.register(SuccessHandlerConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin()) |
||||
.andExpect { |
||||
status().isFound |
||||
redirectedUrl("/success") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class SuccessHandlerConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin { |
||||
authenticationSuccessHandler = SimpleUrlAuthenticationSuccessHandler("/success") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when custom failure handler then used`() { |
||||
this.spring.register(FailureHandlerConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin().password("invalid")) |
||||
.andExpect { |
||||
status().isFound |
||||
redirectedUrl("/failure") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FailureHandlerConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin { |
||||
authenticationFailureHandler = SimpleUrlAuthenticationFailureHandler("/failure") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when custom failure url then used`() { |
||||
this.spring.register(FailureHandlerConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin().password("invalid")) |
||||
.andExpect { |
||||
status().isFound |
||||
redirectedUrl("/failure") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FailureUrlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin { |
||||
failureUrl = "/failure" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when custom login processing url then used`() { |
||||
this.spring.register(LoginProcessingUrlConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin("/custom")) |
||||
.andExpect { |
||||
status().isFound |
||||
redirectedUrl("/") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class LoginProcessingUrlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin { |
||||
loginProcessingUrl = "/custom" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when default success url then redirected to url`() { |
||||
this.spring.register(DefaultSuccessUrlConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(formLogin()) |
||||
.andExpect { |
||||
status().isFound |
||||
redirectedUrl("/custom") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DefaultSuccessUrlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
formLogin { |
||||
defaultSuccessUrl("/custom", true) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login when permit all then login page not protected`() { |
||||
this.spring.register(PermitAllConfig::class.java, UserConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/custom/login") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class PermitAllConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
formLogin { |
||||
loginPage = "/custom/login" |
||||
permitAll() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Controller |
||||
class LoginController { |
||||
@GetMapping("/custom/login") |
||||
fun loginPage() {} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class UserConfig { |
||||
@Autowired |
||||
fun configureGlobal(auth: AuthenticationManagerBuilder) { |
||||
auth |
||||
.inMemoryAuthentication() |
||||
.withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.http.HttpHeaders |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter |
||||
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter |
||||
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter |
||||
import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter |
||||
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [HeadersDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class HeadersDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when defaults enabled then default headers in response`() { |
||||
this.spring.register(DefaultHeadersConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, "nosniff") } |
||||
header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) } |
||||
header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=31536000 ; includeSubDomains") } |
||||
header { string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") } |
||||
header { string(HttpHeaders.EXPIRES, "0") } |
||||
header { string(HttpHeaders.PRAGMA, "no-cache") } |
||||
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DefaultHeadersConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when feature policy configured then header in response`() { |
||||
this.spring.register(FeaturePolicyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { string("Feature-Policy", "geolocation 'self'") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FeaturePolicyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
featurePolicy(policyDirectives = "geolocation 'self'") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,205 @@
@@ -0,0 +1,205 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.Mockito.mock |
||||
import org.mockito.Mockito.verify |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.authentication.AuthenticationDetailsSource |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.AuthenticationException |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic |
||||
import org.springframework.security.web.AuthenticationEntryPoint |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.web.bind.annotation.GetMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
import javax.servlet.http.HttpServletRequest |
||||
import javax.servlet.http.HttpServletResponse |
||||
|
||||
/** |
||||
* Tests for [HttpBasicDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class HttpBasicDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `http basic when configured then insecure request cannot access`() { |
||||
this.spring.register(HttpBasicConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isUnauthorized } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `http basic when configured then response includes basic challenge`() { |
||||
this.spring.register(HttpBasicConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { string("WWW-Authenticate", "Basic realm=\"Realm\"") } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `http basic when valid user then permitted`() { |
||||
this.spring.register(HttpBasicConfig::class.java, UserConfig::class.java, MainController::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
with(httpBasic("user", "password")) |
||||
}.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HttpBasicConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
httpBasic {} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun httpBasicWhenCustomRealmThenUsed() { |
||||
this.spring.register(CustomRealmConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { string("WWW-Authenticate", "Basic realm=\"Custom Realm\"") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomRealmConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
httpBasic { |
||||
realmName = "Custom Realm" |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `http basic when custom authentication entry point then used`() { |
||||
this.spring.register(CustomAuthenticationEntryPointConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
|
||||
verify<AuthenticationEntryPoint>(CustomAuthenticationEntryPointConfig.ENTRY_POINT) |
||||
.commence(any(HttpServletRequest::class.java), |
||||
any(HttpServletResponse::class.java), |
||||
any(AuthenticationException::class.java)) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomAuthenticationEntryPointConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var ENTRY_POINT: AuthenticationEntryPoint = mock(AuthenticationEntryPoint::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
httpBasic { |
||||
authenticationEntryPoint = ENTRY_POINT |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `http basic when custom authentication details source then used`() { |
||||
this.spring.register(CustomAuthenticationDetailsSourceConfig::class.java, |
||||
UserConfig::class.java, MainController::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
with(httpBasic("username", "password")) |
||||
} |
||||
|
||||
verify(CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE) |
||||
.buildDetails(any(HttpServletRequest::class.java)) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomAuthenticationDetailsSourceConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var AUTHENTICATION_DETAILS_SOURCE = mock(AuthenticationDetailsSource::class.java) as AuthenticationDetailsSource<HttpServletRequest, *> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
httpBasic { |
||||
authenticationDetailsSource = AUTHENTICATION_DETAILS_SOURCE |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class UserConfig { |
||||
@Bean |
||||
open fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
class MainController { |
||||
@GetMapping("/") |
||||
fun main() { |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.http.HttpHeaders |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager |
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter |
||||
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter |
||||
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter |
||||
import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter |
||||
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter |
||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.test.web.servlet.post |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc |
||||
|
||||
/** |
||||
* Tests for [HttpSecurityDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class HttpSecurityDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `post when default security configured then CSRF prevents the request`() { |
||||
this.spring.register(DefaultSecurityConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `when default security configured then default headers are in the response`() { |
||||
this.spring.register(DefaultSecurityConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { |
||||
string(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, "nosniff") |
||||
} |
||||
header { |
||||
string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) |
||||
} |
||||
header { |
||||
string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=31536000 ; includeSubDomains") |
||||
} |
||||
header { |
||||
string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") |
||||
} |
||||
header { |
||||
string(HttpHeaders.EXPIRES, "0") |
||||
} |
||||
header { |
||||
string(HttpHeaders.PRAGMA, "no-cache") |
||||
} |
||||
header { |
||||
string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") |
||||
} |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DefaultSecurityConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http {} |
||||
} |
||||
|
||||
@Configuration |
||||
open class UserConfig { |
||||
@Bean |
||||
open fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when it does not match the security request matcher then the security rules do not apply`() { |
||||
this.spring.register(SecurityRequestMatcherConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isNotFound } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when it matches the security request matcher then the security rules apply`() { |
||||
this.spring.register(SecurityRequestMatcherConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/path") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class SecurityRequestMatcherConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
securityMatcher(RegexRequestMatcher("/path", null)) |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when it does not match the security pattern matcher then the security rules do not apply`() { |
||||
this.spring.register(SecurityPatternMatcherConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isNotFound } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when it matches the security pattern matcher then the security rules apply`() { |
||||
this.spring.register(SecurityPatternMatcherConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/path") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class SecurityPatternMatcherConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
securityMatcher("/path") |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `security pattern matcher when used with security request matcher then both apply`() { |
||||
this.spring.register(MultiMatcherConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/path1") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
|
||||
this.mockMvc.get("/path2") |
||||
.andExpect { |
||||
status { isForbidden } |
||||
} |
||||
|
||||
this.mockMvc.get("/path3") |
||||
.andExpect { |
||||
status { isNotFound } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class MultiMatcherConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
securityMatcher("/path1") |
||||
securityMatcher(RegexRequestMatcher("/path2", null)) |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,310 @@
@@ -0,0 +1,310 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.assertj.core.api.Assertions.assertThat |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.Mockito.mock |
||||
import org.mockito.Mockito.verify |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.mock.web.MockHttpSession |
||||
import org.springframework.security.authentication.TestingAuthenticationToken |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.context.SecurityContextHolder |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf |
||||
import org.springframework.security.web.authentication.logout.LogoutHandler |
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler |
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository |
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.post |
||||
|
||||
/** |
||||
* Tests for [LogoutDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class LogoutDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `logout when custom logout url then custom url used`() { |
||||
this.spring.register(CustomLogoutUrlConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/custom/logout") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login?logout") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomLogoutUrlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
logoutUrl = "/custom/logout" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when custom logout request matcher then custom request matcher used`() { |
||||
this.spring.register(CustomLogoutRequestMatcherConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/custom/logout") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login?logout") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomLogoutRequestMatcherConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
logoutRequestMatcher = AntPathRequestMatcher("/custom/logout") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when custom success url then redirects to success url`() { |
||||
this.spring.register(SuccessUrlConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/logout") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class SuccessUrlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
logoutSuccessUrl = "/login" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when custom success handler then redirects to success url`() { |
||||
this.spring.register(SuccessHandlerConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/logout") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class SuccessHandlerConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
logoutSuccessHandler = SimpleUrlLogoutSuccessHandler() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when permit all then logout allowed`() { |
||||
this.spring.register(PermitAllConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/custom/logout") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login?logout") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class PermitAllConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
logout { |
||||
logoutUrl = "/custom/logout" |
||||
permitAll() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when clear authentication false then authentication not cleared`() { |
||||
this.spring.register(ClearAuthenticationFalseConfig::class.java).autowire() |
||||
val currentContext = SecurityContextHolder.createEmptyContext() |
||||
currentContext.authentication = TestingAuthenticationToken("user", "password", "ROLE_USER") |
||||
val currentSession = MockHttpSession() |
||||
currentSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, currentContext) |
||||
|
||||
this.mockMvc.post("/logout") { |
||||
with(csrf()) |
||||
session = currentSession |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login?logout") |
||||
} |
||||
|
||||
assertThat(currentContext.authentication).isNotNull |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ClearAuthenticationFalseConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
clearAuthentication = false |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when invalidate http session false then session not invalidated`() { |
||||
this.spring.register(InvalidateHttpSessionFalseConfig::class.java).autowire() |
||||
val currentSession = MockHttpSession() |
||||
|
||||
this.mockMvc.post("/logout") { |
||||
with(csrf()) |
||||
session = currentSession |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login?logout") |
||||
} |
||||
|
||||
assertThat(currentSession.isInvalid).isFalse() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class InvalidateHttpSessionFalseConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
invalidateHttpSession = false |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when delete cookies then cookies are cleared`() { |
||||
this.spring.register(DeleteCookiesConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/logout") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login?logout") |
||||
cookie { maxAge("remove", 0) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DeleteCookiesConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
deleteCookies("remove") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when default logout success handler for request then custom handler used`() { |
||||
this.spring.register(DefaultLogoutSuccessHandlerForConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/logout/default") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/login?logout") |
||||
} |
||||
|
||||
this.mockMvc.post("/logout/custom") { |
||||
with(csrf()) |
||||
}.andExpect { |
||||
status { isFound } |
||||
redirectedUrl("/") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DefaultLogoutSuccessHandlerForConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
logoutRequestMatcher = AntPathRequestMatcher("/logout/**") |
||||
defaultLogoutSuccessHandlerFor(SimpleUrlLogoutSuccessHandler(), AntPathRequestMatcher("/logout/custom")) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `logout when custom logout handler then custom handler used`() { |
||||
this.spring.register(CustomLogoutHandlerConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.post("/logout") { |
||||
with(csrf()) |
||||
} |
||||
|
||||
verify(CustomLogoutHandlerConfig.HANDLER).logout(any(), any(), any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomLogoutHandlerConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var HANDLER: LogoutHandler = mock(LogoutHandler::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
logout { |
||||
addLogoutHandler(HANDLER) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,144 @@
@@ -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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [OAuth2ClientDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class OAuth2ClientDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Client when custom client registration repository then bean is not required`() { |
||||
this.spring.register(ClientRepoConfig::class.java).autowire() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ClientRepoConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Client { |
||||
clientRegistrationRepository = InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google").clientId("clientId").clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `oauth2Client when custom authorized client repository then repository used`() { |
||||
this.spring.register(ClientRepositoryConfig::class.java, ClientConfig::class.java).autowire() |
||||
val authorizationRequest = OAuth2AuthorizationRequest |
||||
.authorizationCode() |
||||
.state("test") |
||||
.clientId("clientId") |
||||
.authorizationUri("https://test") |
||||
.redirectUri("http://localhost/callback") |
||||
.attributes(mapOf(Pair(OAuth2ParameterNames.REGISTRATION_ID, "registrationId"))) |
||||
.build() |
||||
`when`(ClientRepositoryConfig.REQUEST_REPOSITORY.loadAuthorizationRequest(any())) |
||||
.thenReturn(authorizationRequest) |
||||
`when`(ClientRepositoryConfig.REQUEST_REPOSITORY.removeAuthorizationRequest(any(), any())) |
||||
.thenReturn(authorizationRequest) |
||||
`when`(ClientRepositoryConfig.CLIENT.getTokenResponse(any())) |
||||
.thenReturn(OAuth2AccessTokenResponse |
||||
.withToken("token") |
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER) |
||||
.build()) |
||||
|
||||
this.mockMvc.get("/callback") { |
||||
param("state", "test") |
||||
param("code", "123") |
||||
} |
||||
|
||||
verify(ClientRepositoryConfig.CLIENT_REPOSITORY).saveAuthorizedClient(any(), any(), any(), any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ClientRepositoryConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REQUEST_REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
var CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> = mock(OAuth2AccessTokenResponseClient::class.java) as OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> |
||||
var CLIENT_REPOSITORY: OAuth2AuthorizedClientRepository = mock(OAuth2AuthorizedClientRepository::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Client { |
||||
authorizedClientRepository = CLIENT_REPOSITORY |
||||
authorizationCodeGrant { |
||||
authorizationRequestRepository = REQUEST_REPOSITORY |
||||
accessTokenResponseClient = CLIENT |
||||
} |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class ClientConfig { |
||||
@Bean |
||||
open fun clientRegistrationRepository(): ClientRegistrationRepository { |
||||
return InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google") |
||||
.registrationId("registrationId") |
||||
.clientId("clientId") |
||||
.clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.web.bind.annotation.GetMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
|
||||
/** |
||||
* Tests for [OAuth2LoginDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class OAuth2LoginDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Login when custom client registration repository then bean is not required`() { |
||||
this.spring.register(ClientRepoConfig::class.java).autowire() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ClientRepoConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Login { |
||||
clientRegistrationRepository = InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google").clientId("clientId").clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login page when oAuth2Login configured then default login page created`() { |
||||
this.spring.register(OAuth2LoginConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/login") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class OAuth2LoginConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Login { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login page when custom login page then redirected to custom page`() { |
||||
this.spring.register(LoginPageConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/custom-login") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class LoginPageConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Login { |
||||
loginPage = "/custom-login" |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
class LoginController { |
||||
@GetMapping("/custom-login") |
||||
fun loginPage() { } |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class ClientConfig { |
||||
@Bean |
||||
open fun clientRegistrationRepository(): ClientRegistrationRepository { |
||||
return InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google").clientId("clientId").clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,156 @@
@@ -0,0 +1,156 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames.SUB |
||||
import org.springframework.security.oauth2.jwt.Jwt |
||||
import org.springframework.security.oauth2.jwt.JwtDecoder |
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver |
||||
import org.springframework.security.web.AuthenticationEntryPoint |
||||
import org.springframework.security.web.access.AccessDeniedHandler |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [OAuth2ResourceServerDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class OAuth2ResourceServerDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Resource server when custom entry point then entry point used`() { |
||||
this.spring.register(EntryPointConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
|
||||
verify(EntryPointConfig.ENTRY_POINT).commence(any(), any(), any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class EntryPointConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var ENTRY_POINT: AuthenticationEntryPoint = mock(AuthenticationEntryPoint::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2ResourceServer { |
||||
authenticationEntryPoint = ENTRY_POINT |
||||
jwt { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun jwtDecoder(): JwtDecoder { |
||||
return mock(JwtDecoder::class.java) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `oauth2Resource server when custom bearer token resolver then resolver used`() { |
||||
this.spring.register(BearerTokenResolverConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
|
||||
verify(BearerTokenResolverConfig.RESOLVER).resolve(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class BearerTokenResolverConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var RESOLVER: BearerTokenResolver = mock(BearerTokenResolver::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2ResourceServer { |
||||
bearerTokenResolver = RESOLVER |
||||
jwt { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun jwtDecoder(): JwtDecoder { |
||||
return mock(JwtDecoder::class.java) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `oauth2Resource server when custom access denied handler then handler used`() { |
||||
this.spring.register(AccessDeniedHandlerConfig::class.java).autowire() |
||||
`when`(AccessDeniedHandlerConfig.DECODER.decode(anyString())).thenReturn( |
||||
Jwt.withTokenValue("token") |
||||
.header("alg", "none") |
||||
.claim(SUB, "user") |
||||
.build()) |
||||
|
||||
this.mockMvc.get("/") { |
||||
header("Authorization", "Bearer token") |
||||
} |
||||
|
||||
verify(AccessDeniedHandlerConfig.DENIED_HANDLER).handle(any(), any(), any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class AccessDeniedHandlerConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var DENIED_HANDLER: AccessDeniedHandler = mock(AccessDeniedHandler::class.java) |
||||
var DECODER: JwtDecoder = mock(JwtDecoder::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, denyAll) |
||||
} |
||||
oauth2ResourceServer { |
||||
accessDeniedHandler = DENIED_HANDLER |
||||
jwt { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun jwtDecoder(): JwtDecoder { |
||||
return DECODER |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.PortMapperImpl |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import java.util.* |
||||
|
||||
/** |
||||
* Tests for [PortMapperDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class PortMapperDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `port mapper when specifying map then redirects to https port`() { |
||||
this.spring.register(PortMapperMapConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("http://localhost:543") |
||||
.andExpect { |
||||
redirectedUrl("https://localhost:123") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class PortMapperMapConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
requiresChannel { |
||||
secure(anyRequest, requiresSecure) |
||||
} |
||||
portMapper { |
||||
map(543, 123) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `port mapper when specifying custom mapper then redirects to https port`() { |
||||
this.spring.register(CustomPortMapperConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("http://localhost:543") |
||||
.andExpect { |
||||
redirectedUrl("https://localhost:123") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomPortMapperConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
val customPortMapper = PortMapperImpl() |
||||
customPortMapper.setPortMappings(Collections.singletonMap("543", "123")) |
||||
http { |
||||
requiresChannel { |
||||
secure(anyRequest, requiresSecure) |
||||
} |
||||
portMapper { |
||||
portMapper = customPortMapper |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin |
||||
import org.springframework.security.web.savedrequest.NullRequestCache |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl |
||||
|
||||
/** |
||||
* Tests for [RequestCacheDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class RequestCacheDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `GET when request cache enabled then redirected to cached page`() { |
||||
this.spring.register(RequestCacheConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/test") |
||||
|
||||
this.mockMvc.perform(formLogin()) |
||||
.andExpect { |
||||
redirectedUrl("http://localhost/test") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class RequestCacheConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
requestCache { } |
||||
formLogin { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `GET when custom request cache then custom request cache used`() { |
||||
this.spring.register(CustomRequestCacheConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/test") |
||||
|
||||
this.mockMvc.perform(formLogin()) |
||||
.andExpect { |
||||
redirectedUrl("/") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomRequestCacheConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
requestCache { |
||||
requestCache = NullRequestCache() |
||||
} |
||||
formLogin { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,138 @@
@@ -0,0 +1,138 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.access.channel.ChannelProcessor |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status |
||||
import org.springframework.web.bind.annotation.RequestMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc |
||||
|
||||
/** |
||||
* Tests for [RequiresChannelDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class RequiresChannelDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `requires channel when requires secure then redirects to https`() { |
||||
this.spring.register(RequiresSecureConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
redirectedUrl("https://localhost/") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class RequiresSecureConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
requiresChannel { |
||||
secure(anyRequest, requiresSecure) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when channel matches mvc with servlet path then redirects based on servlet path`() { |
||||
this.spring.register(MvcMatcherServletPathConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/spring/path") |
||||
.with { request -> |
||||
request.servletPath = "/spring" |
||||
request |
||||
}) |
||||
.andExpect(status().isFound) |
||||
.andExpect(redirectedUrl("https://localhost/spring/path")) |
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/other/path") |
||||
.with { request -> |
||||
request.servletPath = "/other" |
||||
request |
||||
}) |
||||
.andExpect(MockMvcResultMatchers.status().isOk) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@EnableWebMvc |
||||
open class MvcMatcherServletPathConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
requiresChannel { |
||||
secure("/path", |
||||
"/spring", |
||||
requiresSecure) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
internal class PathController { |
||||
@RequestMapping("/path") |
||||
fun path() { |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `requires channel when channel processors configured then channel processors used`() { |
||||
`when`(ChannelProcessorsConfig.CHANNEL_PROCESSOR.supports(any())).thenReturn(true) |
||||
this.spring.register(ChannelProcessorsConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
|
||||
verify(ChannelProcessorsConfig.CHANNEL_PROCESSOR).supports(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ChannelProcessorsConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var CHANNEL_PROCESSOR: ChannelProcessor = mock(ChannelProcessor::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
requiresChannel { |
||||
channelProcessors = listOf(CHANNEL_PROCESSOR) |
||||
secure(anyRequest, requiresSecure) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.assertj.core.api.Assertions |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.BeanCreationException |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.core.io.ClassPathResource |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.saml2.credentials.Saml2X509Credential |
||||
import org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.VERIFICATION |
||||
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository |
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration |
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import java.security.cert.Certificate |
||||
import java.security.cert.CertificateFactory |
||||
|
||||
/** |
||||
* Tests for [Saml2Dsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class Saml2DslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `saml2Login when no relying party registration repository then exception`() { |
||||
Assertions.assertThatThrownBy { this.spring.register(Saml2LoginNoRelyingPArtyRegistrationRepoConfig::class.java).autowire() } |
||||
.isInstanceOf(BeanCreationException::class.java) |
||||
.hasMessageContaining("relyingPartyRegistrationRepository cannot be null") |
||||
|
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class Saml2LoginNoRelyingPArtyRegistrationRepoConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
saml2Login { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `login page when saml2Configured then default login page created`() { |
||||
this.spring.register(Saml2LoginConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/login") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class Saml2LoginConfig : WebSecurityConfigurerAdapter() { |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
saml2Login { |
||||
relyingPartyRegistrationRepository = |
||||
InMemoryRelyingPartyRegistrationRepository( |
||||
RelyingPartyRegistration.withRegistrationId("samlId") |
||||
.remoteIdpEntityId("entityId") |
||||
.assertionConsumerServiceUrlTemplate("{baseUrl}" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI) |
||||
.credentials { c -> c.add(Saml2X509Credential(loadCert("rod.cer"), VERIFICATION)) } |
||||
.idpWebSsoUrl("ssoUrl") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun <T : Certificate> loadCert(location: String): T { |
||||
ClassPathResource(location).inputStream.use { inputStream -> |
||||
val certFactory = CertificateFactory.getInstance("X.509") |
||||
return certFactory.generateCertificate(inputStream) as T |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,218 @@
@@ -0,0 +1,218 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.assertj.core.api.Assertions.assertThat |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.mock.web.MockHttpSession |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.http.SessionCreationPolicy |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.Authentication |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication |
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler |
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationException |
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy |
||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status |
||||
import javax.servlet.http.HttpServletRequest |
||||
import javax.servlet.http.HttpServletResponse |
||||
|
||||
/** |
||||
* Tests for [SessionManagementDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class SessionManagementDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `session management when invalid session url then redirected to url`() { |
||||
this.spring.register(InvalidSessionUrlConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with { request -> |
||||
request.isRequestedSessionIdValid = false |
||||
request.requestedSessionId = "id" |
||||
request |
||||
}) |
||||
.andExpect(status().isFound) |
||||
.andExpect(redirectedUrl("/invalid")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class InvalidSessionUrlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
invalidSessionUrl = "/invalid" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session management when invalid session strategy then strategy used`() { |
||||
this.spring.register(InvalidSessionStrategyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with { request -> |
||||
request.isRequestedSessionIdValid = false |
||||
request.requestedSessionId = "id" |
||||
request |
||||
}) |
||||
.andExpect(status().isFound) |
||||
.andExpect(redirectedUrl("/invalid")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class InvalidSessionStrategyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
invalidSessionStrategy = SimpleRedirectInvalidSessionStrategy("/invalid") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session management when session authentication error url then redirected to url`() { |
||||
this.spring.register(SessionAuthenticationErrorUrlConfig::class.java).autowire() |
||||
val session = mock(MockHttpSession::class.java) |
||||
`when`(session.changeSessionId()).thenThrow(SessionAuthenticationException::class.java) |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(authentication(mock(Authentication::class.java))) |
||||
.session(session)) |
||||
.andExpect(status().isFound) |
||||
.andExpect(redirectedUrl("/session-auth-error")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class SessionAuthenticationErrorUrlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
sessionManagement { |
||||
sessionAuthenticationErrorUrl = "/session-auth-error" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session management when session authentication failure handler then handler used`() { |
||||
this.spring.register(SessionAuthenticationFailureHandlerConfig::class.java).autowire() |
||||
val session = mock(MockHttpSession::class.java) |
||||
`when`(session.changeSessionId()).thenThrow(SessionAuthenticationException::class.java) |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(authentication(mock(Authentication::class.java))) |
||||
.session(session)) |
||||
.andExpect(status().isFound) |
||||
.andExpect(redirectedUrl("/session-auth-error")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class SessionAuthenticationFailureHandlerConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
sessionManagement { |
||||
sessionAuthenticationFailureHandler = SimpleUrlAuthenticationFailureHandler("/session-auth-error") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session management when stateless policy then does not store session`() { |
||||
this.spring.register(StatelessSessionManagementConfig::class.java).autowire() |
||||
|
||||
val result = this.mockMvc.perform(get("/")) |
||||
.andReturn() |
||||
|
||||
assertThat(result.request.getSession(false)).isNull() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class StatelessSessionManagementConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
sessionManagement { |
||||
sessionCreationPolicy = SessionCreationPolicy.STATELESS |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session management when session authentication strategy then strategy used`() { |
||||
this.spring.register(SessionAuthenticationStrategyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(authentication(mock(Authentication::class.java))) |
||||
.session(mock(MockHttpSession::class.java))) |
||||
|
||||
verify(this.spring.getContext().getBean(SessionAuthenticationStrategy::class.java)) |
||||
.onAuthentication(any(Authentication::class.java), |
||||
any(HttpServletRequest::class.java), any(HttpServletResponse::class.java)) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class SessionAuthenticationStrategyConfig : WebSecurityConfigurerAdapter() { |
||||
var mockSessionAuthenticationStrategy: SessionAuthenticationStrategy = mock(SessionAuthenticationStrategy::class.java) |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
sessionManagement { |
||||
sessionAuthenticationStrategy = mockSessionAuthenticationStrategy |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy { |
||||
return this.mockSessionAuthenticationStrategy |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,221 @@
@@ -0,0 +1,221 @@
|
||||
/* |
||||
* 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.config.web.servlet |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.Mockito.mock |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.core.io.ClassPathResource |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509 |
||||
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated |
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken |
||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get |
||||
import java.security.cert.Certificate |
||||
import java.security.cert.CertificateFactory |
||||
import java.security.cert.X509Certificate |
||||
|
||||
/** |
||||
* Tests for [X509Dsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class X509DslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `x509 when configured with defaults then user authenticated`() { |
||||
this.spring.register(X509Config::class.java).autowire() |
||||
val certificate = loadCert<X509Certificate>("rod.cer") |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(x509(certificate))) |
||||
.andExpect(authenticated().withUsername("rod")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class X509Config : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
x509 { } |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
override fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("rod") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `x509 when configured with regex then user authenticated`() { |
||||
this.spring.register(X509RegexConfig::class.java).autowire() |
||||
val certificate = loadCert<X509Certificate>("rodatexampledotcom.cer") |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(x509(certificate))) |
||||
.andExpect(authenticated().withUsername("rod")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class X509RegexConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
x509 { |
||||
subjectPrincipalRegex = "CN=(.*?)@example.com(?:,|$)" |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
override fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("rod") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `x509 when user details service configured then user details service used`() { |
||||
this.spring.register(UserDetailsServiceConfig::class.java).autowire() |
||||
val certificate = loadCert<X509Certificate>("rod.cer") |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(x509(certificate))) |
||||
.andExpect(authenticated().withUsername("rod")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class UserDetailsServiceConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("rod") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
val customUserDetailsService = InMemoryUserDetailsManager(userDetails) |
||||
http { |
||||
x509 { |
||||
userDetailsService = customUserDetailsService |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
override fun userDetailsService(): UserDetailsService { |
||||
return mock(UserDetailsService::class.java) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `x509 when authentication user details service configured then custom user details service used`() { |
||||
this.spring.register(AuthenticationUserDetailsServiceConfig::class.java).autowire() |
||||
val certificate = loadCert<X509Certificate>("rod.cer") |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(x509(certificate))) |
||||
.andExpect(authenticated().withUsername("rod")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class AuthenticationUserDetailsServiceConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("rod") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
val customUserDetailsService = InMemoryUserDetailsManager(userDetails) |
||||
val customSource = UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>() |
||||
customSource.setUserDetailsService(customUserDetailsService) |
||||
http { |
||||
x509 { |
||||
authenticationUserDetailsService = customSource |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
override fun userDetailsService(): UserDetailsService { |
||||
return mock(UserDetailsService::class.java) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `x509 when configured with principal extractor then principal extractor used`() { |
||||
this.spring.register(X509PrincipalExtractorConfig::class.java).autowire() |
||||
val certificate = loadCert<X509Certificate>("rodatexampledotcom.cer") |
||||
|
||||
this.mockMvc.perform(get("/") |
||||
.with(x509(certificate))) |
||||
.andExpect(authenticated().withUsername("rod")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class X509PrincipalExtractorConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
val principalExtractor = SubjectDnX509PrincipalExtractor() |
||||
principalExtractor.setSubjectDnRegex("CN=(.*?)@example.com(?:,|$)") |
||||
http { |
||||
x509 { |
||||
x509PrincipalExtractor = principalExtractor |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
override fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("rod") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
|
||||
private fun <T : Certificate> loadCert(location: String): T { |
||||
ClassPathResource(location).inputStream.use { inputStream -> |
||||
val certFactory = CertificateFactory.getInstance("X.509") |
||||
return certFactory.generateCertificate(inputStream) as T |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.http.HttpHeaders |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [CacheControlDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class CacheControlDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when cache control configured then cache control headers in response`() { |
||||
this.spring.register(CacheControlConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") } |
||||
header { string(HttpHeaders.EXPIRES, "0") } |
||||
header { string(HttpHeaders.PRAGMA, "no-cache") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CacheControlConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
cacheControl { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when cache control disabled then no cache control headers in response`() { |
||||
this.spring.register(CacheControlDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { doesNotExist(HttpHeaders.CACHE_CONTROL) } |
||||
header { doesNotExist(HttpHeaders.EXPIRES) } |
||||
header { doesNotExist(HttpHeaders.PRAGMA) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CacheControlDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
cacheControl { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [ContentSecurityPolicyDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class ContentSecurityPolicyDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when content security policy configured then header in response`() { |
||||
this.spring.register(ContentSecurityPolicyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY, "default-src 'self'") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ContentSecurityPolicyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
contentSecurityPolicy { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when content security policy configured with custom policy directives then custom directives in header`() { |
||||
this.spring.register(CustomPolicyDirectivesConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY, "default-src 'self'; script-src trustedscripts.example.com") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomPolicyDirectivesConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
contentSecurityPolicy { |
||||
policyDirectives = "default-src 'self'; script-src trustedscripts.example.com" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when report only content security policy report only header in response`() { |
||||
this.spring.register(ReportOnlyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY_REPORT_ONLY, "default-src 'self'") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ReportOnlyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
contentSecurityPolicy { |
||||
reportOnly = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [ContentTypeOptionsDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class ContentTypeOptionsDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when content type options configured then X-Content-Type-Options header in response`() { |
||||
this.spring.register(ContentTypeOptionsConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { string(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, "nosniff") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ContentTypeOptionsConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
contentTypeOptions { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when content type options disabled then X-Content-Type-Options header not in response`() { |
||||
this.spring.register(ContentTypeOptionsDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { doesNotExist(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ContentTypeOptionsDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
contentTypeOptions { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter |
||||
import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [FrameOptionsDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class FrameOptionsDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when frame options configured then frame options deny header`() { |
||||
this.spring.register(FrameOptionsConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FrameOptionsConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
frameOptions { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when frame options deny configured then frame options deny header`() { |
||||
this.spring.register(FrameOptionsDenyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FrameOptionsDenyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
frameOptions { |
||||
deny = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when frame options same origin configured then frame options same origin header`() { |
||||
this.spring.register(FrameOptionsSameOriginConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN.name) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FrameOptionsSameOriginConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
frameOptions { |
||||
sameOrigin = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when frame options same origin and deny configured then frame options deny header`() { |
||||
this.spring.register(FrameOptionsSameOriginAndDenyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FrameOptionsSameOriginAndDenyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
frameOptions { |
||||
sameOrigin = true |
||||
deny = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when frame options disabled then no frame options header in response`() { |
||||
this.spring.register(FrameOptionsDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { doesNotExist(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class FrameOptionsDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
frameOptions { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,230 @@
@@ -0,0 +1,230 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.assertj.core.api.Assertions |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [HttpPublicKeyPinningDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class HttpPublicKeyPinningDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
private val HPKP_RO_HEADER_NAME = "Public-Key-Pins-Report-Only" |
||||
private val HPKP_HEADER_NAME = "Public-Key-Pins" |
||||
|
||||
@Test |
||||
fun `headers when HPKP configured and no pin then no headers in response`() { |
||||
this.spring.register(HpkpNoPinConfig::class.java).autowire() |
||||
|
||||
val result = this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andReturn() |
||||
|
||||
Assertions.assertThat(result.response.headerNames).isEmpty() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HpkpNoPinConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpPublicKeyPinning { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when HPKP configured with pin then header in response`() { |
||||
this.spring.register(HpkpPinConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(HPKP_RO_HEADER_NAME, "max-age=5184000 ; pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\"") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HpkpPinConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpPublicKeyPinning { |
||||
pins = mapOf(Pair("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "sha256")) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when HPKP configured with maximum age then maximum age in header`() { |
||||
this.spring.register(HpkpMaxAgeConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(HPKP_RO_HEADER_NAME, "max-age=604800 ; pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\"") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HpkpMaxAgeConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpPublicKeyPinning { |
||||
pins = mapOf(Pair("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "sha256")) |
||||
maxAgeInSeconds = 604800 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when HPKP configured with report only false then public key pins header in response`() { |
||||
this.spring.register(HpkpReportOnlyFalseConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(HPKP_HEADER_NAME, "max-age=5184000 ; pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\"") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HpkpReportOnlyFalseConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpPublicKeyPinning { |
||||
pins = mapOf(Pair("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "sha256")) |
||||
reportOnly = false |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when HPKP configured with include subdomains then include subdomains in header`() { |
||||
this.spring.register(HpkpIncludeSubdomainsConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { |
||||
string(HPKP_RO_HEADER_NAME, |
||||
"max-age=5184000 ; pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\" ; includeSubDomains") |
||||
} |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HpkpIncludeSubdomainsConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpPublicKeyPinning { |
||||
pins = mapOf(Pair("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "sha256")) |
||||
includeSubDomains = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when HPKP configured with report uri then report uri in header`() { |
||||
this.spring.register(HpkpReportUriConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { |
||||
string(HPKP_RO_HEADER_NAME, |
||||
"max-age=5184000 ; pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\" ; report-uri=\"https://example.com\"") |
||||
} |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HpkpReportUriConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpPublicKeyPinning { |
||||
pins = mapOf(Pair("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "sha256")) |
||||
reportUri = "https://example.com" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when HPKP disabled then no HPKP header in response`() { |
||||
this.spring.register(HpkpDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { |
||||
doesNotExist(HPKP_RO_HEADER_NAME) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HpkpDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
httpPublicKeyPinning { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.assertj.core.api.Assertions |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter |
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [HttpStrictTransportSecurityDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class HttpStrictTransportSecurityDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when hsts configured then headers in response`() { |
||||
this.spring.register(HstsConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=31536000 ; includeSubDomains") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HstsConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpStrictTransportSecurity { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when hsts configured with preload then preload in header`() { |
||||
this.spring.register(HstsPreloadConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=31536000 ; includeSubDomains ; preload") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HstsPreloadConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpStrictTransportSecurity { |
||||
preload = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when hsts configured with max age then max age in header`() { |
||||
this.spring.register(HstsMaxAgeConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=1 ; includeSubDomains") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HstsMaxAgeConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpStrictTransportSecurity { |
||||
maxAgeInSeconds = 1 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when hsts configured and does not match then hsts header not in response`() { |
||||
this.spring.register(HstsCustomMatcherConfig::class.java).autowire() |
||||
|
||||
val result = this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andReturn() |
||||
|
||||
Assertions.assertThat(result.response.headerNames).isEmpty() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HstsCustomMatcherConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
httpStrictTransportSecurity { |
||||
requestMatcher = AntPathRequestMatcher("/secure/**") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `request when hsts disabled then hsts header not in response`() { |
||||
this.spring.register(HstsDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { doesNotExist(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class HstsDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
httpStrictTransportSecurity { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [ReferrerPolicyDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class ReferrerPolicyDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when referrer policy configured then header in response`() { |
||||
this.spring.register(ReferrerPolicyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { string("Referrer-Policy", ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFERRER.policy) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ReferrerPolicyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
referrerPolicy { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when referrer policy configured with custom policy then custom policy in header`() { |
||||
this.spring.register(ReferrerPolicyCustomPolicyConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
header { string("Referrer-Policy", ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN.policy) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ReferrerPolicyCustomPolicyConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
referrerPolicy { |
||||
policy = ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
/* |
||||
* 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.config.web.servlet.headers |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [XssProtectionConfigDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class XssProtectionConfigDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `headers when XSS protection configured then header in response`() { |
||||
this.spring.register(XssProtectionConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class XssProtectionConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
xssProtection { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when XSS protection with block false then mode is not block in header`() { |
||||
this.spring.register(XssProtectionBlockFalseConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class XssProtectionBlockFalseConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
xssProtection { |
||||
block = false |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when XSS protection disabled then X-XSS-Protection header is 0`() { |
||||
this.spring.register(XssProtectionDisabledConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class XssProtectionDisabledConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
defaultsDisabled = true |
||||
xssProtection { |
||||
xssProtectionEnabled = false |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `headers when XSS protection disabled then X-XSS-Protection header not in response`() { |
||||
this.spring.register(XssProtectionDisabledFunctionConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/") { |
||||
secure = true |
||||
}.andExpect { |
||||
header { doesNotExist(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION) } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class XssProtectionDisabledFunctionConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
headers { |
||||
xssProtection { |
||||
disable() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,192 @@
@@ -0,0 +1,192 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.client |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.Mockito |
||||
import org.mockito.Mockito.verify |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [AuthorizationCodeGrantDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class AuthorizationCodeGrantDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Client when custom authorization request repository then repository used`() { |
||||
this.spring.register(RequestRepositoryConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/callback") { |
||||
param("state", "test") |
||||
param("code", "123") |
||||
} |
||||
|
||||
verify(RequestRepositoryConfig.REQUEST_REPOSITORY).loadAuthorizationRequest(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class RequestRepositoryConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REQUEST_REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = Mockito.mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Client { |
||||
authorizationCodeGrant { |
||||
authorizationRequestRepository = REQUEST_REPOSITORY |
||||
} |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `oauth2Client when custom access token response client then client used`() { |
||||
this.spring.register(AuthorizedClientConfig::class.java, ClientConfig::class.java).autowire() |
||||
val authorizationRequest = getOAuth2AuthorizationRequest() |
||||
Mockito.`when`(AuthorizedClientConfig.REQUEST_REPOSITORY.loadAuthorizationRequest(any())) |
||||
.thenReturn(authorizationRequest) |
||||
Mockito.`when`(AuthorizedClientConfig.REQUEST_REPOSITORY.removeAuthorizationRequest(any(), any())) |
||||
.thenReturn(authorizationRequest) |
||||
Mockito.`when`(AuthorizedClientConfig.CLIENT.getTokenResponse(any())) |
||||
.thenReturn(OAuth2AccessTokenResponse |
||||
.withToken("token") |
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER) |
||||
.build()) |
||||
|
||||
this.mockMvc.get("/callback") { |
||||
param("state", "test") |
||||
param("code", "123") |
||||
} |
||||
|
||||
verify(AuthorizedClientConfig.CLIENT).getTokenResponse(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class AuthorizedClientConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REQUEST_REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = Mockito.mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
var CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> = Mockito.mock(OAuth2AccessTokenResponseClient::class.java) as OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Client { |
||||
authorizationCodeGrant { |
||||
authorizationRequestRepository = REQUEST_REPOSITORY |
||||
accessTokenResponseClient = CLIENT |
||||
} |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `oauth2Client when custom authorization request resolver then request resolver used`() { |
||||
this.spring.register(RequestResolverConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/callback") { |
||||
param("state", "test") |
||||
param("code", "123") |
||||
} |
||||
|
||||
verify(RequestResolverConfig.REQUEST_RESOLVER).resolve(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class RequestResolverConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REQUEST_RESOLVER: OAuth2AuthorizationRequestResolver = Mockito.mock(OAuth2AuthorizationRequestResolver::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Client { |
||||
authorizationCodeGrant { |
||||
authorizationRequestResolver = REQUEST_RESOLVER |
||||
} |
||||
} |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class ClientConfig { |
||||
@Bean |
||||
open fun clientRegistrationRepository(): ClientRegistrationRepository { |
||||
return InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google") |
||||
.registrationId("registrationId") |
||||
.clientId("clientId") |
||||
.clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
|
||||
private fun getOAuth2AuthorizationRequest(): OAuth2AuthorizationRequest? { |
||||
return OAuth2AuthorizationRequest |
||||
.authorizationCode() |
||||
.state("test") |
||||
.clientId("clientId") |
||||
.authorizationUri("https://test") |
||||
.redirectUri("http://localhost/callback") |
||||
.attributes(mapOf(Pair(OAuth2ParameterNames.REGISTRATION_ID, "registrationId"))) |
||||
.build() |
||||
} |
||||
} |
||||
@ -0,0 +1,144 @@
@@ -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.config.web.servlet.oauth2.login |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.Mockito |
||||
import org.mockito.Mockito.verify |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [AuthorizationEndpointDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class AuthorizationEndpointDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Login when custom client registration repository then repository used`() { |
||||
this.spring.register(ResolverConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/oauth2/authorization/google") |
||||
|
||||
verify(ResolverConfig.RESOLVER).resolve(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ResolverConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var RESOLVER: OAuth2AuthorizationRequestResolver = Mockito.mock(OAuth2AuthorizationRequestResolver::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Login { |
||||
authorizationEndpoint { |
||||
authorizationRequestResolver = RESOLVER |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `oauth2Login when custom authorization request repository then repository used`() { |
||||
this.spring.register(RequestRepoConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/oauth2/authorization/google") |
||||
|
||||
verify(RequestRepoConfig.REPOSITORY).saveAuthorizationRequest(any(), any(), any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class RequestRepoConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = Mockito.mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Login { |
||||
authorizationEndpoint { |
||||
authorizationRequestRepository = REPOSITORY |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `oauth2Login when custom authorization uri repository then uri used`() { |
||||
this.spring.register(AuthorizationUriConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.get("/connect/google") |
||||
|
||||
verify(AuthorizationUriConfig.REPOSITORY).saveAuthorizationRequest(any(), any(), any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class AuthorizationUriConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = Mockito.mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2Login { |
||||
authorizationEndpoint { |
||||
authorizationRequestRepository = REPOSITORY |
||||
baseUri = "/connect" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class ClientConfig { |
||||
@Bean |
||||
open fun clientRegistrationRepository(): ClientRegistrationRepository { |
||||
return InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google").clientId("clientId").clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,142 @@
@@ -0,0 +1,142 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.login |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers |
||||
import org.mockito.Mockito |
||||
import org.mockito.Mockito.mock |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest |
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames |
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User |
||||
import org.springframework.security.oauth2.core.user.OAuth2User |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import java.util.* |
||||
|
||||
/** |
||||
* Tests for [RedirectionEndpointDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class RedirectionEndpointDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Login when redirection endpoint configured then custom redirection endpoing used`() { |
||||
this.spring.register(UserServiceConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
val registrationId = "registrationId" |
||||
val attributes = HashMap<String, Any>() |
||||
attributes[OAuth2ParameterNames.REGISTRATION_ID] = registrationId |
||||
val authorizationRequest = OAuth2AuthorizationRequest |
||||
.authorizationCode() |
||||
.state("test") |
||||
.clientId("clientId") |
||||
.authorizationUri("https://test") |
||||
.redirectUri("http://localhost/callback") |
||||
.attributes(attributes) |
||||
.build() |
||||
Mockito.`when`(UserServiceConfig.REPOSITORY.removeAuthorizationRequest(ArgumentMatchers.any(), ArgumentMatchers.any())) |
||||
.thenReturn(authorizationRequest) |
||||
Mockito.`when`(UserServiceConfig.CLIENT.getTokenResponse(ArgumentMatchers.any())) |
||||
.thenReturn(OAuth2AccessTokenResponse |
||||
.withToken("token") |
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER) |
||||
.build()) |
||||
Mockito.`when`(UserServiceConfig.USER_SERVICE.loadUser(ArgumentMatchers.any())) |
||||
.thenReturn(DefaultOAuth2User(listOf(SimpleGrantedAuthority("ROLE_USER")), mapOf(Pair("user", "user")), "user")) |
||||
|
||||
this.mockMvc.get("/callback") { |
||||
param("code", "auth-code") |
||||
param("state", "test") |
||||
}.andExpect { |
||||
redirectedUrl("/") |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class UserServiceConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var USER_SERVICE: OAuth2UserService<OAuth2UserRequest, OAuth2User> = mock(OAuth2UserService::class.java) as OAuth2UserService<OAuth2UserRequest, OAuth2User> |
||||
var CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> = mock(OAuth2AccessTokenResponseClient::class.java) as OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> |
||||
var REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2Login { |
||||
userInfoEndpoint { |
||||
userService = USER_SERVICE |
||||
} |
||||
tokenEndpoint { |
||||
accessTokenResponseClient = CLIENT |
||||
} |
||||
authorizationEndpoint { |
||||
authorizationRequestRepository = REPOSITORY |
||||
} |
||||
redirectionEndpoint { |
||||
baseUri = "/callback" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class ClientConfig { |
||||
@Bean |
||||
open fun clientRegistrationRepository(): ClientRegistrationRepository { |
||||
return InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google") |
||||
.registrationId("registrationId") |
||||
.clientId("clientId") |
||||
.clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,128 @@
@@ -0,0 +1,128 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.login |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.Mockito |
||||
import org.mockito.Mockito.`when` |
||||
import org.mockito.Mockito.mock |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import java.util.* |
||||
|
||||
/** |
||||
* Tests for [TokenEndpointDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class TokenEndpointDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Login when custom access token response client then client used`() { |
||||
this.spring.register(TokenConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
val registrationId = "registrationId" |
||||
val attributes = HashMap<String, Any>() |
||||
attributes[OAuth2ParameterNames.REGISTRATION_ID] = registrationId |
||||
val authorizationRequest = OAuth2AuthorizationRequest |
||||
.authorizationCode() |
||||
.state("test") |
||||
.clientId("clientId") |
||||
.authorizationUri("https://test") |
||||
.redirectUri("http://localhost/login/oauth2/code/google") |
||||
.attributes(attributes) |
||||
.build() |
||||
`when`(TokenConfig.REPOSITORY.removeAuthorizationRequest(any(), any())) |
||||
.thenReturn(authorizationRequest) |
||||
`when`(TokenConfig.CLIENT.getTokenResponse(any())).thenReturn(OAuth2AccessTokenResponse |
||||
.withToken("token") |
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER) |
||||
.build()) |
||||
|
||||
this.mockMvc.get("/login/oauth2/code/google") { |
||||
param("code", "auth-code") |
||||
param("state", "test") |
||||
} |
||||
|
||||
Mockito.verify(TokenConfig.CLIENT).getTokenResponse(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class TokenConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> = mock(OAuth2AccessTokenResponseClient::class.java) as OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> |
||||
var REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2Login { |
||||
tokenEndpoint { |
||||
accessTokenResponseClient = CLIENT |
||||
} |
||||
authorizationEndpoint { |
||||
authorizationRequestRepository = REPOSITORY |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class ClientConfig { |
||||
@Bean |
||||
open fun clientRegistrationRepository(): ClientRegistrationRepository { |
||||
return InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google") |
||||
.registrationId("registrationId") |
||||
.clientId("clientId") |
||||
.clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,139 @@
@@ -0,0 +1,139 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.login |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.Mockito |
||||
import org.mockito.Mockito.`when` |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider |
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient |
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository |
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest |
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService |
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames |
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User |
||||
import org.springframework.security.oauth2.core.user.OAuth2User |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import java.util.* |
||||
|
||||
/** |
||||
* Tests for [UserInfoEndpointDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class UserInfoEndpointDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `oauth2Login when custom user service then user service used`() { |
||||
this.spring.register(UserServiceConfig::class.java, ClientConfig::class.java).autowire() |
||||
|
||||
val registrationId = "registrationId" |
||||
val attributes = HashMap<String, Any>() |
||||
attributes[OAuth2ParameterNames.REGISTRATION_ID] = registrationId |
||||
val authorizationRequest = OAuth2AuthorizationRequest |
||||
.authorizationCode() |
||||
.state("test") |
||||
.clientId("clientId") |
||||
.authorizationUri("https://test") |
||||
.redirectUri("http://localhost/login/oauth2/code/google") |
||||
.attributes(attributes) |
||||
.build() |
||||
`when`(UserServiceConfig.REPOSITORY.removeAuthorizationRequest(any(), any())) |
||||
.thenReturn(authorizationRequest) |
||||
`when`(UserServiceConfig.CLIENT.getTokenResponse(any())) |
||||
.thenReturn(OAuth2AccessTokenResponse |
||||
.withToken("token") |
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER) |
||||
.build()) |
||||
`when`(UserServiceConfig.USER_SERVICE.loadUser(any())) |
||||
.thenReturn(DefaultOAuth2User(listOf(SimpleGrantedAuthority("ROLE_USER")), mapOf(Pair("user", "user")), "user")) |
||||
|
||||
this.mockMvc.get("/login/oauth2/code/google") { |
||||
param("code", "auth-code") |
||||
param("state", "test") |
||||
} |
||||
|
||||
Mockito.verify(UserServiceConfig.USER_SERVICE).loadUser(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class UserServiceConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var USER_SERVICE: OAuth2UserService<OAuth2UserRequest, OAuth2User> = Mockito.mock(OAuth2UserService::class.java) as OAuth2UserService<OAuth2UserRequest, OAuth2User> |
||||
var CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> = Mockito.mock(OAuth2AccessTokenResponseClient::class.java) as OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> |
||||
var REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = Mockito.mock(AuthorizationRequestRepository::class.java) as AuthorizationRequestRepository<OAuth2AuthorizationRequest> |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2Login { |
||||
userInfoEndpoint { |
||||
userService = USER_SERVICE |
||||
} |
||||
tokenEndpoint { |
||||
accessTokenResponseClient = CLIENT |
||||
} |
||||
authorizationEndpoint { |
||||
authorizationRequestRepository = REPOSITORY |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class ClientConfig { |
||||
@Bean |
||||
open fun clientRegistrationRepository(): ClientRegistrationRepository { |
||||
return InMemoryClientRegistrationRepository( |
||||
CommonOAuth2Provider.GOOGLE |
||||
.getBuilder("google") |
||||
.registrationId("registrationId") |
||||
.clientId("clientId") |
||||
.clientSecret("clientSecret") |
||||
.build() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,129 @@
@@ -0,0 +1,129 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.resourceserver |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.core.convert.converter.Converter |
||||
import org.springframework.security.authentication.AbstractAuthenticationToken |
||||
import org.springframework.security.authentication.TestingAuthenticationToken |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames |
||||
import org.springframework.security.oauth2.jwt.Jwt |
||||
import org.springframework.security.oauth2.jwt.JwtDecoder |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
|
||||
/** |
||||
* Tests for [JwtDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class JwtDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `JWT when custom JWT decoder then bean not required`() { |
||||
this.spring.register(CustomJwtDecoderConfig::class.java).autowire() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomJwtDecoderConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2ResourceServer { |
||||
jwt { |
||||
jwtDecoder = mock(JwtDecoder::class.java) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `JWT when custom jwkSetUri then bean not required`() { |
||||
this.spring.register(CustomJwkSetUriConfig::class.java).autowire() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomJwkSetUriConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
oauth2ResourceServer { |
||||
jwt { |
||||
jwkSetUri = "https://jwk-uri" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `opaque token when custom JWT authentication converter then converter used`() { |
||||
this.spring.register(CustomJwtAuthenticationConverterConfig::class.java).autowire() |
||||
`when`(CustomJwtAuthenticationConverterConfig.DECODER.decode(anyString())).thenReturn( |
||||
Jwt.withTokenValue("token") |
||||
.header("alg", "none") |
||||
.claim(IdTokenClaimNames.SUB, "user") |
||||
.build()) |
||||
`when`(CustomJwtAuthenticationConverterConfig.CONVERTER.convert(any())) |
||||
.thenReturn(TestingAuthenticationToken("test", "this", "ROLE")) |
||||
this.mockMvc.get("/") { |
||||
header("Authorization", "Bearer token") |
||||
} |
||||
|
||||
verify(CustomJwtAuthenticationConverterConfig.CONVERTER).convert(any()) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomJwtAuthenticationConverterConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var CONVERTER: Converter<Jwt, out AbstractAuthenticationToken> = mock(Converter::class.java) as Converter<Jwt, out AbstractAuthenticationToken> |
||||
var DECODER: JwtDecoder = mock(JwtDecoder::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2ResourceServer { |
||||
jwt { |
||||
jwtAuthenticationConverter = CONVERTER |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun jwtDecoder(): JwtDecoder { |
||||
return DECODER |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
|
||||
/* |
||||
* 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.config.web.servlet.oauth2.resourceserver |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.ArgumentMatchers |
||||
import org.mockito.ArgumentMatchers.any |
||||
import org.mockito.ArgumentMatchers.eq |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.http.* |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.Authentication |
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal |
||||
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal |
||||
import org.springframework.security.oauth2.jwt.JwtClaimNames |
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector |
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.web.bind.annotation.GetMapping |
||||
import org.springframework.web.bind.annotation.RestController |
||||
import org.springframework.web.client.RestOperations |
||||
|
||||
/** |
||||
* Tests for [OpaqueTokenDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class OpaqueTokenDslTests { |
||||
@Rule |
||||
@JvmField |
||||
val spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `opaque token when defaults then uses introspection`() { |
||||
this.spring.register(DefaultOpaqueConfig::class.java, AuthenticationController::class.java).autowire() |
||||
val headers = HttpHeaders() |
||||
headers.contentType = MediaType.APPLICATION_JSON |
||||
val entity = ResponseEntity("{\n" + |
||||
" \"active\" : true,\n" + |
||||
" \"sub\": \"test-subject\",\n" + |
||||
" \"scope\": \"message:read\",\n" + |
||||
" \"exp\": 4683883211\n" + |
||||
"}", headers, HttpStatus.OK) |
||||
`when`(DefaultOpaqueConfig.REST.exchange(any(RequestEntity::class.java), eq(String::class.java))) |
||||
.thenReturn(entity) |
||||
|
||||
this.mockMvc.get("/authenticated") { |
||||
header("Authorization", "Bearer token") |
||||
}.andExpect { |
||||
status { isOk } |
||||
content { string("test-subject") } |
||||
} |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class DefaultOpaqueConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var REST: RestOperations = mock(RestOperations::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2ResourceServer { |
||||
opaqueToken { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun rest(): RestOperations { |
||||
return REST |
||||
} |
||||
|
||||
@Bean |
||||
open fun tokenIntrospectionClient(): NimbusOpaqueTokenIntrospector { |
||||
return NimbusOpaqueTokenIntrospector("https://example.org/introspect", REST) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `opaque token when custom introspector set then introspector used`() { |
||||
this.spring.register(CustomIntrospectorConfig::class.java, AuthenticationController::class.java).autowire() |
||||
`when`(CustomIntrospectorConfig.INTROSPECTOR.introspect(ArgumentMatchers.anyString())) |
||||
.thenReturn(DefaultOAuth2AuthenticatedPrincipal(mapOf(Pair(JwtClaimNames.SUB, "mock-subject")), emptyList())) |
||||
|
||||
this.mockMvc.get("/authenticated") { |
||||
header("Authorization", "Bearer token") |
||||
} |
||||
|
||||
verify(CustomIntrospectorConfig.INTROSPECTOR).introspect("token") |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class CustomIntrospectorConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
var INTROSPECTOR: OpaqueTokenIntrospector = mock(OpaqueTokenIntrospector::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
oauth2ResourceServer { |
||||
opaqueToken { |
||||
introspector = INTROSPECTOR |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@RestController |
||||
class AuthenticationController { |
||||
@GetMapping("/authenticated") |
||||
fun authenticated(@AuthenticationPrincipal authentication: Authentication): String { |
||||
return authentication.name |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
/* |
||||
* 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.config.web.servlet.session |
||||
|
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.mockito.Mockito.* |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.mock.web.MockHttpSession |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.core.session.SessionInformation |
||||
import org.springframework.security.core.session.SessionRegistry |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf |
||||
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status |
||||
import java.util.* |
||||
|
||||
/** |
||||
* Tests for [SessionConcurrencyDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class SessionConcurrencyDslTests { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `session concurrency when maximum sessions then no more sessions allowed`() { |
||||
this.spring.register(MaximumSessionsConfig::class.java, UserDetailsConfig::class.java).autowire() |
||||
|
||||
this.mockMvc.perform(post("/login") |
||||
.with(csrf()) |
||||
.param("username", "user") |
||||
.param("password", "password")) |
||||
|
||||
this.mockMvc.perform(post("/login") |
||||
.with(csrf()) |
||||
.param("username", "user") |
||||
.param("password", "password")) |
||||
.andExpect(status().isFound) |
||||
.andExpect(redirectedUrl("/login?error")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class MaximumSessionsConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
sessionConcurrency { |
||||
maximumSessions = 1 |
||||
maxSessionsPreventsLogin = true |
||||
} |
||||
} |
||||
formLogin { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session concurrency when expired url then redirects to url`() { |
||||
this.spring.register(ExpiredUrlConfig::class.java).autowire() |
||||
|
||||
val session = MockHttpSession() |
||||
val sessionInformation = SessionInformation("", session.id, Date(0)) |
||||
sessionInformation.expireNow() |
||||
`when`(ExpiredUrlConfig.sessionRegistry.getSessionInformation(any())).thenReturn(sessionInformation) |
||||
|
||||
this.mockMvc.perform(get("/").session(session)) |
||||
.andExpect(redirectedUrl("/expired-session")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ExpiredUrlConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
val sessionRegistry: SessionRegistry = mock(SessionRegistry::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
sessionConcurrency { |
||||
maximumSessions = 1 |
||||
expiredUrl = "/expired-session" |
||||
sessionRegistry = sessionRegistry() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun sessionRegistry(): SessionRegistry { |
||||
return sessionRegistry |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session concurrency when expired session strategy then strategy used`() { |
||||
this.spring.register(ExpiredSessionStrategyConfig::class.java).autowire() |
||||
|
||||
val session = MockHttpSession() |
||||
val sessionInformation = SessionInformation("", session.id, Date(0)) |
||||
sessionInformation.expireNow() |
||||
`when`(ExpiredSessionStrategyConfig.sessionRegistry.getSessionInformation(any())).thenReturn(sessionInformation) |
||||
|
||||
this.mockMvc.perform(get("/").session(session)) |
||||
.andExpect(redirectedUrl("/expired-session")) |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ExpiredSessionStrategyConfig : WebSecurityConfigurerAdapter() { |
||||
companion object { |
||||
val sessionRegistry: SessionRegistry = mock(SessionRegistry::class.java) |
||||
} |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
sessionConcurrency { |
||||
maximumSessions = 1 |
||||
expiredSessionStrategy = SimpleRedirectSessionInformationExpiredStrategy("/expired-session") |
||||
sessionRegistry = sessionRegistry() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
open fun sessionRegistry(): SessionRegistry { |
||||
return sessionRegistry |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class UserDetailsConfig { |
||||
@Bean |
||||
open fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,195 @@
@@ -0,0 +1,195 @@
|
||||
/* |
||||
* 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.config.web.servlet.session |
||||
|
||||
import org.assertj.core.api.Assertions.assertThat |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.mock.web.MockHttpSession |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.config.test.SpringTestRule |
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders |
||||
|
||||
/** |
||||
* Tests for [SessionFixationDsl] |
||||
* |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
class SessionFixationDslTest { |
||||
@Rule |
||||
@JvmField |
||||
var spring = SpringTestRule() |
||||
|
||||
@Autowired |
||||
lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `session fixation when strategy is new session then new session created and attributes are not preserved`() { |
||||
this.spring.register(NewSessionConfig::class.java, UserDetailsConfig::class.java).autowire() |
||||
val givenSession = MockHttpSession() |
||||
val givenSessionId = givenSession.id |
||||
givenSession.clearAttributes() |
||||
givenSession.setAttribute("name", "value") |
||||
|
||||
val result = this.mockMvc.perform(MockMvcRequestBuilders.get("/") |
||||
.with(httpBasic("user", "password")) |
||||
.session(givenSession)) |
||||
.andReturn() |
||||
|
||||
val resultingSession = result.request.getSession(false) |
||||
assertThat(resultingSession).isNotEqualTo(givenSession) |
||||
assertThat(resultingSession!!.id).isNotEqualTo(givenSessionId) |
||||
assertThat(resultingSession.getAttribute("name")).isNull() |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class NewSessionConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
sessionFixation { |
||||
newSession() |
||||
} |
||||
} |
||||
httpBasic { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session fixation when strategy is migrate session then new session created and attributes are preserved`() { |
||||
this.spring.register(MigrateSessionConfig::class.java, UserDetailsConfig::class.java).autowire() |
||||
val givenSession = MockHttpSession() |
||||
val givenSessionId = givenSession.id |
||||
givenSession.clearAttributes() |
||||
givenSession.setAttribute("name", "value") |
||||
|
||||
val result = this.mockMvc.perform(MockMvcRequestBuilders.get("/") |
||||
.with(httpBasic("user", "password")) |
||||
.session(givenSession)) |
||||
.andReturn() |
||||
|
||||
val resultingSession = result.request.getSession(false) |
||||
assertThat(resultingSession).isNotEqualTo(givenSession) |
||||
assertThat(resultingSession!!.id).isNotEqualTo(givenSessionId) |
||||
assertThat(resultingSession.getAttribute("name")).isEqualTo("value") |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class MigrateSessionConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
sessionFixation { |
||||
migrateSession() |
||||
} |
||||
} |
||||
httpBasic { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session fixation when strategy is change session id then session id changes and attributes preserved`() { |
||||
this.spring.register(ChangeSessionIdConfig::class.java, UserDetailsConfig::class.java).autowire() |
||||
val givenSession = MockHttpSession() |
||||
val givenSessionId = givenSession.id |
||||
givenSession.clearAttributes() |
||||
givenSession.setAttribute("name", "value") |
||||
|
||||
val result = this.mockMvc.perform(MockMvcRequestBuilders.get("/") |
||||
.with(httpBasic("user", "password")) |
||||
.session(givenSession)) |
||||
.andReturn() |
||||
|
||||
val resultingSession = result.request.getSession(false) |
||||
assertThat(resultingSession).isEqualTo(givenSession) |
||||
assertThat(resultingSession!!.id).isNotEqualTo(givenSessionId) |
||||
assertThat(resultingSession.getAttribute("name")).isEqualTo("value") |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class ChangeSessionIdConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
sessionFixation { |
||||
changeSessionId() |
||||
} |
||||
} |
||||
httpBasic { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `session fixation when strategy is none then session does not change`() { |
||||
this.spring.register(NoneConfig::class.java, UserDetailsConfig::class.java).autowire() |
||||
val givenSession = MockHttpSession() |
||||
val givenSessionId = givenSession.id |
||||
givenSession.clearAttributes() |
||||
givenSession.setAttribute("name", "value") |
||||
|
||||
val result = this.mockMvc.perform(MockMvcRequestBuilders.get("/") |
||||
.with(httpBasic("user", "password")) |
||||
.session(givenSession)) |
||||
.andReturn() |
||||
|
||||
val resultingSession = result.request.getSession(false) |
||||
assertThat(resultingSession).isEqualTo(givenSession) |
||||
assertThat(resultingSession!!.id).isEqualTo(givenSessionId) |
||||
assertThat(resultingSession.getAttribute("name")).isEqualTo("value") |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
open class NoneConfig : WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
sessionManagement { |
||||
sessionFixation { |
||||
none() |
||||
} |
||||
} |
||||
httpBasic { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
open class UserDetailsConfig { |
||||
@Bean |
||||
open fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
||||
|
||||
plugins { |
||||
id("io.spring.convention.spring-sample-boot") |
||||
kotlin("jvm") |
||||
kotlin("plugin.spring") version "1.3.61" |
||||
} |
||||
|
||||
repositories { |
||||
mavenCentral() |
||||
} |
||||
|
||||
dependencies { |
||||
implementation(project(":spring-security-core")) |
||||
implementation(project(":spring-security-config")) |
||||
implementation(project(":spring-security-web")) |
||||
implementation("org.springframework.boot:spring-boot-starter-web") |
||||
implementation("org.springframework.boot:spring-boot-starter-thymeleaf") |
||||
implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity5") |
||||
implementation("org.jetbrains.kotlin:kotlin-reflect") |
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") |
||||
testImplementation(project(":spring-security-test")) |
||||
testImplementation("org.springframework.boot:spring-boot-starter-test") |
||||
} |
||||
|
||||
tasks.withType<KotlinCompile> { |
||||
kotlinOptions { |
||||
freeCompilerArgs = listOf("-Xjsr305=strict") |
||||
jvmTarget = "1.8" |
||||
} |
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* 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.samples |
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication |
||||
import org.springframework.boot.runApplication |
||||
|
||||
/** |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
@SpringBootApplication |
||||
class KotlinApplication |
||||
|
||||
fun main(args: Array<String>) { |
||||
runApplication<KotlinApplication>(*args) |
||||
} |
||||
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/* |
||||
* 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.samples.config |
||||
|
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||
import org.springframework.security.config.web.servlet.invoke |
||||
import org.springframework.security.core.userdetails.User |
||||
import org.springframework.security.core.userdetails.UserDetailsService |
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager |
||||
|
||||
/** |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
@EnableWebSecurity |
||||
class SecurityConfig : WebSecurityConfigurerAdapter() { |
||||
|
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize("/css/**", permitAll) |
||||
authorize("/user/**", hasAuthority("ROLE_USER")) |
||||
} |
||||
formLogin { |
||||
loginPage = "/log-in" |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
public override fun userDetailsService(): UserDetailsService { |
||||
val userDetails = User.withDefaultPasswordEncoder() |
||||
.username("user") |
||||
.password("password") |
||||
.roles("USER") |
||||
.build() |
||||
return InMemoryUserDetailsManager(userDetails) |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* 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.samples.web |
||||
|
||||
import org.springframework.stereotype.Controller |
||||
import org.springframework.web.bind.annotation.GetMapping |
||||
|
||||
/** |
||||
* @author Eleftheria Stein |
||||
*/ |
||||
@Controller |
||||
class MainController { |
||||
|
||||
@GetMapping("/") |
||||
fun index(): String { |
||||
return "index" |
||||
} |
||||
|
||||
@GetMapping("/user/index") |
||||
fun userIndex(): String { |
||||
return "user/index" |
||||
} |
||||
|
||||
@GetMapping("/log-in") |
||||
fun login(): String { |
||||
return "login" |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
server: |
||||
port: 8080 |
||||
|
||||
spring: |
||||
thymeleaf: |
||||
cache: false |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
body { |
||||
font-family: sans; |
||||
font-size: 1em; |
||||
} |
||||
|
||||
div.logout { |
||||
float: right; |
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<!DOCTYPE html> |
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> |
||||
<head> |
||||
<title>Hello Spring Security</title> |
||||
<meta charset="utf-8" /> |
||||
<link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> |
||||
</head> |
||||
<body> |
||||
<div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()"> |
||||
Logged in user: <span sec:authentication="name"></span> | |
||||
Roles: <span sec:authentication="principal.authorities"></span> |
||||
<div> |
||||
<form action="#" th:action="@{/logout}" method="post"> |
||||
<input type="submit" value="Logout" /> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
<h1>Hello Spring Security</h1> |
||||
<p>This is an unsecured page, but you can access the secured pages after authenticating.</p> |
||||
<ul> |
||||
<li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li> |
||||
</ul> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html> |
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> |
||||
<head> |
||||
<title>Login page</title> |
||||
<meta charset="utf-8" /> |
||||
<link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> |
||||
</head> |
||||
<body> |
||||
<h1>Login page</h1> |
||||
<p>Example user: user / password</p> |
||||
<form th:action="@{/log-in}" method="post"> |
||||
<label for="username">Username</label>: |
||||
<input type="text" id="username" name="username" autofocus="autofocus" /> <br /> |
||||
<label for="password">Password</label>: |
||||
<input type="password" id="password" name="password" /> <br /> |
||||
<input type="submit" value="Log in" /> |
||||
</form> |
||||
<p><a href="/" th:href="@{/}">Back to home page</a></p> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
<!-- |
||||
~ 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. |
||||
--> |
||||
|
||||
<!DOCTYPE html> |
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> |
||||
<head> |
||||
<title>Hello Spring Security</title> |
||||
<meta charset="utf-8" /> |
||||
<link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> |
||||
</head> |
||||
<body> |
||||
<div th:substituteby="index::logout"></div> |
||||
<h1>This is a secured page!</h1> |
||||
<p><a href="/" th:href="@{/}">Back to home page</a></p> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
/* |
||||
* 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.samples |
||||
|
||||
import org.assertj.core.api.Assertions.assertThat |
||||
import org.junit.Test |
||||
import org.junit.runner.RunWith |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc |
||||
import org.springframework.boot.test.context.SpringBootTest |
||||
import org.springframework.mock.web.MockHttpSession |
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin |
||||
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated |
||||
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated |
||||
import org.springframework.test.context.junit4.SpringRunner |
||||
import org.springframework.test.web.servlet.MockMvc |
||||
import org.springframework.test.web.servlet.get |
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status |
||||
|
||||
@RunWith(SpringRunner::class) |
||||
@SpringBootTest |
||||
@AutoConfigureMockMvc |
||||
class KotlinApplicationTests { |
||||
|
||||
@Autowired |
||||
private lateinit var mockMvc: MockMvc |
||||
|
||||
@Test |
||||
fun `index page is not protected`() { |
||||
this.mockMvc.get("/") |
||||
.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `protected page redirects to login`() { |
||||
val mvcResult = this.mockMvc.get("/user/index") |
||||
.andExpect { status { is3xxRedirection } } |
||||
.andReturn() |
||||
|
||||
assertThat(mvcResult.response.redirectedUrl).endsWith("/log-in") |
||||
} |
||||
|
||||
@Test |
||||
fun `valid user permitted to log in`() { |
||||
this.mockMvc.perform(formLogin("/log-in").user("user").password("password")) |
||||
.andExpect(authenticated()) |
||||
} |
||||
|
||||
@Test |
||||
fun `invalid user not permitted to log in`() { |
||||
this.mockMvc.perform(formLogin("/log-in").user("invalid").password("invalid")) |
||||
.andExpect(unauthenticated()) |
||||
.andExpect(status().is3xxRedirection) |
||||
} |
||||
|
||||
@Test |
||||
fun `logged in user can access protected page`() { |
||||
val mvcResult = this.mockMvc.perform(formLogin("/log-in").user("user").password("password")) |
||||
.andExpect(authenticated()).andReturn() |
||||
|
||||
val httpSession = mvcResult.request.getSession(false) as MockHttpSession |
||||
|
||||
this.mockMvc.get("/user/index") { |
||||
session = httpSession |
||||
}.andExpect { |
||||
status { isOk } |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue