Skip to content
Closed
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 @@ -49,8 +49,9 @@
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -91,6 +92,8 @@ public final class WebSecurity extends

private HttpFirewall httpFirewall;

private RequestRejectedHandler requestRejectedHandler;

private boolean debugEnabled;

private WebInvocationPrivilegeEvaluator privilegeEvaluator;
Expand Down Expand Up @@ -295,6 +298,9 @@ protected Filter performBuild() throws Exception {
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
if (requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
}
filterChainProxy.afterPropertiesSet();

Filter result = filterChainProxy;
Expand Down Expand Up @@ -392,5 +398,8 @@ public void setApplicationContext(ApplicationContext applicationContext)
try {
this.httpFirewall = applicationContext.getBean(HttpFirewall.class);
} catch(NoSuchBeanDefinitionException e) {}
try {
this.requestRejectedHandler = applicationContext.getBean(RequestRejectedHandler.class);
} catch(NoSuchBeanDefinitionException e) {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.firewall.DefaultRequestRejectedHandler;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.filter.GenericFilterBean;

Expand Down Expand Up @@ -149,6 +153,8 @@ public class FilterChainProxy extends GenericFilterBean {

private HttpFirewall firewall = new StrictHttpFirewall();

private RequestRejectedHandler requestRejectedHandler = new DefaultRequestRejectedHandler();

// ~ Methods
// ========================================================================================================

Expand Down Expand Up @@ -176,6 +182,8 @@ public void doFilter(ServletRequest request, ServletResponse response,
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
} catch (RequestRejectedException e) {
requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, e);
}
finally {
SecurityContextHolder.clearContext();
Expand Down Expand Up @@ -272,6 +280,17 @@ public void setFirewall(HttpFirewall firewall) {
this.firewall = firewall;
}

/**
* Sets the {@link RequestRejectedHandler} to be used for requests rejected by the firewall.
*
* @since 5.2
* @param requestRejectedHandler the {@link RequestRejectedHandler}
*/
public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
Assert.notNull(requestRejectedHandler, "requestRejectedHandler may not be null");
this.requestRejectedHandler = requestRejectedHandler;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2002-2018 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.firewall;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Default implementation of {@link RequestRejectedHandler} that simply rethrows the exception.
*
* @author Leonard Brünings
* @since 5.2
*/
public class DefaultRequestRejectedHandler implements RequestRejectedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
RequestRejectedException requestRejectedException) throws IOException, ServletException {
throw requestRejectedException;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2002-2018 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.firewall;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* A simple implementation of {@link RequestRejectedHandler} that sends an error with configurable status code.
*
* @author Leonard Brünings
* @since 5.2
*/
public class HttpStatusRequestRejectedHandler implements RequestRejectedHandler {
private static final Log logger = LogFactory.getLog(HttpStatusRequestRejectedHandler.class);

private final int httpError;

/**
* Constructs an instance which uses {@code 400} as response code.
*/
public HttpStatusRequestRejectedHandler() {
httpError = HttpServletResponse.SC_BAD_REQUEST;
}

/**
* Constructs an instance which uses a configurable http code as response.
* @param httpError http status code to use
*/
public HttpStatusRequestRejectedHandler(int httpError) {
this.httpError = httpError;
}

@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
RequestRejectedException requestRejectedException) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Rejecting request due to: " + requestRejectedException.getMessage(),
requestRejectedException);
}
response.sendError(httpError);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2002-2018 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.firewall;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Used by {@link org.springframework.security.web.FilterChainProxy} to handle an
* <code>RequestRejectedException</code>.
*
* @author Leonard Brünings
* @since 5.2
*/
public interface RequestRejectedHandler {
// ~ Methods
// ========================================================================================================

/**
* Handles an request rejected failure.
*
* @param request that resulted in an <code>RequestRejectedException</code>
* @param response so that the user agent can be advised of the failure
* @param requestRejectedException that caused the invocation
*
* @throws IOException in the event of an IOException
* @throws ServletException in the event of a ServletException
*/
void handle(HttpServletRequest request, HttpServletResponse response,
RequestRejectedException requestRejectedException) throws IOException,
ServletException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.Filter;
Expand Down Expand Up @@ -243,4 +245,21 @@ public void doFilterClearsSecurityContextHolderOnceOnForwards() throws Exception
any(HttpServletResponse.class));
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
}

@Test(expected = IllegalArgumentException.class)
public void setRequestRejectedHandlerDoesNotAcceptNull() {
fcp.setRequestRejectedHandler(null);
}

@Test
public void requestRejectedHandlerIsCalledIfFirewallThrowsRequestRejectedException() throws Exception {
HttpFirewall fw = mock(HttpFirewall.class);
RequestRejectedHandler rjh = mock(RequestRejectedHandler.class);
fcp.setFirewall(fw);
fcp.setRequestRejectedHandler(rjh);
RequestRejectedException requestRejectedException = new RequestRejectedException("Contains illegal chars");
when(fw.getFirewalledRequest(request)).thenThrow(requestRejectedException);
fcp.doFilter(request, response, chain);
verify(rjh).handle(eq(request), eq(response), eq((requestRejectedException)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2002-2016 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.firewall;


import static org.mockito.Mockito.mock;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Test;

public class DefaultRequestRejectedHandlerTest {

@Test
public void defaultRequestRejectedHandlerRethrowsTheException() throws Exception {
// given:
RequestRejectedException requestRejectedException = new RequestRejectedException("rejected");
DefaultRequestRejectedHandler sut = new DefaultRequestRejectedHandler();

//when:
try {
sut.handle(mock(HttpServletRequest.class), mock(HttpServletResponse.class), requestRejectedException);
} catch (RequestRejectedException exception) {
//then:
Assert.assertThat(exception.getMessage(), CoreMatchers.is("rejected"));
return;
}
Assert.fail("Exception was not rethrown");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2002-2016 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.firewall;


import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.junit.Test;

public class HttpStatusRequestRejectedHandlerTest {

@Test
public void httpStatusRequestRejectedHandlerUsesStatus400byDefault() throws Exception {
//given:
HttpStatusRequestRejectedHandler sut = new HttpStatusRequestRejectedHandler();
HttpServletResponse response = mock(HttpServletResponse.class);

//when:
sut.handle(mock(HttpServletRequest.class), response, mock(RequestRejectedException.class));

// then:
verify(response).sendError(400);
}

@Test
public void httpStatusRequestRejectedHandlerCanBeConfiguredToUseStatus() throws Exception {
httpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(400);
httpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(403);
httpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(500);
}

private void httpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(int status) throws Exception {

//given:
HttpStatusRequestRejectedHandler sut = new HttpStatusRequestRejectedHandler(status);
HttpServletResponse response = mock(HttpServletResponse.class);

//when:
sut.handle(mock(HttpServletRequest.class), response, mock(RequestRejectedException.class));

// then:
verify(response).sendError(status);
}
}