Skip to content

Commit

Permalink
Defer ObservationRegistry Resolution
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
jzheaux committed Nov 10, 2022
1 parent 7b28df8 commit c45cd6e
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 26 deletions.
@@ -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);
}

}
Expand Up @@ -26,7 +26,6 @@
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;
Expand Down Expand Up @@ -54,19 +53,12 @@ static Advisor jsr250AuthorizationMethodInterceptor(ObjectProvider<GrantedAuthor
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);
}

}
Expand Up @@ -29,7 +29,6 @@
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;
Expand Down Expand Up @@ -120,11 +119,7 @@ private static MethodSecurityExpressionHandler defaultExpressionHandler(

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);
}

}
Expand Up @@ -27,7 +27,6 @@
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;
Expand All @@ -52,19 +51,12 @@ static Advisor securedAuthorizationMethodInterceptor(ObjectProvider<SecurityCont
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);
}

}

0 comments on commit c45cd6e

Please sign in to comment.