Skip to content

Commit c45cd6e

Browse files
committed
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
1 parent 7b28df8 commit c45cd6e

File tree

4 files changed

+55
-26
lines changed

4 files changed

+55
-26
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration;
18+
19+
import java.util.function.Supplier;
20+
21+
import io.micrometer.observation.ObservationRegistry;
22+
23+
import org.springframework.beans.factory.ObjectProvider;
24+
import org.springframework.security.authorization.AuthorizationDecision;
25+
import org.springframework.security.authorization.AuthorizationManager;
26+
import org.springframework.security.authorization.ObservationAuthorizationManager;
27+
import org.springframework.security.core.Authentication;
28+
import org.springframework.util.function.SingletonSupplier;
29+
30+
final class DeferringObservationAuthorizationManager<T> implements AuthorizationManager<T> {
31+
32+
private final Supplier<AuthorizationManager<T>> delegate;
33+
34+
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
35+
AuthorizationManager<T> delegate) {
36+
this.delegate = SingletonSupplier.of(() -> {
37+
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
38+
if (registry.isNoop()) {
39+
return delegate;
40+
}
41+
return new ObservationAuthorizationManager<>(registry, delegate);
42+
});
43+
}
44+
45+
@Override
46+
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
47+
return this.delegate.get().check(authentication, object);
48+
}
49+
50+
}

Diff for: config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java

+2-10
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.springframework.context.annotation.Configuration;
2727
import org.springframework.context.annotation.Role;
2828
import org.springframework.security.authorization.AuthorizationManager;
29-
import org.springframework.security.authorization.ObservationAuthorizationManager;
3029
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
3130
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
3231
import org.springframework.security.config.core.GrantedAuthorityDefaults;
@@ -54,19 +53,12 @@ static Advisor jsr250AuthorizationMethodInterceptor(ObjectProvider<GrantedAuthor
5453
defaultsProvider.ifAvailable((d) -> jsr250.setRolePrefix(d.getRolePrefix()));
5554
SecurityContextHolderStrategy strategy = strategyProvider
5655
.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
57-
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
58-
AuthorizationManager<MethodInvocation> manager = manager(jsr250, registry);
56+
AuthorizationManager<MethodInvocation> manager = new DeferringObservationAuthorizationManager<>(
57+
registryProvider, jsr250);
5958
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
6059
.jsr250(manager);
6160
interceptor.setSecurityContextHolderStrategy(strategy);
6261
return interceptor;
6362
}
6463

65-
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> jsr250, ObservationRegistry registry) {
66-
if (registry.isNoop()) {
67-
return jsr250;
68-
}
69-
return new ObservationAuthorizationManager<>(registry, jsr250);
70-
}
71-
7264
}

Diff for: config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
3030
import org.springframework.security.authorization.AuthorizationEventPublisher;
3131
import org.springframework.security.authorization.AuthorizationManager;
32-
import org.springframework.security.authorization.ObservationAuthorizationManager;
3332
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
3433
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
3534
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
@@ -120,11 +119,7 @@ private static MethodSecurityExpressionHandler defaultExpressionHandler(
120119

121120
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> delegate,
122121
ObjectProvider<ObservationRegistry> registryProvider) {
123-
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
124-
if (registry.isNoop()) {
125-
return delegate;
126-
}
127-
return new ObservationAuthorizationManager<>(registry, delegate);
122+
return new DeferringObservationAuthorizationManager<>(registryProvider, delegate);
128123
}
129124

130125
}

Diff for: config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java

+2-10
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.springframework.context.annotation.Role;
2828
import org.springframework.security.access.annotation.Secured;
2929
import org.springframework.security.authorization.AuthorizationManager;
30-
import org.springframework.security.authorization.ObservationAuthorizationManager;
3130
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
3231
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
3332
import org.springframework.security.core.context.SecurityContextHolder;
@@ -52,19 +51,12 @@ static Advisor securedAuthorizationMethodInterceptor(ObjectProvider<SecurityCont
5251
SecuredAuthorizationManager secured = new SecuredAuthorizationManager();
5352
SecurityContextHolderStrategy strategy = strategyProvider
5453
.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
55-
ObservationRegistry registry = registryProvider.getIfAvailable(() -> ObservationRegistry.NOOP);
56-
AuthorizationManager<MethodInvocation> manager = manager(secured, registry);
54+
AuthorizationManager<MethodInvocation> manager = new DeferringObservationAuthorizationManager<>(
55+
registryProvider, secured);
5756
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
5857
.secured(manager);
5958
interceptor.setSecurityContextHolderStrategy(strategy);
6059
return interceptor;
6160
}
6261

63-
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> jsr250, ObservationRegistry registry) {
64-
if (registry.isNoop()) {
65-
return jsr250;
66-
}
67-
return new ObservationAuthorizationManager<>(registry, jsr250);
68-
}
69-
7062
}

0 commit comments

Comments
 (0)