Skip to content

Commit 9d448a0

Browse files
authored
Introduce FilterRestHandler (#98922)
RestHandler has a number of methods that affect the behaviour of request processing. If the handler is wrapped (e.g. SecurityRestFilter or DeprecationRestHandler) then these methods must be delegated to the underlying handler. This commit introduces a new abstract base class `FilterRestHandler` that correctly delegates these methods so that wrappers (subclasses) do not need to implement the behaviour on a case-by-case basis Backport of: #98861
1 parent a950367 commit 9d448a0

File tree

5 files changed

+78
-42
lines changed

5 files changed

+78
-42
lines changed

server/src/main/java/org/elasticsearch/rest/DeprecationRestHandler.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
* {@code DeprecationRestHandler} provides a proxy for any existing {@link RestHandler} so that usage of the handler can be
2121
* logged using the {@link DeprecationLogger}.
2222
*/
23-
public class DeprecationRestHandler implements RestHandler {
23+
public class DeprecationRestHandler extends FilterRestHandler implements RestHandler {
2424

2525
public static final String DEPRECATED_ROUTE_KEY = "deprecated_route";
26-
private final RestHandler handler;
26+
2727
private final String deprecationMessage;
2828
private final DeprecationLogger deprecationLogger;
2929
private final String deprecationKey;
@@ -50,7 +50,7 @@ public DeprecationRestHandler(
5050
String deprecationMessage,
5151
DeprecationLogger deprecationLogger
5252
) {
53-
this.handler = Objects.requireNonNull(handler);
53+
super(handler);
5454
this.deprecationMessage = requireValidHeader(deprecationMessage);
5555
this.deprecationLogger = Objects.requireNonNull(deprecationLogger);
5656
this.deprecationKey = DEPRECATED_ROUTE_KEY + "_" + method + "_" + path;
@@ -76,12 +76,7 @@ public void handleRequest(RestRequest request, RestChannel channel, NodeClient c
7676
deprecationLogger.warn(DeprecationCategory.API, deprecationKey, deprecationMessage);
7777
}
7878

79-
handler.handleRequest(request, channel, client);
80-
}
81-
82-
@Override
83-
public boolean supportsContentStream() {
84-
return handler.supportsContentStream();
79+
getDelegate().handleRequest(request, channel, client);
8580
}
8681

8782
/**
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.rest;
10+
11+
import java.util.List;
12+
import java.util.Objects;
13+
14+
public abstract class FilterRestHandler implements RestHandler {
15+
private final RestHandler delegate;
16+
17+
protected FilterRestHandler(RestHandler delegate) {
18+
this.delegate = Objects.requireNonNull(delegate);
19+
}
20+
21+
protected RestHandler getDelegate() {
22+
return delegate;
23+
}
24+
25+
@Override
26+
public RestHandler getConcreteRestHandler() {
27+
return delegate.getConcreteRestHandler();
28+
}
29+
30+
@Override
31+
public List<RestHandler.Route> routes() {
32+
return delegate.routes();
33+
}
34+
35+
@Override
36+
public boolean allowSystemIndexAccessByDefault() {
37+
return delegate.allowSystemIndexAccessByDefault();
38+
}
39+
40+
@Override
41+
public boolean canTripCircuitBreaker() {
42+
return delegate.canTripCircuitBreaker();
43+
}
44+
45+
@Override
46+
public boolean allowsUnsafeBuffers() {
47+
return delegate.allowsUnsafeBuffers();
48+
}
49+
50+
@Override
51+
public boolean supportsContentStream() {
52+
return delegate.supportsContentStream();
53+
}
54+
}

server/src/main/java/org/elasticsearch/rest/RestHandler.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ default boolean supportsContentStream() {
4646
return false;
4747
}
4848

49+
/**
50+
* Returns the concrete RestHandler for this RestHandler. That is, if this is a delegating RestHandler it returns the delegate.
51+
* Otherwise it returns itself.
52+
* @return The underlying RestHandler
53+
*/
54+
default RestHandler getConcreteRestHandler() {
55+
return this;
56+
}
57+
4958
/**
5059
* Indicates if the RestHandler supports working with pooled buffers. If the request handler will not escape the return
5160
* {@link RestRequest#content()} or any buffers extracted from it then there is no need to make a copies of any pooled buffers in the

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.http.nio.NioHttpRequest;
2222
import org.elasticsearch.license.XPackLicenseState;
2323
import org.elasticsearch.rest.BytesRestResponse;
24+
import org.elasticsearch.rest.FilterRestHandler;
2425
import org.elasticsearch.rest.RestChannel;
2526
import org.elasticsearch.rest.RestHandler;
2627
import org.elasticsearch.rest.RestRequest;
@@ -31,14 +32,13 @@
3132
import org.elasticsearch.xpack.security.authc.support.SecondaryAuthenticator;
3233

3334
import java.io.IOException;
34-
import java.util.List;
3535

36-
public class SecurityRestFilter implements RestHandler {
36+
public class SecurityRestFilter extends FilterRestHandler implements RestHandler {
3737

3838
private static final Logger logger = LogManager.getLogger(SecurityRestFilter.class);
3939

40-
private final RestHandler restHandler;
4140
private final AuthenticationService authenticationService;
41+
4242
private final SecondaryAuthenticator secondaryAuthenticator;
4343
private final XPackLicenseState licenseState;
4444
private final AuditTrailService auditTrailService;
@@ -50,16 +50,11 @@ public SecurityRestFilter(
5050
AuditTrailService auditTrailService,
5151
RestHandler restHandler
5252
) {
53+
super(restHandler);
5354
this.licenseState = licenseState;
5455
this.authenticationService = authenticationService;
5556
this.secondaryAuthenticator = secondaryAuthenticator;
5657
this.auditTrailService = auditTrailService;
57-
this.restHandler = restHandler;
58-
}
59-
60-
@Override
61-
public boolean allowSystemIndexAccessByDefault() {
62-
return restHandler.allowSystemIndexAccessByDefault();
6358
}
6459

6560
@Override
@@ -75,7 +70,7 @@ public void handleRequest(RestRequest request, RestChannel channel, NodeClient c
7570
if (secondaryAuthentication != null) {
7671
logger.trace("Found secondary authentication {} in REST request [{}]", secondaryAuthentication, requestUri);
7772
}
78-
restHandler.handleRequest(request, channel, client);
73+
getDelegate().handleRequest(request, channel, client);
7974
}, e -> handleException("Secondary authentication", request, channel, e)));
8075
}, e -> handleException("Authentication", request, channel, e)));
8176
} else {
@@ -101,7 +96,7 @@ public void handleRequest(RestRequest request, RestChannel channel, NodeClient c
10196
+ "/security-minimal-setup.html to enable security."
10297
);
10398
}
104-
restHandler.handleRequest(request, channel, client);
99+
getDelegate().handleRequest(request, channel, client);
105100
}
106101
}
107102

@@ -134,29 +129,10 @@ private void handleException(String actionType, RestRequest request, RestChannel
134129
}
135130
}
136131

137-
@Override
138-
public boolean canTripCircuitBreaker() {
139-
return restHandler.canTripCircuitBreaker();
140-
}
141-
142-
@Override
143-
public boolean supportsContentStream() {
144-
return restHandler.supportsContentStream();
145-
}
146-
147-
@Override
148-
public boolean allowsUnsafeBuffers() {
149-
return restHandler.allowsUnsafeBuffers();
150-
}
151-
152-
@Override
153-
public List<Route> routes() {
154-
return restHandler.routes();
155-
}
156-
157132
private RestRequest maybeWrapRestRequest(RestRequest restRequest) throws IOException {
158-
if (restHandler instanceof RestRequestFilter) {
159-
return ((RestRequestFilter) restHandler).getFilteredRequest(restRequest);
133+
final RestHandler handler = getConcreteRestHandler();
134+
if (handler instanceof RestRequestFilter) {
135+
return ((RestRequestFilter) handler).getFilteredRequest(restRequest);
160136
}
161137
return restRequest;
162138
}

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/SecurityRestFilterTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import static org.hamcrest.Matchers.sameInstance;
6565
import static org.mockito.ArgumentMatchers.any;
6666
import static org.mockito.ArgumentMatchers.eq;
67+
import static org.mockito.Mockito.atLeastOnce;
6768
import static org.mockito.Mockito.doAnswer;
6869
import static org.mockito.Mockito.mock;
6970
import static org.mockito.Mockito.spy;
@@ -279,6 +280,7 @@ private void testProcessAuthenticationFailed(
279280
} else {
280281
assertThat(restResponse.content().utf8ToString(), not(containsString(ElasticsearchException.STACK_TRACE)));
281282
}
283+
verify(restHandler, atLeastOnce()).getConcreteRestHandler();
282284
verifyNoMoreInteractions(restHandler);
283285
}
284286

0 commit comments

Comments
 (0)