Skip to content

Commit

Permalink
support CompositeAuthentication (#23)
Browse files Browse the repository at this point in the history
* support `CompositeAuthentication`

* Improve code coverage : `CompositeAuthentication`
  • Loading branch information
Ahoo-Wang authored Dec 6, 2022
1 parent 884702f commit a167350
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 8 deletions.
1 change: 0 additions & 1 deletion cosec-api/src/test/kotlin/me/ahoo/cosec/api/CoSecTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
*/
package me.ahoo.cosec.api

import me.ahoo.cosec.api.CoSec
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright [2021-present] [ahoo wang <[email protected]> (https://github.com/Ahoo-Wang)].
* 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
* http://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 me.ahoo.cosec.authentication

import me.ahoo.cosec.api.authentication.Authentication
import me.ahoo.cosec.api.authentication.AuthenticationProvider
import me.ahoo.cosec.api.authentication.Credentials
import me.ahoo.cosec.api.principal.CoSecPrincipal
import reactor.core.publisher.Mono

class CompositeAuthentication(
private val authenticationProvider: AuthenticationProvider
) : Authentication<Credentials, CoSecPrincipal> {
override val supportCredentials: Class<Credentials>
get() = Credentials::class.java

override fun authenticate(credentials: Credentials): Mono<CoSecPrincipal> {
val credentialsType = credentials.javaClass
return authenticate(credentialsType, credentials)
}

fun authenticate(credentialsType: Class<out Credentials>, credentials: Credentials): Mono<CoSecPrincipal> {
return authenticationProvider.getRequired<Credentials, CoSecPrincipal, Authentication<Credentials, CoSecPrincipal>>(
credentialsType
).authenticate(credentials)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import me.ahoo.cosec.api.authentication.Authentication
import me.ahoo.cosec.api.authentication.AuthenticationProvider
import me.ahoo.cosec.api.authentication.Credentials
import me.ahoo.cosec.api.principal.CoSecPrincipal
import org.slf4j.LoggerFactory
import java.util.concurrent.ConcurrentHashMap

/**
Expand All @@ -24,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap
* @author ahoo wang
*/
object DefaultAuthenticationProvider : AuthenticationProvider {
private val log = LoggerFactory.getLogger(DefaultAuthenticationProvider::class.java)
private val authenticationMaps: MutableMap<Class<out Credentials>, Authentication<out Credentials, out CoSecPrincipal>>

init {
Expand All @@ -34,6 +36,9 @@ object DefaultAuthenticationProvider : AuthenticationProvider {
credentialsType: Class<C>,
authentication: A
) {
if (log.isInfoEnabled) {
log.info("Register Authentication: {} for Credentials: {}", authentication, credentialsType)
}
authenticationMaps[credentialsType] = authentication
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright [2021-present] [ahoo wang <[email protected]> (https://github.com/Ahoo-Wang)].
* 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
* http://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 me.ahoo.cosec.token

import me.ahoo.cosec.api.authentication.Authentication
import me.ahoo.cosec.api.authentication.Credentials
import me.ahoo.cosec.api.principal.CoSecPrincipal
import me.ahoo.cosec.api.token.CompositeToken
import me.ahoo.cosec.authentication.CompositeAuthentication
import reactor.core.publisher.Mono

class TokenCompositeAuthentication(
private val compositeAuthentication: CompositeAuthentication,
private val tokenConverter: TokenConverter
) : Authentication<Credentials, CoSecPrincipal> {
override val supportCredentials: Class<Credentials>
get() = Credentials::class.java

override fun authenticate(credentials: Credentials): Mono<CoSecPrincipal> {
return authenticate(credentials.javaClass, credentials)
}

fun authenticate(credentialsType: Class<out Credentials>, credentials: Credentials): Mono<CoSecPrincipal> {
return compositeAuthentication.authenticate(credentialsType, credentials)
}

fun authenticateAsToken(credentials: Credentials): Mono<CompositeToken> {
return authenticateAsToken(credentials.javaClass, credentials)
}

fun authenticateAsToken(credentialsType: Class<out Credentials>, credentials: Credentials): Mono<CompositeToken> {
return authenticate(credentialsType, credentials)
.map {
tokenConverter.asToken(it)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright [2021-present] [ahoo wang <[email protected]> (https://github.com/Ahoo-Wang)].
* 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
* http://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 me.ahoo.cosec.authentication

import io.mockk.every
import io.mockk.mockk
import me.ahoo.cosec.api.authentication.Authentication
import me.ahoo.cosec.api.authentication.Credentials
import me.ahoo.cosec.api.principal.CoSecPrincipal
import me.ahoo.cosec.principal.SimplePrincipal
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.*
import org.junit.jupiter.api.Test
import reactor.kotlin.core.publisher.toMono
import reactor.kotlin.test.test

class CompositeAuthenticationTest {
private val compositeAuthentication = CompositeAuthentication(DefaultAuthenticationProvider)

@Test
fun getSupportCredentials() {
assertThat(compositeAuthentication.supportCredentials, `is`(Credentials::class.java))
}

@Test
fun authenticate() {
val credentials = mockk<Credentials>()
val authentication = mockk<Authentication<Credentials, CoSecPrincipal>> {
every { authenticate(any()) } returns SimplePrincipal.ANONYMOUS.toMono()
}
DefaultAuthenticationProvider.register(credentials.javaClass, authentication)

compositeAuthentication.authenticate(credentials)
.test()
.expectNext(SimplePrincipal.ANONYMOUS)
.verifyComplete()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright [2021-present] [ahoo wang <[email protected]> (https://github.com/Ahoo-Wang)].
* 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
* http://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 me.ahoo.cosec.token

import io.mockk.every
import io.mockk.mockk
import me.ahoo.cosec.api.authentication.Authentication
import me.ahoo.cosec.api.authentication.Credentials
import me.ahoo.cosec.api.principal.CoSecPrincipal
import me.ahoo.cosec.authentication.CompositeAuthentication
import me.ahoo.cosec.authentication.DefaultAuthenticationProvider
import me.ahoo.cosec.principal.SimplePrincipal
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.*
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import reactor.kotlin.core.publisher.toMono
import reactor.kotlin.test.test

class TokenCompositeAuthenticationTest {

@Test
fun authenticateAsToken() {
val compositeAuthentication = CompositeAuthentication(DefaultAuthenticationProvider)
val compositeToken = SimpleCompositeToken("accessToken", "refreshToken")
val tokenConverter = mockk<TokenConverter>() {
every { asToken(any()) } returns compositeToken
}
val tokenCompositeAuthentication = TokenCompositeAuthentication(compositeAuthentication, tokenConverter)
assertThat(tokenCompositeAuthentication.supportCredentials, `is`(Credentials::class.java))

val credentials = mockk<Credentials>()
val authentication = mockk<Authentication<Credentials, CoSecPrincipal>> {
every { authenticate(any()) } returns SimplePrincipal.ANONYMOUS.toMono()
}
DefaultAuthenticationProvider.register(credentials.javaClass, authentication)

tokenCompositeAuthentication.authenticateAsToken(credentials)
.test()
.expectNext(compositeToken)
.verifyComplete()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ package me.ahoo.cosec.spring.boot.starter.authentication

import me.ahoo.cosec.api.authentication.Authentication
import me.ahoo.cosec.api.authentication.AuthenticationProvider
import me.ahoo.cosec.api.authentication.Credentials
import me.ahoo.cosec.api.principal.CoSecPrincipal
import me.ahoo.cosec.authentication.CompositeAuthentication
import me.ahoo.cosec.authentication.DefaultAuthenticationProvider
import me.ahoo.cosec.spring.boot.starter.ConditionalOnCoSecEnabled
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Primary

/**
* CoSec AutoConfiguration .
Expand All @@ -36,13 +41,19 @@ class CoSecAuthenticationAutoConfiguration {
@Bean
@ConditionalOnMissingBean
fun authenticationProvider(
authentications: List<Authentication<*, *>>
applicationContext: ApplicationContext
): AuthenticationProvider {
authentications.forEach {
DefaultAuthenticationProvider.register(
it
)
applicationContext.getBeansOfType(Authentication::class.java).values.forEach {
@Suppress("UNCHECKED_CAST")
it as Authentication<Credentials, CoSecPrincipal>
DefaultAuthenticationProvider.register(it)
}
return DefaultAuthenticationProvider
}

@Bean
@Primary
fun compositeAuthentication(authenticationProvider: AuthenticationProvider): CompositeAuthentication {
return CompositeAuthentication(authenticationProvider)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package me.ahoo.cosec.spring.boot.starter.authorization

import com.auth0.jwt.algorithms.Algorithm
import me.ahoo.cosec.api.authorization.Authorization
import me.ahoo.cosec.authentication.CompositeAuthentication
import me.ahoo.cosec.authorization.PermissionRepository
import me.ahoo.cosec.authorization.SimpleAuthorization
import me.ahoo.cosec.context.SecurityContextParser
Expand All @@ -25,6 +26,7 @@ import me.ahoo.cosec.servlet.ServletRequestParser
import me.ahoo.cosec.servlet.ServletRequestSecurityContextParser
import me.ahoo.cosec.servlet.ServletRequestTenantIdParser
import me.ahoo.cosec.spring.boot.starter.ConditionalOnCoSecEnabled
import me.ahoo.cosec.token.TokenCompositeAuthentication
import me.ahoo.cosec.token.TokenConverter
import me.ahoo.cosec.webflux.ReactiveAuthorizationFilter
import me.ahoo.cosec.webflux.ReactiveRequestParser
Expand All @@ -33,6 +35,7 @@ import me.ahoo.cosec.webflux.ReactiveSecurityContextParser
import me.ahoo.cosid.IdGenerator
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass
Expand Down Expand Up @@ -94,6 +97,15 @@ class CoSecAuthorizationAutoConfiguration(private val authorizationProperties: A
return SimpleAuthorization(permissionRepository)
}

@Bean
@ConditionalOnBean(CompositeAuthentication::class)
fun tokenCompositeAuthentication(
compositeAuthentication: CompositeAuthentication,
tokenConverter: TokenConverter
): TokenCompositeAuthentication {
return TokenCompositeAuthentication(compositeAuthentication, tokenConverter)
}

companion object {
const val SERVLET_REQUEST_TENANT_ID_PARSER_BEAN_NAME = "servletRequestTenantIdParser"
const val SERVLET_REQUEST_PARSER_BEAN_NAME = "servletRequestParser"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package me.ahoo.cosec.spring.boot.starter.authentication

import me.ahoo.cosec.api.authentication.AuthenticationProvider
import me.ahoo.cosec.authentication.CompositeAuthentication
import org.assertj.core.api.AssertionsForInterfaceTypes
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.assertj.AssertableApplicationContext
Expand All @@ -31,6 +32,7 @@ internal class CoSecAuthenticationAutoConfigurationTest {
.hasSingleBean(AuthenticationProperties::class.java)
.hasSingleBean(CoSecAuthenticationAutoConfiguration::class.java)
.hasSingleBean(AuthenticationProvider::class.java)
.hasSingleBean(CompositeAuthentication::class.java)
}
}

Expand All @@ -44,6 +46,7 @@ internal class CoSecAuthenticationAutoConfigurationTest {
.doesNotHaveBean(AuthenticationProperties::class.java)
.doesNotHaveBean(CoSecAuthenticationAutoConfiguration::class.java)
.doesNotHaveBean(AuthenticationProvider::class.java)
.doesNotHaveBean(CompositeAuthentication::class.java)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import com.auth0.jwt.algorithms.Algorithm
import me.ahoo.cache.spring.boot.starter.CoCacheAutoConfiguration
import me.ahoo.cosec.api.authorization.Authorization
import me.ahoo.cosec.servlet.AuthorizationFilter
import me.ahoo.cosec.spring.boot.starter.authentication.CoSecAuthenticationAutoConfiguration
import me.ahoo.cosec.spring.boot.starter.authorization.cache.CoSecCacheAutoConfiguration
import me.ahoo.cosec.token.TokenCompositeAuthentication
import me.ahoo.cosec.token.TokenConverter
import me.ahoo.cosid.IdGenerator
import me.ahoo.cosid.test.MockIdGenerator
Expand All @@ -38,6 +40,7 @@ internal class CoSecAuthorizationAutoConfigurationTest {
RedisAutoConfiguration::class.java,
CoCacheAutoConfiguration::class.java,
CoSecCacheAutoConfiguration::class.java,
CoSecAuthenticationAutoConfiguration::class.java,
CoSecAuthorizationAutoConfiguration::class.java
)
.run { context: AssertableApplicationContext ->
Expand All @@ -47,6 +50,7 @@ internal class CoSecAuthorizationAutoConfigurationTest {
.hasSingleBean(Algorithm::class.java)
.hasSingleBean(TokenConverter::class.java)
.hasSingleBean(Authorization::class.java)
.hasSingleBean(TokenCompositeAuthentication::class.java)
.hasBean(CoSecAuthorizationAutoConfiguration.SERVLET_REQUEST_TENANT_ID_PARSER_BEAN_NAME)
.hasBean(CoSecAuthorizationAutoConfiguration.SERVLET_REQUEST_PARSER_BEAN_NAME)
.hasBean(CoSecAuthorizationAutoConfiguration.SERVLET_SECURITY_CONTEXT_PARSER_BEAN_NAME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ class ReactiveSecurityContextsTest {
.then()
.verifyComplete()
}
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# limitations under the License.
#
group=me.ahoo.cosec
version=1.2.3
version=1.2.5
description=RBAC-based And Policy-based Multi-Tenant Reactive Security Framework
website=https://github.com/Ahoo-Wang/CoSec
issues=https://github.com/Ahoo-Wang/CoSec/issues
Expand Down

0 comments on commit a167350

Please sign in to comment.