Skip to content

Commit f7cc4ff

Browse files
EST: handle dubious Accept headers
At least one EST client is known to send requests with HTTP header `Accept: text/plain`; see thales-e-security/estclient#5. This behaviour is dubious. It is problematic when communicating with servers/frameworks that have rigid content negotiation behaviour (such as JAX-RS). Nevertheless, the EST protocol uses a narrow range of media types. The method and path are sufficient to determine the request and response media types, regardless of Content-Type and Accept header values. To tolerate bogus Accept header values, define and apply a ContainerRequestFilter that detects when the Accept header does not match any of the response types used in the EST protocol. If it detects this condition it removes the Accept header from the request. NOTE: the JAX-RS spec is ambiguous as to whether our use of the API is legal. Per the spec, `ContainerRequestContext.getAcceptableMediaTypes()` returns an IMMUTABLE `List<MediaType>`. However, `ContainerRequestContext.getHeaders()` returns a MUTABLE map of headers. We are able to delete the Accept header via that map. It seems to be a RestEasy implementation detail that `getAcceptableMediaTypes()` always reads the Accept header afresh from the mutable map. Part of: dogtagpki#3297
1 parent 674f467 commit f7cc4ff

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

base/est/src/main/java/org/dogtagpki/est/ESTApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public ESTApplication() {
2727

2828
// exception mapper
2929
classes.add(PKIExceptionMapper.class);
30+
31+
singletons.add(new HandleBadAcceptHeaderRequestFilter());
3032
}
3133

3234
@Override
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//
2+
// Copyright Red Hat, Inc.
3+
//
4+
// SPDX-License-Identifier: GPL-2.0-or-later
5+
//
6+
package org.dogtagpki.est;
7+
8+
import java.util.Arrays;
9+
import java.util.List;
10+
11+
import javax.ws.rs.container.ContainerRequestContext;
12+
import javax.ws.rs.container.ContainerRequestFilter;
13+
import javax.ws.rs.container.PreMatching;
14+
import javax.ws.rs.core.HttpHeaders;
15+
import javax.ws.rs.core.MediaType;
16+
import javax.ws.rs.ext.Provider;
17+
18+
import com.netscape.cms.servlet.base.PKIService;
19+
20+
/** Filter bad Accept header values.
21+
*
22+
* Some EST clients are known to send requests with Accept:
23+
* text/plain. The JAX-RS API is quite rigid in how it processes
24+
* the Accept header, although the HTTP semantics allow a server to
25+
* ignore the Accept header.
26+
*
27+
* So, we should make some effort to handle these requests despite
28+
* their dubious Accept header values. This request filter fires
29+
* BEFORE resource method matching and handles request thus:
30+
*
31+
* - We do not (yet) know what resource is being requested. We do
32+
* not want to (re)implement the path matching ourselves - too
33+
* complicated.
34+
*
35+
* - So, we have a list of ALL valid (success) response types of the
36+
* EST protocol. If the Accept header matches any of those, we
37+
* leave it alone.
38+
*
39+
* - If the Accept header does not match any valid content-type in
40+
* the EST protocol, then we delete the header and request
41+
* processing continues as if the header had not been included in
42+
* the request.
43+
*
44+
* - The result is that a request has an Accept header that demands
45+
* a content-type that is used within the EST protocol, but which
46+
* is not valid for the method and path combination, will still
47+
* fail 406. But there is only so much complexity we are willing
48+
* to take on to handle dubious client behaviour.
49+
*/
50+
@Provider
51+
@PreMatching
52+
public class HandleBadAcceptHeaderRequestFilter
53+
implements ContainerRequestFilter {
54+
55+
private static org.slf4j.Logger logger =
56+
org.slf4j.LoggerFactory.getLogger(HandleBadAcceptHeaderRequestFilter.class);
57+
58+
private static List<MediaType> RESPONSE_TYPES = Arrays.asList(
59+
// /cacerts, /simpleenroll, /simplereenroll
60+
MediaType.valueOf("application/pkcs7-mime"),
61+
62+
// /serverkeygen
63+
MediaType.valueOf("multipart/mixed"),
64+
65+
// /csrattrs
66+
MediaType.valueOf("application/csrattrs")
67+
);
68+
69+
@Override
70+
public void filter(ContainerRequestContext requestContext) {
71+
logger.debug("HandleBadAcceptHeaderRequestFilter: inspecting request");
72+
List<MediaType> acceptTypes = requestContext.getAcceptableMediaTypes();
73+
MediaType match = PKIService.resolveFormat(acceptTypes, RESPONSE_TYPES);
74+
75+
// if no match, delete the Accept header
76+
if (match == null) {
77+
logger.info("HandleBadAcceptHeaderRequestFilter: no matching Accept header; removing it and proceeding");
78+
requestContext.getHeaders().remove(HttpHeaders.ACCEPT);
79+
}
80+
}
81+
82+
}

0 commit comments

Comments
 (0)