diff --git a/web/src/main/java/org/springframework/security/web/context/DelegatingSecurityContextRepository.java b/web/src/main/java/org/springframework/security/web/context/DelegatingSecurityContextRepository.java index a2ae4c810a..10c6d1e429 100644 --- a/web/src/main/java/org/springframework/security/web/context/DelegatingSecurityContextRepository.java +++ b/web/src/main/java/org/springframework/security/web/context/DelegatingSecurityContextRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -46,7 +46,14 @@ public final class DelegatingSecurityContextRepository implements SecurityContex @Override public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) { - return loadDeferredContext(requestResponseHolder.getRequest()).get(); + SecurityContext result = null; + for (SecurityContextRepository delegate : this.delegates) { + SecurityContext delegateResult = delegate.loadContext(requestResponseHolder); + if (result == null || delegate.containsContext(requestResponseHolder.getRequest())) { + result = delegateResult; + } + } + return result; } @Override diff --git a/web/src/test/java/org/springframework/security/web/context/DelegatingSecurityContextRepositoryTests.java b/web/src/test/java/org/springframework/security/web/context/DelegatingSecurityContextRepositoryTests.java index ae3220e6bf..56b1a1f3bf 100644 --- a/web/src/test/java/org/springframework/security/web/context/DelegatingSecurityContextRepositoryTests.java +++ b/web/src/test/java/org/springframework/security/web/context/DelegatingSecurityContextRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -141,4 +141,64 @@ public class DelegatingSecurityContextRepositoryTests { verifyNoInteractions(delegates.get(2)); } + // gh-12314 + @Test + public void loadContextWhenSecondDelegateReturnsThenContextFromSecondDelegate() { + SecurityContextRepository delegate1 = mock(SecurityContextRepository.class); + SecurityContextRepository delegate2 = mock(SecurityContextRepository.class); + HttpRequestResponseHolder holder = new HttpRequestResponseHolder(this.request, this.response); + SecurityContext securityContext1 = mock(SecurityContext.class); + SecurityContext securityContext2 = mock(SecurityContext.class); + + given(delegate1.loadContext(holder)).willReturn(securityContext1); + given(delegate1.containsContext(holder.getRequest())).willReturn(false); + given(delegate2.loadContext(holder)).willReturn(securityContext2); + given(delegate2.containsContext(holder.getRequest())).willReturn(true); + + DelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegate1, delegate2); + SecurityContext returnedSecurityContext = repository.loadContext(holder); + + assertThat(returnedSecurityContext).isSameAs(securityContext2); + } + + // gh-12314 + @Test + public void loadContextWhenBothDelegateReturnsThenContextFromSecondDelegate() { + SecurityContextRepository delegate1 = mock(SecurityContextRepository.class); + SecurityContextRepository delegate2 = mock(SecurityContextRepository.class); + HttpRequestResponseHolder holder = new HttpRequestResponseHolder(this.request, this.response); + SecurityContext securityContext1 = mock(SecurityContext.class); + SecurityContext securityContext2 = mock(SecurityContext.class); + + given(delegate1.loadContext(holder)).willReturn(securityContext1); + given(delegate1.containsContext(holder.getRequest())).willReturn(true); + given(delegate2.loadContext(holder)).willReturn(securityContext2); + given(delegate2.containsContext(holder.getRequest())).willReturn(true); + + DelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegate1, delegate2); + SecurityContext returnedSecurityContext = repository.loadContext(holder); + + assertThat(returnedSecurityContext).isSameAs(securityContext2); + } + + // gh-12314 + @Test + public void loadContextWhenFirstDelegateReturnsThenContextFromFirstDelegate() { + SecurityContextRepository delegate1 = mock(SecurityContextRepository.class); + SecurityContextRepository delegate2 = mock(SecurityContextRepository.class); + HttpRequestResponseHolder holder = new HttpRequestResponseHolder(this.request, this.response); + SecurityContext securityContext1 = mock(SecurityContext.class); + SecurityContext securityContext2 = mock(SecurityContext.class); + + given(delegate1.loadContext(holder)).willReturn(securityContext1); + given(delegate1.containsContext(holder.getRequest())).willReturn(true); + given(delegate2.loadContext(holder)).willReturn(securityContext2); + given(delegate2.containsContext(holder.getRequest())).willReturn(false); + + DelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegate1, delegate2); + SecurityContext returnedSecurityContext = repository.loadContext(holder); + + assertThat(returnedSecurityContext).isSameAs(securityContext1); + } + }