-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Don't remove warning headers on all failure #76434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
fa37403
Don't remove warning headers on all failure
BigPandaToo cb1aad6
Adding tests
BigPandaToo 2facc27
Addressing PR feedback
BigPandaToo 166de9f
Merge branch 'master' into SecRestHandler
elasticmachine 5f078f8
Merge branch 'master' into SecRestHandler
elasticmachine 381a204
Merge branch 'master' into SecRestHandler
elasticmachine 4b38d40
Merge branch 'master' into SecRestHandler
elasticmachine File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
...est/java/org/elasticsearch/xpack/security/rest/SecurityRestFilterWarningHeadersTests.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.security.rest; | ||
|
||
import org.elasticsearch.ElasticsearchStatusException; | ||
import org.elasticsearch.action.ActionListener; | ||
import org.elasticsearch.common.collect.MapBuilder; | ||
import org.elasticsearch.common.settings.Settings; | ||
import org.elasticsearch.common.util.concurrent.ThreadContext; | ||
import org.elasticsearch.common.xcontent.NamedXContentRegistry; | ||
import org.elasticsearch.common.xcontent.json.JsonXContent; | ||
import org.elasticsearch.rest.BytesRestResponse; | ||
import org.elasticsearch.rest.RestChannel; | ||
import org.elasticsearch.rest.RestHandler; | ||
import org.elasticsearch.rest.RestRequest; | ||
import org.elasticsearch.rest.RestResponse; | ||
import org.elasticsearch.rest.RestStatus; | ||
import org.elasticsearch.test.ESTestCase; | ||
import org.elasticsearch.test.rest.FakeRestRequest; | ||
import org.elasticsearch.xpack.core.security.authc.Authentication; | ||
import org.elasticsearch.xpack.security.authc.AuthenticationService; | ||
import org.elasticsearch.xpack.security.authc.support.SecondaryAuthenticator; | ||
import org.junit.Before; | ||
import org.mockito.ArgumentCaptor; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static org.elasticsearch.mock.orig.Mockito.doThrow; | ||
import static org.elasticsearch.test.ActionListenerUtils.anyActionListener; | ||
import static org.mockito.Matchers.eq; | ||
import static org.mockito.Mockito.doAnswer; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
public class SecurityRestFilterWarningHeadersTests extends ESTestCase { | ||
private ThreadContext threadContext; | ||
private AuthenticationService authcService; | ||
private SecondaryAuthenticator secondaryAuthenticator; | ||
private RestHandler restHandler; | ||
|
||
@Override | ||
protected boolean enableWarningsCheck() { | ||
return false; | ||
} | ||
|
||
@Before | ||
public void init() throws Exception { | ||
authcService = mock(AuthenticationService.class); | ||
restHandler = mock(RestHandler.class); | ||
threadContext = new ThreadContext(Settings.EMPTY); | ||
secondaryAuthenticator = new SecondaryAuthenticator(Settings.EMPTY, threadContext, authcService); | ||
} | ||
|
||
public void testResponseHeadersOnFailure() throws Exception { | ||
MapBuilder<String, List<String>> headers = new MapBuilder<>(); | ||
headers.put("Warning", Collections.singletonList("Some warning header")); | ||
headers.put("X-elastic-product", Collections.singletonList("Some product header")); | ||
Map<String, List<String>> afterHeaders; | ||
|
||
// Remove all the headers on authentication failures | ||
afterHeaders = testProcessAuthenticationFailed(RestStatus.BAD_REQUEST, headers); | ||
assertEquals(afterHeaders.size(), 0); | ||
afterHeaders = testProcessAuthenticationFailed(RestStatus.INTERNAL_SERVER_ERROR, headers); | ||
assertEquals(afterHeaders.size(), 0); | ||
afterHeaders = testProcessAuthenticationFailed(RestStatus.UNAUTHORIZED, headers); | ||
assertEquals(afterHeaders.size(), 0); | ||
afterHeaders = testProcessAuthenticationFailed(RestStatus.FORBIDDEN, headers); | ||
assertEquals(afterHeaders.size(), 0); | ||
|
||
// On rest handling failures only remove headers if rest status is UNAUTHORIZED or FORBIDDEN | ||
afterHeaders = testProcessRestHandlingFailed(RestStatus.BAD_REQUEST, headers); | ||
assertEquals(afterHeaders.size(), 2); | ||
afterHeaders = testProcessRestHandlingFailed(RestStatus.INTERNAL_SERVER_ERROR, headers); | ||
assertEquals(afterHeaders.size(), 2); | ||
afterHeaders = testProcessRestHandlingFailed(RestStatus.UNAUTHORIZED, headers); | ||
assertEquals(afterHeaders.size(), 0); | ||
afterHeaders = testProcessRestHandlingFailed(RestStatus.FORBIDDEN, headers); | ||
assertEquals(afterHeaders.size(), 0); | ||
} | ||
|
||
private Map<String, List<String>> testProcessRestHandlingFailed(RestStatus restStatus, MapBuilder<String, List<String>> headers) | ||
throws Exception { | ||
RestChannel channel = mock(RestChannel.class); | ||
SecurityRestFilter filter = new SecurityRestFilter(Settings.EMPTY, threadContext, authcService, secondaryAuthenticator, | ||
restHandler, false); | ||
RestRequest request = new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY).build(); | ||
Authentication primaryAuthentication = mock(Authentication.class); | ||
when(primaryAuthentication.encode()).thenReturn(randomAlphaOfLengthBetween(12, 36)); | ||
doAnswer(i -> { | ||
final Object[] arguments = i.getArguments(); | ||
@SuppressWarnings("unchecked") | ||
ActionListener<Authentication> callback = (ActionListener<Authentication>) arguments[arguments.length - 1]; | ||
callback.onResponse(primaryAuthentication); | ||
return null; | ||
}).when(authcService).authenticate(eq(request), anyActionListener()); | ||
Authentication secondaryAuthentication = mock(Authentication.class); | ||
when(secondaryAuthentication.encode()).thenReturn(randomAlphaOfLengthBetween(12, 36)); | ||
doAnswer(i -> { | ||
final Object[] arguments = i.getArguments(); | ||
@SuppressWarnings("unchecked") | ||
ActionListener<Authentication> callback = (ActionListener<Authentication>) arguments[arguments.length - 1]; | ||
callback.onResponse(secondaryAuthentication); | ||
return null; | ||
}).when(authcService).authenticate(eq(request), eq(false), anyActionListener()); | ||
doThrow(new ElasticsearchStatusException("Rest handling failed", restStatus, "")) | ||
.when(restHandler).handleRequest(request, channel, null); | ||
when(channel.request()).thenReturn(request); | ||
when(channel.newErrorBuilder()).thenReturn(JsonXContent.contentBuilder()); | ||
filter.handleRequest(request, channel, null); | ||
ArgumentCaptor<BytesRestResponse> response = ArgumentCaptor.forClass(BytesRestResponse.class); | ||
verify(channel).sendResponse(response.capture()); | ||
RestResponse restResponse = response.getValue(); | ||
return restResponse.filterHeaders(headers.immutableMap()); | ||
} | ||
|
||
private Map<String, List<String>> testProcessAuthenticationFailed(RestStatus restStatus, MapBuilder<String, List<String>> headers) | ||
throws Exception { | ||
RestChannel channel = mock(RestChannel.class); | ||
SecurityRestFilter filter = new SecurityRestFilter(Settings.EMPTY, threadContext, authcService, secondaryAuthenticator, | ||
restHandler, false); | ||
RestRequest request = new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY).build(); | ||
doAnswer((i) -> { | ||
ActionListener<?> callback = (ActionListener<?>) i.getArguments()[1]; | ||
callback.onFailure(new ElasticsearchStatusException("Authentication failed", restStatus, "")); | ||
return Void.TYPE; | ||
}).when(authcService).authenticate(eq(request), anyActionListener()); | ||
when(channel.request()).thenReturn(request); | ||
when(channel.newErrorBuilder()).thenReturn(JsonXContent.contentBuilder()); | ||
filter.handleRequest(request, channel, null); | ||
ArgumentCaptor<BytesRestResponse> response = ArgumentCaptor.forClass(BytesRestResponse.class); | ||
verify(channel).sendResponse(response.capture()); | ||
RestResponse restResponse = response.getValue(); | ||
return restResponse.filterHeaders(headers.immutableMap()); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to remove warning headers on
403
(FORBIDDEN
)? A403
means authentication is successful. IIUC, the original intention is to remove warnings if authentication fails, i.e.401
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed it with @tvernum. More broadly the intention was to remove warnings in case request fails because of any security problem, which include failing authentication and failing the request handling because access was denied at some point of the handling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification!