mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-08 13:12:12 +00:00
Make Observations Selectable
Closes gh-15678
This commit is contained in:
parent
69e3c248fa
commit
d6b620b9f7
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
|
||||||
|
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.MethodInvocationResult;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
class MethodObservationConfiguration {
|
||||||
|
|
||||||
|
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveRequests(true)
|
||||||
|
.shouldObserveAuthentications(true)
|
||||||
|
.shouldObserveAuthorizations(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<AuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager postProcess(AuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager postProcess(AuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -68,8 +68,7 @@ final class MethodSecuritySelector implements ImportSelector {
|
|||||||
imports.add(AuthorizationProxyDataConfiguration.class.getName());
|
imports.add(AuthorizationProxyDataConfiguration.class.getName());
|
||||||
}
|
}
|
||||||
if (isObservabilityPresent) {
|
if (isObservabilityPresent) {
|
||||||
imports.add(
|
imports.add(MethodObservationConfiguration.class.getName());
|
||||||
"org.springframework.security.config.annotation.observation.configuration.ObservationConfiguration");
|
|
||||||
}
|
}
|
||||||
return imports.toArray(new String[0]);
|
return imports.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
|
||||||
|
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.ObservationReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
class ReactiveMethodObservationConfiguration {
|
||||||
|
|
||||||
|
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveRequests(true)
|
||||||
|
.shouldObserveAuthentications(true)
|
||||||
|
.shouldObserveAuthorizations(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Fallback;
|
||||||
import org.springframework.context.annotation.ImportAware;
|
import org.springframework.context.annotation.ImportAware;
|
||||||
import org.springframework.context.annotation.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
@ -82,6 +83,7 @@ class ReactiveMethodSecurityConfiguration implements ImportAware {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
@Fallback
|
||||||
static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(
|
static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(
|
||||||
ReactiveMethodSecurityConfiguration configuration) {
|
ReactiveMethodSecurityConfiguration configuration) {
|
||||||
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
||||||
|
@ -62,8 +62,7 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
|
|||||||
imports.add(AuthorizationProxyDataConfiguration.class.getName());
|
imports.add(AuthorizationProxyDataConfiguration.class.getName());
|
||||||
}
|
}
|
||||||
if (isObservabilityPresent) {
|
if (isObservabilityPresent) {
|
||||||
imports.add(
|
imports.add(ReactiveMethodObservationConfiguration.class.getName());
|
||||||
"org.springframework.security.config.annotation.observation.configuration.ReactiveObservationConfiguration");
|
|
||||||
}
|
}
|
||||||
imports.add(AuthorizationProxyConfiguration.class.getName());
|
imports.add(AuthorizationProxyConfiguration.class.getName());
|
||||||
return imports.toArray(new String[0]);
|
return imports.toArray(new String[0]);
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2024 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.observation.configuration;
|
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
|
||||||
|
|
||||||
abstract class AbstractObservationObjectPostProcessor<O> implements ObjectPostProcessor<O> {
|
|
||||||
|
|
||||||
private final ObjectProvider<ObservationRegistry> observationRegistry;
|
|
||||||
|
|
||||||
private final BiFunction<ObservationRegistry, O, O> wrapper;
|
|
||||||
|
|
||||||
AbstractObservationObjectPostProcessor(ObjectProvider<ObservationRegistry> observationRegistry,
|
|
||||||
Function<ObservationRegistry, O> constructor) {
|
|
||||||
this(observationRegistry, (registry, object) -> constructor.apply(registry));
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractObservationObjectPostProcessor(ObjectProvider<ObservationRegistry> observationRegistry,
|
|
||||||
BiFunction<ObservationRegistry, O, O> constructor) {
|
|
||||||
this.observationRegistry = observationRegistry;
|
|
||||||
this.wrapper = constructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <O1 extends O> O1 postProcess(O1 object) {
|
|
||||||
ObservationRegistry registry = this.observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP);
|
|
||||||
if (registry.isNoop()) {
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
return (O1) this.wrapper.apply(registry, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2024 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.observation.configuration;
|
|
||||||
|
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
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.authentication.AuthenticationManager;
|
|
||||||
import org.springframework.security.authentication.ObservationAuthenticationManager;
|
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
|
||||||
import org.springframework.security.web.ObservationFilterChainDecorator;
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
class ObservationConfiguration {
|
|
||||||
|
|
||||||
private final ObjectProvider<ObservationRegistry> observationRegistry;
|
|
||||||
|
|
||||||
ObservationConfiguration(ObjectProvider<ObservationRegistry> observationRegistry) {
|
|
||||||
this.observationRegistry = observationRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationAuthorizationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationAuthorizationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> webAuthorizationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationAuthorizationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<AuthenticationManager> authenticationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationAuthenticationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<FilterChainProxy.FilterChainDecorator> filterChainDecoratorPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationFilterChainDecorator::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2024 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.observation.configuration;
|
|
||||||
|
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
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.authentication.ObservationReactiveAuthenticationManager;
|
|
||||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
|
||||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
|
||||||
import org.springframework.security.web.server.ObservationWebFilterChainDecorator;
|
|
||||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
class ReactiveObservationConfiguration {
|
|
||||||
|
|
||||||
private final ObjectProvider<ObservationRegistry> observationRegistry;
|
|
||||||
|
|
||||||
ReactiveObservationConfiguration(ObjectProvider<ObservationRegistry> observationRegistry) {
|
|
||||||
this.observationRegistry = observationRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationReactiveAuthorizationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationReactiveAuthorizationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> webAuthorizationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationReactiveAuthorizationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<ReactiveAuthenticationManager> authenticationManagerPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationReactiveAuthenticationManager::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
ObjectPostProcessor<WebFilterChainProxy.WebFilterChainDecorator> filterChainDecoratorPostProcessor() {
|
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
|
||||||
ObservationWebFilterChainDecorator::new) {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.rsocket;
|
||||||
|
|
||||||
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
|
|
||||||
|
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.authentication.ObservationReactiveAuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||||
|
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
|
import org.springframework.security.web.server.ObservationWebFilterChainDecorator;
|
||||||
|
import org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
class ReactiveObservationConfiguration {
|
||||||
|
|
||||||
|
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveRequests(true)
|
||||||
|
.shouldObserveAuthentications(true)
|
||||||
|
.shouldObserveAuthorizations(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> webAuthorizationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<ReactiveAuthenticationManager> authenticationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public ReactiveAuthenticationManager postProcess(ReactiveAuthenticationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();
|
||||||
|
return active ? new ObservationReactiveAuthenticationManager(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<WebFilterChainDecorator> filterChainDecoratorPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public WebFilterChainDecorator postProcess(WebFilterChainDecorator object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveRequests();
|
||||||
|
return active ? new ObservationWebFilterChainDecorator(r) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -44,8 +44,7 @@ class ReactiveObservationImportSelector implements ImportSelector {
|
|||||||
if (!observabilityPresent) {
|
if (!observabilityPresent) {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
return new String[] {
|
return new String[] { ReactiveObservationConfiguration.class.getName() };
|
||||||
"org.springframework.security.config.annotation.observation.configuration.ReactiveObservationConfiguration" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.web.configuration;
|
||||||
|
|
||||||
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
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.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.ObservationAuthenticationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
|
import org.springframework.security.web.FilterChainProxy.FilterChainDecorator;
|
||||||
|
import org.springframework.security.web.ObservationFilterChainDecorator;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
class ObservationConfiguration {
|
||||||
|
|
||||||
|
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveRequests(true)
|
||||||
|
.shouldObserveAuthentications(true)
|
||||||
|
.shouldObserveAuthorizations(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> webAuthorizationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager postProcess(AuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<AuthenticationManager> authenticationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public AuthenticationManager postProcess(AuthenticationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();
|
||||||
|
return active ? new ObservationAuthenticationManager(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<FilterChainDecorator> filterChainDecoratorPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public FilterChainDecorator postProcess(FilterChainDecorator object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveRequests();
|
||||||
|
return active ? new ObservationFilterChainDecorator(r) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -43,8 +43,7 @@ class ObservationImportSelector implements ImportSelector {
|
|||||||
if (!observabilityPresent) {
|
if (!observabilityPresent) {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
return new String[] {
|
return new String[] { ObservationConfiguration.class.getName() };
|
||||||
"org.springframework.security.config.annotation.observation.configuration.ObservationConfiguration" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.web.reactive;
|
||||||
|
|
||||||
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
|
|
||||||
|
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.authentication.ObservationReactiveAuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||||
|
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
|
import org.springframework.security.web.server.ObservationWebFilterChainDecorator;
|
||||||
|
import org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
class ReactiveObservationConfiguration {
|
||||||
|
|
||||||
|
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveRequests(true)
|
||||||
|
.shouldObserveAuthentications(true)
|
||||||
|
.shouldObserveAuthorizations(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> webAuthorizationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<ReactiveAuthenticationManager> authenticationManagerPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public ReactiveAuthenticationManager postProcess(ReactiveAuthenticationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();
|
||||||
|
return active ? new ObservationReactiveAuthenticationManager(r, object) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
static ObjectPostProcessor<WebFilterChainDecorator> filterChainDecoratorPostProcessor(
|
||||||
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public WebFilterChainDecorator postProcess(WebFilterChainDecorator object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveRequests();
|
||||||
|
return active ? new ObservationWebFilterChainDecorator(r) : object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -43,8 +43,7 @@ class ReactiveObservationImportSelector implements ImportSelector {
|
|||||||
if (!observabilityPresent) {
|
if (!observabilityPresent) {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
return new String[] {
|
return new String[] { ReactiveObservationConfiguration.class.getName() };
|
||||||
"org.springframework.security.config.annotation.observation.configuration.ReactiveObservationConfiguration" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.observation.configuration;
|
package org.springframework.security.config.annotation.web.socket;
|
||||||
|
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
|
|
||||||
@ -27,22 +27,29 @@ import org.springframework.messaging.Message;
|
|||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
class WebSocketObservationConfiguration {
|
class WebSocketObservationConfiguration {
|
||||||
|
|
||||||
private final ObjectProvider<ObservationRegistry> observationRegistry;
|
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveRequests(true)
|
||||||
WebSocketObservationConfiguration(ObjectProvider<ObservationRegistry> observationRegistry) {
|
.shouldObserveAuthentications(true)
|
||||||
this.observationRegistry = observationRegistry;
|
.shouldObserveAuthorizations(true)
|
||||||
}
|
.build();
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
ObjectPostProcessor<AuthorizationManager<Message<?>>> messageAuthorizationManagerPostProcessor() {
|
static ObjectPostProcessor<AuthorizationManager<Message<?>>> webAuthorizationManagerPostProcessor(
|
||||||
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
|
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||||
ObservationAuthorizationManager::new) {
|
return new ObjectPostProcessor<>() {
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager postProcess(AuthorizationManager object) {
|
||||||
|
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||||
|
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||||
|
return active ? new ObservationAuthorizationManager<>(r, object) : object;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -43,8 +43,7 @@ class WebSocketObservationImportSelector implements ImportSelector {
|
|||||||
if (!observabilityPresent) {
|
if (!observabilityPresent) {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
return new String[] {
|
return new String[] { WebSocketObservationConfiguration.class.getName() };
|
||||||
"org.springframework.security.config.annotation.observation.configuration.WebSocketObservationConfiguration" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.observation;
|
||||||
|
|
||||||
|
import io.micrometer.observation.ObservationPredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link ObservationPredicate} that can be used to change which Spring Security
|
||||||
|
* observations are made with Micrometer.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By default, web requests are not observed and authentications and authorizations are
|
||||||
|
* observed.
|
||||||
|
*
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 6.4
|
||||||
|
*/
|
||||||
|
public final class SecurityObservationSettings {
|
||||||
|
|
||||||
|
private final boolean observeRequests;
|
||||||
|
|
||||||
|
private final boolean observeAuthentications;
|
||||||
|
|
||||||
|
private final boolean observeAuthorizations;
|
||||||
|
|
||||||
|
private SecurityObservationSettings(boolean observeRequests, boolean observeAuthentications,
|
||||||
|
boolean observeAuthorizations) {
|
||||||
|
this.observeRequests = observeRequests;
|
||||||
|
this.observeAuthentications = observeAuthentications;
|
||||||
|
this.observeAuthorizations = observeAuthorizations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make no Spring Security observations
|
||||||
|
* @return a {@link SecurityObservationSettings} with all exclusions turned on
|
||||||
|
*/
|
||||||
|
public static SecurityObservationSettings noObservations() {
|
||||||
|
return new SecurityObservationSettings(false, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin the configuration of a {@link SecurityObservationSettings}
|
||||||
|
* @return a {@link Builder} where filter chain observations are off and authn/authz
|
||||||
|
* observations are on
|
||||||
|
*/
|
||||||
|
public static Builder withDefaults() {
|
||||||
|
return new Builder(false, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldObserveRequests() {
|
||||||
|
return this.observeRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldObserveAuthentications() {
|
||||||
|
return this.observeAuthentications;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldObserveAuthorizations() {
|
||||||
|
return this.observeAuthorizations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for configuring a {@link SecurityObservationSettings}
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private boolean observeRequests;
|
||||||
|
|
||||||
|
private boolean observeAuthentications;
|
||||||
|
|
||||||
|
private boolean observeAuthorizations;
|
||||||
|
|
||||||
|
Builder(boolean observeRequests, boolean observeAuthentications, boolean observeAuthorizations) {
|
||||||
|
this.observeRequests = observeRequests;
|
||||||
|
this.observeAuthentications = observeAuthentications;
|
||||||
|
this.observeAuthorizations = observeAuthorizations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder shouldObserveRequests(boolean excludeFilters) {
|
||||||
|
this.observeRequests = excludeFilters;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder shouldObserveAuthentications(boolean excludeAuthentications) {
|
||||||
|
this.observeAuthentications = excludeAuthentications;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder shouldObserveAuthorizations(boolean excludeAuthorizations) {
|
||||||
|
this.observeAuthorizations = excludeAuthorizations;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SecurityObservationSettings build() {
|
||||||
|
return new SecurityObservationSettings(this.observeRequests, this.observeAuthentications,
|
||||||
|
this.observeAuthorizations);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -87,6 +87,7 @@ import org.springframework.security.authorization.method.PrePostTemplateDefaults
|
|||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.config.test.SpringTestParentApplicationContextExecutionListener;
|
import org.springframework.security.config.test.SpringTestParentApplicationContextExecutionListener;
|
||||||
@ -114,6 +115,7 @@ import static org.mockito.Mockito.never;
|
|||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
||||||
@ -1062,6 +1064,43 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
verify(handler).onError(any());
|
verify(handler).onError(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
public void prePostMethodWhenExcludeAuthorizationObservationsThenUnobserved() {
|
||||||
|
this.spring
|
||||||
|
.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class,
|
||||||
|
SelectableObservationsConfig.class)
|
||||||
|
.autowire();
|
||||||
|
this.methodSecurityService.preAuthorizePermitAll();
|
||||||
|
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||||
|
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize);
|
||||||
|
verifyNoInteractions(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
public void securedMethodWhenExcludeAuthorizationObservationsThenUnobserved() {
|
||||||
|
this.spring
|
||||||
|
.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class,
|
||||||
|
SelectableObservationsConfig.class)
|
||||||
|
.autowire();
|
||||||
|
this.methodSecurityService.securedUser();
|
||||||
|
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||||
|
verifyNoInteractions(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
public void jsr250MethodWhenExcludeAuthorizationObservationsThenUnobserved() {
|
||||||
|
this.spring
|
||||||
|
.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class,
|
||||||
|
SelectableObservationsConfig.class)
|
||||||
|
.autowire();
|
||||||
|
this.methodSecurityService.jsr250RolesAllowedUser();
|
||||||
|
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||||
|
verifyNoInteractions(handler);
|
||||||
|
}
|
||||||
|
|
||||||
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
||||||
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
||||||
}
|
}
|
||||||
@ -1742,4 +1781,14 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class SelectableObservationsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings observabilityDefaults() {
|
||||||
|
return SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,11 @@ import reactor.core.publisher.Flux;
|
|||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
@ -55,6 +57,7 @@ import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
|||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -69,6 +72,7 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Tadaya Tsuyukubo
|
* @author Tadaya Tsuyukubo
|
||||||
@ -260,6 +264,27 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||||||
verify(handler).onError(any());
|
verify(handler).onError(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
public void prePostMethodWhenExcludeAuthorizationObservationsThenUnobserved() {
|
||||||
|
this.spring
|
||||||
|
.register(MethodSecurityServiceConfig.class, ObservationRegistryConfig.class,
|
||||||
|
SelectableObservationsConfig.class)
|
||||||
|
.autowire();
|
||||||
|
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||||
|
Authentication user = TestAuthentication.authenticatedUser();
|
||||||
|
StepVerifier
|
||||||
|
.create(service.preAuthorizeUser().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))
|
||||||
|
.expectNextCount(1)
|
||||||
|
.verifyComplete();
|
||||||
|
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||||
|
StepVerifier
|
||||||
|
.create(service.preAuthorizeAdmin().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))
|
||||||
|
.expectError()
|
||||||
|
.verify();
|
||||||
|
verifyNoInteractions(handler);
|
||||||
|
}
|
||||||
|
|
||||||
private static Consumer<User.UserBuilder> authorities(String... authorities) {
|
private static Consumer<User.UserBuilder> authorities(String... authorities) {
|
||||||
return (builder) -> builder.authorities(authorities);
|
return (builder) -> builder.authorities(authorities);
|
||||||
}
|
}
|
||||||
@ -432,9 +457,37 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
PrePostMethodSecurityConfigurationTests.ObservationRegistryPostProcessor observationRegistryPostProcessor(
|
ObservationRegistryPostProcessor observationRegistryPostProcessor(
|
||||||
ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||||
return new PrePostMethodSecurityConfigurationTests.ObservationRegistryPostProcessor(handler);
|
return new ObservationRegistryPostProcessor(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ObservationRegistryPostProcessor implements BeanPostProcessor {
|
||||||
|
|
||||||
|
private final ObjectProvider<ObservationHandler<Observation.Context>> handler;
|
||||||
|
|
||||||
|
ObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
if (bean instanceof ObservationRegistry registry) {
|
||||||
|
registry.observationConfig().observationHandler(this.handler.getObject());
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class SelectableObservationsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings observabilityDefaults() {
|
||||||
|
return SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ import org.springframework.security.config.annotation.web.AbstractRequestMatcher
|
|||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -80,6 +81,7 @@ import static org.mockito.Mockito.atLeastOnce;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
@ -650,6 +652,18 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||||||
verify(handler).onError(any());
|
verify(handler).onError(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenExcludeAuthorizationObservationsThenUnobserved() throws Exception {
|
||||||
|
this.spring
|
||||||
|
.register(RoleUserConfig.class, BasicController.class, ObservationRegistryConfig.class,
|
||||||
|
SelectableObservationsConfig.class)
|
||||||
|
.autowire();
|
||||||
|
ObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||||
|
this.mvc.perform(get("/").with(user("user").roles("USER"))).andExpect(status().isOk());
|
||||||
|
this.mvc.perform(get("/").with(user("user").roles("WRONG"))).andExpect(status().isForbidden());
|
||||||
|
verifyNoInteractions(handler);
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class GrantedAuthorityDefaultHasRoleConfig {
|
static class GrantedAuthorityDefaultHasRoleConfig {
|
||||||
@ -1288,4 +1302,14 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class SelectableObservationsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings observabilityDefaults() {
|
||||||
|
return SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import org.springframework.security.config.annotation.ObjectPostProcessor;
|
|||||||
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
@ -63,6 +64,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
@ -189,6 +191,26 @@ public class HttpBasicConfigurerTests {
|
|||||||
assertThat(context.getValue()).isInstanceOf(AuthenticationObservationContext.class);
|
assertThat(context.getValue()).isInstanceOf(AuthenticationObservationContext.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void httpBasicWhenExcludeAuthenticationObservationsThenUnobserved() throws Exception {
|
||||||
|
this.spring
|
||||||
|
.register(HttpBasic.class, Users.class, Home.class, ObservationRegistryConfig.class,
|
||||||
|
SelectableObservationsConfig.class)
|
||||||
|
.autowire();
|
||||||
|
ObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||||
|
this.mvc.perform(get("/").with(httpBasic("user", "password")))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string("user"));
|
||||||
|
ArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);
|
||||||
|
verify(handler, atLeastOnce()).onStart(context.capture());
|
||||||
|
assertThat(context.getAllValues()).noneMatch((c) -> c instanceof AuthenticationObservationContext);
|
||||||
|
context = ArgumentCaptor.forClass(Observation.Context.class);
|
||||||
|
verify(handler, atLeastOnce()).onStop(context.capture());
|
||||||
|
assertThat(context.getAllValues()).noneMatch((c) -> c instanceof AuthenticationObservationContext);
|
||||||
|
this.mvc.perform(get("/").with(httpBasic("user", "wrong"))).andExpect(status().isUnauthorized());
|
||||||
|
verify(handler, never()).onError(any());
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class ObjectPostProcessorConfig {
|
static class ObjectPostProcessorConfig {
|
||||||
@ -455,4 +477,14 @@ public class HttpBasicConfigurerTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class SelectableObservationsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings observabilityDefaults() {
|
||||||
|
return SecurityObservationSettings.withDefaults().shouldObserveAuthentications(false).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ import org.springframework.security.authorization.AuthorizationDecision;
|
|||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
||||||
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
|
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
|
||||||
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
@ -106,6 +107,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;
|
import static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;
|
||||||
|
|
||||||
public class WebSocketMessageBrokerSecurityConfigurationTests {
|
public class WebSocketMessageBrokerSecurityConfigurationTests {
|
||||||
@ -414,6 +416,28 @@ public class WebSocketMessageBrokerSecurityConfigurationTests {
|
|||||||
verify(observationHandler).onError(any());
|
verify(observationHandler).onError(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendMessageWhenExcludeAuthorizationObservationsThenUnobserved() {
|
||||||
|
loadConfig(WebSocketSecurityConfig.class, ObservationRegistryConfig.class, SelectableObservationsConfig.class);
|
||||||
|
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);
|
||||||
|
headers.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);
|
||||||
|
Message<?> message = message(headers, "/authenticated");
|
||||||
|
headers.getSessionAttributes().put(CsrfToken.class.getName(), this.token);
|
||||||
|
clientInboundChannel().send(message);
|
||||||
|
ObservationHandler<Observation.Context> observationHandler = this.context.getBean(ObservationHandler.class);
|
||||||
|
headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);
|
||||||
|
headers.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);
|
||||||
|
message = message(headers, "/denyAll");
|
||||||
|
headers.getSessionAttributes().put(CsrfToken.class.getName(), this.token);
|
||||||
|
try {
|
||||||
|
clientInboundChannel().send(message);
|
||||||
|
}
|
||||||
|
catch (MessageDeliveryException ex) {
|
||||||
|
// okay
|
||||||
|
}
|
||||||
|
verifyNoInteractions(observationHandler);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertHandshake(HttpServletRequest request) {
|
private void assertHandshake(HttpServletRequest request) {
|
||||||
TestHandshakeHandler handshakeHandler = this.context.getBean(TestHandshakeHandler.class);
|
TestHandshakeHandler handshakeHandler = this.context.getBean(TestHandshakeHandler.class);
|
||||||
assertThatCsrfToken(handshakeHandler.attributes.get(CsrfToken.class.getName())).isEqualTo(this.token);
|
assertThatCsrfToken(handshakeHandler.attributes.get(CsrfToken.class.getName())).isEqualTo(this.token);
|
||||||
@ -968,4 +992,14 @@ public class WebSocketMessageBrokerSecurityConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class SelectableObservationsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings observabilityDefaults() {
|
||||||
|
return SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.observation;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SecurityObservationSettings}
|
||||||
|
*/
|
||||||
|
public class SecurityObservationSettingsTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withDefaultsThenFilterOffAuthenticationOnAuthorizationOn() {
|
||||||
|
SecurityObservationSettings defaults = SecurityObservationSettings.withDefaults().build();
|
||||||
|
assertThat(defaults.shouldObserveRequests()).isFalse();
|
||||||
|
assertThat(defaults.shouldObserveAuthentications()).isTrue();
|
||||||
|
assertThat(defaults.shouldObserveAuthorizations()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void noObservationsWhenConstructedThenAllOff() {
|
||||||
|
SecurityObservationSettings defaults = SecurityObservationSettings.noObservations();
|
||||||
|
assertThat(defaults.shouldObserveRequests()).isFalse();
|
||||||
|
assertThat(defaults.shouldObserveAuthentications()).isFalse();
|
||||||
|
assertThat(defaults.shouldObserveAuthorizations()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withDefaultsWhenExclusionsThenInstanceReflects() {
|
||||||
|
SecurityObservationSettings defaults = SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveAuthentications(false)
|
||||||
|
.shouldObserveAuthorizations(false)
|
||||||
|
.shouldObserveRequests(true)
|
||||||
|
.build();
|
||||||
|
assertThat(defaults.shouldObserveRequests()).isTrue();
|
||||||
|
assertThat(defaults.shouldObserveAuthentications()).isFalse();
|
||||||
|
assertThat(defaults.shouldObserveAuthorizations()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -187,7 +187,7 @@ Xml::
|
|||||||
If you don't want any Spring Security observations, in a Spring Boot application you can publish a `ObservationRegistry.NOOP` `@Bean`.
|
If you don't want any Spring Security observations, in a Spring Boot application you can publish a `ObservationRegistry.NOOP` `@Bean`.
|
||||||
However, this may turn off observations for more than just Spring Security.
|
However, this may turn off observations for more than just Spring Security.
|
||||||
|
|
||||||
Instead, you can alter the provided `ObservationRegistry` with an `ObservationPredicate` like the following:
|
Instead, you can publish a `SecurityObservationSettings` like the following:
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
@ -196,9 +196,8 @@ Java::
|
|||||||
[source,java,role="primary"]
|
[source,java,role="primary"]
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
ObservationRegistryCustomizer<ObservationRegistry> noSpringSecurityObservations() {
|
SecurityObservationSettings noSpringSecurityObservations() {
|
||||||
ObservationPredicate predicate = (name, context) -> !name.startsWith("spring.security.");
|
return SecurityObservationSettings.noObservations();
|
||||||
return (registry) -> registry.observationConfig().observationPredicate(predicate);
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -207,17 +206,77 @@ Kotlin::
|
|||||||
[source,kotlin,role="secondary"]
|
[source,kotlin,role="secondary"]
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
fun noSpringSecurityObservations(): ObservationRegistryCustomizer<ObservationRegistry> {
|
fun noSpringSecurityObservations(): SecurityObservationSettings {
|
||||||
ObservationPredicate predicate = (name: String, context: Observation.Context) -> !name.startsWith("spring.security.")
|
return SecurityObservationSettings.noObservations()
|
||||||
(registry: ObservationRegistry) -> registry.observationConfig().observationPredicate(predicate)
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
|
and then Spring Security will not wrap any filter chains, authentications, or authorizations in their `ObservationXXX` counterparts.
|
||||||
|
|
||||||
[TIP]
|
[TIP]
|
||||||
There is no facility for disabling observations with XML support.
|
There is no facility for disabling observations with XML support.
|
||||||
Instead, simply do not set the `observation-registry-ref` attribute.
|
Instead, simply do not set the `observation-registry-ref` attribute.
|
||||||
|
|
||||||
|
You can also disable security for only a subset of Security's observations.
|
||||||
|
For example, the `SecurityObservationSettings` bean excludes the filter chain observations by default.
|
||||||
|
So, you can also do:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings defaultSpringSecurityObservations() {
|
||||||
|
return SecurityObservationSettings.withDefaults().build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
fun defaultSpringSecurityObservations(): SecurityObservationSettings {
|
||||||
|
return SecurityObservationSettings.withDefaults().build()
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
Or you can turn on and off observations individually, based on the defaults:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings allSpringSecurityObservations() {
|
||||||
|
return SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveFilterChains(true).build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
fun allSpringSecurityObservations(): SecurityObservationSettings {
|
||||||
|
return SecurityObservabilityDefaults.builder()
|
||||||
|
.shouldObserveFilterChains(true).build()
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
=====
|
||||||
|
For backward compatibility, all Spring Security observations are made unless a `SecurityObservationSettings` is published.
|
||||||
|
=====
|
||||||
|
|
||||||
[[webflux-observability-tracing-listing]]
|
[[webflux-observability-tracing-listing]]
|
||||||
=== Trace Listing
|
=== Trace Listing
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ Xml::
|
|||||||
If you don't want any Spring Security observations, in a Spring Boot application you can publish a `ObservationRegistry.NOOP` `@Bean`.
|
If you don't want any Spring Security observations, in a Spring Boot application you can publish a `ObservationRegistry.NOOP` `@Bean`.
|
||||||
However, this may turn off observations for more than just Spring Security.
|
However, this may turn off observations for more than just Spring Security.
|
||||||
|
|
||||||
Instead, you can alter the provided `ObservationRegistry` with an `ObservationPredicate` like the following:
|
Instead, you can publish a `SecurityObservationSettings` like the following:
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
@ -201,9 +201,8 @@ Java::
|
|||||||
[source,java,role="primary"]
|
[source,java,role="primary"]
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
ObservationRegistryCustomizer<ObservationRegistry> noSpringSecurityObservations() {
|
SecurityObservationSettings noSpringSecurityObservations() {
|
||||||
ObservationPredicate predicate = (name, context) -> !name.startsWith("spring.security.");
|
return SecurityObservationSettings.noObservations();
|
||||||
return (registry) -> registry.observationConfig().observationPredicate(predicate);
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -212,21 +211,77 @@ Kotlin::
|
|||||||
[source,kotlin,role="secondary"]
|
[source,kotlin,role="secondary"]
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
fun noSpringSecurityObservations(): ObservationRegistryCustomizer<ObservationRegistry> {
|
fun noSpringSecurityObservations(): SecurityObservationSettings {
|
||||||
val predicate = ObservationPredicate { name: String, _: Observation.Context? ->
|
return SecurityObservationSettings.noObservations()
|
||||||
!name.startsWith("spring.security.")
|
|
||||||
}
|
|
||||||
return ObservationRegistryCustomizer { registry: ObservationRegistry ->
|
|
||||||
registry.observationConfig().observationPredicate(predicate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
|
and then Spring Security will not wrap any filter chains, authentications, or authorizations in their `ObservationXXX` counterparts.
|
||||||
|
|
||||||
[TIP]
|
[TIP]
|
||||||
There is no facility for disabling observations with XML support.
|
There is no facility for disabling observations with XML support.
|
||||||
Instead, simply do not set the `observation-registry-ref` attribute.
|
Instead, simply do not set the `observation-registry-ref` attribute.
|
||||||
|
|
||||||
|
You can also disable security for only a subset of Security's observations.
|
||||||
|
For example, the `SecurityObservationSettings` bean excludes the filter chain observations by default.
|
||||||
|
So, you can also do:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings defaultSpringSecurityObservations() {
|
||||||
|
return SecurityObservationSettings.withDefaults().build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
fun defaultSpringSecurityObservations(): SecurityObservationSettings {
|
||||||
|
return SecurityObservationSettings.withDefaults().build()
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
Or you can turn on and off observations individually, based on the defaults:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
SecurityObservationSettings allSpringSecurityObservations() {
|
||||||
|
return SecurityObservationSettings.withDefaults()
|
||||||
|
.shouldObserveFilterChains(true).build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
fun allSpringSecurityObservations(): SecurityObservationSettings {
|
||||||
|
return SecurityObservationSettings.builder()
|
||||||
|
.shouldObserveFilterChains(true).build()
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
=====
|
||||||
|
For backward compatibility, the all Spring Security observations are made unless a `SecurityObservationSettings` is published.
|
||||||
|
=====
|
||||||
|
|
||||||
[[observability-tracing-listing]]
|
[[observability-tracing-listing]]
|
||||||
=== Trace Listing
|
=== Trace Listing
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user