spring-projects / spring-projects/spring-security

Throw custom Exception when the HTTP Method is rejected

Open
#12,191 0 comments 1 reaction 1 assignee View on GitHub

@rwinch is already working on this.

Since Nov 16, 2022.

in: web type: enhancement
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

Updated

When the HTTP Method is rejected MethodRejectedException will be thrown.

NOTE: We are not going to do custom exceptions for every type of rejection. Instead we will start with HTTP Methods since that is the requirement in this issue.

Original

Expected Behavior

The StrictHttpFirewall should throw specialized RequestRejectedException exceptions for each type of rejection, making it easier to map rejections to appropriate HTTP response status codes by configuring RequestRejectedHandlers.

I believe the best approach would be to introduce new exceptions that extend the existing RequestRejectedException. If this approach is taken, it might be appropriate to introduce a new type of RequestRejectedHandler that accepts a generic <? extends RequestRejectedException> parameter.

private void rejectForbiddenHttpMethod(HttpServletRequest request) {
	if (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {
		return;
	}
	if (!this.allowedHttpMethods.contains(request.getMethod())) {
		throw new ForbiddenHttpMethodException(
				"The request was rejected because the HTTP method \"" + request.getMethod()
						+ "\" was not included within the list of allowed HTTP methods " + this.allowedHttpMethods);
	}
}
public class ForbiddenHttpMethodException extends RequestRejectedException {
	public ForbiddenHttpMethodException(String message) {
		super(message);
	}
}
@Bean
public TypedRequestRejectedHandler<ForbiddenHttpMethodException> forbiddenMethodHandler() {
	return (request, response, exception) ->
		response.setStatus(Status.NOT_IMPLEMENTED.getStatusCode());
}

Another approach would be to introduce a flag in RequestRejectedException indicating the rejection reason, allowing RequestRejectedHandler to return the correct HTTP status according to the rejection reason of the exception.

class RequestRejectedException extends RuntimeException {
    
    private RequestRejectedReason reason;
    
    public RequestRejectedException(String message) {
        this(message, RequestRejectedReason.GENERIC);
    }

    public RequestRejectedException(String message, RequestRejectedReason reason) {
        super(message);
        this.reason = reason;
    }
    
    public RequestRejectedReason getReason() {
        return reason;
    }

    public enum RequestRejectedReason {
        FORBIDDEN_METHOD,
        BLOCKLISTED_URL,
        UNTRUSTED_HOST,
        NOT_NORMALIZED,
        NOT_PRINTABLE_ASCII_CHARS,
        INVALID_HEADER_NAME,
        INVALID_HEADER_VALUE,
        INVALID_PARAM_NAME,
        INVALID_PARAM_VALUE,
        GENERIC
    }
}

Current Behavior

The StrictHttpFirewall currently throws RequestRejectedException for all types rejections. This makes it a bit difficult to target specific types of request rejections for specific HTTP response status codes.

If we introduce a RequestRejectedHandler, the handler catches all types of rejections (forbidden methods, blocklisted URLs, untrusted hosts, etc.). If we needed to map only one rejection type (e.g. forbidden method) to a different HTTP status (404, 501, etc), we would need to introduce logic that compares against the exception message, which would be brittle.

Context

My team is developing a server implementation of the IEEE-2030.5-2018 communication protocol. To meet standardized conformance tests of the Common Smart Inverter Profile (CSIP), our application must respond with a 501 Not Implemented when an invalid HTTP method (e.g. FOO) is used.

Currently there's no elegant way to target this specific type of firewall rejection. For now, we simply register a handler that maps all rejections to a status of 501.

    @Bean
    public RequestRejectedHandler requestRejectedHandler() {
        return (request, response, exception) -> response.setStatus(Status.NOT_IMPLEMENTED.getStatusCode());
    }

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.