[
https://issues.apache.org/jira/browse/DRILL-8540?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18037801#comment-18037801
]
ASF GitHub Bot commented on DRILL-8540:
---------------------------------------
joakime commented on code in PR #3031:
URL: https://github.com/apache/drill/pull/3031#discussion_r2518795093
##########
exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java:
##########
@@ -181,21 +147,8 @@ public void testAuthClientRequestForSpnegoLoginResource()
throws Exception {
*/
@Test
public void testAuthClientRequestForOtherPage() throws Exception {
-
- final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
- final HttpServletResponse response =
Mockito.mock(HttpServletResponse.class);
- final HttpSession session = Mockito.mock(HttpSession.class);
- final Authentication authentication =
Mockito.mock(UserAuthentication.class);
-
- Mockito.when(request.getSession(true)).thenReturn(session);
-
Mockito.when(request.getRequestURI()).thenReturn(WebServerConstants.WEBSERVER_ROOT_PATH);
-
Mockito.when(session.getAttribute(SessionAuthentication.__J_AUTHENTICATED)).thenReturn(authentication);
-
- final UserAuthentication returnedAuthentication = (UserAuthentication)
spnegoAuthenticator.validateRequest
- (request, response, false);
- assertEquals(authentication, returnedAuthentication);
- verify(response, never()).sendError(401);
- verify(response,
never()).setHeader(HttpHeader.WWW_AUTHENTICATE.asString(),
HttpHeader.NEGOTIATE.asString());
+ // This test needs to be rewritten for Jetty 12 API
+ // TODO: Rewrite for Jetty 12
Review Comment:
Directly mocking the HttpServletRequest isn't a great choice for testing.
It doesn't test the actual implementation, which depends heavily on internal
HttpServletRequest instance types.
Using a real server, and real HTTP requests, is often faster to setup, and
test.
##########
exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java:
##########
@@ -262,17 +202,7 @@ public void testSpnegoLoginInvalidToken() throws Exception
{
}
});
- Mockito.when(request.getSession(true)).thenReturn(session);
-
- final String httpReqAuthHeader = String.format("%s:%s",
HttpHeader.NEGOTIATE.asString(), String.format
- ("%s%s","1234", token));
-
Mockito.when(request.getHeader(HttpHeader.AUTHORIZATION.asString())).thenReturn(httpReqAuthHeader);
-
Mockito.when(request.getRequestURI()).thenReturn(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH);
-
- assertEquals(spnegoAuthenticator.validateRequest(request, response,
false), Authentication.UNAUTHENTICATED);
-
- verify(session,
never()).setAttribute(SessionAuthentication.__J_AUTHENTICATED, null);
- verify(response, never()).sendError(401);
- verify(response,
never()).setHeader(HttpHeader.WWW_AUTHENTICATE.asString(),
HttpHeader.NEGOTIATE.asString());
+ // This test needs to be rewritten for Jetty 12 API
+ // TODO: Rewrite for Jetty 12
Review Comment:
It's often easier, and faster to execute, using a real server instance +
using an HTTP client to connect to it.
##########
pom.xml:
##########
@@ -98,8 +98,8 @@
<javassist.version>3.29.2-GA</javassist.version>
<javax.el.version>3.0.0</javax.el.version>
<javax.validation.api>2.0.1.Final</javax.validation.api>
- <jersey.version>2.40</jersey.version>
- <jetty.version>9.4.56.v20240826</jetty.version>
+ <jersey.version>3.1.9</jersey.version>
+ <jetty.version>12.0.15</jetty.version>
Review Comment:
btw, Jetty has BOM dependencies that can be used as well.
One for core, and one for the environment you choose to use.
Would you be interested in that?
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java:
##########
@@ -38,11 +37,11 @@ public class DrillUserPrincipal implements Principal {
public static final String[] NON_ADMIN_USER_ROLES = new
String[]{AUTHENTICATED_ROLE};
- public static final List<RolePrincipal> ADMIN_PRINCIPALS =
- ImmutableList.of(new RolePrincipal(AUTHENTICATED_ROLE), new
RolePrincipal(ADMIN_ROLE));
+ public static final List<String> ADMIN_PRINCIPALS =
Review Comment:
Use ...
``` java
import org.eclipse.jetty.security.RolePrincipal;
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java:
##########
@@ -173,23 +177,23 @@ public void setHandler(Handler handler) {
}
@Override
- public void doStop() throws Exception {
+ protected void doStop() throws Exception {
super.doStop();
for (DrillHttpConstraintSecurityHandler securityHandler :
securityHandlers.values()) {
securityHandler.doStop();
}
}
public boolean isSpnegoEnabled() {
- return securityHandlers.containsKey(Constraint.__SPNEGO_AUTH);
+ return securityHandlers.containsKey("SPNEGO");
}
public boolean isFormEnabled() {
- return securityHandlers.containsKey(Constraint.__FORM_AUTH);
+ return securityHandlers.containsKey("FORM");
Review Comment:
```suggestion
return
securityHandlers.containsKey(org.eclipse.jetty.security.Authenticator.FORM_AUTH);
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java:
##########
@@ -114,23 +112,27 @@ public DrillHttpSecurityHandlerProvider(DrillConfig
config, DrillbitContext dril
}
@Override
- public void doStart() throws Exception {
+ protected void doStart() throws Exception {
super.doStart();
for (DrillHttpConstraintSecurityHandler securityHandler :
securityHandlers.values()) {
securityHandler.doStart();
}
}
@Override
- public void handle(String target, Request baseRequest, HttpServletRequest
request, HttpServletResponse response)
- throws IOException, ServletException {
+ public boolean handle(Request request, Response response, Callback callback)
throws Exception {
+ // Get servlet request/response from the core request/response
+ org.eclipse.jetty.ee10.servlet.ServletContextRequest servletContextRequest
=
+ org.eclipse.jetty.server.Request.as(request,
org.eclipse.jetty.ee10.servlet.ServletContextRequest.class);
+ HttpServletRequest httpServletRequest =
servletContextRequest.getServletApiRequest();
+ HttpServletResponse httpServletResponse =
servletContextRequest.getHttpServletResponse();
Review Comment:
This class is a `Handler.Wrapper`
It has no access to the `ee10` implementation.
Converting from core `Request` / `Response` to Servlet `HttpServletRequest`
and `HttpServletResponse` can only be done within a `ServletContext`, which
this handler does not participate in.
If you need this, then extend from the `ConstraintSecurityHandler` from the
ee10 layer.
The `org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler` is
the appropriate `ee10` specific implementation of
`org.eclipse.jetty.security.SecurityHandler` and will be inserted into the
`WebAppContext` (or `ServletContextHandler`) in the right place if your class
uses the correct type.
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillErrorHandler.java:
##########
@@ -18,25 +18,49 @@
package org.apache.drill.exec.server.rest.auth;
import org.apache.drill.exec.server.rest.WebServerConstants;
-import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.Callback;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.Writer;
/**
- * Custom ErrorHandler class for Drill's WebServer to have better error
message in case when SPNEGO login failed and
- * what to do next. In all other cases this would use the generic error page.
+ * Custom ErrorHandler class for Drill's WebServer to handle errors
appropriately based on the request type.
+ * - For JSON API endpoints (*.json), returns JSON error responses
+ * - For SPNEGO login failures, provides helpful HTML error page
+ * - For all other cases, returns standard HTML error page
*/
-public class DrillErrorHandler extends ErrorHandler {
+public class DrillErrorHandler extends ErrorPageErrorHandler {
+
+ @Override
+ public boolean handle(Request target, org.eclipse.jetty.server.Response
response, Callback callback) throws Exception {
+ // Check if this is a JSON API request
+ String pathInContext = Request.getPathInContext(target);
Review Comment:
Be careful here.
This will return the raw path in context.
There's no decoding, there's no normalization of the path, no collapsing of
path navigation, etc ...
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java:
##########
@@ -296,11 +309,11 @@ public void sessionDestroyed(HttpSessionEvent se) {
return;
}
- final Object authCreds =
session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
+ final Object authCreds =
session.getAttribute(SessionAuthentication.AUTHENTICATED_ATTRIBUTE);
if (authCreds != null) {
final SessionAuthentication sessionAuth = (SessionAuthentication)
authCreds;
- securityHandler.logout(sessionAuth);
- session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
+ // In Jetty 12, logout is handled differently - we just remove the
attribute
Review Comment:
logout is still important to cleanup runAsRole entries.
It is also used by some authenticator types (eg: OpenID)
##########
docs/dev/Jetty12Migration.md:
##########
@@ -0,0 +1,183 @@
+# Jetty 12 Migration Guide
+
+## Overview
+
+Apache Drill has been upgraded from Jetty 9 to Jetty 12 to address security
vulnerabilities and maintain compatibility with modern Java versions. This
document describes the changes made, known limitations, and guidance for
developers.
+
+## What Changed
+
+### Core API Changes
+
+Jetty 12 introduced significant API changes as part of the Jakarta EE 10
migration:
+
+1. **Servlet API Migration**: `javax.servlet.*` → `jakarta.servlet.*`
+2. **Package Restructuring**: Servlet components moved to
`org.eclipse.jetty.ee10.servlet.*`
+3. **Handler API Redesign**: New `org.eclipse.jetty.server.Handler` interface
+4. **Resource Loading**: New `ResourceFactory` API replaces old `Resource` API
+5. **Authentication APIs**: `LoginService.login()` signature changed
+
+### Modified Files
+
+#### Production Code
+
+-
**exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java**
+ - Updated to use `ResourceFactory.root()` for resource loading
+ - Fixed null pointer issues when HTTP server is disabled
+
+-
**drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java**
+ - Updated all imports to `org.eclipse.jetty.ee10.servlet.*`
+ - Modified `LoginService.login()` to new signature with `Function<Boolean,
Session>` parameter
+ - Changed to use `IdentityService.newUserIdentity()` for user identity
creation
+ - Updated `ResourceFactory` API usage
+ - Updated `SessionAuthentication` constants
+ - Fixed `ServletContextHandler` constructor usage
+
+#### Test Code
+
+The following test classes are temporarily disabled (see Known Limitations
below):
+- `TestImpersonationDisabledWithMiniDFS.java`
+- `TestImpersonationMetadata.java`
+- `TestImpersonationQueries.java`
+- `TestInboundImpersonation.java`
+
+## Known Limitations
+
+### Hadoop MiniDFSCluster Test Incompatibility
+
+**Issue**: Tests using Hadoop's MiniDFSCluster cannot run due to Jetty version
conflicts.
+
+**Root Cause**: Apache Hadoop 3.x depends on Jetty 9, while Drill now uses
Jetty 12. When tests attempt to start both:
+- Drill's embedded web server (Jetty 12)
+- Hadoop's MiniDFSCluster (Jetty 9)
+
+The conflicting Jetty versions on the classpath cause `NoClassDefFoundError`
exceptions.
+
+**Affected Tests**:
+- Impersonation tests with HDFS
+- Any tests requiring MiniDFSCluster with Drill's HTTP server enabled
+
+**Resolution Timeline**: These tests will be re-enabled when:
+- Apache Hadoop 4.x is released with Jetty 12 support
+- A Hadoop 3.x maintenance release upgrades to Jetty 12 (tracked in
[HADOOP-19625](https://issues.apache.org/jira/browse/HADOOP-19625))
+
+**Current Status**: HADOOP-19625 is open and targets Jetty 12 EE10, but
requires Java 17 baseline (tracked in HADOOP-17177). No specific release
version or timeline is available yet.
+
+### Why Alternative Solutions Failed
+
+Several approaches were attempted to resolve the Jetty conflict:
+
+1. **Dual Jetty versions in test scope**: Failed because Maven cannot have two
different versions of the same artifact on the classpath simultaneously, and
the Jetty BOM forces all Jetty artifacts to the same version.
+
+2. **Disabling Drill's HTTP server in tests**: Failed because drill-java-exec
classes are compiled against Jetty 12, and the bytecode contains hard
references to Jetty 12 classes that fail to load even when the HTTP server is
disabled.
+
+3. **Separate test module with Jetty 9**: Failed because depending on the
drill-java-exec JAR (compiled with Jetty 12) brings Jetty 12 class references
into the test classpath.
+
+### Workarounds for Developers
+
+If you need to test HDFS impersonation functionality:
+
+1. **Integration tests**: Use a real Hadoop cluster instead of MiniDFSCluster
+2. **Manual testing**: Test in HDFS-enabled environments
+3. **Alternative tests**: Use tests with local filesystem instead of
MiniDFSCluster (see other impersonation tests that don't require HDFS)
+
+## Developer Guidelines
+
+### Writing New Web Server Code
+
+When adding new HTTP/servlet functionality:
+
+1. Use Jakarta EE 10 imports:
+ ```java
+ import jakarta.servlet.http.HttpServletRequest;
+ import jakarta.servlet.http.HttpServletResponse;
+ ```
+
+2. Use Jetty 12 EE10 servlet packages:
+ ```java
+ import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+ import org.eclipse.jetty.ee10.servlet.ServletHolder;
+ ```
+
+3. Use `ResourceFactory.root()` for resource loading:
+ ```java
+ ResourceFactory rf = ResourceFactory.root();
+ InputStream stream =
rf.newClassLoaderResource("/path/to/resource").newInputStream();
+ ```
+
+4. Use new `ServletContextHandler` constructor pattern:
+ ```java
+ ServletContextHandler handler = new
ServletContextHandler(ServletContextHandler.SESSIONS);
+ handler.setContextPath("/");
+ ```
+
+### Writing Tests
+
+1. Tests that start Drill's HTTP server should **not** use Hadoop
MiniDFSCluster
+2. If HDFS testing is required, use local filesystem or mark test with
`@Ignore` and add comprehensive documentation
+3. When adding `@Ignore` for Jetty conflicts, reference
`TestImpersonationDisabledWithMiniDFS` for standard explanation
+
+### Debugging Jetty Issues
+
+Common issues and solutions:
+
+- **NoClassDefFoundError for Jetty classes**: Check that all Jetty
dependencies are Jetty 12, not Jetty 9
+- **ClassNotFoundException for javax.servlet**: Should be `jakarta.servlet`
with Jetty 12
+- **NullPointerException in ResourceFactory**: Use `ResourceFactory.root()`
instead of `ResourceFactory.of(server)`
Review Comment:
The use of `ResourceFactory.root()` should be avoided at all costs.
It's a very problematic API and is likely going to be deprecated with
suggestions to use the other instantiation methods. (`ResourceFactory.of(...)`,
`ResourceFactory.closable()`, `ResourceFactory.lifecycle()`, etc...)
Pass around the `ResourceFactory` created from a `LifeCycle` or `Container`.
You can optionally pass around the `LifeCycle` or `Container` object itself,
and just use `ResourceFactory.of(container)` or ...
``` java
// As a raw LifeCycle ...
ResourceFactory rf = ResourceFactory.lifecycle();
// Let container manage this child lifecycle
container.addBean(rf);
```
##########
docs/dev/Jetty12Migration.md:
##########
@@ -0,0 +1,183 @@
+# Jetty 12 Migration Guide
+
+## Overview
+
+Apache Drill has been upgraded from Jetty 9 to Jetty 12 to address security
vulnerabilities and maintain compatibility with modern Java versions. This
document describes the changes made, known limitations, and guidance for
developers.
+
+## What Changed
+
+### Core API Changes
+
+Jetty 12 introduced significant API changes as part of the Jakarta EE 10
migration:
+
+1. **Servlet API Migration**: `javax.servlet.*` → `jakarta.servlet.*`
+2. **Package Restructuring**: Servlet components moved to
`org.eclipse.jetty.ee10.servlet.*`
+3. **Handler API Redesign**: New `org.eclipse.jetty.server.Handler` interface
+4. **Resource Loading**: New `ResourceFactory` API replaces old `Resource` API
+5. **Authentication APIs**: `LoginService.login()` signature changed
+
+### Modified Files
+
+#### Production Code
+
+-
**exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java**
+ - Updated to use `ResourceFactory.root()` for resource loading
+ - Fixed null pointer issues when HTTP server is disabled
+
+-
**drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java**
+ - Updated all imports to `org.eclipse.jetty.ee10.servlet.*`
+ - Modified `LoginService.login()` to new signature with `Function<Boolean,
Session>` parameter
+ - Changed to use `IdentityService.newUserIdentity()` for user identity
creation
+ - Updated `ResourceFactory` API usage
+ - Updated `SessionAuthentication` constants
+ - Fixed `ServletContextHandler` constructor usage
+
+#### Test Code
+
+The following test classes are temporarily disabled (see Known Limitations
below):
+- `TestImpersonationDisabledWithMiniDFS.java`
+- `TestImpersonationMetadata.java`
+- `TestImpersonationQueries.java`
+- `TestInboundImpersonation.java`
+
+## Known Limitations
+
+### Hadoop MiniDFSCluster Test Incompatibility
+
+**Issue**: Tests using Hadoop's MiniDFSCluster cannot run due to Jetty version
conflicts.
+
+**Root Cause**: Apache Hadoop 3.x depends on Jetty 9, while Drill now uses
Jetty 12. When tests attempt to start both:
+- Drill's embedded web server (Jetty 12)
+- Hadoop's MiniDFSCluster (Jetty 9)
+
+The conflicting Jetty versions on the classpath cause `NoClassDefFoundError`
exceptions.
+
+**Affected Tests**:
+- Impersonation tests with HDFS
+- Any tests requiring MiniDFSCluster with Drill's HTTP server enabled
+
+**Resolution Timeline**: These tests will be re-enabled when:
+- Apache Hadoop 4.x is released with Jetty 12 support
+- A Hadoop 3.x maintenance release upgrades to Jetty 12 (tracked in
[HADOOP-19625](https://issues.apache.org/jira/browse/HADOOP-19625))
+
+**Current Status**: HADOOP-19625 is open and targets Jetty 12 EE10, but
requires Java 17 baseline (tracked in HADOOP-17177). No specific release
version or timeline is available yet.
+
+### Why Alternative Solutions Failed
+
+Several approaches were attempted to resolve the Jetty conflict:
+
+1. **Dual Jetty versions in test scope**: Failed because Maven cannot have two
different versions of the same artifact on the classpath simultaneously, and
the Jetty BOM forces all Jetty artifacts to the same version.
+
+2. **Disabling Drill's HTTP server in tests**: Failed because drill-java-exec
classes are compiled against Jetty 12, and the bytecode contains hard
references to Jetty 12 classes that fail to load even when the HTTP server is
disabled.
+
+3. **Separate test module with Jetty 9**: Failed because depending on the
drill-java-exec JAR (compiled with Jetty 12) brings Jetty 12 class references
into the test classpath.
+
+### Workarounds for Developers
+
+If you need to test HDFS impersonation functionality:
+
+1. **Integration tests**: Use a real Hadoop cluster instead of MiniDFSCluster
+2. **Manual testing**: Test in HDFS-enabled environments
+3. **Alternative tests**: Use tests with local filesystem instead of
MiniDFSCluster (see other impersonation tests that don't require HDFS)
+
+## Developer Guidelines
+
+### Writing New Web Server Code
+
+When adding new HTTP/servlet functionality:
+
+1. Use Jakarta EE 10 imports:
+ ```java
+ import jakarta.servlet.http.HttpServletRequest;
+ import jakarta.servlet.http.HttpServletResponse;
+ ```
+
+2. Use Jetty 12 EE10 servlet packages:
+ ```java
+ import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+ import org.eclipse.jetty.ee10.servlet.ServletHolder;
+ ```
+
+3. Use `ResourceFactory.root()` for resource loading:
+ ```java
+ ResourceFactory rf = ResourceFactory.root();
+ InputStream stream =
rf.newClassLoaderResource("/path/to/resource").newInputStream();
+ ```
+
+4. Use new `ServletContextHandler` constructor pattern:
+ ```java
+ ServletContextHandler handler = new
ServletContextHandler(ServletContextHandler.SESSIONS);
+ handler.setContextPath("/");
+ ```
+
+### Writing Tests
+
+1. Tests that start Drill's HTTP server should **not** use Hadoop
MiniDFSCluster
+2. If HDFS testing is required, use local filesystem or mark test with
`@Ignore` and add comprehensive documentation
+3. When adding `@Ignore` for Jetty conflicts, reference
`TestImpersonationDisabledWithMiniDFS` for standard explanation
+
+### Debugging Jetty Issues
+
+Common issues and solutions:
+
+- **NoClassDefFoundError for Jetty classes**: Check that all Jetty
dependencies are Jetty 12, not Jetty 9
+- **ClassNotFoundException for javax.servlet**: Should be `jakarta.servlet`
with Jetty 12
Review Comment:
You can use the maven `maven-enforcer-plugin` and it's `enforce` goal with
the `<bannedDependencies>` configuration to ensure that `javax.servlet` and
other EE8 packages and/or classes do not exist in your project. (even
transitively)
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoSecurityHandler.java:
##########
@@ -19,17 +19,17 @@
import org.apache.drill.common.exceptions.DrillException;
import org.apache.drill.exec.server.DrillbitContext;
-import org.eclipse.jetty.util.security.Constraint;
+@SuppressWarnings({"rawtypes", "unchecked"})
public class SpnegoSecurityHandler extends DrillHttpConstraintSecurityHandler {
@Override
public String getImplName() {
- return Constraint.__SPNEGO_AUTH;
+ return "SPNEGO";
Review Comment:
```suggestion
return org.eclipse.jetty.security.Authenticator.SPNEGO_AUTH;
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/RolePrincipal.java:
##########
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.drill.exec.server.rest.auth;
+
+import java.io.Serializable;
+import java.security.Principal;
+
+/**
+ * Role principal implementation for Jetty 11+.
+ * Replaced the removed RolePrincipal from AbstractLoginService.
Review Comment:
This exists as `org.eclipse.jetty.security.RolePrincipal`
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/FormSecurityHandler.java:
##########
@@ -22,12 +22,11 @@
import org.apache.drill.exec.server.DrillbitContext;
import org.apache.drill.exec.server.rest.WebServerConstants;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
-import org.eclipse.jetty.util.security.Constraint;
public class FormSecurityHandler extends DrillHttpConstraintSecurityHandler {
@Override
public String getImplName() {
- return Constraint.__FORM_AUTH;
+ return "FORM";
Review Comment:
```suggestion
return org.eclipse.jetty.security.Authenticator.FORM_AUTH;
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/HttpBasicAuthSecurityHandler.java:
##########
@@ -21,15 +21,14 @@
import org.apache.drill.exec.rpc.security.plain.PlainFactory;
import org.apache.drill.exec.server.DrillbitContext;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
-import org.eclipse.jetty.util.security.Constraint;
/**
* Implement HTTP Basic authentication for REST API access
*/
public class HttpBasicAuthSecurityHandler extends
DrillHttpConstraintSecurityHandler {
@Override
public String getImplName() {
- return Constraint.__BASIC_AUTH;
+ return "BASIC";
Review Comment:
```suggestion
return org.eclipse.jetty.security.Authenticator.BASIC_AUTH;
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoLoginService.java:
##########
@@ -36,32 +39,30 @@
import org.ietf.jgss.Oid;
import javax.security.auth.Subject;
-import javax.servlet.ServletRequest;
import java.io.IOException;
-import java.lang.reflect.Field;
import java.security.Principal;
import java.security.PrivilegedExceptionAction;
import java.util.Base64;
+import java.util.function.Function;
/**
* Custom implementation of DrillSpnegoLoginService to avoid the need of
passing targetName in a config file,
* to include the SPNEGO OID and the way UserIdentity is created.
*/
-public class DrillSpnegoLoginService extends SpnegoLoginService {
+public class DrillSpnegoLoginService implements LoginService {
Review Comment:
There's already a `org.eclipse.jetty.security.SPNEGOLoginService`
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoAuthenticator.java:
##########
@@ -20,165 +20,142 @@
import org.apache.drill.exec.server.rest.WebServerConstants;
import org.apache.parquet.Strings;
+import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.security.AuthenticationState;
import org.eclipse.jetty.security.ServerAuthException;
-import org.eclipse.jetty.security.UserAuthentication;
-import org.eclipse.jetty.security.authentication.DeferredAuthentication;
+import org.eclipse.jetty.security.UserIdentity;
+import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.security.authentication.SessionAuthentication;
-import org.eclipse.jetty.security.authentication.SpnegoAuthenticator;
-import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import java.io.IOException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
/**
- * Custom SpnegoAuthenticator for Drill
+ * Custom SpnegoAuthenticator for Drill - Jetty 12 version
+ *
+ * This class extends LoginAuthenticator and provides SPNEGO authentication
support.
*/
-public class DrillSpnegoAuthenticator extends SpnegoAuthenticator {
+public class DrillSpnegoAuthenticator extends LoginAuthenticator {
Review Comment:
Use `org.eclipse.jetty.security.authentication.SPNEGOAuthenticator`
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java:
##########
@@ -173,23 +177,23 @@ public void setHandler(Handler handler) {
}
@Override
- public void doStop() throws Exception {
+ protected void doStop() throws Exception {
super.doStop();
for (DrillHttpConstraintSecurityHandler securityHandler :
securityHandlers.values()) {
securityHandler.doStop();
}
}
public boolean isSpnegoEnabled() {
- return securityHandlers.containsKey(Constraint.__SPNEGO_AUTH);
+ return securityHandlers.containsKey("SPNEGO");
Review Comment:
```suggestion
return
securityHandlers.containsKey(org.eclipse.jetty.security.Authenticator.SPNEGO_AUTH);
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoAuthenticator.java:
##########
@@ -20,165 +20,142 @@
import org.apache.drill.exec.server.rest.WebServerConstants;
import org.apache.parquet.Strings;
+import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.security.AuthenticationState;
import org.eclipse.jetty.security.ServerAuthException;
-import org.eclipse.jetty.security.UserAuthentication;
-import org.eclipse.jetty.security.authentication.DeferredAuthentication;
+import org.eclipse.jetty.security.UserIdentity;
+import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.security.authentication.SessionAuthentication;
-import org.eclipse.jetty.security.authentication.SpnegoAuthenticator;
-import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import java.io.IOException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
/**
- * Custom SpnegoAuthenticator for Drill
+ * Custom SpnegoAuthenticator for Drill - Jetty 12 version
+ *
+ * This class extends LoginAuthenticator and provides SPNEGO authentication
support.
*/
-public class DrillSpnegoAuthenticator extends SpnegoAuthenticator {
+public class DrillSpnegoAuthenticator extends LoginAuthenticator {
private static final org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger(DrillSpnegoAuthenticator.class);
+ private static final String AUTH_METHOD = "SPNEGO";
- public DrillSpnegoAuthenticator(String authMethod) {
- super(authMethod);
+ public DrillSpnegoAuthenticator() {
+ super();
}
+
/**
- * Updated logic as compared to default implementation in
- * {@link SpnegoAuthenticator#validateRequest(ServletRequest,
ServletResponse, boolean)} to handle below cases:
- * 1) Perform SPNEGO authentication only when spnegoLogin resource is
requested. This helps to avoid authentication
- * for each and every resource which the JETTY provided authenticator
does.
- * 2) Helps to redirect to the target URL after authentication is done
successfully.
- * 3) Clear-Up in memory session information once LogOut is triggered such
that any future request also triggers SPNEGO
- * authentication.
- * @param request
- * @param response
- * @param mandatoryAuth
- * @return
- * @throws ServerAuthException
+ * Jetty 12 validateRequest implementation using core
Request/Response/Callback API.
+ * Handles:
+ * 1) Perform SPNEGO authentication only when spnegoLogin resource is
requested
+ * 2) Redirect to target URL after authentication
+ * 3) Clear session information on logout
*/
@Override
- public Authentication validateRequest(ServletRequest request,
ServletResponse response, boolean mandatoryAuth)
+ public AuthenticationState validateRequest(Request request, Response
response, Callback callback)
throws ServerAuthException {
- final HttpServletRequest req = (HttpServletRequest) request;
- final HttpSession session = req.getSession(true);
- final Authentication authentication = (Authentication)
session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
- final String uri = req.getRequestURI();
+ // Get the servlet request from the core request
+ ServletContextRequest servletContextRequest = Request.as(request,
ServletContextRequest.class);
+ if (servletContextRequest == null) {
+ return AuthenticationState.CHALLENGE;
Review Comment:
Returning CHALLENGE requires you to trigger the challenge.
This also means you have to handle the `callback` properly.
See the implementation at
`org.eclipse.jetty.security.authentication.SPNEGOAuthenticator` and it's
`sendChallenge` method.
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java:
##########
@@ -168,11 +167,11 @@ public class MainLoginPageModel {
}
public boolean isSpnegoEnabled() {
- return authEnabled && configuredMechs.contains(Constraint.__SPNEGO_AUTH);
+ return authEnabled && configuredMechs.contains("SPNEGO");
}
public boolean isFormEnabled() {
- return authEnabled && configuredMechs.contains(Constraint.__FORM_AUTH);
+ return authEnabled && configuredMechs.contains("FORM");
Review Comment:
```suggestion
return authEnabled &&
configuredMechs.contains(org.eclipse.jetty.security.Authenticator.FORM_AUTH);
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java:
##########
@@ -173,23 +177,23 @@ public void setHandler(Handler handler) {
}
@Override
- public void doStop() throws Exception {
+ protected void doStop() throws Exception {
super.doStop();
for (DrillHttpConstraintSecurityHandler securityHandler :
securityHandlers.values()) {
securityHandler.doStop();
}
}
public boolean isSpnegoEnabled() {
- return securityHandlers.containsKey(Constraint.__SPNEGO_AUTH);
+ return securityHandlers.containsKey("SPNEGO");
}
public boolean isFormEnabled() {
- return securityHandlers.containsKey(Constraint.__FORM_AUTH);
+ return securityHandlers.containsKey("FORM");
}
public boolean isBasicEnabled() {
- return securityHandlers.containsKey(Constraint.__BASIC_AUTH);
+ return securityHandlers.containsKey("BASIC");
Review Comment:
```suggestion
return
securityHandlers.containsKey(org.eclipse.jetty.security.Authenticator.BASIC_AUTH);
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/OAuthRequests.java:
##########
@@ -162,7 +162,7 @@ public static Response updateAuthToken(String name, String
code, HttpServletRequ
// Get success page
String successPage = null;
- try (InputStream inputStream =
Resource.newClassPathResource(OAUTH_SUCCESS_PAGE).getInputStream()) {
+ try (InputStream inputStream =
ResourceFactory.root().newClassLoaderResource(OAUTH_SUCCESS_PAGE).newInputStream())
{
Review Comment:
Don't use `ResourceFactory.root()`, use a proper one tied to a `LifeCycle`
or `Container`.
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillErrorHandler.java:
##########
@@ -18,25 +18,49 @@
package org.apache.drill.exec.server.rest.auth;
import org.apache.drill.exec.server.rest.WebServerConstants;
-import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.Callback;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.Writer;
/**
- * Custom ErrorHandler class for Drill's WebServer to have better error
message in case when SPNEGO login failed and
- * what to do next. In all other cases this would use the generic error page.
+ * Custom ErrorHandler class for Drill's WebServer to handle errors
appropriately based on the request type.
+ * - For JSON API endpoints (*.json), returns JSON error responses
+ * - For SPNEGO login failures, provides helpful HTML error page
+ * - For all other cases, returns standard HTML error page
*/
-public class DrillErrorHandler extends ErrorHandler {
+public class DrillErrorHandler extends ErrorPageErrorHandler {
+
+ @Override
+ public boolean handle(Request target, org.eclipse.jetty.server.Response
response, Callback callback) throws Exception {
+ // Check if this is a JSON API request
+ String pathInContext = Request.getPathInContext(target);
+ if (pathInContext != null && pathInContext.endsWith(".json")) {
+ // For JSON API endpoints, return JSON error response instead of HTML
+ response.getHeaders().put("Content-Type", "application/json");
Review Comment:
This is only valid if the request has the appropriate `Accept` header to
allow it.
You should probably look at the `ErrorHandler` implementation and look at
how it's done with the `generateAcceptableResponse` methods.
##########
drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java:
##########
@@ -213,18 +215,20 @@ private void setupStaticResources(
// non-Servlet
// version.)
+ ResourceFactory resourceFactory =
ResourceFactory.of(servletContextHandler);
Review Comment:
:+1: this is the correct way to initialize a `ResourceFactory`.
The `ServletContextHandler` LifeCycle will cleanup the held resources from
that `ResourceFactory` when that `ServletContextHandler` is stopped.
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java:
##########
@@ -168,11 +167,11 @@ public class MainLoginPageModel {
}
public boolean isSpnegoEnabled() {
- return authEnabled && configuredMechs.contains(Constraint.__SPNEGO_AUTH);
+ return authEnabled && configuredMechs.contains("SPNEGO");
Review Comment:
```suggestion
return authEnabled &&
configuredMechs.contains(org.eclipse.jetty.security.Authenticator.SPNEGO_AUTH);
```
##########
exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java:
##########
@@ -443,7 +456,8 @@ private void generateOptionsDescriptionJSFile() throws
IOException {
int numLeftToWrite = options.size();
// Template source Javascript file
- InputStream optionsDescribeTemplateStream =
Resource.newClassPathResource(OPTIONS_DESCRIBE_TEMPLATE_JS).getInputStream();
+ ResourceFactory rf = ResourceFactory.root();
Review Comment:
Avoid using `ResourceFactory.root()` for anything.
Bind it to a valid `LifeCycle` (eg: the `WebAppContext` or the `Server`) to
have the Resources be cleaned up properly when the `LifeCycle` stops.
Eg:
``` java
ResourceFactory rf = webappContext.getResourceFactory();
// or
ResourceFactory rf = ResourceFactory.of(webappContext);
// or
ResourceFactory rf = ResourceFactory.of(server);
```
> Update Jetty to Version 12
> --------------------------
>
> Key: DRILL-8540
> URL: https://issues.apache.org/jira/browse/DRILL-8540
> Project: Apache Drill
> Issue Type: Task
> Components: Web Server
> Affects Versions: 1.22.0
> Reporter: Charles Givre
> Assignee: Charles Givre
> Priority: Major
> Fix For: 1.23.0
>
>
> Drill was using Jetty version 9.X which was EOL some time ago. This PR bumps
> Jetty to version 12. Jetty 12 is not compatible with with Java 11, so this
> PR also requires that Drill remove support for Java 11. At this state, this
> seems reasonable.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)