Move Web Access API
Issue gh-17847
This commit is contained in:
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.web.access;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.intercept.RunAsManager;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
|
||||
/**
|
||||
* Tests
|
||||
* {@link org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class DefaultWebInvocationPrivilegeEvaluatorTests {
|
||||
|
||||
private AccessDecisionManager adm;
|
||||
|
||||
private FilterInvocationSecurityMetadataSource ods;
|
||||
|
||||
private RunAsManager ram;
|
||||
|
||||
private FilterSecurityInterceptor interceptor;
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
this.interceptor = new FilterSecurityInterceptor();
|
||||
this.ods = Mockito.mock(FilterInvocationSecurityMetadataSource.class);
|
||||
this.adm = Mockito.mock(AccessDecisionManager.class);
|
||||
this.ram = Mockito.mock(RunAsManager.class);
|
||||
this.interceptor.setAuthenticationManager(Mockito.mock(AuthenticationManager.class));
|
||||
this.interceptor.setSecurityMetadataSource(this.ods);
|
||||
this.interceptor.setAccessDecisionManager(this.adm);
|
||||
this.interceptor.setRunAsManager(this.ram);
|
||||
this.interceptor.setApplicationEventPublisher(Mockito.mock(ApplicationEventPublisher.class));
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void permitsAccessIfNoMatchingAttributesAndPublicInvocationsAllowed() {
|
||||
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);
|
||||
BDDMockito.given(this.ods.getAttributes(ArgumentMatchers.any())).willReturn(null);
|
||||
Assertions.assertThat(wipe.isAllowed("/context", "/foo/index.jsp", "GET", Mockito.mock(Authentication.class)))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deniesAccessIfNoMatchingAttributesAndPublicInvocationsNotAllowed() {
|
||||
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);
|
||||
BDDMockito.given(this.ods.getAttributes(ArgumentMatchers.any())).willReturn(null);
|
||||
this.interceptor.setRejectPublicInvocations(true);
|
||||
Assertions.assertThat(wipe.isAllowed("/context", "/foo/index.jsp", "GET", Mockito.mock(Authentication.class)))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deniesAccessIfAuthenticationIsNull() {
|
||||
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);
|
||||
Assertions.assertThat(wipe.isAllowed("/foo/index.jsp", null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowsAccessIfAccessDecisionManagerDoes() {
|
||||
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
||||
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);
|
||||
Assertions.assertThat(wipe.isAllowed("/foo/index.jsp", token)).isTrue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void deniesAccessIfAccessDecisionManagerDoes() {
|
||||
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
||||
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);
|
||||
BDDMockito.willThrow(new AccessDeniedException(""))
|
||||
.given(this.adm)
|
||||
.decide(ArgumentMatchers.any(Authentication.class), ArgumentMatchers.any(), ArgumentMatchers.anyList());
|
||||
Assertions.assertThat(wipe.isAllowed("/foo/index.jsp", token)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAllowedWhenServletContextIsSetThenPassedFilterInvocationHasServletContext() {
|
||||
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
ArgumentCaptor<FilterInvocation> filterInvocationArgumentCaptor = ArgumentCaptor
|
||||
.forClass(FilterInvocation.class);
|
||||
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);
|
||||
wipe.setServletContext(servletContext);
|
||||
wipe.isAllowed("/foo/index.jsp", token);
|
||||
Mockito.verify(this.adm)
|
||||
.decide(ArgumentMatchers.eq(token), filterInvocationArgumentCaptor.capture(), ArgumentMatchers.any());
|
||||
Assertions.assertThat(filterInvocationArgumentCaptor.getValue().getRequest().getServletContext()).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.channel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link ChannelDecisionManagerImpl}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ChannelDecisionManagerImplTests {
|
||||
|
||||
@Test
|
||||
public void testCannotSetEmptyChannelProcessorsList() throws Exception {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
||||
cdm.setChannelProcessors(new Vector());
|
||||
cdm.afterPropertiesSet();
|
||||
}).withMessage("A list of ChannelProcessors is required");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotSetIncorrectObjectTypesIntoChannelProcessorsList() {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
List list = new Vector();
|
||||
list.add("THIS IS NOT A CHANNELPROCESSOR");
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> cdm.setChannelProcessors(list));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotSetNullChannelProcessorsList() throws Exception {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
||||
cdm.setChannelProcessors(null);
|
||||
cdm.afterPropertiesSet();
|
||||
}).withMessage("A list of ChannelProcessors is required");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecideIsOperational() throws Exception {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
|
||||
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", true);
|
||||
List list = new Vector();
|
||||
list.add(cpXyz);
|
||||
list.add(cpAbc);
|
||||
cdm.setChannelProcessors(list);
|
||||
cdm.afterPropertiesSet();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));
|
||||
List<ConfigAttribute> cad = SecurityConfig.createList("xyz");
|
||||
cdm.decide(fi, cad);
|
||||
Assertions.assertThat(fi.getResponse().isCommitted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnyChannelAttributeCausesProcessorsToBeSkipped() throws Exception {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", true);
|
||||
List list = new Vector();
|
||||
list.add(cpAbc);
|
||||
cdm.setChannelProcessors(list);
|
||||
cdm.afterPropertiesSet();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));
|
||||
cdm.decide(fi, SecurityConfig.createList(new String[] { "abc", "ANY_CHANNEL" }));
|
||||
Assertions.assertThat(fi.getResponse().isCommitted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecideIteratesAllProcessorsIfNoneCommitAResponse() throws Exception {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
|
||||
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
|
||||
List list = new Vector();
|
||||
list.add(cpXyz);
|
||||
list.add(cpAbc);
|
||||
cdm.setChannelProcessors(list);
|
||||
cdm.afterPropertiesSet();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));
|
||||
cdm.decide(fi, SecurityConfig.createList("SOME_ATTRIBUTE_NO_PROCESSORS_SUPPORT"));
|
||||
Assertions.assertThat(fi.getResponse().isCommitted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelegatesSupports() throws Exception {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
|
||||
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
|
||||
List list = new Vector();
|
||||
list.add(cpXyz);
|
||||
list.add(cpAbc);
|
||||
cdm.setChannelProcessors(list);
|
||||
cdm.afterPropertiesSet();
|
||||
assertThat(cdm.supports(new SecurityConfig("xyz"))).isTrue();
|
||||
assertThat(cdm.supports(new SecurityConfig("abc"))).isTrue();
|
||||
assertThat(cdm.supports(new SecurityConfig("UNSUPPORTED"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettersSetters() {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
assertThat(cdm.getChannelProcessors()).isNull();
|
||||
MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
|
||||
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
|
||||
List list = new Vector();
|
||||
list.add(cpXyz);
|
||||
list.add(cpAbc);
|
||||
cdm.setChannelProcessors(list);
|
||||
assertThat(cdm.getChannelProcessors()).isEqualTo(list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartupFailsWithEmptyChannelProcessorsList() throws Exception {
|
||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||
assertThatIllegalArgumentException().isThrownBy(cdm::afterPropertiesSet)
|
||||
.withMessage("A list of ChannelProcessors is required");
|
||||
}
|
||||
|
||||
private class MockChannelProcessor implements ChannelProcessor {
|
||||
|
||||
private String configAttribute;
|
||||
|
||||
private boolean failIfCalled;
|
||||
|
||||
MockChannelProcessor(String configAttribute, boolean failIfCalled) {
|
||||
this.configAttribute = configAttribute;
|
||||
this.failIfCalled = failIfCalled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException {
|
||||
Iterator iter = config.iterator();
|
||||
if (this.failIfCalled) {
|
||||
fail("Should not have called this channel processor: " + this.configAttribute);
|
||||
}
|
||||
while (iter.hasNext()) {
|
||||
ConfigAttribute attr = (ConfigAttribute) iter.next();
|
||||
if (attr.getAttribute().equals(this.configAttribute)) {
|
||||
invocation.getHttpResponse().sendRedirect("/redirected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute.getAttribute().equals(this.configAttribute);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.channel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.servlet.TestMockHttpServletRequests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link ChannelProcessingFilter}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class ChannelProcessingFilterTests {
|
||||
|
||||
@Test
|
||||
public void testDetectsMissingChannelDecisionManager() {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "MOCK");
|
||||
filter.setSecurityMetadataSource(fids);
|
||||
assertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDetectsMissingFilterInvocationSecurityMetadataSource() {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
filter.setChannelDecisionManager(new MockChannelDecisionManager(false, "MOCK"));
|
||||
assertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDetectsSupportedConfigAttribute() {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
filter.setChannelDecisionManager(new MockChannelDecisionManager(false, "SUPPORTS_MOCK_ONLY"));
|
||||
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true,
|
||||
"SUPPORTS_MOCK_ONLY");
|
||||
filter.setSecurityMetadataSource(fids);
|
||||
filter.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDetectsUnsupportedConfigAttribute() {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
filter.setChannelDecisionManager(new MockChannelDecisionManager(false, "SUPPORTS_MOCK_ONLY"));
|
||||
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true,
|
||||
"SUPPORTS_MOCK_ONLY", "INVALID_ATTRIBUTE");
|
||||
filter.setSecurityMetadataSource(fids);
|
||||
assertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterWhenManagerDoesCommitResponse() throws Exception {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
filter.setChannelDecisionManager(new MockChannelDecisionManager(true, "SOME_ATTRIBUTE"));
|
||||
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SOME_ATTRIBUTE");
|
||||
filter.setSecurityMetadataSource(fids);
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("/path").build();
|
||||
request.setQueryString("info=now");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
filter.doFilter(request, response, mock(FilterChain.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterWhenManagerDoesNotCommitResponse() throws Exception {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
filter.setChannelDecisionManager(new MockChannelDecisionManager(false, "SOME_ATTRIBUTE"));
|
||||
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "SOME_ATTRIBUTE");
|
||||
filter.setSecurityMetadataSource(fids);
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("/path").build();
|
||||
request.setQueryString("info=now");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
filter.doFilter(request, response, mock(FilterChain.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoFilterWhenNullConfigAttributeReturned() throws Exception {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
filter.setChannelDecisionManager(new MockChannelDecisionManager(false, "NOT_USED"));
|
||||
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", true, "NOT_USED");
|
||||
filter.setSecurityMetadataSource(fids);
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("/PATH_NOT_MATCHING_CONFIG_ATTRIBUTE").build();
|
||||
request.setQueryString("info=now");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
filter.doFilter(request, response, mock(FilterChain.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetterSetters() {
|
||||
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||
filter.setChannelDecisionManager(new MockChannelDecisionManager(false, "MOCK"));
|
||||
assertThat(filter.getChannelDecisionManager() != null).isTrue();
|
||||
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path", false, "MOCK");
|
||||
filter.setSecurityMetadataSource(fids);
|
||||
assertThat(filter.getSecurityMetadataSource()).isSameAs(fids);
|
||||
filter.afterPropertiesSet();
|
||||
}
|
||||
|
||||
private class MockChannelDecisionManager implements ChannelDecisionManager {
|
||||
|
||||
private String supportAttribute;
|
||||
|
||||
private boolean commitAResponse;
|
||||
|
||||
MockChannelDecisionManager(boolean commitAResponse, String supportAttribute) {
|
||||
this.commitAResponse = commitAResponse;
|
||||
this.supportAttribute = supportAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException {
|
||||
if (this.commitAResponse) {
|
||||
invocation.getHttpResponse().sendRedirect("/redirected");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute.getAttribute().equals(this.supportAttribute);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MockFilterInvocationDefinitionMap implements FilterInvocationSecurityMetadataSource {
|
||||
|
||||
private Collection<ConfigAttribute> toReturn;
|
||||
|
||||
private String servletPath;
|
||||
|
||||
private boolean provideIterator;
|
||||
|
||||
MockFilterInvocationDefinitionMap(String servletPath, boolean provideIterator, String... toReturn) {
|
||||
this.servletPath = servletPath;
|
||||
this.toReturn = SecurityConfig.createList(toReturn);
|
||||
this.provideIterator = provideIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
|
||||
FilterInvocation fi = (FilterInvocation) object;
|
||||
if (this.servletPath.equals(fi.getHttpRequest().getServletPath())) {
|
||||
return this.toReturn;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
if (!this.provideIterator) {
|
||||
return null;
|
||||
}
|
||||
return this.toReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.channel;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.servlet.TestMockHttpServletRequests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link InsecureChannelProcessor}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class InsecureChannelProcessorTests {
|
||||
|
||||
@Test
|
||||
public void testDecideDetectsAcceptableChannel() throws Exception {
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("http://localhost:8080")
|
||||
.requestUri("/bigapp", "/servlet", null)
|
||||
.queryString("info=true")
|
||||
.build();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));
|
||||
InsecureChannelProcessor processor = new InsecureChannelProcessor();
|
||||
processor.decide(fi, SecurityConfig.createList("SOME_IGNORED_ATTRIBUTE", "REQUIRES_INSECURE_CHANNEL"));
|
||||
Assertions.assertThat(fi.getResponse().isCommitted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecideDetectsUnacceptableChannel() throws Exception {
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("https://localhost:8443")
|
||||
.requestUri("/bigapp", "/servlet", null)
|
||||
.queryString("info=true")
|
||||
.build();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));
|
||||
InsecureChannelProcessor processor = new InsecureChannelProcessor();
|
||||
processor.decide(fi,
|
||||
SecurityConfig.createList(new String[] { "SOME_IGNORED_ATTRIBUTE", "REQUIRES_INSECURE_CHANNEL" }));
|
||||
Assertions.assertThat(fi.getResponse().isCommitted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecideRejectsNulls() throws Exception {
|
||||
InsecureChannelProcessor processor = new InsecureChannelProcessor();
|
||||
processor.afterPropertiesSet();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> processor.decide(null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettersSetters() {
|
||||
InsecureChannelProcessor processor = new InsecureChannelProcessor();
|
||||
assertThat(processor.getInsecureKeyword()).isEqualTo("REQUIRES_INSECURE_CHANNEL");
|
||||
processor.setInsecureKeyword("X");
|
||||
assertThat(processor.getInsecureKeyword()).isEqualTo("X");
|
||||
assertThat(processor.getEntryPoint() != null).isTrue();
|
||||
processor.setEntryPoint(null);
|
||||
assertThat(processor.getEntryPoint() == null).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingEntryPoint() throws Exception {
|
||||
InsecureChannelProcessor processor = new InsecureChannelProcessor();
|
||||
processor.setEntryPoint(null);
|
||||
assertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)
|
||||
.withMessage("entryPoint required");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingSecureChannelKeyword() throws Exception {
|
||||
InsecureChannelProcessor processor = new InsecureChannelProcessor();
|
||||
processor.setInsecureKeyword(null);
|
||||
assertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)
|
||||
.withMessage("insecureKeyword required");
|
||||
processor.setInsecureKeyword("");
|
||||
assertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)
|
||||
.withMessage("insecureKeyword required");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSupports() {
|
||||
InsecureChannelProcessor processor = new InsecureChannelProcessor();
|
||||
assertThat(processor.supports(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"))).isTrue();
|
||||
assertThat(processor.supports(null)).isFalse();
|
||||
assertThat(processor.supports(new SecurityConfig("NOT_SUPPORTED"))).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.channel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.PortMapperImpl;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link RetryWithHttpEntryPoint}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class RetryWithHttpEntryPointTests {
|
||||
|
||||
@Test
|
||||
public void testDetectsMissingPortMapper() {
|
||||
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> ep.setPortMapper(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettersSetters() {
|
||||
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||
PortMapper portMapper = mock(PortMapper.class);
|
||||
RedirectStrategy redirector = mock(RedirectStrategy.class);
|
||||
ep.setPortMapper(portMapper);
|
||||
ep.setRedirectStrategy(redirector);
|
||||
assertThat(ep.getPortMapper()).isSameAs(portMapper);
|
||||
assertThat(ep.getRedirectStrategy()).isSameAs(redirector);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalOperation() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp/hello/pathInfo.html");
|
||||
request.setQueryString("open=true");
|
||||
request.setScheme("https");
|
||||
request.setServerName("localhost");
|
||||
request.setServerPort(443);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||
ep.setPortMapper(new PortMapperImpl());
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/bigWebApp/hello/pathInfo.html?open=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalOperationWithNullQueryString() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp/hello");
|
||||
request.setScheme("https");
|
||||
request.setServerName("localhost");
|
||||
request.setServerPort(443);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||
ep.setPortMapper(new PortMapperImpl());
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/bigWebApp/hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationWhenTargetPortIsUnknown() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp");
|
||||
request.setQueryString("open=true");
|
||||
request.setScheme("https");
|
||||
request.setServerName("www.example.com");
|
||||
request.setServerPort(8768);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||
ep.setPortMapper(new PortMapperImpl());
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp?open=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationWithNonStandardPort() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp/hello/pathInfo.html");
|
||||
request.setQueryString("open=true");
|
||||
request.setScheme("https");
|
||||
request.setServerName("localhost");
|
||||
request.setServerPort(9999);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
PortMapperImpl portMapper = new PortMapperImpl();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("8888", "9999");
|
||||
portMapper.setPortMappings(map);
|
||||
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||
ep.setPortMapper(portMapper);
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl())
|
||||
.isEqualTo("http://localhost:8888/bigWebApp/hello/pathInfo.html?open=true");
|
||||
}
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.channel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.web.PortMapperImpl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests {@link RetryWithHttpsEntryPoint}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class RetryWithHttpsEntryPointTests {
|
||||
|
||||
@Test
|
||||
public void testDetectsMissingPortMapper() {
|
||||
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> ep.setPortMapper(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettersSetters() {
|
||||
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||
ep.setPortMapper(new PortMapperImpl());
|
||||
assertThat(ep.getPortMapper() != null).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalOperation() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp/hello/pathInfo.html");
|
||||
request.setQueryString("open=true");
|
||||
request.setScheme("http");
|
||||
request.setServerName("www.example.com");
|
||||
request.setServerPort(80);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||
ep.setPortMapper(new PortMapperImpl());
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl())
|
||||
.isEqualTo("https://www.example.com/bigWebApp/hello/pathInfo.html?open=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalOperationWithNullQueryString() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp/hello");
|
||||
request.setScheme("http");
|
||||
request.setServerName("www.example.com");
|
||||
request.setServerPort(80);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||
ep.setPortMapper(new PortMapperImpl());
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("https://www.example.com/bigWebApp/hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationWhenTargetPortIsUnknown() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp");
|
||||
request.setQueryString("open=true");
|
||||
request.setScheme("http");
|
||||
request.setServerName("www.example.com");
|
||||
request.setServerPort(8768);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||
ep.setPortMapper(new PortMapperImpl());
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp?open=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationWithNonStandardPort() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bigWebApp/hello/pathInfo.html");
|
||||
request.setQueryString("open=true");
|
||||
request.setScheme("http");
|
||||
request.setServerName("www.example.com");
|
||||
request.setServerPort(8888);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
PortMapperImpl portMapper = new PortMapperImpl();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("8888", "9999");
|
||||
portMapper.setPortMappings(map);
|
||||
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||
ep.setPortMapper(portMapper);
|
||||
ep.commence(request, response);
|
||||
assertThat(response.getRedirectedUrl())
|
||||
.isEqualTo("https://www.example.com:9999/bigWebApp/hello/pathInfo.html?open=true");
|
||||
}
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.channel;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.servlet.TestMockHttpServletRequests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link SecureChannelProcessor}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class SecureChannelProcessorTests {
|
||||
|
||||
@Test
|
||||
public void testDecideDetectsAcceptableChannel() throws Exception {
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("https://localhost:8443")
|
||||
.requestUri("/bigapp", "/servlet", null)
|
||||
.queryString("info=true")
|
||||
.build();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));
|
||||
SecureChannelProcessor processor = new SecureChannelProcessor();
|
||||
processor.decide(fi, SecurityConfig.createList("SOME_IGNORED_ATTRIBUTE", "REQUIRES_SECURE_CHANNEL"));
|
||||
Assertions.assertThat(fi.getResponse().isCommitted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecideDetectsUnacceptableChannel() throws Exception {
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("http://localhost:8080")
|
||||
.requestUri("/bigapp", "/servlet", null)
|
||||
.queryString("info=true")
|
||||
.build();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));
|
||||
SecureChannelProcessor processor = new SecureChannelProcessor();
|
||||
processor.decide(fi,
|
||||
SecurityConfig.createList(new String[] { "SOME_IGNORED_ATTRIBUTE", "REQUIRES_SECURE_CHANNEL" }));
|
||||
Assertions.assertThat(fi.getResponse().isCommitted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecideRejectsNulls() throws Exception {
|
||||
SecureChannelProcessor processor = new SecureChannelProcessor();
|
||||
processor.afterPropertiesSet();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> processor.decide(null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettersSetters() {
|
||||
SecureChannelProcessor processor = new SecureChannelProcessor();
|
||||
assertThat(processor.getSecureKeyword()).isEqualTo("REQUIRES_SECURE_CHANNEL");
|
||||
processor.setSecureKeyword("X");
|
||||
assertThat(processor.getSecureKeyword()).isEqualTo("X");
|
||||
assertThat(processor.getEntryPoint() != null).isTrue();
|
||||
processor.setEntryPoint(null);
|
||||
assertThat(processor.getEntryPoint() == null).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingEntryPoint() throws Exception {
|
||||
SecureChannelProcessor processor = new SecureChannelProcessor();
|
||||
processor.setEntryPoint(null);
|
||||
assertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)
|
||||
.withMessage("entryPoint required");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingSecureChannelKeyword() throws Exception {
|
||||
SecureChannelProcessor processor = new SecureChannelProcessor();
|
||||
processor.setSecureKeyword(null);
|
||||
assertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)
|
||||
.withMessage("secureKeyword required");
|
||||
processor.setSecureKeyword("");
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> processor.afterPropertiesSet())
|
||||
.withMessage("secureKeyword required");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSupports() {
|
||||
SecureChannelProcessor processor = new SecureChannelProcessor();
|
||||
assertThat(processor.supports(new SecurityConfig("REQUIRES_SECURE_CHANNEL"))).isTrue();
|
||||
assertThat(processor.supports(null)).isFalse();
|
||||
assertThat(processor.supports(new SecurityConfig("NOT_SUPPORTED"))).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.web.access.expression;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DefaultWebSecurityExpressionHandlerTests {
|
||||
|
||||
@Mock
|
||||
private AuthenticationTrustResolver trustResolver;
|
||||
|
||||
@Mock
|
||||
private Authentication authentication;
|
||||
|
||||
@Mock
|
||||
private FilterInvocation invocation;
|
||||
|
||||
private DefaultWebSecurityExpressionHandler handler;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.handler = new DefaultWebSecurityExpressionHandler();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expressionPropertiesAreResolvedAgainstAppContextBeans() {
|
||||
StaticApplicationContext appContext = new StaticApplicationContext();
|
||||
RootBeanDefinition bean = new RootBeanDefinition(SecurityConfig.class);
|
||||
bean.getConstructorArgumentValues().addGenericArgumentValue("ROLE_A");
|
||||
appContext.registerBeanDefinition("role", bean);
|
||||
this.handler.setApplicationContext(appContext);
|
||||
EvaluationContext ctx = this.handler.createEvaluationContext(mock(Authentication.class),
|
||||
mock(FilterInvocation.class));
|
||||
ExpressionParser parser = this.handler.getExpressionParser();
|
||||
assertThat(parser.parseExpression("@role.getAttribute() == 'ROLE_A'").getValue(ctx, Boolean.class)).isTrue();
|
||||
assertThat(parser.parseExpression("@role.attribute == 'ROLE_A'").getValue(ctx, Boolean.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTrustResolverNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.setTrustResolver(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createEvaluationContextCustomTrustResolver() {
|
||||
this.handler.setTrustResolver(this.trustResolver);
|
||||
Expression expression = this.handler.getExpressionParser().parseExpression("anonymous");
|
||||
EvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.invocation);
|
||||
assertThat(expression.getValue(context, Boolean.class)).isFalse();
|
||||
verify(this.trustResolver).isAnonymous(this.authentication);
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.web.access.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class ExpressionBasedFilterInvocationSecurityMetadataSourceTests {
|
||||
|
||||
@Test
|
||||
public void expectedAttributeIsReturned() {
|
||||
final String expression = "hasRole('X')";
|
||||
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
|
||||
requestMap.put(AnyRequestMatcher.INSTANCE, SecurityConfig.createList(expression));
|
||||
ExpressionBasedFilterInvocationSecurityMetadataSource mds = new ExpressionBasedFilterInvocationSecurityMetadataSource(
|
||||
requestMap, new DefaultWebSecurityExpressionHandler());
|
||||
assertThat(mds.getAllConfigAttributes()).hasSize(1);
|
||||
Collection<ConfigAttribute> attrs = mds.getAttributes(new FilterInvocation("/path", "GET"));
|
||||
assertThat(attrs).hasSize(1);
|
||||
WebExpressionConfigAttribute attribute = (WebExpressionConfigAttribute) attrs.toArray()[0];
|
||||
assertThat(attribute.getAttribute()).isNull();
|
||||
assertThat(attribute.getAuthorizeExpression().getExpressionString()).isEqualTo(expression);
|
||||
assertThat(attribute.toString()).isEqualTo(expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidExpressionIsRejected() {
|
||||
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
|
||||
requestMap.put(AnyRequestMatcher.INSTANCE, SecurityConfig.createList("hasRole('X'"));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap,
|
||||
new DefaultWebSecurityExpressionHandler()));
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.web.access.expression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class WebExpressionVoterTests {
|
||||
|
||||
private Authentication user = new TestingAuthenticationToken("user", "pass", "X");
|
||||
|
||||
@Test
|
||||
public void supportsWebConfigAttributeAndFilterInvocation() {
|
||||
WebExpressionVoter voter = new WebExpressionVoter();
|
||||
assertThat(voter.supports(
|
||||
new WebExpressionConfigAttribute(mock(Expression.class), mock(EvaluationContextPostProcessor.class))))
|
||||
.isTrue();
|
||||
assertThat(voter.supports(FilterInvocation.class)).isTrue();
|
||||
assertThat(voter.supports(MethodInvocation.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void abstainsIfNoAttributeFound() {
|
||||
WebExpressionVoter voter = new WebExpressionVoter();
|
||||
assertThat(
|
||||
voter.vote(this.user, new FilterInvocation("/path", "GET"), SecurityConfig.createList("A", "B", "C")))
|
||||
.isEqualTo(AccessDecisionVoter.ACCESS_ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void grantsAccessIfExpressionIsTrueDeniesIfFalse() {
|
||||
WebExpressionVoter voter = new WebExpressionVoter();
|
||||
Expression ex = mock(Expression.class);
|
||||
EvaluationContextPostProcessor postProcessor = mock(EvaluationContextPostProcessor.class);
|
||||
given(postProcessor.postProcess(any(EvaluationContext.class), any(FilterInvocation.class)))
|
||||
.willAnswer((invocation) -> invocation.getArgument(0));
|
||||
WebExpressionConfigAttribute weca = new WebExpressionConfigAttribute(ex, postProcessor);
|
||||
EvaluationContext ctx = mock(EvaluationContext.class);
|
||||
SecurityExpressionHandler eh = mock(SecurityExpressionHandler.class);
|
||||
FilterInvocation fi = new FilterInvocation("/path", "GET");
|
||||
voter.setExpressionHandler(eh);
|
||||
given(eh.createEvaluationContext(this.user, fi)).willReturn(ctx);
|
||||
given(ex.getValue(ctx, Boolean.class)).willReturn(Boolean.TRUE, Boolean.FALSE);
|
||||
ArrayList attributes = new ArrayList();
|
||||
attributes.addAll(SecurityConfig.createList("A", "B", "C"));
|
||||
attributes.add(weca);
|
||||
assertThat(voter.vote(this.user, fi, attributes)).isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
|
||||
// Second time false
|
||||
assertThat(voter.vote(this.user, fi, attributes)).isEqualTo(AccessDecisionVoter.ACCESS_DENIED);
|
||||
}
|
||||
|
||||
// SEC-2507
|
||||
@Test
|
||||
public void supportFilterInvocationSubClass() {
|
||||
WebExpressionVoter voter = new WebExpressionVoter();
|
||||
assertThat(voter.supports(FilterInvocationChild.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportFilterInvocation() {
|
||||
WebExpressionVoter voter = new WebExpressionVoter();
|
||||
assertThat(voter.supports(FilterInvocation.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsObjectIsFalse() {
|
||||
WebExpressionVoter voter = new WebExpressionVoter();
|
||||
assertThat(voter.supports(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
private static class FilterInvocationChild extends FilterInvocation {
|
||||
|
||||
FilterInvocationChild(ServletRequest request, ServletResponse response, FilterChain chain) {
|
||||
super(request, response, chain);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.servlet.TestMockHttpServletRequests;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link DefaultFilterInvocationSecurityMetadataSource}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class DefaultFilterInvocationSecurityMetadataSourceTests {
|
||||
|
||||
private DefaultFilterInvocationSecurityMetadataSource fids;
|
||||
|
||||
private Collection<ConfigAttribute> def = SecurityConfig.createList("ROLE_ONE");
|
||||
|
||||
private void createFids(String pattern, HttpMethod method) {
|
||||
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
|
||||
requestMap.put(PathPatternRequestMatcher.pathPattern(method, pattern), this.def);
|
||||
this.fids = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupNotRequiringExactMatchSucceedsIfNotMatching() {
|
||||
createFids("/secure/super/**", null);
|
||||
FilterInvocation fi = createFilterInvocation("/secure/super/somefile.html", null, null, "GET");
|
||||
assertThat(this.fids.getAttributes(fi)).isEqualTo(this.def);
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-501. Note that as of 2.0, lower case comparisons are the default for this
|
||||
* class.
|
||||
*/
|
||||
@Test
|
||||
public void lookupNotRequiringExactMatchSucceedsIfSecureUrlPathContainsUpperCase() {
|
||||
createFids("/secure/super/**", null);
|
||||
FilterInvocation fi = createFilterInvocation("/secure", "/super/somefile.html", null, "GET");
|
||||
Collection<ConfigAttribute> response = this.fids.getAttributes(fi);
|
||||
assertThat(response).isEqualTo(this.def);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupRequiringExactMatchIsSuccessful() {
|
||||
createFids("/SeCurE/super/**", null);
|
||||
FilterInvocation fi = createFilterInvocation("/SeCurE/super/somefile.html", null, null, "GET");
|
||||
Collection<ConfigAttribute> response = this.fids.getAttributes(fi);
|
||||
assertThat(response).isEqualTo(this.def);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupRequiringExactMatchWithAdditionalSlashesIsSuccessful() {
|
||||
createFids("/someAdminPage.html**", null);
|
||||
FilterInvocation fi = createFilterInvocation("/someAdminPage.html", null, "a=/test", "GET");
|
||||
Collection<ConfigAttribute> response = this.fids.getAttributes(fi);
|
||||
assertThat(response); // see SEC-161 (it should truncate after ?
|
||||
// sign).isEqualTo(def)
|
||||
}
|
||||
|
||||
@Test
|
||||
public void httpMethodLookupSucceeds() {
|
||||
createFids("/somepage**", HttpMethod.GET);
|
||||
FilterInvocation fi = createFilterInvocation("/somepage", null, null, "GET");
|
||||
Collection<ConfigAttribute> attrs = this.fids.getAttributes(fi);
|
||||
assertThat(attrs).isEqualTo(this.def);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generalMatchIsUsedIfNoMethodSpecificMatchExists() {
|
||||
createFids("/somepage**", null);
|
||||
FilterInvocation fi = createFilterInvocation("/somepage", null, null, "GET");
|
||||
Collection<ConfigAttribute> attrs = this.fids.getAttributes(fi);
|
||||
assertThat(attrs).isEqualTo(this.def);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithDifferentHttpMethodDoesntMatch() {
|
||||
createFids("/somepage**", HttpMethod.GET);
|
||||
FilterInvocation fi = createFilterInvocation("/somepage", null, null, "POST");
|
||||
Collection<ConfigAttribute> attrs = this.fids.getAttributes(fi);
|
||||
assertThat(attrs).isEmpty();
|
||||
}
|
||||
|
||||
// SEC-1236
|
||||
@Test
|
||||
public void mixingPatternsWithAndWithoutHttpMethodsIsSupported() {
|
||||
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
|
||||
Collection<ConfigAttribute> userAttrs = SecurityConfig.createList("A");
|
||||
requestMap.put(PathPatternRequestMatcher.pathPattern("/user/**"), userAttrs);
|
||||
requestMap.put(PathPatternRequestMatcher.pathPattern(HttpMethod.GET, "/teller/**"),
|
||||
SecurityConfig.createList("B"));
|
||||
this.fids = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
|
||||
FilterInvocation fi = createFilterInvocation("/user", null, null, "GET");
|
||||
Collection<ConfigAttribute> attrs = this.fids.getAttributes(fi);
|
||||
assertThat(attrs).isEqualTo(userAttrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check fixes for SEC-321
|
||||
*/
|
||||
@Test
|
||||
public void extraQuestionMarkStillMatches() {
|
||||
createFids("/someAdminPage.html*", null);
|
||||
FilterInvocation fi = createFilterInvocation("/someAdminPage.html", null, null, "GET");
|
||||
Collection<ConfigAttribute> response = this.fids.getAttributes(fi);
|
||||
assertThat(response).isEqualTo(this.def);
|
||||
fi = createFilterInvocation("/someAdminPage.html", null, "?", "GET");
|
||||
response = this.fids.getAttributes(fi);
|
||||
assertThat(response).isEqualTo(this.def);
|
||||
}
|
||||
|
||||
private FilterInvocation createFilterInvocation(String servletPath, String pathInfo, String queryString,
|
||||
String method) {
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.request(method)
|
||||
.requestUri(null, servletPath, pathInfo)
|
||||
.queryString(queryString)
|
||||
.build();
|
||||
return new FilterInvocation(request, new MockHttpServletResponse(), mock(FilterChain.class));
|
||||
}
|
||||
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.web.access.intercept;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.event.AuthorizedEvent;
|
||||
import org.springframework.security.access.intercept.AfterInvocationManager;
|
||||
import org.springframework.security.access.intercept.RunAsManager;
|
||||
import org.springframework.security.access.intercept.RunAsUserToken;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.servlet.TestMockHttpServletRequests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyCollection;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* Tests {@link FilterSecurityInterceptor}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class FilterSecurityInterceptorTests {
|
||||
|
||||
private AuthenticationManager am;
|
||||
|
||||
private AccessDecisionManager adm;
|
||||
|
||||
private FilterInvocationSecurityMetadataSource ods;
|
||||
|
||||
private RunAsManager ram;
|
||||
|
||||
private FilterSecurityInterceptor interceptor;
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
this.interceptor = new FilterSecurityInterceptor();
|
||||
this.am = mock(AuthenticationManager.class);
|
||||
this.ods = mock(FilterInvocationSecurityMetadataSource.class);
|
||||
this.adm = mock(AccessDecisionManager.class);
|
||||
this.ram = mock(RunAsManager.class);
|
||||
this.publisher = mock(ApplicationEventPublisher.class);
|
||||
this.interceptor.setAuthenticationManager(this.am);
|
||||
this.interceptor.setSecurityMetadataSource(this.ods);
|
||||
this.interceptor.setAccessDecisionManager(this.adm);
|
||||
this.interceptor.setRunAsManager(this.ram);
|
||||
this.interceptor.setApplicationEventPublisher(this.publisher);
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnsuresAccessDecisionManagerSupportsFilterInvocationClass() throws Exception {
|
||||
given(this.adm.supports(FilterInvocation.class)).willReturn(true);
|
||||
assertThatIllegalArgumentException().isThrownBy(this.interceptor::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnsuresRunAsManagerSupportsFilterInvocationClass() throws Exception {
|
||||
given(this.adm.supports(FilterInvocation.class)).willReturn(false);
|
||||
assertThatIllegalArgumentException().isThrownBy(this.interceptor::afterPropertiesSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* We just test invocation works in a success event. There is no need to test access
|
||||
* denied events as the abstract parent enforces that logic, which is extensively
|
||||
* tested separately.
|
||||
*/
|
||||
@Test
|
||||
public void testSuccessfulInvocation() throws Throwable {
|
||||
// Setup a Context
|
||||
Authentication token = new TestingAuthenticationToken("Test", "Password", "NOT_USED");
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
FilterInvocation fi = createinvocation();
|
||||
given(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList("MOCK_OK"));
|
||||
this.interceptor.invoke(fi);
|
||||
// SEC-1697
|
||||
verify(this.publisher, never()).publishEvent(any(AuthorizedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterInvocationIsNotInvokedIfExceptionThrown() throws Exception {
|
||||
Authentication token = new TestingAuthenticationToken("Test", "Password", "NOT_USED");
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
FilterInvocation fi = createinvocation();
|
||||
FilterChain chain = fi.getChain();
|
||||
willThrow(new RuntimeException()).given(chain)
|
||||
.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
given(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList("MOCK_OK"));
|
||||
AfterInvocationManager aim = mock(AfterInvocationManager.class);
|
||||
this.interceptor.setAfterInvocationManager(aim);
|
||||
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.interceptor.invoke(fi));
|
||||
verifyNoMoreInteractions(aim);
|
||||
}
|
||||
|
||||
// SEC-1967
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void finallyInvocationIsInvokedIfExceptionThrown() throws Exception {
|
||||
SecurityContext ctx = SecurityContextHolder.getContext();
|
||||
Authentication token = new TestingAuthenticationToken("Test", "Password", "NOT_USED");
|
||||
token.setAuthenticated(true);
|
||||
ctx.setAuthentication(token);
|
||||
RunAsManager runAsManager = mock(RunAsManager.class);
|
||||
given(runAsManager.buildRunAs(eq(token), any(), anyCollection()))
|
||||
.willReturn(new RunAsUserToken("key", "someone", "creds", token.getAuthorities(), token.getClass()));
|
||||
this.interceptor.setRunAsManager(runAsManager);
|
||||
FilterInvocation fi = createinvocation();
|
||||
FilterChain chain = fi.getChain();
|
||||
willThrow(new RuntimeException()).given(chain)
|
||||
.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
given(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList("MOCK_OK"));
|
||||
AfterInvocationManager aim = mock(AfterInvocationManager.class);
|
||||
this.interceptor.setAfterInvocationManager(aim);
|
||||
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.interceptor.invoke(fi));
|
||||
// Check we've changed back
|
||||
assertThat(SecurityContextHolder.getContext()).isSameAs(ctx);
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
// gh-4997
|
||||
public void doFilterWhenObserveOncePerRequestThenAttributeNotSet() throws Exception {
|
||||
this.interceptor.setObserveOncePerRequest(false);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
this.interceptor.doFilter(request, response, new MockFilterChain());
|
||||
assertThat(request.getAttributeNames().hasMoreElements()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenObserveOncePerRequestFalseAndInvokedTwiceThenObserveTwice() throws Throwable {
|
||||
Authentication token = new TestingAuthenticationToken("Test", "Password", "NOT_USED");
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
FilterInvocation fi = createinvocation();
|
||||
given(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList("MOCK_OK"));
|
||||
this.interceptor.invoke(fi);
|
||||
this.interceptor.invoke(fi);
|
||||
verify(this.adm, times(2)).decide(any(), any(), any());
|
||||
}
|
||||
|
||||
private FilterInvocation createinvocation() {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockHttpServletRequest request = TestMockHttpServletRequests.get("/secure/page.html").build();
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
FilterInvocation fi = new FilterInvocation(request, response, chain);
|
||||
return fi;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user