Instrument Authentication and Authorization
Closes gh-11989 Closes gh-11990
This commit is contained in:
parent
827384e386
commit
8c610684f3
|
@ -16,12 +16,17 @@
|
|||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
|
@ -40,28 +45,28 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
|||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class Jsr250MethodSecurityConfiguration {
|
||||
|
||||
private final Jsr250AuthorizationManager jsr250AuthorizationManager = new Jsr250AuthorizationManager();
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor jsr250AuthorizationMethodInterceptor() {
|
||||
static Advisor jsr250AuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
Jsr250AuthorizationManager jsr250 = new Jsr250AuthorizationManager();
|
||||
defaultsProvider.ifAvailable((d) -> jsr250.setRolePrefix(d.getRolePrefix()));
|
||||
SecurityContextHolderStrategy strategy = strategyProvider
|
||||
.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
|
||||
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
|
||||
AuthorizationManager<MethodInvocation> manager = manager(jsr250, registry);
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.jsr250(this.jsr250AuthorizationManager);
|
||||
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
.jsr250(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(strategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
|
||||
this.jsr250AuthorizationManager.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> jsr250, ObservationRegistry registry) {
|
||||
if (registry.isNoop()) {
|
||||
return jsr250;
|
||||
}
|
||||
return new ObservationAuthorizationManager<>(registry, jsr250);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -26,7 +28,8 @@ import org.springframework.context.annotation.Role;
|
|||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
|
||||
|
@ -48,85 +51,80 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
|||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class PrePostMethodSecurityConfiguration {
|
||||
|
||||
private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
|
||||
|
||||
private final AuthorizationManagerBeforeMethodInterceptor preAuthorizeAuthorizationMethodInterceptor;
|
||||
|
||||
private final PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
|
||||
|
||||
private final AuthorizationManagerAfterMethodInterceptor postAuthorizeAuthorizaitonMethodInterceptor;
|
||||
|
||||
private final PostAuthorizeAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeAuthorizationManager();
|
||||
|
||||
private final PostFilterAuthorizationMethodInterceptor postFilterAuthorizationMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
|
||||
|
||||
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
@Autowired
|
||||
PrePostMethodSecurityConfiguration(ApplicationContext context) {
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
this.preAuthorizeAuthorizationMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.preAuthorize(this.preAuthorizeAuthorizationManager);
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
this.postAuthorizeAuthorizaitonMethodInterceptor = AuthorizationManagerAfterMethodInterceptor
|
||||
.postAuthorize(this.postAuthorizeAuthorizationManager);
|
||||
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||
this.expressionHandler.setApplicationContext(context);
|
||||
AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(context);
|
||||
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static Advisor preFilterAuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider, ApplicationContext context) {
|
||||
PreFilterAuthorizationMethodInterceptor preFilter = new PreFilterAuthorizationMethodInterceptor();
|
||||
strategyProvider.ifAvailable(preFilter::setSecurityContextHolderStrategy);
|
||||
preFilter.setExpressionHandler(
|
||||
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
|
||||
return preFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor preFilterAuthorizationMethodInterceptor() {
|
||||
return this.preFilterAuthorizationMethodInterceptor;
|
||||
static Advisor preAuthorizeAuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<AuthorizationEventPublisher> eventPublisherProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
|
||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
|
||||
manager.setExpressionHandler(
|
||||
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
|
||||
AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor
|
||||
.preAuthorize(manager(manager, registryProvider));
|
||||
strategyProvider.ifAvailable(preAuthorize::setSecurityContextHolderStrategy);
|
||||
eventPublisherProvider.ifAvailable(preAuthorize::setAuthorizationEventPublisher);
|
||||
return preAuthorize;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor preAuthorizeAuthorizationMethodInterceptor() {
|
||||
return this.preAuthorizeAuthorizationMethodInterceptor;
|
||||
static Advisor postAuthorizeAuthorizationMethodInterceptor(
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<AuthorizationEventPublisher> eventPublisherProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
|
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
||||
manager.setExpressionHandler(
|
||||
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
|
||||
AuthorizationManagerAfterMethodInterceptor postAuthorize = AuthorizationManagerAfterMethodInterceptor
|
||||
.postAuthorize(manager(manager, registryProvider));
|
||||
strategyProvider.ifAvailable(postAuthorize::setSecurityContextHolderStrategy);
|
||||
eventPublisherProvider.ifAvailable(postAuthorize::setAuthorizationEventPublisher);
|
||||
return postAuthorize;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor postAuthorizeAuthorizationMethodInterceptor() {
|
||||
return this.postAuthorizeAuthorizaitonMethodInterceptor;
|
||||
static Advisor postFilterAuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider, ApplicationContext context) {
|
||||
PostFilterAuthorizationMethodInterceptor postFilter = new PostFilterAuthorizationMethodInterceptor();
|
||||
strategyProvider.ifAvailable(postFilter::setSecurityContextHolderStrategy);
|
||||
postFilter.setExpressionHandler(
|
||||
expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context)));
|
||||
return postFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor postFilterAuthorizationMethodInterceptor() {
|
||||
return this.postFilterAuthorizationMethodInterceptor;
|
||||
private static MethodSecurityExpressionHandler defaultExpressionHandler(
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider, ApplicationContext context) {
|
||||
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
||||
defaultsProvider.ifAvailable((d) -> handler.setDefaultRolePrefix(d.getRolePrefix()));
|
||||
handler.setApplicationContext(context);
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
|
||||
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(methodSecurityExpressionHandler);
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
|
||||
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(methodSecurityExpressionHandler);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {
|
||||
this.preFilterAuthorizationMethodInterceptor.setSecurityContextHolderStrategy(strategy);
|
||||
this.preAuthorizeAuthorizationMethodInterceptor.setSecurityContextHolderStrategy(strategy);
|
||||
this.postAuthorizeAuthorizaitonMethodInterceptor.setSecurityContextHolderStrategy(strategy);
|
||||
this.postFilterAuthorizationMethodInterceptor.setSecurityContextHolderStrategy(strategy);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
|
||||
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> delegate,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
|
||||
if (registry.isNoop()) {
|
||||
return delegate;
|
||||
}
|
||||
return new ObservationAuthorizationManager<>(registry, delegate);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -24,8 +28,11 @@ import org.springframework.context.annotation.Role;
|
|||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.PostAuthorizeReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PreAuthorizeReactiveAuthorizationManager;
|
||||
|
@ -43,39 +50,39 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration {
|
|||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
PreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor(
|
||||
static PreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler) {
|
||||
return new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler) {
|
||||
PreAuthorizeReactiveAuthorizationManager authorizationManager = new PreAuthorizeReactiveAuthorizationManager(
|
||||
expressionHandler);
|
||||
static AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler, ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
ReactiveAuthorizationManager<MethodInvocation> authorizationManager = manager(
|
||||
new PreAuthorizeReactiveAuthorizationManager(expressionHandler), registryProvider);
|
||||
return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(authorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
PostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor(
|
||||
static PostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler) {
|
||||
return new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler) {
|
||||
PostAuthorizeReactiveAuthorizationManager authorizationManager = new PostAuthorizeReactiveAuthorizationManager(
|
||||
expressionHandler);
|
||||
static AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler, ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
ReactiveAuthorizationManager<MethodInvocationResult> authorizationManager = manager(
|
||||
new PostAuthorizeReactiveAuthorizationManager(expressionHandler), registryProvider);
|
||||
return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(authorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(
|
||||
static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(
|
||||
@Autowired(required = false) GrantedAuthorityDefaults grantedAuthorityDefaults) {
|
||||
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
||||
if (grantedAuthorityDefaults != null) {
|
||||
|
@ -84,4 +91,13 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration {
|
|||
return handler;
|
||||
}
|
||||
|
||||
static <T> ReactiveAuthorizationManager<T> manager(ReactiveAuthorizationManager<T> delegate,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
|
||||
if (registry.isNoop()) {
|
||||
return delegate;
|
||||
}
|
||||
return new ObservationReactiveAuthorizationManager<>(registry, delegate);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,14 +16,20 @@
|
|||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
|
||||
|
@ -39,20 +45,26 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
|||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class SecuredMethodSecurityConfiguration {
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor securedAuthorizationMethodInterceptor() {
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor.secured();
|
||||
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
static Advisor securedAuthorizationMethodInterceptor(ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
SecuredAuthorizationManager secured = new SecuredAuthorizationManager();
|
||||
SecurityContextHolderStrategy strategy = strategyProvider
|
||||
.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
|
||||
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
|
||||
AuthorizationManager<MethodInvocation> manager = manager(secured, registry);
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.secured(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(strategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> jsr250, ObservationRegistry registry) {
|
||||
if (registry.isNoop()) {
|
||||
return jsr250;
|
||||
}
|
||||
return new ObservationAuthorizationManager<>(registry, jsr250);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
|
||||
package org.springframework.security.config.annotation.rsocket;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.security.authentication.ObservationReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
|
||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||
|
@ -43,6 +46,8 @@ class RSocketSecurityConfiguration {
|
|||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
|
@ -58,6 +63,11 @@ class RSocketSecurityConfiguration {
|
|||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
@Bean(name = RSOCKET_SECURITY_BEAN_NAME)
|
||||
@Scope("prototype")
|
||||
RSocketSecurity rsocketSecurity(ApplicationContext context) {
|
||||
|
@ -76,6 +86,9 @@ class RSocketSecurityConfiguration {
|
|||
if (this.passwordEncoder != null) {
|
||||
manager.setPasswordEncoder(this.passwordEncoder);
|
||||
}
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
return new ObservationReactiveAuthenticationManager(this.observationRegistry, manager);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
|
@ -34,6 +35,7 @@ import org.springframework.core.OrderComparator;
|
|||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ObservationAuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
|
@ -2994,7 +2996,14 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
|||
setSharedObject(AuthenticationManager.class, this.authenticationManager);
|
||||
}
|
||||
else {
|
||||
setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
|
||||
ObservationRegistry registry = getObservationRegistry();
|
||||
AuthenticationManager manager = getAuthenticationRegistry().build();
|
||||
if (!registry.isNoop()) {
|
||||
setSharedObject(AuthenticationManager.class, new ObservationAuthenticationManager(registry, manager));
|
||||
}
|
||||
else {
|
||||
setSharedObject(AuthenticationManager.class, manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3418,6 +3427,15 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
|||
return apply(configurer);
|
||||
}
|
||||
|
||||
private ObservationRegistry getObservationRegistry() {
|
||||
ApplicationContext context = getContext();
|
||||
String[] names = context.getBeanNamesForType(ObservationRegistry.class);
|
||||
if (names.length == 1) {
|
||||
return (ObservationRegistry) context.getBean(names[0]);
|
||||
}
|
||||
return ObservationRegistry.NOOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows mapping HTTP requests that this {@link HttpSecurity} will be used for
|
||||
*
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.web.configurers;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -26,6 +27,7 @@ import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
|||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
|
@ -102,6 +104,17 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||
return this.registry;
|
||||
}
|
||||
|
||||
private ObservationRegistry getObservationRegistry() {
|
||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||
String[] names = context.getBeanNamesForType(ObservationRegistry.class);
|
||||
if (names.length == 1) {
|
||||
return context.getBean(ObservationRegistry.class);
|
||||
}
|
||||
else {
|
||||
return ObservationRegistry.NOOP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||
*
|
||||
|
@ -141,7 +154,12 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
|
||||
Assert.state(this.mappingCount > 0,
|
||||
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
|
||||
return postProcess(this.managerBuilder.build());
|
||||
ObservationRegistry registry = getObservationRegistry();
|
||||
RequestMatcherDelegatingAuthorizationManager manager = postProcess(this.managerBuilder.build());
|
||||
if (registry.isNoop()) {
|
||||
return manager;
|
||||
}
|
||||
return new ObservationAuthorizationManager<>(registry, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.security.config.annotation.web.reactive;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
@ -27,6 +29,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.security.authentication.ObservationReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
|
@ -60,6 +63,8 @@ class ServerHttpSecurityConfiguration {
|
|||
|
||||
private ReactiveUserDetailsPasswordService userDetailsPasswordService;
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
@Autowired(required = false)
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
@ -88,6 +93,11 @@ class ServerHttpSecurityConfiguration {
|
|||
this.userDetailsPasswordService = userDetailsPasswordService;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
static WebFluxConfigurer authenticationPrincipalArgumentResolverConfigurer(
|
||||
ObjectProvider<AuthenticationPrincipalArgumentResolver> authenticationPrincipalArgumentResolver) {
|
||||
|
@ -143,6 +153,9 @@ class ServerHttpSecurityConfiguration {
|
|||
manager.setPasswordEncoder(this.passwordEncoder);
|
||||
}
|
||||
manager.setUserDetailsPasswordService(this.userDetailsPasswordService);
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
return new ObservationReactiveAuthenticationManager(this.observationRegistry, manager);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -31,6 +33,7 @@ import org.springframework.messaging.handler.invocation.HandlerMethodArgumentRes
|
|||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
|
@ -68,8 +71,9 @@ final class WebSocketMessageBrokerSecurityConfiguration
|
|||
|
||||
private final ChannelInterceptor csrfChannelInterceptor = new CsrfChannelInterceptor();
|
||||
|
||||
private AuthorizationChannelInterceptor authorizationChannelInterceptor = new AuthorizationChannelInterceptor(
|
||||
ANY_MESSAGE_AUTHENTICATED);
|
||||
private AuthorizationManager<Message<?>> authorizationManager = ANY_MESSAGE_AUTHENTICATED;
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
|
@ -86,12 +90,15 @@ final class WebSocketMessageBrokerSecurityConfiguration
|
|||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
this.authorizationChannelInterceptor
|
||||
.setAuthorizationEventPublisher(new SpringAuthorizationEventPublisher(this.context));
|
||||
this.authorizationChannelInterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
AuthorizationManager<Message<?>> manager = this.authorizationManager;
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
manager = new ObservationAuthorizationManager<>(this.observationRegistry, manager);
|
||||
}
|
||||
AuthorizationChannelInterceptor interceptor = new AuthorizationChannelInterceptor(manager);
|
||||
interceptor.setAuthorizationEventPublisher(new SpringAuthorizationEventPublisher(this.context));
|
||||
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
this.securityContextChannelInterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
registration.interceptors(this.securityContextChannelInterceptor, this.csrfChannelInterceptor,
|
||||
this.authorizationChannelInterceptor);
|
||||
registration.interceptors(this.securityContextChannelInterceptor, this.csrfChannelInterceptor, interceptor);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
|
@ -102,7 +109,12 @@ final class WebSocketMessageBrokerSecurityConfiguration
|
|||
|
||||
@Autowired(required = false)
|
||||
void setAuthorizationManager(AuthorizationManager<Message<?>> authorizationManager) {
|
||||
this.authorizationChannelInterceptor = new AuthorizationChannelInterceptor(authorizationManager);
|
||||
this.authorizationManager = authorizationManager;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.springframework.security.config.authentication;
|
|||
|
||||
import java.util.Arrays;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
|
@ -25,6 +27,7 @@ import org.springframework.beans.factory.FactoryBean;
|
|||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ObservationAuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
|
@ -43,6 +46,8 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
|
|||
|
||||
private BeanFactory bf;
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
public static final String MISSING_BEAN_ERROR_MESSAGE = "Did you forget to add a global <authentication-manager> element "
|
||||
+ "to your configuration (with child <authentication-provider> elements)? Alternatively you can use the "
|
||||
+ "authentication-manager-ref attribute on your <http> and <global-method-security> elements.";
|
||||
|
@ -67,7 +72,11 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
|
|||
provider.setPasswordEncoder(passwordEncoder);
|
||||
}
|
||||
provider.afterPropertiesSet();
|
||||
return new ProviderManager(Arrays.<AuthenticationProvider>asList(provider));
|
||||
ProviderManager manager = new ProviderManager(Arrays.<AuthenticationProvider>asList(provider));
|
||||
if (this.observationRegistry.isNoop()) {
|
||||
return manager;
|
||||
}
|
||||
return new ObservationAuthenticationManager(this.observationRegistry, manager);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,6 +95,10 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
|
|||
this.bf = beanFactory;
|
||||
}
|
||||
|
||||
public void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
try {
|
||||
return this.bf.getBean(type);
|
||||
|
|
|
@ -19,10 +19,12 @@ package org.springframework.security.config.http;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
|
@ -34,6 +36,7 @@ import org.springframework.beans.factory.xml.ParserContext;
|
|||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
|
||||
|
@ -51,6 +54,8 @@ class AuthorizationFilterParser implements BeanDefinitionParser {
|
|||
|
||||
private static final String ATT_ACCESS_DECISION_MANAGER_REF = "access-decision-manager-ref";
|
||||
|
||||
private static final String ATT_OBSERVATION_REGISTRY_REF = "observation-registry-ref";
|
||||
|
||||
private static final String ATT_HTTP_METHOD = "method";
|
||||
|
||||
private static final String ATT_PATTERN = "pattern";
|
||||
|
@ -127,9 +132,9 @@ class AuthorizationFilterParser implements BeanDefinitionParser {
|
|||
matcherToExpression.put(matcher, authorizationManager.getBeanDefinition());
|
||||
}
|
||||
BeanDefinitionBuilder mds = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherDelegatingAuthorizationManagerFactory.class);
|
||||
mds.setFactoryMethod("createRequestMatcherDelegatingAuthorizationManager");
|
||||
mds.addConstructorArgValue(matcherToExpression);
|
||||
.rootBeanDefinition(RequestMatcherDelegatingAuthorizationManagerFactory.class)
|
||||
.addPropertyValue("requestMatcherMap", matcherToExpression)
|
||||
.addPropertyValue("observationRegistry", getObservationRegistry(element));
|
||||
return context.registerWithGeneratedName(mds.getBeanDefinition());
|
||||
}
|
||||
|
||||
|
@ -169,17 +174,48 @@ class AuthorizationFilterParser implements BeanDefinitionParser {
|
|||
return !StringUtils.hasText(useExpressions) || "true".equals(useExpressions);
|
||||
}
|
||||
|
||||
private static class RequestMatcherDelegatingAuthorizationManagerFactory {
|
||||
private BeanMetadataElement getObservationRegistry(Element methodSecurityElmt) {
|
||||
String holderStrategyRef = methodSecurityElmt.getAttribute(ATT_OBSERVATION_REGISTRY_REF);
|
||||
if (StringUtils.hasText(holderStrategyRef)) {
|
||||
return new RuntimeBeanReference(holderStrategyRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();
|
||||
}
|
||||
|
||||
private static AuthorizationManager<HttpServletRequest> createRequestMatcherDelegatingAuthorizationManager(
|
||||
Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> beans) {
|
||||
public static final class RequestMatcherDelegatingAuthorizationManagerFactory
|
||||
implements FactoryBean<AuthorizationManager<HttpServletRequest>> {
|
||||
|
||||
private Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> beans;
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
@Override
|
||||
public AuthorizationManager<HttpServletRequest> getObject() throws Exception {
|
||||
RequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager
|
||||
.builder();
|
||||
for (Map.Entry<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> entry : beans
|
||||
for (Map.Entry<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> entry : this.beans
|
||||
.entrySet()) {
|
||||
builder.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return builder.add(AnyRequestMatcher.INSTANCE, AuthenticatedAuthorizationManager.authenticated()).build();
|
||||
AuthorizationManager<HttpServletRequest> manager = builder
|
||||
.add(AnyRequestMatcher.INSTANCE, AuthenticatedAuthorizationManager.authenticated()).build();
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
return new ObservationAuthorizationManager<>(this.observationRegistry, manager);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return AuthorizationManager.class;
|
||||
}
|
||||
|
||||
public void setRequestMatcherMap(Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> beans) {
|
||||
this.beans = beans;
|
||||
}
|
||||
|
||||
public void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -199,4 +235,18 @@ class AuthorizationFilterParser implements BeanDefinitionParser {
|
|||
|
||||
}
|
||||
|
||||
static class ObservationRegistryFactory implements FactoryBean<ObservationRegistry> {
|
||||
|
||||
@Override
|
||||
public ObservationRegistry getObject() throws Exception {
|
||||
return ObservationRegistry.NOOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ObservationRegistry.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,12 +20,14 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
@ -43,8 +45,11 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
|||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.ObservationAuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.Elements;
|
||||
|
@ -70,6 +75,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref";
|
||||
|
||||
private static final String ATT_OBSERVATION_REGISTRY_REF = "observation-registry-ref";
|
||||
|
||||
static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref";
|
||||
|
||||
static final String ATT_PATH_PATTERN = "pattern";
|
||||
|
@ -246,7 +253,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
private BeanReference createAuthenticationManager(Element element, ParserContext pc,
|
||||
ManagedList<BeanReference> authenticationProviders) {
|
||||
String parentMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);
|
||||
BeanDefinitionBuilder authManager = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
|
||||
BeanDefinitionBuilder authManager = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ChildAuthenticationManagerFactoryBean.class);
|
||||
authManager.addConstructorArgValue(authenticationProviders);
|
||||
if (StringUtils.hasText(parentMgrRef)) {
|
||||
RuntimeBeanReference parentAuthManager = new RuntimeBeanReference(parentMgrRef);
|
||||
|
@ -273,6 +281,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
// gh-6009
|
||||
authManager.addPropertyValue("authenticationEventPublisher",
|
||||
new RootBeanDefinition(DefaultAuthenticationEventPublisher.class));
|
||||
authManager.addPropertyValue("observationRegistry", getObservationRegistry(element));
|
||||
authManager.getRawBeanDefinition().setSource(pc.extractSource(element));
|
||||
BeanDefinition authMgrBean = authManager.getBeanDefinition();
|
||||
String id = pc.getReaderContext().generateBeanName(authMgrBean);
|
||||
|
@ -368,6 +377,14 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
registry.registerBeanDefinition(requestRejectedPostProcessorName, requestRejectedBean);
|
||||
}
|
||||
|
||||
private static BeanMetadataElement getObservationRegistry(Element methodSecurityElmt) {
|
||||
String holderStrategyRef = methodSecurityElmt.getAttribute(ATT_OBSERVATION_REGISTRY_REF);
|
||||
if (StringUtils.hasText(holderStrategyRef)) {
|
||||
return new RuntimeBeanReference(holderStrategyRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();
|
||||
}
|
||||
|
||||
static class RequestRejectedHandlerPostProcessor implements BeanDefinitionRegistryPostProcessor {
|
||||
|
||||
private final String beanName;
|
||||
|
@ -434,4 +451,62 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
}
|
||||
|
||||
public static final class ChildAuthenticationManagerFactoryBean implements FactoryBean<AuthenticationManager> {
|
||||
|
||||
private final ProviderManager delegate;
|
||||
|
||||
private AuthenticationEventPublisher authenticationEventPublisher = new DefaultAuthenticationEventPublisher();
|
||||
|
||||
private boolean eraseCredentialsAfterAuthentication = true;
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
public ChildAuthenticationManagerFactoryBean(List<AuthenticationProvider> providers,
|
||||
AuthenticationManager parent) {
|
||||
this.delegate = new ProviderManager(providers, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationManager getObject() throws Exception {
|
||||
this.delegate.setAuthenticationEventPublisher(this.authenticationEventPublisher);
|
||||
this.delegate.setEraseCredentialsAfterAuthentication(this.eraseCredentialsAfterAuthentication);
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
return new ObservationAuthenticationManager(this.observationRegistry, this.delegate);
|
||||
}
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return AuthenticationManager.class;
|
||||
}
|
||||
|
||||
public void setEraseCredentialsAfterAuthentication(boolean eraseCredentialsAfterAuthentication) {
|
||||
this.eraseCredentialsAfterAuthentication = eraseCredentialsAfterAuthentication;
|
||||
}
|
||||
|
||||
public void setAuthenticationEventPublisher(AuthenticationEventPublisher authenticationEventPublisher) {
|
||||
this.authenticationEventPublisher = authenticationEventPublisher;
|
||||
}
|
||||
|
||||
public void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ObservationRegistryFactory implements FactoryBean<ObservationRegistry> {
|
||||
|
||||
@Override
|
||||
public ObservationRegistry getObject() throws Exception {
|
||||
return ObservationRegistry.NOOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ObservationRegistry.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Element;
|
||||
|
@ -42,14 +44,18 @@ import org.springframework.context.ApplicationContext;
|
|||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodExpressionAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
@ -75,6 +81,8 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
|
||||
private static final String ATT_AUTHORIZATION_MGR = "authorization-manager-ref";
|
||||
|
||||
private static final String ATT_OBSERVATION_REGISTRY_REF = "observation-registry-ref";
|
||||
|
||||
private static final String ATT_ACCESS = "access";
|
||||
|
||||
private static final String ATT_EXPRESSION = "expression";
|
||||
|
@ -89,6 +97,7 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
pc.extractSource(element));
|
||||
pc.pushContainingComponent(compositeDef);
|
||||
BeanMetadataElement securityContextHolderStrategy = getSecurityContextHolderStrategy(element);
|
||||
BeanMetadataElement observationRegistry = getObservationRegistry(element);
|
||||
boolean prePostAnnotationsEnabled = !element.hasAttribute(ATT_USE_PREPOST)
|
||||
|| "true".equals(element.getAttribute(ATT_USE_PREPOST));
|
||||
boolean useAspectJ = "aspectj".equals(element.getAttribute(ATT_MODE));
|
||||
|
@ -100,11 +109,13 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
BeanDefinitionBuilder preAuthorizeInterceptor = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(PreAuthorizeAuthorizationMethodInterceptor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy);
|
||||
.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy)
|
||||
.addPropertyValue("observationRegistry", observationRegistry);
|
||||
BeanDefinitionBuilder postAuthorizeInterceptor = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(PostAuthorizeAuthorizationMethodInterceptor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy);
|
||||
.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy)
|
||||
.addPropertyValue("observationRegistry", observationRegistry);
|
||||
BeanDefinitionBuilder postFilterInterceptor = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(PostFilterAuthorizationMethodInterceptor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
|
@ -137,10 +148,10 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
boolean securedEnabled = "true".equals(element.getAttribute(ATT_USE_SECURED));
|
||||
if (securedEnabled) {
|
||||
BeanDefinitionBuilder securedInterceptor = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class)
|
||||
.rootBeanDefinition(SecuredAuthorizationMethodInterceptor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy)
|
||||
.setFactoryMethod("secured");
|
||||
.addPropertyValue("observationRegistry", observationRegistry);
|
||||
pc.getRegistry().registerBeanDefinition("securedAuthorizationMethodInterceptor",
|
||||
securedInterceptor.getBeanDefinition());
|
||||
}
|
||||
|
@ -149,7 +160,8 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
BeanDefinitionBuilder jsr250Interceptor = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Jsr250AuthorizationMethodInterceptor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy);
|
||||
.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy)
|
||||
.addPropertyValue("observationRegistry", observationRegistry);
|
||||
pc.getRegistry().registerBeanDefinition("jsr250AuthorizationMethodInterceptor",
|
||||
jsr250Interceptor.getBeanDefinition());
|
||||
}
|
||||
|
@ -182,6 +194,14 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
return null;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getObservationRegistry(Element methodSecurityElmt) {
|
||||
String holderStrategyRef = methodSecurityElmt.getAttribute(ATT_OBSERVATION_REGISTRY_REF);
|
||||
if (StringUtils.hasText(holderStrategyRef)) {
|
||||
return new RuntimeBeanReference(holderStrategyRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getSecurityContextHolderStrategy(Element methodSecurityElmt) {
|
||||
String holderStrategyRef = methodSecurityElmt.getAttribute(ATT_SECURITY_CONTEXT_HOLDER_STRATEGY_REF);
|
||||
if (StringUtils.hasText(holderStrategyRef)) {
|
||||
|
@ -295,12 +315,18 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
private final Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
|
||||
|
||||
@Override
|
||||
public AuthorizationManagerBeforeMethodInterceptor getObject() {
|
||||
AuthorizationManager<MethodInvocation> manager = this.manager;
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
manager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);
|
||||
}
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.jsr250(this.manager);
|
||||
.jsr250(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
@ -325,6 +351,47 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
public void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class SecuredAuthorizationMethodInterceptor
|
||||
implements FactoryBean<AuthorizationManagerBeforeMethodInterceptor> {
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
private final SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
|
||||
|
||||
@Override
|
||||
public AuthorizationManagerBeforeMethodInterceptor getObject() {
|
||||
AuthorizationManager<MethodInvocation> manager = this.manager;
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
manager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);
|
||||
}
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.secured(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return AuthorizationManagerBeforeMethodInterceptor.class;
|
||||
}
|
||||
|
||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
public void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class PreAuthorizeAuthorizationMethodInterceptor
|
||||
|
@ -333,12 +400,18 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
private final PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
|
||||
|
||||
@Override
|
||||
public AuthorizationManagerBeforeMethodInterceptor getObject() {
|
||||
AuthorizationManager<MethodInvocation> manager = this.manager;
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
manager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);
|
||||
}
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.preAuthorize(this.manager);
|
||||
.preAuthorize(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
@ -356,6 +429,10 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
this.manager.setExpressionHandler(expressionHandler);
|
||||
}
|
||||
|
||||
public void setObservationRegistry(ObservationRegistry registry) {
|
||||
this.observationRegistry = registry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class PostAuthorizeAuthorizationMethodInterceptor
|
||||
|
@ -364,12 +441,18 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
private final PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
||||
|
||||
@Override
|
||||
public AuthorizationManagerAfterMethodInterceptor getObject() {
|
||||
AuthorizationManager<MethodInvocationResult> manager = this.manager;
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
manager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);
|
||||
}
|
||||
AuthorizationManagerAfterMethodInterceptor interceptor = AuthorizationManagerAfterMethodInterceptor
|
||||
.postAuthorize(this.manager);
|
||||
.postAuthorize(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
@ -387,6 +470,10 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
this.manager.setExpressionHandler(expressionHandler);
|
||||
}
|
||||
|
||||
public void setObservationRegistry(ObservationRegistry registry) {
|
||||
this.observationRegistry = registry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecurityContextHolderStrategyFactory implements FactoryBean<SecurityContextHolderStrategy> {
|
||||
|
@ -403,4 +490,18 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
|
||||
}
|
||||
|
||||
static class ObservationRegistryFactory implements FactoryBean<ObservationRegistry> {
|
||||
|
||||
@Override
|
||||
public ObservationRegistry getObject() throws Exception {
|
||||
return ObservationRegistry.NOOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ObservationRegistry.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.UUID;
|
|||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
|
@ -50,6 +51,7 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager
|
|||
import org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorityReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
@ -1549,6 +1551,14 @@ public class ServerHttpSecurity {
|
|||
return this.context.getBean(beanClass);
|
||||
}
|
||||
|
||||
private <T> T getBeanOrDefault(Class<T> beanClass, T defaultInstance) {
|
||||
T bean = getBeanOrNull(beanClass);
|
||||
if (bean == null) {
|
||||
return defaultInstance;
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> beanClass) {
|
||||
return getBeanOrNull(ResolvableType.forClass(beanClass));
|
||||
}
|
||||
|
@ -1623,7 +1633,12 @@ public class ServerHttpSecurity {
|
|||
protected void configure(ServerHttpSecurity http) {
|
||||
Assert.state(this.matcher == null,
|
||||
() -> "The matcher " + this.matcher + " does not have an access rule defined");
|
||||
AuthorizationWebFilter result = new AuthorizationWebFilter(this.managerBldr.build());
|
||||
ReactiveAuthorizationManager<ServerWebExchange> manager = this.managerBldr.build();
|
||||
ObservationRegistry registry = getBeanOrDefault(ObservationRegistry.class, ObservationRegistry.NOOP);
|
||||
if (!registry.isNoop()) {
|
||||
manager = new ObservationReactiveAuthorizationManager<>(registry, manager);
|
||||
}
|
||||
AuthorizationWebFilter result = new AuthorizationWebFilter(manager);
|
||||
http.addFilterAt(result, SecurityWebFiltersOrder.AUTHORIZATION);
|
||||
}
|
||||
|
||||
|
|
|
@ -222,6 +222,9 @@ method-security.attlist &=
|
|||
method-security.attlist &=
|
||||
## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy
|
||||
attribute security-context-holder-strategy-ref {xsd:string}?
|
||||
method-security.attlist &=
|
||||
## Use this ObservationRegistry to collect metrics on various parts of the filter chain
|
||||
attribute observation-registry-ref {xsd:token}?
|
||||
|
||||
global-method-security =
|
||||
## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of "protect-pointcut" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie "protect-pointcut" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.
|
||||
|
@ -396,6 +399,9 @@ http.attlist &=
|
|||
name?
|
||||
http.attlist &=
|
||||
authentication-manager-ref?
|
||||
http.attlist &=
|
||||
## Use this ObservationRegistry to collect metrics on various parts of the filter chain
|
||||
attribute observation-registry-ref {xsd:token}?
|
||||
|
||||
access-denied-handler =
|
||||
## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.
|
||||
|
@ -1057,6 +1063,9 @@ authman.attlist &=
|
|||
authman.attlist &=
|
||||
## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.
|
||||
attribute erase-credentials {xsd:boolean}?
|
||||
authman.attlist &=
|
||||
## Use this ObservationRegistry to collect metrics on various parts of the filter chain
|
||||
attribute observation-registry-ref {xsd:token}?
|
||||
|
||||
authentication-provider =
|
||||
## Indicates that the contained user-service should be used as an authentication source.
|
||||
|
|
|
@ -695,6 +695,12 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="observation-registry-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="global-method-security">
|
||||
<xs:annotation>
|
||||
|
@ -1389,6 +1395,12 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="observation-registry-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:attributeGroup name="access-denied-handler.attlist">
|
||||
|
@ -2990,6 +3002,12 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="observation-registry-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:attributeGroup name="ap.attlist">
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authentication;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Observation.Context} used during authentications
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public class AuthenticationObservationContext extends Observation.Context {
|
||||
|
||||
private Authentication authenticationRequest;
|
||||
|
||||
private Class<?> authenticationManager;
|
||||
|
||||
private Authentication authenticationResult;
|
||||
|
||||
/**
|
||||
* Get the {@link Authentication} request that was observed
|
||||
* @return the observed {@link Authentication} request
|
||||
*/
|
||||
public Authentication getAuthenticationRequest() {
|
||||
return this.authenticationRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Authentication} request that was observed
|
||||
* @param authenticationRequest the observed {@link Authentication} request
|
||||
*/
|
||||
public void setAuthenticationRequest(Authentication authenticationRequest) {
|
||||
Assert.notNull(authenticationRequest, "authenticationRequest cannot be null");
|
||||
this.authenticationRequest = authenticationRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Authentication} result that was observed
|
||||
*
|
||||
* <p>
|
||||
* Note that if authentication failed, no {@link Authentication} result can be
|
||||
* observed. In that case, this returns {@code null}.
|
||||
* @return any observed {@link Authentication} result, {@code null} otherwise
|
||||
*/
|
||||
public Authentication getAuthenticationResult() {
|
||||
return this.authenticationResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Authentication} result that was observed
|
||||
* @param authenticationResult the observed {@link Authentication} result
|
||||
*/
|
||||
public void setAuthenticationResult(Authentication authenticationResult) {
|
||||
this.authenticationResult = authenticationResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link AuthenticationManager} class that processed the authentication
|
||||
* @return the observed {@link AuthenticationManager} class
|
||||
*/
|
||||
public Class<?> getAuthenticationManagerClass() {
|
||||
return this.authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link AuthenticationManager} class that processed the authentication
|
||||
* @param authenticationManagerClass the observed {@link AuthenticationManager} class
|
||||
*/
|
||||
public void setAuthenticationManagerClass(Class<?> authenticationManagerClass) {
|
||||
Assert.notNull(authenticationManagerClass, "authenticationManagerClass class cannot be null");
|
||||
this.authenticationManager = authenticationManagerClass;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authentication;
|
||||
|
||||
import io.micrometer.common.KeyValues;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationConvention;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* An {@link ObservationConvention} for translating authentications into
|
||||
* {@link KeyValues}.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class AuthenticationObservationConvention
|
||||
implements ObservationConvention<AuthenticationObservationContext> {
|
||||
|
||||
static final String OBSERVATION_NAME = "spring.security.authentications";
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return OBSERVATION_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(@NonNull AuthenticationObservationContext context) {
|
||||
return KeyValues.of("authentication.request.type", getAuthenticationType(context))
|
||||
.and("authentication.method", getAuthenticationMethod(context))
|
||||
.and("authentication.result.type", getAuthenticationResult(context))
|
||||
.and("authentication.failure.type", getAuthenticationFailureType(context));
|
||||
}
|
||||
|
||||
private String getAuthenticationType(AuthenticationObservationContext context) {
|
||||
if (context.getAuthenticationRequest() == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return context.getAuthenticationRequest().getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private String getAuthenticationMethod(AuthenticationObservationContext context) {
|
||||
if (context.getAuthenticationManagerClass() == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return context.getAuthenticationManagerClass().getSimpleName();
|
||||
}
|
||||
|
||||
private String getAuthenticationResult(AuthenticationObservationContext context) {
|
||||
if (context.getAuthenticationResult() == null) {
|
||||
return "n/a";
|
||||
}
|
||||
return context.getAuthenticationResult().getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private String getAuthenticationFailureType(AuthenticationObservationContext context) {
|
||||
if (context.getError() == null) {
|
||||
return "n/a";
|
||||
}
|
||||
return context.getError().getClass().getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsContext(@NotNull Observation.Context context) {
|
||||
return context instanceof AuthenticationObservationContext;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authentication;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationManager} that observes the authentication
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ObservationAuthenticationManager implements AuthenticationManager {
|
||||
|
||||
private final ObservationRegistry registry;
|
||||
|
||||
private final AuthenticationManager delegate;
|
||||
|
||||
private final AuthenticationObservationConvention convention = new AuthenticationObservationConvention();
|
||||
|
||||
public ObservationAuthenticationManager(ObservationRegistry registry, AuthenticationManager delegate) {
|
||||
Assert.notNull(registry, "observationRegistry cannot be null");
|
||||
Assert.notNull(delegate, "authenticationManager cannot be null");
|
||||
this.registry = registry;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
AuthenticationObservationContext context = new AuthenticationObservationContext();
|
||||
context.setAuthenticationRequest(authentication);
|
||||
context.setAuthenticationManagerClass(this.delegate.getClass());
|
||||
return Observation.createNotStarted(this.convention, () -> context, this.registry).observe(() -> {
|
||||
Authentication result = this.delegate.authenticate(authentication);
|
||||
context.setAuthenticationResult(result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authentication;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
* An {@link ReactiveAuthenticationManager} that observes the authentication
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ObservationReactiveAuthenticationManager implements ReactiveAuthenticationManager {
|
||||
|
||||
private final ObservationRegistry registry;
|
||||
|
||||
private final ReactiveAuthenticationManager delegate;
|
||||
|
||||
private final AuthenticationObservationConvention convention = new AuthenticationObservationConvention();
|
||||
|
||||
public ObservationReactiveAuthenticationManager(ObservationRegistry registry,
|
||||
ReactiveAuthenticationManager delegate) {
|
||||
this.registry = registry;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Authentication> authenticate(Authentication authentication) throws AuthenticationException {
|
||||
AuthenticationObservationContext context = new AuthenticationObservationContext();
|
||||
context.setAuthenticationRequest(authentication);
|
||||
context.setAuthenticationManagerClass(this.delegate.getClass());
|
||||
Observation observation = Observation.createNotStarted(this.convention, () -> context, this.registry).start();
|
||||
return this.delegate.authenticate(authentication).doOnSuccess((result) -> {
|
||||
context.setAuthenticationResult(result);
|
||||
observation.stop();
|
||||
}).doOnCancel(observation::stop).doOnError((t) -> {
|
||||
observation.error(t);
|
||||
observation.stop();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authorization;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Observation.Context} used during authorizations
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public class AuthorizationObservationContext<T> extends Observation.Context {
|
||||
|
||||
private Authentication authentication;
|
||||
|
||||
private final T object;
|
||||
|
||||
private AuthorizationDecision decision;
|
||||
|
||||
public AuthorizationObservationContext(T object) {
|
||||
Assert.notNull(object, "object cannot be null");
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the observed {@link Authentication} for this authorization
|
||||
*
|
||||
* <p>
|
||||
* Note that if the authorization did not require inspecting the
|
||||
* {@link Authentication}, this will return {@code null}.
|
||||
* @return any observed {@link Authentication}, {@code null} otherwise
|
||||
*/
|
||||
public Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the observed {@link Authentication} for this authorization
|
||||
* @param authentication the observed {@link Authentication}
|
||||
*/
|
||||
public void setAuthentication(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object for which access was requested
|
||||
* @return the requested object
|
||||
*/
|
||||
public T getObject() {
|
||||
return this.object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the observed {@link AuthorizationDecision}
|
||||
* @return the observed {@link AuthorizationDecision}
|
||||
*/
|
||||
public AuthorizationDecision getDecision() {
|
||||
return this.decision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the observed {@link AuthorizationDecision}
|
||||
* @param decision the observed {@link AuthorizationDecision}
|
||||
*/
|
||||
public void setDecision(AuthorizationDecision decision) {
|
||||
this.decision = decision;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authorization;
|
||||
|
||||
import io.micrometer.common.KeyValues;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationConvention;
|
||||
|
||||
/**
|
||||
* An {@link ObservationConvention} for translating authorizations into {@link KeyValues}.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class AuthorizationObservationConvention
|
||||
implements ObservationConvention<AuthorizationObservationContext<?>> {
|
||||
|
||||
static final String OBSERVATION_NAME = "spring.security.authorizations";
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return OBSERVATION_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(AuthorizationObservationContext<?> context) {
|
||||
return KeyValues.of("authentication.type", getAuthenticationType(context))
|
||||
.and("object.type", getObjectType(context))
|
||||
.and("authorization.decision", getAuthorizationDecision(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public KeyValues getHighCardinalityKeyValues(AuthorizationObservationContext<?> context) {
|
||||
return KeyValues.of("authentication.authorities", getAuthorities(context)).and("authorization.decision.details",
|
||||
getDecisionDetails(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsContext(Observation.Context context) {
|
||||
return context instanceof AuthorizationObservationContext<?>;
|
||||
}
|
||||
|
||||
private String getAuthenticationType(AuthorizationObservationContext<?> context) {
|
||||
if (context.getAuthentication() == null) {
|
||||
return "n/a";
|
||||
}
|
||||
return context.getAuthentication().getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private String getObjectType(AuthorizationObservationContext<?> context) {
|
||||
if (context.getObject() == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return context.getObject().getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private String getAuthorizationDecision(AuthorizationObservationContext<?> context) {
|
||||
if (context.getDecision() == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return String.valueOf(context.getDecision().isGranted());
|
||||
}
|
||||
|
||||
private String getAuthorities(AuthorizationObservationContext<?> context) {
|
||||
if (context.getAuthentication() == null) {
|
||||
return "n/a";
|
||||
}
|
||||
return String.valueOf(context.getAuthentication().getAuthorities());
|
||||
}
|
||||
|
||||
private String getDecisionDetails(AuthorizationObservationContext<?> context) {
|
||||
if (context.getDecision() == null) {
|
||||
return "unknown";
|
||||
}
|
||||
AuthorizationDecision decision = context.getDecision();
|
||||
return String.valueOf(decision);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authorization;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* An {@link AuthorizationManager} that observes the authorization
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ObservationAuthorizationManager<T> implements AuthorizationManager<T> {
|
||||
|
||||
private final ObservationRegistry registry;
|
||||
|
||||
private final AuthorizationManager<T> delegate;
|
||||
|
||||
private final AuthorizationObservationConvention convention = new AuthorizationObservationConvention();
|
||||
|
||||
public ObservationAuthorizationManager(ObservationRegistry registry, AuthorizationManager<T> delegate) {
|
||||
this.registry = registry;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
|
||||
AuthorizationObservationContext<T> context = new AuthorizationObservationContext<>(object);
|
||||
Supplier<Authentication> wrapped = () -> {
|
||||
context.setAuthentication(authentication.get());
|
||||
return context.getAuthentication();
|
||||
};
|
||||
Observation observation = Observation.createNotStarted(this.convention, () -> context, this.registry).start();
|
||||
try (Observation.Scope scope = observation.openScope()) {
|
||||
AuthorizationDecision decision = this.delegate.check(wrapped, object);
|
||||
context.setDecision(decision);
|
||||
if (decision != null && !decision.isGranted()) {
|
||||
observation.error(new AccessDeniedException("Access Denied"));
|
||||
}
|
||||
return decision;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
observation.error(ex);
|
||||
throw ex;
|
||||
}
|
||||
finally {
|
||||
observation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authorization;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* An {@link ReactiveAuthorizationManager} that observes the authentication
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ObservationReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
|
||||
|
||||
private final ObservationRegistry registry;
|
||||
|
||||
private final ReactiveAuthorizationManager<T> delegate;
|
||||
|
||||
private final AuthorizationObservationConvention convention = new AuthorizationObservationConvention();
|
||||
|
||||
public ObservationReactiveAuthorizationManager(ObservationRegistry registry,
|
||||
ReactiveAuthorizationManager<T> delegate) {
|
||||
this.registry = registry;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
|
||||
AuthorizationObservationContext<T> context = new AuthorizationObservationContext<>(object);
|
||||
Mono<Authentication> wrapped = authentication.map((auth) -> {
|
||||
context.setAuthentication(auth);
|
||||
return context.getAuthentication();
|
||||
});
|
||||
Observation observation = Observation.createNotStarted(this.convention, () -> context, this.registry).start();
|
||||
return this.delegate.check(wrapped, object).doOnSuccess((decision) -> {
|
||||
context.setDecision(decision);
|
||||
if (decision == null || !decision.isGranted()) {
|
||||
observation.error(new AccessDeniedException("Access Denied"));
|
||||
}
|
||||
observation.stop();
|
||||
}).doOnCancel(observation::stop).doOnError((t) -> {
|
||||
observation.error(t);
|
||||
observation.stop();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -98,6 +98,20 @@ public final class AuthorizationManagerAfterMethodInterceptor
|
|||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interceptor for the {@link PostAuthorize} annotation
|
||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
||||
* @return the interceptor
|
||||
* @since 6.0
|
||||
*/
|
||||
public static AuthorizationManagerAfterMethodInterceptor postAuthorize(
|
||||
AuthorizationManager<MethodInvocationResult> authorizationManager) {
|
||||
AuthorizationManagerAfterMethodInterceptor interceptor = new AuthorizationManagerAfterMethodInterceptor(
|
||||
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), authorizationManager);
|
||||
interceptor.setOrder(500);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
|
||||
* using the {@link AuthorizationManager}.
|
||||
|
|
|
@ -102,6 +102,20 @@ public final class AuthorizationManagerBeforeMethodInterceptor
|
|||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interceptor for the {@link PreAuthorize} annotation
|
||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
||||
* @return the interceptor
|
||||
* @since 6.0
|
||||
*/
|
||||
public static AuthorizationManagerBeforeMethodInterceptor preAuthorize(
|
||||
AuthorizationManager<MethodInvocation> authorizationManager) {
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(
|
||||
AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), authorizationManager);
|
||||
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interceptor for the {@link Secured} annotation
|
||||
* @return the interceptor
|
||||
|
@ -123,6 +137,20 @@ public final class AuthorizationManagerBeforeMethodInterceptor
|
|||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interceptor for the {@link Secured} annotation
|
||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
||||
* @return the interceptor
|
||||
* @since 6.0
|
||||
*/
|
||||
public static AuthorizationManagerBeforeMethodInterceptor secured(
|
||||
AuthorizationManager<MethodInvocation> authorizationManager) {
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(
|
||||
AuthorizationMethodPointcuts.forAnnotations(Secured.class), authorizationManager);
|
||||
interceptor.setOrder(AuthorizationInterceptorsOrder.SECURED.getOrder());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interceptor for the JSR-250 annotations
|
||||
* @return the interceptor
|
||||
|
@ -144,6 +172,21 @@ public final class AuthorizationManagerBeforeMethodInterceptor
|
|||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interceptor for the JSR-250 annotations
|
||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
||||
* @return the interceptor
|
||||
* @since 6.0
|
||||
*/
|
||||
public static AuthorizationManagerBeforeMethodInterceptor jsr250(
|
||||
AuthorizationManager<MethodInvocation> authorizationManager) {
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(
|
||||
AuthorizationMethodPointcuts.forAnnotations(RolesAllowed.class, DenyAll.class, PermitAll.class),
|
||||
authorizationManager);
|
||||
interceptor.setOrder(AuthorizationInterceptorsOrder.JSR250.getOrder());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
|
||||
* using the configured {@link AuthorizationManager}.
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authentication;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link ObservationAuthenticationManager}
|
||||
*/
|
||||
public class ObservationAuthenticationManagerTests {
|
||||
|
||||
private ObservationRegistry registry;
|
||||
|
||||
private ObservationHandler<Observation.Context> handler;
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
private ObservationAuthenticationManager tested;
|
||||
|
||||
private final Authentication token = new TestingAuthenticationToken("user", "pass");
|
||||
|
||||
private final Authentication authentication = new TestingAuthenticationToken("user", "pass", "app");
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.handler = mock(ObservationHandler.class);
|
||||
ObservationRegistry registry = ObservationRegistry.create();
|
||||
registry.observationConfig().observationHandler(this.handler);
|
||||
this.registry = registry;
|
||||
this.authenticationManager = mock(AuthenticationManager.class);
|
||||
this.tested = new ObservationAuthenticationManager(this.registry, this.authenticationManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticateWhenDefaultsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authenticationManager.authenticate(any())).willReturn(this.authentication);
|
||||
this.tested.authenticate(this.token);
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isNull();
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);
|
||||
AuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();
|
||||
assertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());
|
||||
assertThat(context.getAuthenticationRequest()).isEqualTo(this.token);
|
||||
assertThat(context.getAuthenticationResult()).isEqualTo(this.authentication);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationWhenErrorsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authenticationManager.authenticate(any())).willThrow(BadCredentialsException.class);
|
||||
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.tested.authenticate(this.token));
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isInstanceOf(AuthenticationException.class);
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);
|
||||
AuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();
|
||||
assertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());
|
||||
assertThat(context.getAuthenticationRequest()).isEqualTo(this.token);
|
||||
assertThat(context.getAuthenticationResult()).isNull();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authentication;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link ObservationAuthenticationManager}
|
||||
*/
|
||||
public class ObservationReactiveAuthenticationManagerTests {
|
||||
|
||||
private ObservationRegistry registry;
|
||||
|
||||
private ObservationHandler<Observation.Context> handler;
|
||||
|
||||
private ReactiveAuthenticationManager authenticationManager;
|
||||
|
||||
private ObservationReactiveAuthenticationManager tested;
|
||||
|
||||
private final Authentication token = new TestingAuthenticationToken("user", "pass");
|
||||
|
||||
private final Authentication authentication = new TestingAuthenticationToken("user", "pass", "app");
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.handler = mock(ObservationHandler.class);
|
||||
ObservationRegistry registry = ObservationRegistry.create();
|
||||
registry.observationConfig().observationHandler(this.handler);
|
||||
this.registry = registry;
|
||||
this.authenticationManager = mock(ReactiveAuthenticationManager.class);
|
||||
this.tested = new ObservationReactiveAuthenticationManager(this.registry, this.authenticationManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticateWhenDefaultsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authenticationManager.authenticate(any())).willReturn(Mono.just(this.authentication));
|
||||
this.tested.authenticate(this.token).block();
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isNull();
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);
|
||||
AuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();
|
||||
assertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());
|
||||
assertThat(context.getAuthenticationRequest()).isEqualTo(this.token);
|
||||
assertThat(context.getAuthenticationResult()).isEqualTo(this.authentication);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationWhenErrorsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authenticationManager.authenticate(any()))
|
||||
.willReturn(Mono.error(new BadCredentialsException("fail")));
|
||||
assertThatExceptionOfType(BadCredentialsException.class)
|
||||
.isThrownBy(() -> this.tested.authenticate(this.token).block());
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isInstanceOf(AuthenticationException.class);
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);
|
||||
AuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();
|
||||
assertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());
|
||||
assertThat(context.getAuthenticationRequest()).isEqualTo(this.token);
|
||||
assertThat(context.getAuthenticationResult()).isNull();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authorization;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link ObservationAuthorizationManager}
|
||||
*/
|
||||
public class ObservationAuthorizationManagerTests {
|
||||
|
||||
private ObservationRegistry registry;
|
||||
|
||||
private ObservationHandler<Observation.Context> handler;
|
||||
|
||||
private AuthorizationManager<Object> authorizationManager;
|
||||
|
||||
private ObservationAuthorizationManager<Object> tested;
|
||||
|
||||
private final Supplier<Authentication> token = () -> new TestingAuthenticationToken("user", "pass");
|
||||
|
||||
private final Object object = new Object();
|
||||
|
||||
private final AuthorizationDecision grant = new AuthorizationDecision(true);
|
||||
|
||||
private final AuthorizationDecision deny = new AuthorizationDecision(false);
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.handler = mock(ObservationHandler.class);
|
||||
ObservationRegistry registry = ObservationRegistry.create();
|
||||
registry.observationConfig().observationHandler(this.handler);
|
||||
this.registry = registry;
|
||||
this.authorizationManager = mock(AuthorizationManager.class);
|
||||
this.tested = new ObservationAuthorizationManager<>(this.registry, this.authorizationManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyWhenDefaultsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authorizationManager.check(any(), any())).willReturn(this.grant);
|
||||
this.tested.verify(this.token, this.object);
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isNull();
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);
|
||||
AuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();
|
||||
assertThat(context.getAuthentication()).isNull();
|
||||
assertThat(context.getObject()).isEqualTo(this.object);
|
||||
assertThat(context.getDecision()).isEqualTo(this.grant);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyWhenErrorsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authorizationManager.check(any(), any())).willReturn(this.deny);
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.tested.verify(this.token, this.object));
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isInstanceOf(AccessDeniedException.class);
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);
|
||||
AuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();
|
||||
assertThat(context.getAuthentication()).isNull();
|
||||
assertThat(context.getObject()).isEqualTo(this.object);
|
||||
assertThat(context.getDecision()).isEqualTo(this.deny);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyWhenLooksUpAuthenticationThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authorizationManager.check(any(), any())).willAnswer((invocation) -> {
|
||||
((Supplier<Authentication>) invocation.getArgument(0)).get();
|
||||
return this.grant;
|
||||
});
|
||||
this.tested.verify(this.token, this.object);
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isNull();
|
||||
AuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();
|
||||
assertThat(context.getAuthentication()).isEqualTo(this.token.get());
|
||||
assertThat(context.getObject()).isEqualTo(this.object);
|
||||
assertThat(context.getDecision()).isEqualTo(this.grant);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.authorization;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link ObservationAuthorizationManager}
|
||||
*/
|
||||
public class ObservationReactiveAuthorizationManagerTests {
|
||||
|
||||
private ObservationRegistry registry;
|
||||
|
||||
private ObservationHandler<Observation.Context> handler;
|
||||
|
||||
private ReactiveAuthorizationManager<Object> authorizationManager;
|
||||
|
||||
private ObservationReactiveAuthorizationManager<Object> tested;
|
||||
|
||||
private final Mono<Authentication> token = Mono.just(new TestingAuthenticationToken("user", "pass"));
|
||||
|
||||
private final Object object = new Object();
|
||||
|
||||
private final AuthorizationDecision grant = new AuthorizationDecision(true);
|
||||
|
||||
private final AuthorizationDecision deny = new AuthorizationDecision(false);
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.handler = mock(ObservationHandler.class);
|
||||
ObservationRegistry registry = ObservationRegistry.create();
|
||||
registry.observationConfig().observationHandler(this.handler);
|
||||
this.registry = registry;
|
||||
this.authorizationManager = mock(ReactiveAuthorizationManager.class);
|
||||
this.tested = new ObservationReactiveAuthorizationManager<>(this.registry, this.authorizationManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyWhenDefaultsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authorizationManager.check(any(), any())).willReturn(Mono.just(this.grant));
|
||||
this.tested.verify(this.token, this.object).block();
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isNull();
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);
|
||||
AuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();
|
||||
assertThat(context.getAuthentication()).isNull();
|
||||
assertThat(context.getObject()).isEqualTo(this.object);
|
||||
assertThat(context.getDecision()).isEqualTo(this.grant);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyWhenErrorsThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authorizationManager.check(any(), any())).willReturn(Mono.just(this.deny));
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.tested.verify(this.token, this.object).block());
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isInstanceOf(AccessDeniedException.class);
|
||||
assertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);
|
||||
AuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();
|
||||
assertThat(context.getAuthentication()).isNull();
|
||||
assertThat(context.getObject()).isEqualTo(this.object);
|
||||
assertThat(context.getDecision()).isEqualTo(this.deny);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyWhenLooksUpAuthenticationThenObserves() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
given(this.authorizationManager.check(any(), any())).willAnswer((invocation) -> {
|
||||
((Mono<Authentication>) invocation.getArgument(0)).block();
|
||||
return Mono.just(this.grant);
|
||||
});
|
||||
this.tested.verify(this.token, this.object).block();
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.handler).onStart(captor.capture());
|
||||
assertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);
|
||||
assertThat(captor.getValue().getError()).isNull();
|
||||
AuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();
|
||||
assertThat(context.getAuthentication()).isEqualTo(this.token.block());
|
||||
assertThat(context.getObject()).isEqualTo(this.object);
|
||||
assertThat(context.getDecision()).isEqualTo(this.grant);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue