9 changed files with 524 additions and 12 deletions
@ -0,0 +1,186 @@
@@ -0,0 +1,186 @@
|
||||
/* |
||||
* Copyright 2002-2021 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.config.annotation.method.configuration |
||||
|
||||
import kotlinx.coroutines.flow.collect |
||||
import kotlinx.coroutines.flow.toList |
||||
import kotlinx.coroutines.runBlocking |
||||
import org.assertj.core.api.Assertions.assertThat |
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType |
||||
import org.junit.Test |
||||
import org.junit.runner.RunWith |
||||
import org.springframework.beans.factory.annotation.Autowired |
||||
import org.springframework.context.annotation.Bean |
||||
import org.springframework.context.annotation.Configuration |
||||
import org.springframework.security.access.AccessDeniedException |
||||
import org.springframework.security.test.context.support.WithMockUser |
||||
import org.springframework.test.context.ContextConfiguration |
||||
import org.springframework.test.context.junit4.SpringRunner |
||||
|
||||
@RunWith(SpringRunner::class) |
||||
@ContextConfiguration |
||||
class KotlinEnableReactiveMethodSecurityTests { |
||||
|
||||
@Autowired |
||||
var messageService: KotlinReactiveMessageService? = null |
||||
|
||||
@Test |
||||
fun suspendingGetResultWhenPermitAllThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.suspendingNoAuth()).isEqualTo("success") |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = ["ROLE_ADMIN"]) |
||||
fun suspendingPreAuthorizeHasRoleWhenGrantedThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.suspendingPreAuthorizeHasRole()).isEqualTo("admin") |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun suspendingPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() { |
||||
assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy { |
||||
runBlocking { |
||||
messageService!!.suspendingPreAuthorizeHasRole() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser |
||||
fun suspendingPreAuthorizeBeanWhenGrantedThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.suspendingPreAuthorizeBean(true)).isEqualTo("check") |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun suspendingPreAuthorizeBeanWhenNotAuthorizedThenDenied() { |
||||
assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy { |
||||
runBlocking { |
||||
messageService!!.suspendingPreAuthorizeBean(false) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser("user") |
||||
fun suspendingPostAuthorizeWhenAuthorizedThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.suspendingPostAuthorizeContainsName()).isEqualTo("user") |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser("other-user") |
||||
fun suspendingPostAuthorizeWhenNotAuthorizedThenDenied() { |
||||
assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy { |
||||
runBlocking { |
||||
messageService!!.suspendingPostAuthorizeContainsName() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = ["ROLE_ADMIN"]) |
||||
fun suspendingFlowPreAuthorizeHasRoleWhenGrantedThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.suspendingFlowPreAuthorize().toList()).containsExactly(1, 2, 3) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun suspendingFlowPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() { |
||||
assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy { |
||||
runBlocking { |
||||
messageService!!.suspendingFlowPreAuthorize().collect() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun suspendingFlowPostAuthorizeWhenAuthorizedThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.suspendingFlowPostAuthorize(true).toList()).containsExactly(1, 2, 3) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun suspendingFlowPostAuthorizeWhenNotAuthorizedThenDenied() { |
||||
assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy { |
||||
runBlocking { |
||||
messageService!!.suspendingFlowPostAuthorize(false).collect() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
@WithMockUser(authorities = ["ROLE_ADMIN"]) |
||||
fun flowPreAuthorizeHasRoleWhenGrantedThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.flowPreAuthorize().toList()).containsExactly(1, 2, 3) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun flowPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() { |
||||
assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy { |
||||
runBlocking { |
||||
messageService!!.flowPreAuthorize().collect() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun flowPostAuthorizeWhenAuthorizedThenSuccess() { |
||||
runBlocking { |
||||
assertThat(messageService!!.flowPostAuthorize(true).toList()).containsExactly(1, 2, 3) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun flowPostAuthorizeWhenNotAuthorizedThenDenied() { |
||||
assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy { |
||||
runBlocking { |
||||
messageService!!.flowPostAuthorize(false).collect() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@EnableReactiveMethodSecurity |
||||
@Configuration |
||||
open class Config { |
||||
|
||||
@Bean |
||||
open fun messageService(): KotlinReactiveMessageServiceImpl { |
||||
return KotlinReactiveMessageServiceImpl() |
||||
} |
||||
|
||||
@Bean |
||||
open fun authz(): Authz { |
||||
return Authz() |
||||
} |
||||
|
||||
open class Authz { |
||||
fun check(r: Boolean): Boolean { |
||||
return r |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/* |
||||
* Copyright 2002-2021 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.config.annotation.method.configuration |
||||
|
||||
import kotlinx.coroutines.flow.Flow |
||||
|
||||
interface KotlinReactiveMessageService { |
||||
|
||||
suspend fun suspendingNoAuth(): String |
||||
|
||||
suspend fun suspendingPreAuthorizeHasRole(): String |
||||
|
||||
suspend fun suspendingPreAuthorizeBean(id: Boolean): String |
||||
|
||||
suspend fun suspendingPostAuthorizeContainsName(): String |
||||
|
||||
suspend fun suspendingFlowPreAuthorize(): Flow<Int> |
||||
|
||||
suspend fun suspendingFlowPostAuthorize(id: Boolean): Flow<Int> |
||||
|
||||
fun flowPreAuthorize(): Flow<Int> |
||||
|
||||
fun flowPostAuthorize(id: Boolean): Flow<Int> |
||||
} |
||||
@ -0,0 +1,91 @@
@@ -0,0 +1,91 @@
|
||||
/* |
||||
* Copyright 2002-2021 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.config.annotation.method.configuration |
||||
|
||||
import kotlinx.coroutines.delay |
||||
import kotlinx.coroutines.flow.Flow |
||||
import kotlinx.coroutines.flow.flow |
||||
import org.springframework.security.access.prepost.PostAuthorize |
||||
import org.springframework.security.access.prepost.PreAuthorize |
||||
|
||||
class KotlinReactiveMessageServiceImpl : KotlinReactiveMessageService { |
||||
|
||||
override suspend fun suspendingNoAuth(): String { |
||||
delay(1) |
||||
return "success" |
||||
} |
||||
|
||||
@PreAuthorize("hasRole('ADMIN')") |
||||
override suspend fun suspendingPreAuthorizeHasRole(): String { |
||||
delay(1) |
||||
return "admin" |
||||
} |
||||
|
||||
@PreAuthorize("@authz.check(#id)") |
||||
override suspend fun suspendingPreAuthorizeBean(id: Boolean): String { |
||||
delay(1) |
||||
return "check" |
||||
} |
||||
|
||||
@PostAuthorize("returnObject?.contains(authentication?.name)") |
||||
override suspend fun suspendingPostAuthorizeContainsName(): String { |
||||
delay(1) |
||||
return "user" |
||||
} |
||||
|
||||
@PreAuthorize("hasRole('ADMIN')") |
||||
override suspend fun suspendingFlowPreAuthorize(): Flow<Int> { |
||||
delay(1) |
||||
return flow { |
||||
for (i in 1..3) { |
||||
delay(1) |
||||
emit(i) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@PostAuthorize("@authz.check(#id)") |
||||
override suspend fun suspendingFlowPostAuthorize(id: Boolean): Flow<Int> { |
||||
delay(1) |
||||
return flow { |
||||
for (i in 1..3) { |
||||
delay(1) |
||||
emit(i) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@PreAuthorize("hasRole('ADMIN')") |
||||
override fun flowPreAuthorize(): Flow<Int> { |
||||
return flow { |
||||
for (i in 1..3) { |
||||
delay(1) |
||||
emit(i) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@PostAuthorize("@authz.check(#id)") |
||||
override fun flowPostAuthorize(id: Boolean): Flow<Int> { |
||||
return flow { |
||||
for (i in 1..3) { |
||||
delay(1) |
||||
emit(i) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue