Skip to content

Add One-Time Token Login support to Kotlin DSL #15727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web

import jakarta.servlet.Filter
import jakarta.servlet.http.HttpServletRequest
import org.checkerframework.checker.units.qual.C
import org.springframework.context.ApplicationContext
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.SecurityConfigurerAdapter
Expand Down Expand Up @@ -60,7 +59,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher
* @param httpConfiguration the configurations to apply to [HttpSecurity]
*/
operator fun HttpSecurity.invoke(httpConfiguration: HttpSecurityDsl.() -> Unit) =
HttpSecurityDsl(this, httpConfiguration).build()
HttpSecurityDsl(this, httpConfiguration).build()

/**
* An [HttpSecurity] Kotlin DSL created by [`http { }`][invoke]
Expand Down Expand Up @@ -104,7 +103,10 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* @param configurer
* the [SecurityConfigurerAdapter] for further customizations
*/
fun <C : SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> apply(configurer: C, configuration: C.() -> Unit = { }): C {
fun <C : SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> apply(
configurer: C,
configuration: C.() -> Unit = { }
): C {
return this.http.apply(configurer).apply(configuration)
}

Expand Down Expand Up @@ -134,7 +136,10 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* the [HttpSecurity] for further customizations
* @since 6.2
*/
fun <C : SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> with(configurer: C, configuration: C.() -> Unit = { }): HttpSecurity? {
fun <C : SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> with(
configurer: C,
configuration: C.() -> Unit = { }
): HttpSecurity? {
return this.http.with(configurer, configuration)
}

Expand Down Expand Up @@ -299,7 +304,8 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* @since 5.7
*/
fun authorizeHttpRequests(authorizeHttpRequestsConfiguration: AuthorizeHttpRequestsDsl.() -> Unit) {
val authorizeHttpRequestsCustomizer = AuthorizeHttpRequestsDsl(this.context).apply(authorizeHttpRequestsConfiguration).get()
val authorizeHttpRequestsCustomizer =
AuthorizeHttpRequestsDsl(this.context).apply(authorizeHttpRequestsConfiguration).get()
this.http.authorizeHttpRequests(authorizeHttpRequestsCustomizer)
}

Expand Down Expand Up @@ -772,42 +778,42 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
this.http.saml2Logout(saml2LogoutCustomizer)
}

/**
* Configures a SAML 2.0 relying party metadata endpoint.
*
* A [RelyingPartyRegistrationRepository] is required and must be registered with
* the [ApplicationContext] or configured via
* [Saml2Dsl.relyingPartyRegistrationRepository]
*
* Example:
*
* The following example shows the minimal configuration required, using a
* hypothetical asserting party.
*
* ```
* @Configuration
* @EnableWebSecurity
* class SecurityConfig {
*
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* saml2Login { }
* saml2Metadata { }
* }
* return http.build()
* }
* }
* ```
* @param saml2MetadataConfiguration custom configuration to configure the
* SAML2 relying party metadata endpoint
* @see [Saml2MetadataDsl]
* @since 6.1
*/
fun saml2Metadata(saml2MetadataConfiguration: Saml2MetadataDsl.() -> Unit) {
val saml2MetadataCustomizer = Saml2MetadataDsl().apply(saml2MetadataConfiguration).get()
this.http.saml2Metadata(saml2MetadataCustomizer)
}
/**
* Configures a SAML 2.0 relying party metadata endpoint.
*
* A [RelyingPartyRegistrationRepository] is required and must be registered with
* the [ApplicationContext] or configured via
* [Saml2Dsl.relyingPartyRegistrationRepository]
*
* Example:
*
* The following example shows the minimal configuration required, using a
* hypothetical asserting party.
*
* ```
* @Configuration
* @EnableWebSecurity
* class SecurityConfig {
*
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* saml2Login { }
* saml2Metadata { }
* }
* return http.build()
* }
* }
* ```
* @param saml2MetadataConfiguration custom configuration to configure the
* SAML2 relying party metadata endpoint
* @see [Saml2MetadataDsl]
* @since 6.1
*/
fun saml2Metadata(saml2MetadataConfiguration: Saml2MetadataDsl.() -> Unit) {
val saml2MetadataCustomizer = Saml2MetadataDsl().apply(saml2MetadataConfiguration).get()
this.http.saml2Metadata(saml2MetadataCustomizer)
}

/**
* Allows configuring how an anonymous user is represented.
Expand Down Expand Up @@ -965,6 +971,36 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
this.http.oidcLogout(oidcLogoutCustomizer)
}

/**
* Configures One-Time Token Login Support.
*
* Example:
*
* ```
* @Configuration
* @EnableWebSecurity
* class SecurityConfig {
*
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* oneTimeTokenLogin {
* generatedOneTimeTokenHandler = MyMagicLinkGeneratedOneTimeTokenHandler()
* }
* }
* return http.build()
* }
* }
*
* ```
* @since 6.4
* @param oneTimeTokenLoginConfiguration custom configuration to configure one-time token login
*/
fun oneTimeTokenLogin(oneTimeTokenLoginConfiguration: OneTimeTokenLoginDsl.() -> Unit) {
val oneTimeTokenLoginCustomizer = OneTimeTokenLoginDsl().apply(oneTimeTokenLoginConfiguration).get()
this.http.oneTimeTokenLogin(oneTimeTokenLoginCustomizer)
}

/**
* Configures Remember Me authentication.
*
Expand Down Expand Up @@ -1050,7 +1086,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* (i.e. known) with Spring Security.
*/
@Suppress("DEPRECATION")
inline fun <reified T: Filter> addFilterAt(filter: Filter) {
inline fun <reified T : Filter> addFilterAt(filter: Filter) {
this.addFilterAt(filter, T::class.java)
}

Expand Down Expand Up @@ -1109,7 +1145,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* (i.e. known) with Spring Security.
*/
@Suppress("DEPRECATION")
inline fun <reified T: Filter> addFilterAfter(filter: Filter) {
inline fun <reified T : Filter> addFilterAfter(filter: Filter) {
this.addFilterAfter(filter, T::class.java)
}

Expand Down Expand Up @@ -1168,7 +1204,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
* (i.e. known) with Spring Security.
*/
@Suppress("DEPRECATION")
inline fun <reified T: Filter> addFilterBefore(filter: Filter) {
inline fun <reified T : Filter> addFilterBefore(filter: Filter) {
this.addFilterBefore(filter, T::class.java)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2002-2024 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.annotation.web

import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.ott.OneTimeTokenService
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.ott.OneTimeTokenLoginConfigurer
import org.springframework.security.web.authentication.AuthenticationConverter
import org.springframework.security.web.authentication.AuthenticationFailureHandler
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
import org.springframework.security.web.authentication.ott.GeneratedOneTimeTokenHandler

/**
* A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 login using idiomatic Kotlin code.
*
* @author Max Batischev
* @since 6.4
* @property oneTimeTokenService configures the [OneTimeTokenService] used to generate and consume
* @property authenticationConverter Use this [AuthenticationConverter] when converting incoming requests to an authentication
* @property authenticationFailureHandler the [AuthenticationFailureHandler] to use when authentication
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] to be used
* @property defaultSubmitPageUrl sets the URL that the default submit page will be generated
* @property showDefaultSubmitPage configures whether the default one-time token submit page should be shown
* @property loginProcessingUrl the URL to process the login request
* @property generateTokenUrl the URL that a One-Time Token generate request will be processed
* @property generatedOneTimeTokenHandler the strategy to be used to handle generated one-time tokens
* @property authenticationProvider the [AuthenticationProvider] to use when authenticating the user
*/
@SecurityMarker
class OneTimeTokenLoginDsl {
var oneTimeTokenService: OneTimeTokenService? = null
var authenticationConverter: AuthenticationConverter? = null
var authenticationFailureHandler: AuthenticationFailureHandler? = null
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null
var defaultSubmitPageUrl: String? = null
var loginProcessingUrl: String? = null
var generateTokenUrl: String? = null
var showDefaultSubmitPage: Boolean? = true
var generatedOneTimeTokenHandler: GeneratedOneTimeTokenHandler? = null
var authenticationProvider: AuthenticationProvider? = null

internal fun get(): (OneTimeTokenLoginConfigurer<HttpSecurity>) -> Unit {
return { oneTimeTokenLoginConfigurer ->
oneTimeTokenService?.also { oneTimeTokenLoginConfigurer.oneTimeTokenService(oneTimeTokenService) }
authenticationConverter?.also { oneTimeTokenLoginConfigurer.authenticationConverter(authenticationConverter) }
authenticationFailureHandler?.also {
oneTimeTokenLoginConfigurer.authenticationFailureHandler(
authenticationFailureHandler
)
}
authenticationSuccessHandler?.also {
oneTimeTokenLoginConfigurer.authenticationSuccessHandler(
authenticationSuccessHandler
)
}
defaultSubmitPageUrl?.also { oneTimeTokenLoginConfigurer.defaultSubmitPageUrl(defaultSubmitPageUrl) }
showDefaultSubmitPage?.also { oneTimeTokenLoginConfigurer.showDefaultSubmitPage(showDefaultSubmitPage!!) }
loginProcessingUrl?.also { oneTimeTokenLoginConfigurer.loginProcessingUrl(loginProcessingUrl) }
generateTokenUrl?.also { oneTimeTokenLoginConfigurer.generateTokenUrl(generateTokenUrl) }
generatedOneTimeTokenHandler?.also {
oneTimeTokenLoginConfigurer.generatedOneTimeTokenHandler(
generatedOneTimeTokenHandler
)
}
authenticationProvider?.also { oneTimeTokenLoginConfigurer.authenticationProvider(authenticationProvider) }
}
}
}
Loading