Skip to content

Commit

Permalink
Make Authn and Audit take a HTTP interface when dealing with Rest req…
Browse files Browse the repository at this point in the history
…uests (#94990)

AuthN and Audit are NOT going to have access to the Rest
interface anymore, because the AuthenticationService is
going to be invoked before the RestRequest is built.

This refactoring makes the AuthenticationService and
the AuditTrail take as parameters HTTP-related request
interfaces.
  • Loading branch information
albertzaharovits committed Apr 4, 2023
1 parent e24a572 commit a8d5723
Show file tree
Hide file tree
Showing 22 changed files with 244 additions and 198 deletions.
48 changes: 48 additions & 0 deletions server/src/main/java/org/elasticsearch/http/HttpPreRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.http;

import org.elasticsearch.rest.RestRequest;

import java.util.List;
import java.util.Map;

/**
* A slim interface for precursors to HTTP requests, which doesn't expose access to the request's body,
* because it's not available yet.
*/
public interface HttpPreRequest {

/**
* Returns the HTTP method used in the HTTP request.
*
* @return the {@link RestRequest.Method} used in the request
* @throws IllegalArgumentException if the HTTP method is invalid
*/
RestRequest.Method method();

/**
* The uri with the query string.
*/
String uri();

/**
* Get all of the headers and values associated with the HTTP headers.
* Modifications of this map are not supported.
*/
Map<String, List<String>> getHeaders();

default String header(String name) {
List<String> values = getHeaders().get(name);
if (values != null && values.isEmpty() == false) {
return values.get(0);
}
return null;
}
}
33 changes: 3 additions & 30 deletions server/src/main/java/org/elasticsearch/http/HttpRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,24 @@
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.rest.ChunkedRestResponseBody;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestStatus;

import java.util.List;
import java.util.Map;

/**
* A basic http request abstraction. Http modules needs to implement this interface to integrate with the
* server package's rest handling.
* server package's rest handling. This interface exposes the request's content as well as methods to be used
* to generate a response.
*/
public interface HttpRequest {
public interface HttpRequest extends HttpPreRequest {

enum HttpVersion {
HTTP_1_0,
HTTP_1_1
}

/**
* Returns the HTTP method used in the HTTP request.
*
* @return the {@link RestRequest.Method} used in the REST request
* @throws IllegalArgumentException if the HTTP method is invalid
*/
RestRequest.Method method();

/**
* The uri of the rest request, with the query string.
*/
String uri();

BytesReference content();

/**
* Get all of the headers and values associated with the headers. Modifications of this map are not supported.
*/
Map<String, List<String>> getHeaders();

default String header(String name) {
List<String> values = getHeaders().get(name);
if (values != null && values.isEmpty() == false) {
return values.get(0);
}
return null;
}

List<String> strictCookies();

HttpVersion protocolVersion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.http.HttpPreRequest;
import org.elasticsearch.transport.TransportMessage;

/**
Expand Down Expand Up @@ -39,7 +39,7 @@ public interface AuthenticationFailureHandler {
* @param context The context of the request that failed authentication that could not be authenticated
* @return ElasticsearchSecurityException with the appropriate headers and message
*/
ElasticsearchSecurityException failedAuthentication(RestRequest request, AuthenticationToken token, ThreadContext context);
ElasticsearchSecurityException failedAuthentication(HttpPreRequest request, AuthenticationToken token, ThreadContext context);

/**
* This method is called when there has been an authentication failure for the given message and token
Expand All @@ -66,7 +66,7 @@ ElasticsearchSecurityException failedAuthentication(
* @param context The context of the request that failed authentication that could not be authenticated
* @return ElasticsearchSecurityException with the appropriate headers and message
*/
ElasticsearchSecurityException exceptionProcessingRequest(RestRequest request, Exception e, ThreadContext context);
ElasticsearchSecurityException exceptionProcessingRequest(HttpPreRequest request, Exception e, ThreadContext context);

/**
* The method is called when an exception has occurred while processing the transport message. This could be an error that
Expand All @@ -88,7 +88,7 @@ ElasticsearchSecurityException failedAuthentication(
* @param context The context of the request that failed authentication that could not be authenticated
* @return ElasticsearchSecurityException with the appropriate headers and message
*/
ElasticsearchSecurityException missingToken(RestRequest request, ThreadContext context);
ElasticsearchSecurityException missingToken(HttpPreRequest request, ThreadContext context);

/**
* This method is called when a transport message is received and no authentication token could be extracted AND
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.elasticsearch.ElasticsearchAuthenticationProcessingError;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.http.HttpPreRequest;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.xpack.core.XPackField;
Expand Down Expand Up @@ -92,7 +92,7 @@ private static Integer authSchemePriority(final String headerValue) {
}

@Override
public ElasticsearchSecurityException failedAuthentication(RestRequest request, AuthenticationToken token, ThreadContext context) {
public ElasticsearchSecurityException failedAuthentication(HttpPreRequest request, AuthenticationToken token, ThreadContext context) {
return createAuthenticationError("unable to authenticate user [{}] for REST request [{}]", null, token.principal(), request.uri());
}

Expand All @@ -107,7 +107,7 @@ public ElasticsearchSecurityException failedAuthentication(
}

@Override
public ElasticsearchSecurityException exceptionProcessingRequest(RestRequest request, Exception e, ThreadContext context) {
public ElasticsearchSecurityException exceptionProcessingRequest(HttpPreRequest request, Exception e, ThreadContext context) {
// a couple of authn processing errors can also return {@link RestStatus#INTERNAL_SERVER_ERROR} or
// {@link RestStatus#SERVICE_UNAVAILABLE}, besides the obvious {@link RestStatus#UNAUTHORIZED}
if (e instanceof ElasticsearchAuthenticationProcessingError) {
Expand All @@ -132,7 +132,7 @@ public ElasticsearchSecurityException exceptionProcessingRequest(
}

@Override
public ElasticsearchSecurityException missingToken(RestRequest request, ThreadContext context) {
public ElasticsearchSecurityException missingToken(HttpPreRequest request, ThreadContext context) {
return createAuthenticationError("missing authentication credentials for REST request [{}]", null, request.uri());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.http.HttpPreRequest;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.XPackField;
Expand Down Expand Up @@ -60,7 +60,7 @@ public void testAuthenticationRequired() {

public void testMissingToken() {
final DefaultAuthenticationFailureHandler handler = new DefaultAuthenticationFailureHandler(Collections.emptyMap());
final RestRequest request = mock(RestRequest.class);
final HttpPreRequest request = mock(HttpPreRequest.class);
when(request.uri()).thenReturn("https://secret.es.shield.gov/");
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final ElasticsearchSecurityException ese = handler.missingToken(request, threadContext);
Expand Down Expand Up @@ -92,7 +92,7 @@ public void testExceptionProcessingRequest() {
if (causeIsElasticsearchSecurityException) {
if (causeIsEseAndUnauthorized) {
final ElasticsearchSecurityException ese = failureHandler.exceptionProcessingRequest(
mock(RestRequest.class),
mock(HttpPreRequest.class),
cause,
new ThreadContext(Settings.builder().build())
);
Expand All @@ -113,15 +113,15 @@ public void testExceptionProcessingRequest() {
expectThrows(
AssertionError.class,
() -> failureHandler.exceptionProcessingRequest(
mock(RestRequest.class),
mock(HttpPreRequest.class),
cause,
new ThreadContext(Settings.builder().build())
)
);
}
} else {
final ElasticsearchSecurityException ese = failureHandler.exceptionProcessingRequest(
mock(RestRequest.class),
mock(HttpPreRequest.class),
cause,
new ThreadContext(Settings.builder().build())
);
Expand All @@ -145,7 +145,7 @@ public void testSortsWWWAuthenticateHeaderValues() {
final DefaultAuthenticationFailureHandler failuerHandler = new DefaultAuthenticationFailureHandler(failureResponeHeaders);

final ElasticsearchSecurityException ese = failuerHandler.exceptionProcessingRequest(
mock(RestRequest.class),
mock(HttpPreRequest.class),
null,
new ThreadContext(Settings.builder().build())
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.elasticsearch.xpack.security.audit;

import org.elasticsearch.http.HttpPreRequest;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.transport.TransportResponse;
Expand All @@ -28,19 +29,19 @@ public interface AuditTrail {

void anonymousAccessDenied(String requestId, String action, TransportRequest transportRequest);

void anonymousAccessDenied(String requestId, RestRequest request);
void anonymousAccessDenied(String requestId, HttpPreRequest request);

void authenticationFailed(String requestId, RestRequest request);
void authenticationFailed(String requestId, HttpPreRequest request);

void authenticationFailed(String requestId, String action, TransportRequest transportRequest);

void authenticationFailed(String requestId, AuthenticationToken token, String action, TransportRequest transportRequest);

void authenticationFailed(String requestId, AuthenticationToken token, RestRequest request);
void authenticationFailed(String requestId, AuthenticationToken token, HttpPreRequest request);

void authenticationFailed(String requestId, String realm, AuthenticationToken token, String action, TransportRequest transportRequest);

void authenticationFailed(String requestId, String realm, AuthenticationToken token, RestRequest request);
void authenticationFailed(String requestId, String realm, AuthenticationToken token, HttpPreRequest request);

void accessGranted(
String requestId,
Expand All @@ -58,7 +59,7 @@ void accessDenied(
AuthorizationInfo authorizationInfo
);

void tamperedRequest(String requestId, RestRequest request);
void tamperedRequest(String requestId, HttpPreRequest request);

void tamperedRequest(String requestId, String action, TransportRequest transportRequest);

Expand Down Expand Up @@ -90,7 +91,7 @@ void runAsDenied(
AuthorizationInfo authorizationInfo
);

void runAsDenied(String requestId, Authentication authentication, RestRequest request, AuthorizationInfo authorizationInfo);
void runAsDenied(String requestId, Authentication authentication, HttpPreRequest request, AuthorizationInfo authorizationInfo);

/**
* This is a "workaround" method to log index "access_granted" and "access_denied" events for actions not tied to a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.http.HttpPreRequest;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.transport.TransportRequest;
Expand Down Expand Up @@ -94,10 +95,10 @@ public void authenticationSuccess(
public void anonymousAccessDenied(String requestId, String action, TransportRequest transportRequest) {}

@Override
public void anonymousAccessDenied(String requestId, RestRequest request) {}
public void anonymousAccessDenied(String requestId, HttpPreRequest request) {}

@Override
public void authenticationFailed(String requestId, RestRequest request) {}
public void authenticationFailed(String requestId, HttpPreRequest request) {}

@Override
public void authenticationFailed(String requestId, String action, TransportRequest transportRequest) {}
Expand All @@ -106,7 +107,7 @@ public void authenticationFailed(String requestId, String action, TransportReque
public void authenticationFailed(String requestId, AuthenticationToken token, String action, TransportRequest transportRequest) {}

@Override
public void authenticationFailed(String requestId, AuthenticationToken token, RestRequest request) {}
public void authenticationFailed(String requestId, AuthenticationToken token, HttpPreRequest request) {}

@Override
public void authenticationFailed(
Expand All @@ -118,7 +119,7 @@ public void authenticationFailed(
) {}

@Override
public void authenticationFailed(String requestId, String realm, AuthenticationToken token, RestRequest request) {}
public void authenticationFailed(String requestId, String realm, AuthenticationToken token, HttpPreRequest request) {}

@Override
public void accessGranted(
Expand All @@ -139,7 +140,7 @@ public void accessDenied(
) {}

@Override
public void tamperedRequest(String requestId, RestRequest request) {}
public void tamperedRequest(String requestId, HttpPreRequest request) {}

@Override
public void tamperedRequest(String requestId, String action, TransportRequest transportRequest) {}
Expand Down Expand Up @@ -175,7 +176,7 @@ public void runAsDenied(
public void runAsDenied(
String requestId,
Authentication authentication,
RestRequest request,
HttpPreRequest request,
AuthorizationInfo authorizationInfo
) {}

Expand Down

0 comments on commit a8d5723

Please sign in to comment.