8 changed files with 1164 additions and 0 deletions
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.config.annotation.method.configuration; |
||||
|
||||
import org.springframework.context.annotation.AdviceMode; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.core.Ordered; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* |
||||
* @author Rob Winch |
||||
* @since 5.0 |
||||
*/ |
||||
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) |
||||
@Target(value = { java.lang.annotation.ElementType.TYPE }) |
||||
@Documented |
||||
@Import({ ReactiveMethodSecuritySelector.class }) |
||||
@Configuration |
||||
public @interface EnableReactiveMethodSecurity { |
||||
/** |
||||
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed |
||||
* to standard Java interface-based proxies. The default is {@code false}. <strong> |
||||
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>. |
||||
* <p>Note that setting this attribute to {@code true} will affect <em>all</em> |
||||
* Spring-managed beans requiring proxying, not just those marked with {@code @Cacheable}. |
||||
* For example, other beans marked with Spring's {@code @Transactional} annotation will |
||||
* be upgraded to subclass proxying at the same time. This approach has no negative |
||||
* impact in practice unless one is explicitly expecting one type of proxy vs another, |
||||
* e.g. in tests. |
||||
*/ |
||||
boolean proxyTargetClass() default false; |
||||
|
||||
/** |
||||
* Indicate how security advice should be applied. The default is |
||||
* {@link AdviceMode#PROXY}. |
||||
* @see AdviceMode |
||||
* |
||||
* @return the {@link AdviceMode} to use |
||||
*/ |
||||
AdviceMode mode() default AdviceMode.PROXY; |
||||
|
||||
/** |
||||
* Indicate the ordering of the execution of the security advisor when multiple |
||||
* advices are applied at a specific joinpoint. The default is |
||||
* {@link Ordered#LOWEST_PRECEDENCE}. |
||||
* |
||||
* @return the order the security advisor should be applied |
||||
*/ |
||||
int order() default Ordered.LOWEST_PRECEDENCE; |
||||
} |
||||
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.config.annotation.method.configuration; |
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.ImportAware; |
||||
import org.springframework.context.annotation.Role; |
||||
import org.springframework.core.type.AnnotationMetadata; |
||||
import org.springframework.security.access.expression.method.*; |
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor; |
||||
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource; |
||||
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; |
||||
import org.springframework.security.access.method.PrePostAdviceMethodInterceptor; |
||||
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
/** |
||||
* @author Rob Winch |
||||
* @since 5.0 |
||||
*/ |
||||
@Configuration |
||||
class ReactiveMethodSecurityConfiguration implements ImportAware { |
||||
private int advisorOrder; |
||||
|
||||
@Bean |
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) |
||||
public MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMethodSecurityMetadataSource source) throws Exception { |
||||
MethodSecurityMetadataSourceAdvisor advisor = new MethodSecurityMetadataSourceAdvisor( |
||||
"securityMethodInterceptor", source, "methodMetadataSource"); |
||||
advisor.setOrder(advisorOrder); |
||||
return advisor; |
||||
} |
||||
|
||||
@Bean |
||||
public DelegatingMethodSecurityMetadataSource methodMetadataSource() { |
||||
ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory( |
||||
new DefaultMethodSecurityExpressionHandler()); |
||||
PrePostAnnotationSecurityMetadataSource prePostSource = new PrePostAnnotationSecurityMetadataSource( |
||||
attributeFactory); |
||||
return new DelegatingMethodSecurityMetadataSource(Arrays.asList(prePostSource)); |
||||
} |
||||
|
||||
@Bean |
||||
public PrePostAdviceMethodInterceptor securityMethodInterceptor(AbstractMethodSecurityMetadataSource source, MethodSecurityExpressionHandler handler) { |
||||
|
||||
ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice( |
||||
handler); |
||||
ExpressionBasedPreInvocationAdvice preAdvice = new ExpressionBasedPreInvocationAdvice(); |
||||
preAdvice.setExpressionHandler(handler); |
||||
|
||||
PrePostAdviceMethodInterceptor result = new PrePostAdviceMethodInterceptor(source); |
||||
result.setPostAdvice(postAdvice); |
||||
result.setPreAdvice(preAdvice); |
||||
return result; |
||||
} |
||||
|
||||
@Bean |
||||
public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() { |
||||
return new DefaultMethodSecurityExpressionHandler(); |
||||
} |
||||
|
||||
@Override |
||||
public void setImportMetadata(AnnotationMetadata importMetadata) { |
||||
this.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableReactiveMethodSecurity.class.getName()).get("order"); |
||||
} |
||||
} |
||||
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.config.annotation.method.configuration; |
||||
|
||||
import org.springframework.cache.annotation.ProxyCachingConfiguration; |
||||
import org.springframework.context.annotation.AdviceMode; |
||||
import org.springframework.context.annotation.AdviceModeImportSelector; |
||||
import org.springframework.context.annotation.AutoProxyRegistrar; |
||||
import org.springframework.lang.Nullable; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Rob Winch |
||||
* @since 5.0 |
||||
*/ |
||||
class ReactiveMethodSecuritySelector extends |
||||
AdviceModeImportSelector<EnableReactiveMethodSecurity> { |
||||
|
||||
@Override |
||||
protected String[] selectImports(AdviceMode adviceMode) { |
||||
switch (adviceMode) { |
||||
case PROXY: |
||||
return getProxyImports(); |
||||
default: |
||||
throw new IllegalStateException("AdviceMode " + adviceMode + " is not supported"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}. |
||||
* <p>Take care of adding the necessary JSR-107 import if it is available. |
||||
*/ |
||||
private String[] getProxyImports() { |
||||
List<String> result = new ArrayList<>(); |
||||
result.add(AutoProxyRegistrar.class.getName()); |
||||
result.add(ReactiveMethodSecurityConfiguration.class.getName()); |
||||
return result.toArray(new String[result.size()]); |
||||
} |
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.config.annotation.method.configuration; |
||||
|
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* @author Rob Winch |
||||
* @since 5.0 |
||||
*/ |
||||
@Component |
||||
public class Authz { |
||||
public boolean check(long id) { |
||||
return id % 2 == 0; |
||||
} |
||||
|
||||
public boolean check(Authentication authentication, String message) { |
||||
return message != null && |
||||
message.contains(authentication.getName()); |
||||
} |
||||
} |
||||
@ -0,0 +1,131 @@
@@ -0,0 +1,131 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.config.annotation.method.configuration; |
||||
|
||||
import org.reactivestreams.Publisher; |
||||
import org.springframework.security.access.prepost.PostAuthorize; |
||||
import org.springframework.security.access.prepost.PreAuthorize; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
public class DelegatingReactiveMessageService implements ReactiveMessageService { |
||||
private final ReactiveMessageService delegate; |
||||
|
||||
public DelegatingReactiveMessageService(ReactiveMessageService delegate) { |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
@Override |
||||
public Mono<String> monoFindById(long id) { |
||||
return delegate.monoFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PreAuthorize("hasRole('ADMIN')") |
||||
public Mono<String> monoPreAuthorizeHasRoleFindById( |
||||
long id) { |
||||
return delegate.monoPreAuthorizeHasRoleFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PostAuthorize("returnObject?.contains(authentication?.name)") |
||||
public Mono<String> monoPostAuthorizeFindById( |
||||
long id) { |
||||
return delegate.monoPostAuthorizeFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PreAuthorize("@authz.check(#id)") |
||||
public Mono<String> monoPreAuthorizeBeanFindById( |
||||
long id) { |
||||
return delegate.monoPreAuthorizeBeanFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PostAuthorize("@authz.check(authentication, returnObject)") |
||||
public Mono<String> monoPostAuthorizeBeanFindById( |
||||
long id) { |
||||
return delegate.monoPostAuthorizeBeanFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
public Flux<String> fluxFindById(long id) { |
||||
return delegate.fluxFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PreAuthorize("hasRole('ADMIN')") |
||||
public Flux<String> fluxPreAuthorizeHasRoleFindById( |
||||
long id) { |
||||
return delegate.fluxPreAuthorizeHasRoleFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PostAuthorize("returnObject?.contains(authentication?.name)") |
||||
public Flux<String> fluxPostAuthorizeFindById( |
||||
long id) { |
||||
return delegate.fluxPostAuthorizeFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PreAuthorize("@authz.check(#id)") |
||||
public Flux<String> fluxPreAuthorizeBeanFindById( |
||||
long id) { |
||||
return delegate.fluxPreAuthorizeBeanFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PostAuthorize("@authz.check(authentication, returnObject)") |
||||
public Flux<String> fluxPostAuthorizeBeanFindById( |
||||
long id) { |
||||
return delegate.fluxPostAuthorizeBeanFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
public Publisher<String> publisherFindById(long id) { |
||||
return delegate.publisherFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PreAuthorize("hasRole('ADMIN')") |
||||
public Publisher<String> publisherPreAuthorizeHasRoleFindById( |
||||
long id) { |
||||
return delegate.publisherPreAuthorizeHasRoleFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PostAuthorize("returnObject?.contains(authentication?.name)") |
||||
public Publisher<String> publisherPostAuthorizeFindById( |
||||
long id) { |
||||
return delegate.publisherPostAuthorizeFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PreAuthorize("@authz.check(#id)") |
||||
public Publisher<String> publisherPreAuthorizeBeanFindById( |
||||
long id) { |
||||
return delegate.publisherPreAuthorizeBeanFindById(id); |
||||
} |
||||
|
||||
@Override |
||||
@PostAuthorize("@authz.check(authentication, returnObject)") |
||||
public Publisher<String> publisherPostAuthorizeBeanFindById( |
||||
long id) { |
||||
return delegate.publisherPostAuthorizeBeanFindById(id); |
||||
} |
||||
} |
||||
@ -0,0 +1,593 @@
@@ -0,0 +1,593 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.config.annotation.method.configuration; |
||||
|
||||
import org.assertj.core.api.AssertionsForClassTypes; |
||||
import org.junit.After; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.reactivestreams.Publisher; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.authentication.TestingAuthenticationToken; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.junit4.SpringRunner; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
import reactor.test.StepVerifier; |
||||
import reactor.test.publisher.TestPublisher; |
||||
import reactor.util.context.Context; |
||||
|
||||
import java.util.function.Function; |
||||
|
||||
import static org.mockito.Mockito.*; |
||||
|
||||
/** |
||||
* @author Rob Winch |
||||
* @since 5.0 |
||||
*/ |
||||
@RunWith(SpringRunner.class) |
||||
@ContextConfiguration |
||||
public class EnableReactiveMethodSecurityTests { |
||||
@Autowired ReactiveMessageService messageService; |
||||
ReactiveMessageService delegate; |
||||
TestPublisher<String> result = TestPublisher.create(); |
||||
|
||||
Function<Context, Context> withAdmin = context -> context.put(Authentication.class, Mono |
||||
.just(new TestingAuthenticationToken("admin","password","ROLE_USER", "ROLE_ADMIN"))); |
||||
Function<Context, Context> withUser = context -> context.put(Authentication.class, Mono |
||||
.just(new TestingAuthenticationToken("user","password","ROLE_USER"))); |
||||
|
||||
@After |
||||
public void cleanup() { |
||||
reset(delegate); |
||||
} |
||||
|
||||
@Autowired |
||||
public void setConfig(Config config) { |
||||
this.delegate = config.delegate; |
||||
} |
||||
|
||||
@Test |
||||
public void monoWhenPermitAllThenAopDoesNotSubscribe() { |
||||
when(this.delegate.monoFindById(1L)).thenReturn(Mono.from(result)); |
||||
|
||||
this.delegate.monoFindById(1L); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoWhenPermitAllThenSuccess() { |
||||
when(this.delegate.monoFindById(1L)).thenReturn(Mono.just("success")); |
||||
|
||||
StepVerifier.create(this.delegate.monoFindById(1L)) |
||||
.expectNext("success") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPreAuthorizeHasRoleWhenGrantedThenSuccess() { |
||||
when(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).thenReturn(Mono.just("result")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L) |
||||
.contextStart(withAdmin); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("result") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() { |
||||
when(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).thenReturn(Mono.from(result)); |
||||
|
||||
Mono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).thenReturn(Mono.from(result)); |
||||
|
||||
Mono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPreAuthorizeBeanWhenGrantedThenSuccess() { |
||||
when(this.delegate.monoPreAuthorizeBeanFindById(2L)).thenReturn(Mono.just("result")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(2L) |
||||
.contextStart(withAdmin); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("result") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() { |
||||
when(this.delegate.monoPreAuthorizeBeanFindById(2L)).thenReturn(Mono.just("result")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(2L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("result") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPreAuthorizeBeanWhenNoAuthenticationThenDenied() { |
||||
when(this.delegate.monoPreAuthorizeBeanFindById(1L)).thenReturn(Mono.from(result)); |
||||
|
||||
Mono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(1L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPreAuthorizeBeanWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.monoPreAuthorizeBeanFindById(1L)).thenReturn(Mono.from(result)); |
||||
|
||||
Mono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPostAuthorizeWhenAuthorizedThenSuccess() { |
||||
when(this.delegate.monoPostAuthorizeFindById(1L)).thenReturn(Mono.just("user")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPostAuthorizeFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("user") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPostAuthorizeWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.monoPostAuthorizeBeanFindById(1L)).thenReturn(Mono.just("not-authorized")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPostAuthorizeWhenBeanAndAuthorizedThenSuccess() { |
||||
when(this.delegate.monoPostAuthorizeBeanFindById(2L)).thenReturn(Mono.just("user")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(2L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("user") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() { |
||||
when(this.delegate.monoPostAuthorizeBeanFindById(2L)).thenReturn(Mono.just("anonymous")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(2L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("anonymous") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void monoPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() { |
||||
when(this.delegate.monoPostAuthorizeBeanFindById(1L)).thenReturn(Mono.just("not-authorized")); |
||||
|
||||
Mono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
} |
||||
|
||||
// Flux tests
|
||||
|
||||
@Test |
||||
public void fluxWhenPermitAllThenAopDoesNotSubscribe() { |
||||
when(this.delegate.fluxFindById(1L)).thenReturn(Flux.from(result)); |
||||
|
||||
this.delegate.fluxFindById(1L); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxWhenPermitAllThenSuccess() { |
||||
when(this.delegate.fluxFindById(1L)).thenReturn(Flux.just("success")); |
||||
|
||||
StepVerifier.create(this.delegate.fluxFindById(1L)) |
||||
.expectNext("success") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPreAuthorizeHasRoleWhenGrantedThenSuccess() { |
||||
when(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).thenReturn(Flux.just("result")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L) |
||||
.contextStart(withAdmin); |
||||
StepVerifier |
||||
.create(findById) |
||||
.consumeNextWith( s -> AssertionsForClassTypes.assertThat(s).isEqualTo("result")) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() { |
||||
when(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).thenReturn(Flux.from(result)); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).thenReturn(Flux.from(result)); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPreAuthorizeBeanWhenGrantedThenSuccess() { |
||||
when(this.delegate.fluxPreAuthorizeBeanFindById(2L)).thenReturn(Flux.just("result")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(2L) |
||||
.contextStart(withAdmin); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("result") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() { |
||||
when(this.delegate.fluxPreAuthorizeBeanFindById(2L)).thenReturn(Flux.just("result")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(2L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("result") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPreAuthorizeBeanWhenNoAuthenticationThenDenied() { |
||||
when(this.delegate.fluxPreAuthorizeBeanFindById(1L)).thenReturn(Flux.from(result)); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(1L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPreAuthorizeBeanWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.fluxPreAuthorizeBeanFindById(1L)).thenReturn(Flux.from(result)); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPostAuthorizeWhenAuthorizedThenSuccess() { |
||||
when(this.delegate.fluxPostAuthorizeFindById(1L)).thenReturn(Flux.just("user")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPostAuthorizeFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("user") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPostAuthorizeWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.fluxPostAuthorizeBeanFindById(1L)).thenReturn(Flux.just("not-authorized")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPostAuthorizeWhenBeanAndAuthorizedThenSuccess() { |
||||
when(this.delegate.fluxPostAuthorizeBeanFindById(2L)).thenReturn(Flux.just("user")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(2L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("user") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() { |
||||
when(this.delegate.fluxPostAuthorizeBeanFindById(2L)).thenReturn(Flux.just("anonymous")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(2L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("anonymous") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void fluxPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() { |
||||
when(this.delegate.fluxPostAuthorizeBeanFindById(1L)).thenReturn(Flux.just("not-authorized")); |
||||
|
||||
Flux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(1L) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
} |
||||
|
||||
// Publisher tests
|
||||
|
||||
@Test |
||||
public void publisherWhenPermitAllThenAopDoesNotSubscribe() { |
||||
when(this.delegate.publisherFindById(1L)).thenReturn(result); |
||||
|
||||
this.delegate.publisherFindById(1L); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherWhenPermitAllThenSuccess() { |
||||
when(this.delegate.publisherFindById(1L)).thenReturn(publisherJust("success")); |
||||
|
||||
StepVerifier.create(this.delegate.publisherFindById(1L)) |
||||
.expectNext("success") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPreAuthorizeHasRoleWhenGrantedThenSuccess() { |
||||
when(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).thenReturn(publisherJust("result")); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeHasRoleFindById(1L)) |
||||
.contextStart(withAdmin); |
||||
StepVerifier |
||||
.create(findById) |
||||
.consumeNextWith( s -> AssertionsForClassTypes.assertThat(s).isEqualTo("result")) |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() { |
||||
when(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).thenReturn(result); |
||||
|
||||
Publisher<String> findById = this.messageService.publisherPreAuthorizeHasRoleFindById(1L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).thenReturn(result); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeHasRoleFindById(1L)) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPreAuthorizeBeanWhenGrantedThenSuccess() { |
||||
when(this.delegate.publisherPreAuthorizeBeanFindById(2L)).thenReturn(publisherJust("result")); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeBeanFindById(2L)) |
||||
.contextStart(withAdmin); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("result") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() { |
||||
when(this.delegate.publisherPreAuthorizeBeanFindById(2L)).thenReturn(publisherJust("result")); |
||||
|
||||
Publisher<String> findById = this.messageService.publisherPreAuthorizeBeanFindById(2L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("result") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPreAuthorizeBeanWhenNoAuthenticationThenDenied() { |
||||
when(this.delegate.publisherPreAuthorizeBeanFindById(1L)).thenReturn(result); |
||||
|
||||
Publisher<String> findById = this.messageService.publisherPreAuthorizeBeanFindById(1L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPreAuthorizeBeanWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.publisherPreAuthorizeBeanFindById(1L)).thenReturn(result); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeBeanFindById(1L)) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
|
||||
result.assertNoSubscribers(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPostAuthorizeWhenAuthorizedThenSuccess() { |
||||
when(this.delegate.publisherPostAuthorizeFindById(1L)).thenReturn(publisherJust("user")); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeFindById(1L)) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("user") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPostAuthorizeWhenNotAuthorizedThenDenied() { |
||||
when(this.delegate.publisherPostAuthorizeBeanFindById(1L)).thenReturn(publisherJust("not-authorized")); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(1L)) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPostAuthorizeWhenBeanAndAuthorizedThenSuccess() { |
||||
when(this.delegate.publisherPostAuthorizeBeanFindById(2L)).thenReturn(publisherJust("user")); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(2L)) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("user") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() { |
||||
when(this.delegate.publisherPostAuthorizeBeanFindById(2L)).thenReturn(publisherJust("anonymous")); |
||||
|
||||
Publisher<String> findById = this.messageService.publisherPostAuthorizeBeanFindById(2L); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectNext("anonymous") |
||||
.verifyComplete(); |
||||
} |
||||
|
||||
@Test |
||||
public void publisherPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() { |
||||
when(this.delegate.publisherPostAuthorizeBeanFindById(1L)).thenReturn(publisherJust("not-authorized")); |
||||
|
||||
Publisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(1L)) |
||||
.contextStart(withUser); |
||||
StepVerifier |
||||
.create(findById) |
||||
.expectError(AccessDeniedException.class) |
||||
.verify(); |
||||
} |
||||
|
||||
static <T> Publisher<T> publisher(Flux<T> flux) { |
||||
return subscriber -> flux.subscribe(subscriber); |
||||
} |
||||
|
||||
static <T> Publisher<T> publisherJust(T... data) { |
||||
return publisher(Flux.just(data)); |
||||
} |
||||
|
||||
@EnableReactiveMethodSecurity |
||||
static class Config { |
||||
ReactiveMessageService delegate = mock(ReactiveMessageService.class); |
||||
|
||||
@Bean |
||||
public DelegatingReactiveMessageService defaultMessageService() { |
||||
return new DelegatingReactiveMessageService(delegate); |
||||
} |
||||
|
||||
@Bean |
||||
public Authz authz() { |
||||
return new Authz(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.config.annotation.method.configuration; |
||||
|
||||
import org.reactivestreams.Publisher; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
public interface ReactiveMessageService { |
||||
Mono<String> monoFindById(long id); |
||||
Mono<String> monoPreAuthorizeHasRoleFindById(long id); |
||||
Mono<String> monoPostAuthorizeFindById(long id); |
||||
Mono<String> monoPreAuthorizeBeanFindById(long id); |
||||
Mono<String> monoPostAuthorizeBeanFindById(long id); |
||||
|
||||
Flux<String> fluxFindById(long id); |
||||
Flux<String> fluxPreAuthorizeHasRoleFindById(long id); |
||||
Flux<String> fluxPostAuthorizeFindById(long id); |
||||
Flux<String> fluxPreAuthorizeBeanFindById(long id); |
||||
Flux<String> fluxPostAuthorizeBeanFindById(long id); |
||||
|
||||
Publisher<String> publisherFindById(long id); |
||||
Publisher<String> publisherPreAuthorizeHasRoleFindById(long id); |
||||
Publisher<String> publisherPostAuthorizeFindById(long id); |
||||
Publisher<String> publisherPreAuthorizeBeanFindById(long id); |
||||
Publisher<String> publisherPostAuthorizeBeanFindById(long id); |
||||
} |
||||
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
/* |
||||
* |
||||
* * Copyright 2002-2017 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 |
||||
* * |
||||
* * 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 org.springframework.security.access.method; |
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor; |
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import org.reactivestreams.Publisher; |
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.access.ConfigAttribute; |
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice; |
||||
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice; |
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; |
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource; |
||||
import org.springframework.security.access.prepost.PostInvocationAttribute; |
||||
import org.springframework.security.access.prepost.PostInvocationAuthorizationAdvice; |
||||
import org.springframework.security.access.prepost.PreInvocationAttribute; |
||||
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice; |
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.core.authority.AuthorityUtils; |
||||
import org.springframework.util.Assert; |
||||
import reactor.core.Exceptions; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
import reactor.util.context.Context; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.Collection; |
||||
|
||||
/** |
||||
* @author Rob Winch |
||||
* @since 5.0 |
||||
*/ |
||||
public class PrePostAdviceMethodInterceptor implements MethodInterceptor { |
||||
private Authentication anonymous = new AnonymousAuthenticationToken("key", "anonymous", |
||||
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); |
||||
|
||||
private final MethodSecurityMetadataSource attributeSource; |
||||
|
||||
private PostInvocationAuthorizationAdvice postAdvice; |
||||
|
||||
private PreInvocationAuthorizationAdvice preAdvice; |
||||
|
||||
public PrePostAdviceMethodInterceptor(MethodSecurityMetadataSource attributeSource) { |
||||
this.attributeSource = attributeSource; |
||||
|
||||
MethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); |
||||
this.postAdvice = new ExpressionBasedPostInvocationAdvice(handler); |
||||
this.preAdvice = new ExpressionBasedPreInvocationAdvice(); |
||||
} |
||||
|
||||
public void setPostAdvice(PostInvocationAuthorizationAdvice postAdvice) { |
||||
Assert.notNull(postAdvice, "postAdvice cannot be null"); |
||||
this.postAdvice = postAdvice; |
||||
} |
||||
|
||||
public void setPreAdvice(PreInvocationAuthorizationAdvice preAdvice) { |
||||
Assert.notNull(preAdvice, "preAdvice cannot be null"); |
||||
this.preAdvice = preAdvice; |
||||
} |
||||
|
||||
@Override |
||||
public Object invoke(final MethodInvocation invocation) |
||||
throws Throwable { |
||||
Method method = invocation.getMethod(); |
||||
Class<?> returnType = method.getReturnType(); |
||||
Class<?> targetClass = invocation.getThis().getClass(); |
||||
Collection<ConfigAttribute> attributes = this.attributeSource |
||||
.getAttributes(method, targetClass); |
||||
|
||||
PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes); |
||||
Mono<Authentication> toInvoke = Mono.currentContext() |
||||
.defaultIfEmpty(Context.empty()) |
||||
.flatMap( cxt -> cxt.getOrDefault(Authentication.class, Mono.just(anonymous))) |
||||
.filter( auth -> this.preAdvice.before(auth, invocation, preAttr)) |
||||
.switchIfEmpty(Mono.error(new AccessDeniedException("Denied"))); |
||||
|
||||
|
||||
PostInvocationAttribute attr = findPostInvocationAttribute(attributes); |
||||
|
||||
if(Mono.class.isAssignableFrom(returnType)) { |
||||
return toInvoke |
||||
.flatMap( auth -> this.<Mono<?>>proceed(invocation) |
||||
.map( r -> attr == null ? r : this.postAdvice.after(auth, invocation, attr, r)) |
||||
); |
||||
} |
||||
|
||||
if(Flux.class.isAssignableFrom(returnType)) { |
||||
return toInvoke |
||||
.flatMapMany( auth -> this.<Flux<?>>proceed(invocation) |
||||
.map( r -> attr == null ? r : this.postAdvice.after(auth, invocation, attr, r)) |
||||
); |
||||
} |
||||
|
||||
return toInvoke |
||||
.flatMapMany( auth -> Flux.from(this.<Publisher<?>>proceed(invocation)) |
||||
.map( r -> attr == null ? r : this.postAdvice.after(auth, invocation, attr, r)) |
||||
); |
||||
} |
||||
|
||||
private<T extends Publisher<?>> T proceed(final MethodInvocation invocation) { |
||||
try { |
||||
return (T) invocation.proceed(); |
||||
} catch(Throwable throwable) { |
||||
throw Exceptions.propagate(throwable); |
||||
} |
||||
} |
||||
|
||||
private static PostInvocationAttribute findPostInvocationAttribute( |
||||
Collection<ConfigAttribute> config) { |
||||
for (ConfigAttribute attribute : config) { |
||||
if (attribute instanceof PostInvocationAttribute) { |
||||
return (PostInvocationAttribute) attribute; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
private PreInvocationAttribute findPreInvocationAttribute( |
||||
Collection<ConfigAttribute> config) { |
||||
for (ConfigAttribute attribute : config) { |
||||
if (attribute instanceof PreInvocationAttribute) { |
||||
return (PreInvocationAttribute) attribute; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue