Defer ObservationRegistry Resolution

- If Method Security asks for  too early, it is no longer
eligible for post-processing. As such, this commit defers loading it until
the first authorization request.

Issue gh-11990
This commit is contained in:
Josh Cummings 2022-11-09 22:07:57 -07:00
parent 7b28df8ebe
commit c45cd6ec9f
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
4 changed files with 55 additions and 26 deletions

View File

@ -0,0 +1,50 @@
/*
* 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.config.annotation.method.configuration;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.function.SingletonSupplier;
final class DeferringObservationAuthorizationManager<T> implements AuthorizationManager<T> {
private final Supplier<AuthorizationManager<T>> delegate;
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
AuthorizationManager<T> delegate) {
this.delegate = SingletonSupplier.of(() -> {
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
if (registry.isNoop()) {
return delegate;
}
return new ObservationAuthorizationManager<>(registry, delegate);
});
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
return this.delegate.get().check(authentication, object);
}
}

View File

@ -26,7 +26,6 @@ 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;
@ -54,19 +53,12 @@ final class Jsr250MethodSecurityConfiguration {
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);
AuthorizationManager<MethodInvocation> manager = new DeferringObservationAuthorizationManager<>(
registryProvider, jsr250);
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
.jsr250(manager);
interceptor.setSecurityContextHolderStrategy(strategy);
return interceptor;
}
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> jsr250, ObservationRegistry registry) {
if (registry.isNoop()) {
return jsr250;
}
return new ObservationAuthorizationManager<>(registry, jsr250);
}
}

View File

@ -29,7 +29,6 @@ import org.springframework.security.access.expression.method.DefaultMethodSecuri
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationEventPublisher;
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;
@ -120,11 +119,7 @@ final class PrePostMethodSecurityConfiguration {
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);
return new DeferringObservationAuthorizationManager<>(registryProvider, delegate);
}
}

View File

@ -27,7 +27,6 @@ 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;
@ -52,19 +51,12 @@ final class SecuredMethodSecurityConfiguration {
SecuredAuthorizationManager secured = new SecuredAuthorizationManager();
SecurityContextHolderStrategy strategy = strategyProvider
.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
AuthorizationManager<MethodInvocation> manager = manager(secured, registry);
AuthorizationManager<MethodInvocation> manager = new DeferringObservationAuthorizationManager<>(
registryProvider, secured);
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
.secured(manager);
interceptor.setSecurityContextHolderStrategy(strategy);
return interceptor;
}
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> jsr250, ObservationRegistry registry) {
if (registry.isNoop()) {
return jsr250;
}
return new ObservationAuthorizationManager<>(registry, jsr250);
}
}