Skip to content

Oidc Logout Improvements #15540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,101 +16,148 @@

package org.springframework.security.config.annotation.method.configuration;

import java.util.function.Consumer;
import java.util.function.Supplier;

import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Fallback;
import org.springframework.context.annotation.Role;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationAdvisor;
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.PostAuthorizeReactiveAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeReactiveAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor;
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;

/**
* Configuration for a {@link ReactiveAuthenticationManager} based Method Security.
*
* @author Evgeniy Cheban
* @since 5.8
*/
@Configuration(proxyBeanMethods = false)
final class ReactiveAuthorizationManagerMethodSecurityConfiguration implements AopInfrastructureBean {
@Configuration(value = "_reactiveMethodSecurityConfiguration", proxyBeanMethods = false)
final class ReactiveAuthorizationManagerMethodSecurityConfiguration
implements AopInfrastructureBean, ApplicationContextAware {

private static final Pointcut preFilterPointcut = new PreFilterAuthorizationReactiveMethodInterceptor()
.getPointcut();

private static final Pointcut preAuthorizePointcut = AuthorizationManagerBeforeReactiveMethodInterceptor
.preAuthorize()
.getPointcut();

private static final Pointcut postAuthorizePointcut = AuthorizationManagerAfterReactiveMethodInterceptor
.postAuthorize()
.getPointcut();

private static final Pointcut postFilterPointcut = new PostFilterAuthorizationReactiveMethodInterceptor()
.getPointcut();

private PreFilterAuthorizationReactiveMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor();

private PreAuthorizeReactiveAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager();

private PostAuthorizeReactiveAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager();

private PostFilterAuthorizationReactiveMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor();

private AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeMethodInterceptor;

private AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;

@Autowired(required = false)
ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler) {
if (expressionHandler != null) {
this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);
this.postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
this.postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager(expressionHandler);
}
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor
.preAuthorize(this.preAuthorizeAuthorizationManager);
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor
.postAuthorize(this.postAuthorizeAuthorizationManager);
}

@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.preAuthorizeAuthorizationManager.setApplicationContext(context);
this.postAuthorizeAuthorizationManager.setApplicationContext(context);
}

@Autowired(required = false)
void setTemplateDefaults(PrePostTemplateDefaults templateDefaults) {
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
this.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
this.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
}

@Autowired(required = false)
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
this.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
this.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
}

@Autowired(required = false)
void setObservationRegistry(ObservationRegistry registry) {
if (registry.isNoop()) {
return;
}
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(
new ObservationReactiveAuthorizationManager<>(registry, this.preAuthorizeAuthorizationManager));
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(
new ObservationReactiveAuthorizationManager<>(registry, this.postAuthorizeAuthorizationManager));
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor preFilterAuthorizationMethodInterceptor(MethodSecurityExpressionHandler expressionHandler,
ObjectProvider<PrePostTemplateDefaults> defaultsObjectProvider) {
PreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor(
expressionHandler);
return new DeferringMethodInterceptor<>(interceptor,
(i) -> defaultsObjectProvider.ifAvailable(i::setTemplateDefaults));
static MethodInterceptor preFilterAuthorizationMethodInterceptor(
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
return new DeferringMethodInterceptor<>(preFilterPointcut,
() -> _reactiveMethodSecurityConfiguration.getObject().preFilterMethodInterceptor);
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
MethodSecurityExpressionHandler expressionHandler,
ObjectProvider<PrePostTemplateDefaults> defaultsObjectProvider,
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
PreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager(
expressionHandler);
manager.setApplicationContext(context);
ReactiveAuthorizationManager<MethodInvocation> authorizationManager = manager(manager, registryProvider);
AuthorizationAdvisor interceptor = AuthorizationManagerBeforeReactiveMethodInterceptor
.preAuthorize(authorizationManager);
return new DeferringMethodInterceptor<>(interceptor,
(i) -> defaultsObjectProvider.ifAvailable(manager::setTemplateDefaults));
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
return new DeferringMethodInterceptor<>(preAuthorizePointcut,
() -> _reactiveMethodSecurityConfiguration.getObject().preAuthorizeMethodInterceptor);
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor postFilterAuthorizationMethodInterceptor(MethodSecurityExpressionHandler expressionHandler,
ObjectProvider<PrePostTemplateDefaults> defaultsObjectProvider) {
PostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor(
expressionHandler);
return new DeferringMethodInterceptor<>(interceptor,
(i) -> defaultsObjectProvider.ifAvailable(i::setTemplateDefaults));
static MethodInterceptor postFilterAuthorizationMethodInterceptor(
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
return new DeferringMethodInterceptor<>(postFilterPointcut,
() -> _reactiveMethodSecurityConfiguration.getObject().postFilterMethodInterceptor);
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
MethodSecurityExpressionHandler expressionHandler,
ObjectProvider<PrePostTemplateDefaults> defaultsObjectProvider,
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
PostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager(
expressionHandler);
manager.setApplicationContext(context);
ReactiveAuthorizationManager<MethodInvocationResult> authorizationManager = manager(manager, registryProvider);
AuthorizationAdvisor interceptor = AuthorizationManagerAfterReactiveMethodInterceptor
.postAuthorize(authorizationManager);
return new DeferringMethodInterceptor<>(interceptor,
(i) -> defaultsObjectProvider.ifAvailable(manager::setTemplateDefaults));
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
return new DeferringMethodInterceptor<>(postAuthorizePointcut,
() -> _reactiveMethodSecurityConfiguration.getObject().postAuthorizeMethodInterceptor);
}

@Bean
Expand All @@ -125,55 +172,4 @@ static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(
return handler;
}

static <T> ReactiveAuthorizationManager<T> manager(ReactiveAuthorizationManager<T> delegate,
ObjectProvider<ObservationRegistry> registryProvider) {
return new DeferringObservationReactiveAuthorizationManager<>(registryProvider, delegate);
}

private static final class DeferringMethodInterceptor<M extends AuthorizationAdvisor>
implements AuthorizationAdvisor {

private final Pointcut pointcut;

private final int order;

private final Supplier<M> delegate;

DeferringMethodInterceptor(M delegate, Consumer<M> supplier) {
this.pointcut = delegate.getPointcut();
this.order = delegate.getOrder();
this.delegate = SingletonSupplier.of(() -> {
supplier.accept(delegate);
return delegate;
});
}

@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
return this.delegate.get().invoke(invocation);
}

@Override
public Pointcut getPointcut() {
return this.pointcut;
}

@Override
public Advice getAdvice() {
return this;
}

@Override
public int getOrder() {
return this.order;
}

@Override
public boolean isPerInstance() {
return true;
}

}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;

/**
* @author Rob Winch
Expand All @@ -34,6 +35,9 @@
*/
class ReactiveMethodSecuritySelector implements ImportSelector {

private static final boolean isDataPresent = ClassUtils
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);

private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();

@Override
Expand All @@ -51,7 +55,10 @@ public String[] selectImports(AnnotationMetadata importMetadata) {
else {
imports.add(ReactiveMethodSecurityConfiguration.class.getName());
}
imports.add(ReactiveAuthorizationProxyConfiguration.class.getName());
if (isDataPresent) {
imports.add(AuthorizationProxyDataConfiguration.class.getName());
}
imports.add(AuthorizationProxyConfiguration.class.getName());
return imports.toArray(new String[0]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,17 @@ private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService

static <B extends HttpSecurityBuilder<B>> OidcSessionRegistry getOidcSessionRegistry(B builder) {
OidcSessionRegistry sessionRegistry = builder.getSharedObject(OidcSessionRegistry.class);
if (sessionRegistry == null) {
if (sessionRegistry != null) {
return sessionRegistry;
}
ApplicationContext context = builder.getSharedObject(ApplicationContext.class);
if (context.getBeanNamesForType(OidcSessionRegistry.class).length == 1) {
sessionRegistry = context.getBean(OidcSessionRegistry.class);
}
else {
sessionRegistry = new InMemoryOidcSessionRegistry();
builder.setSharedObject(OidcSessionRegistry.class, sessionRegistry);
}
builder.setSharedObject(OidcSessionRegistry.class, sessionRegistry);
return sessionRegistry;
}

Expand Down
Loading