DRILL-5425: Support HTTP Kerberos auth using SPNEGO closes #1040
Project: http://git-wip-us.apache.org/repos/asf/drill/repo Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/adee4614 Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/adee4614 Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/adee4614 Branch: refs/heads/master Commit: adee46149734908ad20568951c683e49e88a67a3 Parents: e25c58f Author: Sindhuri Rayavaram <srayava...@mapr.com> Authored: Mon Sep 11 16:56:22 2017 -0700 Committer: Arina Ielchiieva <arina.yelchiy...@gmail.com> Committed: Tue Jan 2 15:43:10 2018 +0200 ---------------------------------------------------------------------- .../org/apache/drill/exec/ExecConstants.java | 4 +- .../exec/server/rest/LogInLogOutResources.java | 124 +++++-- .../server/rest/ViewableWithPermissions.java | 21 +- .../drill/exec/server/rest/WebServer.java | 52 ++- .../exec/server/rest/WebServerConstants.java | 45 +++ .../server/rest/auth/AuthDynamicFeature.java | 9 +- .../server/rest/auth/DrillErrorHandler.java | 45 +++ .../DrillHttpConstraintSecurityHandler.java | 61 ++++ .../auth/DrillHttpSecurityHandlerProvider.java | 184 +++++++++++ .../rest/auth/DrillSpnegoAuthenticator.java | 192 +++++++++++ .../rest/auth/DrillSpnegoLoginService.java | 149 +++++++++ .../server/rest/auth/DrillUserPrincipal.java | 6 +- .../server/rest/auth/FormSecurityHanlder.java | 49 +++ .../exec/server/rest/auth/SpnegoConfig.java | 112 +++++++ .../server/rest/auth/SpnegoSecurityHandler.java | 36 ++ .../src/main/resources/drill-module.conf | 4 +- .../src/main/resources/rest/generic.ftl | 2 +- .../src/main/resources/rest/mainLogin.ftl | 32 ++ .../drill/exec/rpc/data/TestBitBitKerberos.java | 3 +- .../drill/exec/rpc/security/KerberosHelper.java | 8 +- .../rpc/user/security/TestUserBitKerberos.java | 2 +- .../security/TestUserBitKerberosEncryption.java | 2 +- .../spnego/TestDrillSpnegoAuthenticator.java | 286 ++++++++++++++++ .../rest/spnego/TestSpnegoAuthentication.java | 326 +++++++++++++++++++ .../server/rest/spnego/TestSpnegoConfig.java | 167 ++++++++++ .../org/apache/drill/exec/rpc/BasicServer.java | 1 + 26 files changed, 1850 insertions(+), 72 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java index 52aa52d..5059b4f 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java @@ -30,7 +30,6 @@ import org.apache.drill.exec.server.options.TypeValidators.PowerOfTwoLongValidat import org.apache.drill.exec.server.options.TypeValidators.RangeDoubleValidator; import org.apache.drill.exec.server.options.TypeValidators.RangeLongValidator; import org.apache.drill.exec.server.options.TypeValidators.StringValidator; -import org.apache.drill.exec.server.options.TypeValidators.MaxWidthValidator; import org.apache.drill.exec.server.options.TypeValidators.AdminUsersValidator; import org.apache.drill.exec.server.options.TypeValidators.AdminUserGroupsValidator; import org.apache.drill.exec.testing.ExecutionControls; @@ -149,6 +148,9 @@ public final class ExecConstants { public static final String HTTP_KEYSTORE_PASSWORD = SSL_KEYSTORE_PASSWORD; public static final String HTTP_TRUSTSTORE_PATH = SSL_TRUSTSTORE_PATH; public static final String HTTP_TRUSTSTORE_PASSWORD = SSL_TRUSTSTORE_PASSWORD; + public static final String HTTP_AUTHENTICATION_MECHANISMS = "drill.exec.http.auth.mechanisms"; + public static final String HTTP_SPNEGO_PRINCIPAL = "drill.exec.http.auth.spnego.principal"; + public static final String HTTP_SPNEGO_KEYTAB = "drill.exec.http.auth.spnego.keytab"; public static final String SYS_STORE_PROVIDER_CLASS = "drill.exec.sys.store.provider.class"; public static final String SYS_STORE_PROVIDER_LOCAL_PATH = "drill.exec.sys.store.provider.local.path"; public static final String SYS_STORE_PROVIDER_LOCAL_ENABLE_WRITE = "drill.exec.sys.store.provider.local.write"; http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java index 20cd6da..34ac4d6 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java @@ -1,4 +1,4 @@ -/** +/* * 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 @@ -17,7 +17,18 @@ */ package org.apache.drill.exec.server.rest; +import org.apache.commons.lang3.StringUtils; +import org.apache.drill.common.config.DrillConfig; +import org.apache.drill.exec.ExecConstants; +import org.apache.drill.exec.rpc.security.AuthStringUtil; +import org.apache.drill.exec.server.rest.auth.AuthDynamicFeature; +import org.apache.drill.exec.work.WorkManager; +import org.eclipse.jetty.security.authentication.FormAuthenticator; +import org.eclipse.jetty.util.security.Constraint; +import org.glassfish.jersey.server.mvc.Viewable; + import javax.annotation.security.PermitAll; +import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -31,33 +42,26 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; - -import org.apache.commons.lang3.StringUtils; -import org.apache.drill.exec.server.rest.auth.AuthDynamicFeature; -import org.eclipse.jetty.security.authentication.FormAuthenticator; -import org.glassfish.jersey.server.mvc.Viewable; - import java.net.URI; import java.net.URLDecoder; +import java.util.Set; -@Path("/") +@Path(WebServerConstants.WEBSERVER_ROOT_PATH) @PermitAll public class LogInLogOutResources { - public static final String REDIRECT_QUERY_PARM = "redirect"; - public static final String LOGIN_RESOURCE = "login"; - @GET - @Path("/login") - @Produces(MediaType.TEXT_HTML) - public Viewable getLoginPage(@Context HttpServletRequest request, @Context HttpServletResponse response, - @Context SecurityContext sc, @Context UriInfo uriInfo, @QueryParam(REDIRECT_QUERY_PARM) String redirect) - throws Exception { - if (AuthDynamicFeature.isUserLoggedIn(sc)) { - // if the user is already login, forward the request to homepage. - request.getRequestDispatcher("/").forward(request, response); - return null; - } + @Inject + WorkManager workManager; + + /** + * Update the destination URI to be redirect URI if specified in the request URL so that after the login is + * successful, request is forwarded to redirect page. + * @param redirect - Redirect parameter in the request URI + * @param request - Http Servlet Request + * @throws Exception + */ + private void updateSessionRedirectInfo(String redirect, HttpServletRequest request) throws Exception { if (!StringUtils.isEmpty(redirect)) { // If the URL has redirect in it, set the redirect URI in session, so that after the login is successful, request // is forwarded to the redirect page. @@ -65,27 +69,99 @@ public class LogInLogOutResources { final URI destURI = UriBuilder.fromUri(URLDecoder.decode(redirect, "UTF-8")).build(); session.setAttribute(FormAuthenticator.__J_URI, destURI.toString()); } + } + + @GET + @Path(WebServerConstants.FORM_LOGIN_RESOURCE_PATH) + @Produces(MediaType.TEXT_HTML) + public Viewable getLoginPage(@Context HttpServletRequest request, @Context HttpServletResponse response, + @Context SecurityContext sc, @Context UriInfo uriInfo, + @QueryParam(WebServerConstants.REDIRECT_QUERY_PARM) String redirect) throws Exception { + + if (AuthDynamicFeature.isUserLoggedIn(sc)) { + // if the user is already login, forward the request to homepage. + request.getRequestDispatcher(WebServerConstants.WEBSERVER_ROOT_PATH).forward(request, response); + return null; + } + updateSessionRedirectInfo(redirect, request); return ViewableWithPermissions.createLoginPage(null); } + @GET + @Path(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH) + @Produces(MediaType.TEXT_HTML) + public Viewable getSpnegoLogin(@Context HttpServletRequest request, @Context HttpServletResponse response, + @Context SecurityContext sc, @Context UriInfo uriInfo, + @QueryParam(WebServerConstants.REDIRECT_QUERY_PARM) String redirect) throws Exception { + if (AuthDynamicFeature.isUserLoggedIn(sc)) { + request.getRequestDispatcher(WebServerConstants.WEBSERVER_ROOT_PATH).forward(request, response); + return null; + } + + final String errorString = "Invalid SPNEGO credentials or SPNEGO is not configured"; + final DrillConfig drillConfig = workManager.getContext().getConfig(); + MainLoginPageModel model = new MainLoginPageModel(errorString, drillConfig); + return ViewableWithPermissions.createMainLoginPage(model); + } + // Request type is POST because POST request which contains the login credentials are invalid and the request is // dispatched here directly. @POST - @Path("/login") + @Path(WebServerConstants.FORM_LOGIN_RESOURCE_PATH) @Produces(MediaType.TEXT_HTML) public Viewable getLoginPageAfterValidationError() { return ViewableWithPermissions.createLoginPage("Invalid username/password credentials."); } @GET - @Path("/logout") + @Path(WebServerConstants.LOGOUT_RESOURCE_PATH) public void logout(@Context HttpServletRequest req, @Context HttpServletResponse resp) throws Exception { final HttpSession session = req.getSession(); if (session != null) { session.invalidate(); } - req.getRequestDispatcher("/").forward(req, resp); + req.getRequestDispatcher(WebServerConstants.WEBSERVER_ROOT_PATH).forward(req, resp); + } + + @GET + @Path(WebServerConstants.MAIN_LOGIN_RESOURCE_PATH) + @Produces(MediaType.TEXT_HTML) + public Viewable getMainLoginPage(@Context HttpServletRequest request, @Context HttpServletResponse response, + @Context SecurityContext sc, @Context UriInfo uriInfo, + @QueryParam(WebServerConstants.REDIRECT_QUERY_PARM) String redirect) throws Exception { + updateSessionRedirectInfo(redirect, request); + final DrillConfig drillConfig = workManager.getContext().getConfig(); + MainLoginPageModel model = new MainLoginPageModel(null, drillConfig); + return ViewableWithPermissions.createMainLoginPage(model); + } + + private class MainLoginPageModel { + + private final String error; + + private final boolean authEnabled; + + private final Set<String> configuredMechs; + + MainLoginPageModel(String error, DrillConfig drillConfig) { + this.error = error; + authEnabled = drillConfig.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED); + configuredMechs = AuthStringUtil.asSet( + drillConfig.getStringList(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS)); + } + + public boolean isSpnegoEnabled() { + return authEnabled && configuredMechs.contains(Constraint.__SPNEGO_AUTH); + } + + public boolean isFormEnabled() { + return authEnabled && configuredMechs.contains(Constraint.__FORM_AUTH); + } + + public String getError() { + return error; + } } } http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java index 73019aa..2eed0b8 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java @@ -1,4 +1,4 @@ -/** +/* * 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 @@ -47,8 +47,8 @@ public class ViewableWithPermissions extends Viewable { * @param model * @return */ - public static Viewable create(final boolean authEnabled, final String templateName, final SecurityContext sc, - final Object model) { + public static Viewable create(final boolean authEnabled, final String templateName, + final SecurityContext sc, final Object model) { return new ViewableWithPermissions(authEnabled, templateName, sc, true, model); } @@ -61,13 +61,18 @@ public class ViewableWithPermissions extends Viewable { return new ViewableWithPermissions(true, "/rest/login.ftl", null, false, errorMsg); } - private ViewableWithPermissions(final boolean authEnabled, final String templateName, final SecurityContext sc, - final boolean showControls, final Object model) throws IllegalArgumentException { + public static Viewable createMainLoginPage(Object mainPageModel) { + return new ViewableWithPermissions(true, "/rest/mainLogin.ftl", null, false, mainPageModel); + } + + private ViewableWithPermissions(final boolean authEnabled, final String templateName, + final SecurityContext sc, final boolean showControls, + final Object model) throws IllegalArgumentException { super(templateName, createModel(authEnabled, sc, showControls, model)); } private static Map<String, Object> createModel(final boolean authEnabled, final SecurityContext sc, - final boolean showControls, final Object pageModel) { + final boolean showControls, final Object pageModel) { final boolean isAdmin = !authEnabled /* when auth is disabled every user is an admin user */ || (showControls && sc.isUserInRole(DrillUserPrincipal.ADMIN_ROLE)); @@ -82,8 +87,8 @@ public class ViewableWithPermissions extends Viewable { .put("showLogin", authEnabled && showControls && !isUserLoggedIn) .put("showLogout", authEnabled && showControls && isUserLoggedIn) .put("loggedInUserName", authEnabled && showControls && - isUserLoggedIn ? sc.getUserPrincipal().getName() : DrillUserPrincipal.ANONYMOUS_USER) - .put("showControls", showControls); + isUserLoggedIn ? sc.getUserPrincipal().getName() + : DrillUserPrincipal.ANONYMOUS_USER).put("showControls", showControls); if (pageModel != null) { mapBuilder.put("model", pageModel); http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java index f0e822f..c702314 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java @@ -28,10 +28,11 @@ import org.apache.drill.common.exceptions.DrillException; import org.apache.drill.exec.ExecConstants; import org.apache.drill.exec.ssl.SSLConfig; import org.apache.drill.exec.exception.DrillbitStartupException; -import org.apache.drill.exec.rpc.security.plain.PlainFactory; import org.apache.drill.exec.server.BootStrapContext; import org.apache.drill.exec.server.Drillbit; +import org.apache.drill.exec.server.rest.auth.DrillErrorHandler; import org.apache.drill.exec.server.rest.auth.DrillRestLoginService; +import org.apache.drill.exec.server.rest.auth.DrillHttpSecurityHandlerProvider; import org.apache.drill.exec.ssl.SSLConfigBuilder; import org.apache.drill.exec.work.WorkManager; import org.bouncycastle.asn1.x500.X500NameBuilder; @@ -114,7 +115,7 @@ public class WebServer implements AutoCloseable { /** * Create Jetty based web server. * - * @param context Bootstrap context. + * @param context Bootstrap context. * @param workManager WorkManager instance. */ public WebServer(final BootStrapContext context, final WorkManager workManager, final Drillbit drillbit) { @@ -136,7 +137,8 @@ public class WebServer implements AutoCloseable { * @return true if impersonation without authentication is enabled, false otherwise */ public static boolean isImpersonationOnlyEnabled(DrillConfig config) { - return !config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED) && config.getBoolean(ExecConstants.IMPERSONATION_ENABLED); + return !config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED) + && config.getBoolean(ExecConstants.IMPERSONATION_ENABLED); } /** @@ -150,11 +152,6 @@ public class WebServer implements AutoCloseable { } final boolean authEnabled = config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED); - if (authEnabled && !context.getAuthProvider().containsFactory(PlainFactory.SIMPLE_NAME)) { - logger.warn("Not starting web server. Currently Drill supports web authentication only through " + - "username/password. But PLAIN mechanism is not configured."); - return; - } port = config.getInt(ExecConstants.HTTP_PORT); boolean portHunt = config.getBoolean(ExecConstants.HTTP_PORT_HUNT); @@ -187,9 +184,10 @@ public class WebServer implements AutoCloseable { } } - private ServletContextHandler createServletContextHandler(final boolean authEnabled) { + private ServletContextHandler createServletContextHandler(final boolean authEnabled) throws DrillbitStartupException { // Add resources - final ErrorHandler errorHandler = new ErrorHandler(); + final ErrorHandler errorHandler = new DrillErrorHandler(); + errorHandler.setShowStacks(true); errorHandler.setShowMessageInTitle(true); @@ -197,7 +195,8 @@ public class WebServer implements AutoCloseable { servletContextHandler.setErrorHandler(errorHandler); servletContextHandler.setContextPath("/"); - final ServletHolder servletHolder = new ServletHolder(new ServletContainer(new DrillRestServer(workManager, servletContextHandler.getServletContext(), drillbit))); + final ServletHolder servletHolder = new ServletHolder(new ServletContainer( + new DrillRestServer(workManager, servletContextHandler.getServletContext(), drillbit))); servletHolder.setInitOrder(1); servletContextHandler.addServlet(servletHolder, "/*"); @@ -207,16 +206,16 @@ public class WebServer implements AutoCloseable { final ServletHolder staticHolder = new ServletHolder("static", DefaultServlet.class); // Get resource URL for Drill static assets, based on where Drill icon is located String drillIconResourcePath = - Resource.newClassPathResource(BASE_STATIC_PATH + DRILL_ICON_RESOURCE_RELATIVE_PATH).getURL().toString(); - staticHolder.setInitParameter( - "resourceBase", - drillIconResourcePath.substring(0, drillIconResourcePath.length() - DRILL_ICON_RESOURCE_RELATIVE_PATH.length())); + Resource.newClassPathResource(BASE_STATIC_PATH + DRILL_ICON_RESOURCE_RELATIVE_PATH).getURL().toString(); + staticHolder.setInitParameter("resourceBase", + drillIconResourcePath.substring(0, drillIconResourcePath.length() - DRILL_ICON_RESOURCE_RELATIVE_PATH.length())); staticHolder.setInitParameter("dirAllowed", "false"); staticHolder.setInitParameter("pathInfoOnly", "true"); servletContextHandler.addServlet(staticHolder, "/static/*"); if (authEnabled) { - servletContextHandler.setSecurityHandler(createSecurityHandler()); + //DrillSecurityHandler is used to support SPNEGO and FORM authentication together + servletContextHandler.setSecurityHandler(new DrillHttpSecurityHandlerProvider(config, workManager.getContext())); servletContextHandler.setSessionHandler(createSessionHandler(servletContextHandler.getSecurityHandler())); } @@ -229,13 +228,13 @@ public class WebServer implements AutoCloseable { if (config.getBoolean(ExecConstants.HTTP_CORS_ENABLED)) { FilterHolder holder = new FilterHolder(CrossOriginFilter.class); holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, - StringUtils.join(config.getStringList(ExecConstants.HTTP_CORS_ALLOWED_ORIGINS), ",")); + StringUtils.join(config.getStringList(ExecConstants.HTTP_CORS_ALLOWED_ORIGINS), ",")); holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, - StringUtils.join(config.getStringList(ExecConstants.HTTP_CORS_ALLOWED_METHODS), ",")); + StringUtils.join(config.getStringList(ExecConstants.HTTP_CORS_ALLOWED_METHODS), ",")); holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, - StringUtils.join(config.getStringList(ExecConstants.HTTP_CORS_ALLOWED_HEADERS), ",")); + StringUtils.join(config.getStringList(ExecConstants.HTTP_CORS_ALLOWED_HEADERS), ",")); holder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, - String.valueOf(config.getBoolean(ExecConstants.HTTP_CORS_CREDENTIALS))); + String.valueOf(config.getBoolean(ExecConstants.HTTP_CORS_CREDENTIALS))); for (String path : new String[]{"*.json", "/storage/*/enable/*", "/status*"}) { servletContextHandler.addFilter(holder, path, EnumSet.of(DispatcherType.REQUEST)); @@ -314,8 +313,7 @@ public class WebServer implements AutoCloseable { if (config.getBoolean(ExecConstants.HTTP_ENABLE_SSL)) { try { serverConnector = createHttpsConnector(port); - } - catch(DrillException e){ + } catch (DrillException e) { throw new DrillbitStartupException(e.getMessage(), e); } } else { @@ -366,11 +364,10 @@ public class WebServer implements AutoCloseable { final DateTime now = DateTime.now(); // Create builder for certificate attributes - final X500NameBuilder nameBuilder = - new X500NameBuilder(BCStyle.INSTANCE) - .addRDN(BCStyle.OU, "Apache Drill (auth-generated)") - .addRDN(BCStyle.O, "Apache Software Foundation (auto-generated)") - .addRDN(BCStyle.CN, workManager.getContext().getEndpoint().getAddress()); + final X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE) + .addRDN(BCStyle.OU, "Apache Drill (auth-generated)") + .addRDN(BCStyle.O, "Apache Software Foundation (auto-generated)") + .addRDN(BCStyle.CN, workManager.getContext().getEndpoint().getAddress()); final Date notBefore = now.minusMinutes(1).toDate(); final Date notAfter = now.plusYears(5).toDate(); @@ -422,6 +419,7 @@ public class WebServer implements AutoCloseable { /** * Create HTTP connector. + * * @return Initialized {@link ServerConnector} instance for HTTP connections. * @throws Exception */ http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java new file mode 100644 index 0000000..5650d43 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java @@ -0,0 +1,45 @@ +/* + * 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; + +/** + * Holds various constants used by WebServer components. + */ +public final class WebServerConstants { + + private WebServerConstants() {} + + public static final String REDIRECT_QUERY_PARM = "redirect"; + public static final String WEBSERVER_ROOT_PATH = "/"; + + // Main Login page which help to choose between Form and Spnego authentication + public static final String MAIN_LOGIN_RESOURCE_NAME = "mainLogin"; + public static final String MAIN_LOGIN_RESOURCE_PATH = WEBSERVER_ROOT_PATH + MAIN_LOGIN_RESOURCE_NAME; + + // Login page for FORM authentication + public static final String FORM_LOGIN_RESOURCE_NAME = "login"; + public static final String FORM_LOGIN_RESOURCE_PATH = WEBSERVER_ROOT_PATH + FORM_LOGIN_RESOURCE_NAME; + + // Login page for SPNEGO authentication + public static final String SPENGO_LOGIN_RESOURCE_NAME = "spnegoLogin"; + public static final String SPENGO_LOGIN_RESOURCE_PATH = WEBSERVER_ROOT_PATH + SPENGO_LOGIN_RESOURCE_NAME; + + // Logout page + public static final String LOGOUT_RESOURCE_NAME = "logout"; + public static final String LOGOUT_RESOURCE_PATH = WEBSERVER_ROOT_PATH + LOGOUT_RESOURCE_NAME; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AuthDynamicFeature.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AuthDynamicFeature.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AuthDynamicFeature.java index bee0c9d..7ca739b 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AuthDynamicFeature.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AuthDynamicFeature.java @@ -17,7 +17,7 @@ */ package org.apache.drill.exec.server.rest.auth; -import org.apache.drill.exec.server.rest.LogInLogOutResources; +import org.apache.drill.exec.server.rest.WebServerConstants; import org.glassfish.jersey.server.model.AnnotatedMethod; import javax.annotation.Priority; @@ -55,6 +55,9 @@ public class AuthDynamicFeature implements DynamicFeature { } // PermitAll takes precedence over RolesAllowed on the class + // This avoids putting AuthCheckFilter in the request flow for all path's which + // are defined under PermitAll annotation. That is requests for "/", "/login", "/mainLogin" and "/spnegoLogin" + // path's doesn't go through AuthCheckFilter. if (am.isAnnotationPresent(PermitAll.class)) { // Do nothing. return; @@ -79,8 +82,8 @@ public class AuthDynamicFeature implements DynamicFeature { final String destResource = URLEncoder.encode(requestContext.getUriInfo().getRequestUri().toString(), "UTF-8"); final URI loginURI = requestContext.getUriInfo().getBaseUriBuilder() - .path(LogInLogOutResources.LOGIN_RESOURCE) - .queryParam(LogInLogOutResources.REDIRECT_QUERY_PARM, destResource) + .path(WebServerConstants.MAIN_LOGIN_RESOURCE_NAME) + .queryParam(WebServerConstants.REDIRECT_QUERY_PARM, destResource) .build(); requestContext.abortWith(Response.temporaryRedirect(loginURI).build() ); http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillErrorHandler.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillErrorHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillErrorHandler.java new file mode 100644 index 0000000..df4825f --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillErrorHandler.java @@ -0,0 +1,45 @@ +/* + * 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 org.apache.drill.exec.server.rest.WebServerConstants; +import org.eclipse.jetty.server.handler.ErrorHandler; + +import javax.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. + */ +public class DrillErrorHandler extends ErrorHandler { + + @Override + protected void writeErrorPageMessage(HttpServletRequest request, Writer writer, + int code, String message, String uri) throws IOException { + + super.writeErrorPageMessage(request, writer, code, message, uri); + + if (uri.equals(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH)) { + writer.write("<p>SPNEGO Login Failed</p>"); + writer.write("<p>Please check the requirements or use below link to use Form Authentication instead</p>"); + writer.write("<a href='/login'> login </a>"); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpConstraintSecurityHandler.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpConstraintSecurityHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpConstraintSecurityHandler.java new file mode 100644 index 0000000..0b095fb --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpConstraintSecurityHandler.java @@ -0,0 +1,61 @@ +/* + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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 com.google.common.collect.ImmutableSet; +import org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.server.DrillbitContext; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.security.authentication.LoginAuthenticator; + +import java.util.Collections; +import java.util.Set; + +import static org.apache.drill.exec.server.rest.auth.DrillUserPrincipal.ADMIN_ROLE; +import static org.apache.drill.exec.server.rest.auth.DrillUserPrincipal.AUTHENTICATED_ROLE; + +/** + * Accessor class that extends the ConstraintSecurityHandler to expose protected method's for start and stop of Handler. + * This is needed since now {@link DrillHttpSecurityHandlerProvider} composes of 2 security handlers - + * For FORM and SPNEGO and has responsibility to start/stop of those handlers. + **/ +public abstract class DrillHttpConstraintSecurityHandler extends ConstraintSecurityHandler { + + @Override + public void doStart() throws Exception { + super.doStart(); + } + + @Override + public void doStop() throws Exception { + super.doStop(); + } + + public abstract void doSetup(DrillbitContext dbContext) throws DrillException; + + public void setup(LoginAuthenticator authenticator, LoginService loginService) { + final Set<String> knownRoles = ImmutableSet.of(AUTHENTICATED_ROLE, ADMIN_ROLE); + setConstraintMappings(Collections.<ConstraintMapping>emptyList(), knownRoles); + setAuthenticator(authenticator); + setLoginService(loginService); + } + + public abstract String getImplName(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java new file mode 100644 index 0000000..3d77596 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillHttpSecurityHandlerProvider.java @@ -0,0 +1,184 @@ +/* + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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 com.google.common.base.Preconditions; +import org.apache.drill.common.config.DrillConfig; +import org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.common.map.CaseInsensitiveMap; +import org.apache.drill.common.scanner.persistence.ScanResult; +import org.apache.drill.exec.ExecConstants; +import org.apache.drill.exec.exception.DrillbitStartupException; +import org.apache.drill.exec.rpc.security.AuthStringUtil; +import org.apache.drill.exec.server.DrillbitContext; +import org.apache.drill.exec.server.rest.WebServerConstants; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.authentication.SessionAuthentication; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.security.Constraint; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + + +public class DrillHttpSecurityHandlerProvider extends ConstraintSecurityHandler { + + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillHttpSecurityHandlerProvider.class); + + private final Map<String, DrillHttpConstraintSecurityHandler> securityHandlers = + CaseInsensitiveMap.newHashMapWithExpectedSize(2); + + public DrillHttpSecurityHandlerProvider(DrillConfig config, DrillbitContext drillContext) + throws DrillbitStartupException { + + Preconditions.checkState(config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED)); + final Set<String> configuredMechanisms = new HashSet<>(); + + if (config.hasPath(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS)) { + configuredMechanisms.addAll(AuthStringUtil.asSet(config.getStringList(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS))); + } else { // for backward compatibility + configuredMechanisms.add(Constraint.__FORM_AUTH); + } + + final ScanResult scan = drillContext.getClasspathScan(); + final Collection<Class<? extends DrillHttpConstraintSecurityHandler>> factoryImpls = + scan.getImplementations(DrillHttpConstraintSecurityHandler.class); + logger.debug("Found DrillHttpConstraintSecurityHandler implementations: {}", factoryImpls); + for (final Class<? extends DrillHttpConstraintSecurityHandler> clazz : factoryImpls) { + + // If all the configured mechanisms handler is added then break out of this loop + if (configuredMechanisms.isEmpty()) { + break; + } + + Constructor<? extends DrillHttpConstraintSecurityHandler> validConstructor = null; + for (final Constructor<?> c : clazz.getConstructors()) { + final Class<?>[] params = c.getParameterTypes(); + if (params.length == 0) { + validConstructor = (Constructor<? extends DrillHttpConstraintSecurityHandler>) c; // unchecked + break; + } + } + + if (validConstructor == null) { + logger.warn("Skipping DrillHttpConstraintSecurityHandler class {}. It must implement at least one" + + " constructor with signature [{}()]", clazz.getCanonicalName(), clazz.getName()); + continue; + } + + try { + final DrillHttpConstraintSecurityHandler instance = validConstructor.newInstance(); + if (configuredMechanisms.remove(instance.getImplName())) { + instance.doSetup(drillContext); + securityHandlers.put(instance.getImplName(), instance); + } + } catch (IllegalArgumentException | ReflectiveOperationException | DrillException e) { + logger.warn(String.format("Failed to create DrillHttpConstraintSecurityHandler of type '%s'", + clazz.getCanonicalName()), e); + } + } + + if (securityHandlers.size() == 0) { + throw new DrillbitStartupException("Authentication is enabled for WebServer but none of the security mechanism " + + "was configured properly. Please verify the configurations and try again."); + } + + logger.info("Configure auth mechanisms for WebServer are: {}", securityHandlers.keySet()); + } + + @Override + public 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 { + + Preconditions.checkState(securityHandlers.size() > 0); + + HttpSession session = request.getSession(true); + SessionAuthentication authentication = + (SessionAuthentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED); + String uri = request.getRequestURI(); + final DrillHttpConstraintSecurityHandler securityHandler; + + // Before authentication, all requests go through the FormAuthenticator if configured except for /spnegoLogin + // request. For SPNEGO authentication all requests will be forced going via /spnegoLogin before authentication is + // done, this is to ensure that we don't have to authenticate same client session multiple times for each resource. + // + // If this authentication is null, user hasn't logged in yet + if (authentication == null) { + + // 1) If only SPNEGOSecurity handler then use SPNEGOSecurity + // 2) If both but uri equals spnegoLogin then use SPNEGOSecurity + // 3) If both but uri doesn't equals spnegoLogin then use FORMSecurity + // 4) If only FORMSecurity handler then use FORMSecurity + if (isSpnegoEnabled() && (!isFormEnabled() || uri.equals(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH))) { + securityHandler = securityHandlers.get(Constraint.__SPNEGO_AUTH); + securityHandler.handle(target, baseRequest, request, response); + } else if (isFormEnabled()) { + securityHandler = securityHandlers.get(Constraint.__FORM_AUTH); + securityHandler.handle(target, baseRequest, request, response); + } + } + // If user has logged in, use the corresponding handler to handle the request + else { + final String authMethod = authentication.getAuthMethod(); + securityHandler = securityHandlers.get(authMethod); + securityHandler.handle(target, baseRequest, request, response); + } + } + + @Override + public void setHandler(Handler handler) { + super.setHandler(handler); + for (DrillHttpConstraintSecurityHandler securityHandler : securityHandlers.values()) { + securityHandler.setHandler(handler); + } + } + + public void doStop() throws Exception { + super.doStop(); + for (DrillHttpConstraintSecurityHandler securityHandler : securityHandlers.values()) { + securityHandler.doStop(); + } + } + + public boolean isSpnegoEnabled() { + return securityHandlers.containsKey(Constraint.__SPNEGO_AUTH); + } + + public boolean isFormEnabled() { + return securityHandlers.containsKey(Constraint.__FORM_AUTH); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoAuthenticator.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoAuthenticator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoAuthenticator.java new file mode 100644 index 0000000..10f21ac --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoAuthenticator.java @@ -0,0 +1,192 @@ +/* + * 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 org.apache.drill.exec.server.rest.WebServerConstants; +import org.apache.parquet.Strings; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.security.UserAuthentication; +import org.eclipse.jetty.security.authentication.DeferredAuthentication; +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.HttpChannel; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.UserIdentity; + +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; + +/** + * Custom SpnegoAuthenticator for Drill + */ +public class DrillSpnegoAuthenticator extends SpnegoAuthenticator { + + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillSpnegoAuthenticator.class); + + public DrillSpnegoAuthenticator(String authMethod) { + super(authMethod); + } + + /** + * 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 + */ + @Override + public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatoryAuth) + 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(); + + // If the Request URI is for /spnegoLogin then perform login + final boolean mandatory = mandatoryAuth || uri.equals(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH); + + // For logout remove the attribute from the session that holds UserIdentity + if (authentication != null) { + if (uri.equals(WebServerConstants.LOGOUT_RESOURCE_PATH)) { + logger.debug("Logging out user {}", req.getRemoteAddr()); + session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); + return null; + } + + // Already logged in so just return the session attribute. + return authentication; + } + + // Try to authenticate an unauthenticated session. + return authenticateSession(request, response, mandatory); + } + + /** + * Method to authenticate a user session using the SPNEGO token passed in AUTHORIZATION header of request. + * @param request + * @param response + * @param mandatory + * @return + * @throws ServerAuthException + */ + private Authentication authenticateSession(ServletRequest request, ServletResponse response, boolean mandatory) + throws ServerAuthException { + + final HttpServletRequest req = (HttpServletRequest) request; + final HttpServletResponse res = (HttpServletResponse) response; + final HttpSession session = req.getSession(true); + + // Defer the authentication if not mandatory. + if (!mandatory) { + return new DeferredAuthentication(this); + } + + // Authentication is mandatory, get the Authorization header + final String header = req.getHeader(HttpHeader.AUTHORIZATION.asString()); + + // Authorization header is null, so send the 401 error code to client along with negotiate header + if (header == null) { + try { + if (DeferredAuthentication.isDeferred(res)) { + return Authentication.UNAUTHENTICATED; + } else { + res.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), HttpHeader.NEGOTIATE.asString()); + res.sendError(401); + logger.debug("DrillSpnegoAuthenticator: Sending challenge to client {}", req.getRemoteAddr()); + return Authentication.SEND_CONTINUE; + } + } catch (IOException e) { + logger.error("DrillSpnegoAuthenticator: Failed while sending challenge to client {}", req.getRemoteAddr(), e); + throw new ServerAuthException(e); + } + } + + // Valid Authorization header received. Get the SPNEGO token sent by client and try to authenticate + logger.debug("DrillSpnegoAuthenticator: Received NEGOTIATE Response back from client {}", req.getRemoteAddr()); + final String negotiateString = HttpHeader.NEGOTIATE.asString(); + + if (header.startsWith(negotiateString)) { + final String spnegoToken = header.substring(negotiateString.length() + 1); + final UserIdentity user = this.login(null, spnegoToken, request); + + //redirect the request to the desired page after successful login + if (user != null) { + String newUri = (String) session.getAttribute("org.eclipse.jetty.security.form_URI"); + if (Strings.isNullOrEmpty(newUri)) { + newUri = req.getContextPath(); + if (Strings.isNullOrEmpty(newUri)) { + newUri = WebServerConstants.WEBSERVER_ROOT_PATH; + } + } + + response.setContentLength(0); + final HttpChannel channel = HttpChannel.getCurrentHttpChannel(); + final Response base_response = channel.getResponse(); + final Request base_request = channel.getRequest(); + final int redirectCode = + base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? 302 : 303; + try { + base_response.sendRedirect(redirectCode, res.encodeRedirectURL(newUri)); + } catch (IOException e) { + logger.error("DrillSpnegoAuthenticator: Failed while using the redirect URL {} from client {}", newUri, + req.getRemoteAddr(), e); + throw new ServerAuthException(e); + } + + logger.debug("DrillSpnegoAuthenticator: Successfully authenticated this client session: {}", + user.getUserPrincipal().getName()); + return new UserAuthentication(this.getAuthMethod(), user); + } + } + + logger.debug("DrillSpnegoAuthenticator: Authentication failed for client session: {}", req.getRemoteAddr()); + return Authentication.UNAUTHENTICATED; + + } + + public UserIdentity login(String username, Object password, ServletRequest request) { + final UserIdentity user = super.login(username, password, request); + + if (user != null) { + final HttpSession session = ((HttpServletRequest) request).getSession(true); + final Authentication cached = new SessionAuthentication(this.getAuthMethod(), user, password); + session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached); + } + + return user; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoLoginService.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoLoginService.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoLoginService.java new file mode 100644 index 0000000..e7fbc16 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillSpnegoLoginService.java @@ -0,0 +1,149 @@ +/* + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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 org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.ExecConstants; +import org.apache.drill.exec.server.DrillbitContext; +import org.apache.drill.exec.server.options.SystemOptionManager; +import org.apache.drill.exec.util.ImpersonationUtil; +import org.apache.hadoop.security.UserGroupInformation; +import org.eclipse.jetty.security.DefaultIdentityService; +import org.eclipse.jetty.security.SpnegoLoginService; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.B64Code; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +import javax.security.auth.Subject; +import java.lang.reflect.Field; +import java.security.Principal; +import java.security.PrivilegedExceptionAction; + +/** + * 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 { + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillSpnegoLoginService.class); + + private static final String TARGET_NAME_FIELD_NAME = "_targetName"; + + private final DrillbitContext drillContext; + + private final SpnegoConfig spnegoConfig; + + private final UserGroupInformation loggedInUgi; + + public DrillSpnegoLoginService(DrillbitContext drillBitContext) throws DrillException { + super(DrillSpnegoLoginService.class.getName()); + setIdentityService(new DefaultIdentityService()); + drillContext = drillBitContext; + + // Load and verify SPNEGO config. Then Login using creds to get an UGI instance + spnegoConfig = new SpnegoConfig(drillBitContext.getConfig()); + spnegoConfig.validateSpnegoConfig(); + loggedInUgi = spnegoConfig.getLoggedInUgi(); + } + + @Override + protected void doStart() throws Exception { + // Override the parent implementation, setting _targetName to be the serverPrincipal + // without the need for a one-line file to do the same thing. + final Field targetNameField = SpnegoLoginService.class.getDeclaredField(TARGET_NAME_FIELD_NAME); + targetNameField.setAccessible(true); + targetNameField.set(this, spnegoConfig.getSpnegoPrincipal()); + } + + @Override + public UserIdentity login(final String username, final Object credentials) { + + UserIdentity identity = null; + try { + identity = loggedInUgi.doAs(new PrivilegedExceptionAction<UserIdentity>() { + @Override + public UserIdentity run() { + return spnegoLogin(credentials); + } + }); + } catch (Exception e) { + logger.error("Failed to login using SPNEGO", e); + } + + return identity; + } + + private UserIdentity spnegoLogin(Object credentials) { + + String encodedAuthToken = (String) credentials; + byte[] authToken = B64Code.decode(encodedAuthToken); + GSSManager manager = GSSManager.getInstance(); + + try { + // Providing both OID's is required here. If we provide only one, + // we're requiring that clients provide us the SPNEGO OID to authenticate via Kerberos. + Oid[] knownOids = new Oid[2]; + knownOids[0] = new Oid("1.3.6.1.5.5.2"); // spnego + knownOids[1] = new Oid("1.2.840.113554.1.2.2"); // kerberos + + GSSName gssName = manager.createName(spnegoConfig.getSpnegoPrincipal(), null); + GSSCredential serverCreds = manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME, + knownOids, GSSCredential.ACCEPT_ONLY); + GSSContext gContext = manager.createContext(serverCreds); + + if (gContext == null) { + logger.debug("SPNEGOUserRealm: failed to establish GSSContext"); + } else { + while (!gContext.isEstablished()) { + authToken = gContext.acceptSecContext(authToken, 0, authToken.length); + } + + if (gContext.isEstablished()) { + String clientName = gContext.getSrcName().toString(); + String role = clientName.substring(clientName.indexOf(64) + 1); + + final SystemOptionManager sysOptions = drillContext.getOptionManager(); + final boolean isAdmin = ImpersonationUtil.hasAdminPrivileges(role, + ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(sysOptions), + ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(sysOptions)); + + final Principal user = new DrillUserPrincipal(clientName, isAdmin); + final Subject subject = new Subject(); + subject.getPrincipals().add(user); + + if (isAdmin) { + return this._identityService.newUserIdentity(subject, user, DrillUserPrincipal.ADMIN_USER_ROLES); + } else { + return this._identityService.newUserIdentity(subject, user, DrillUserPrincipal.NON_ADMIN_USER_ROLES); + } + } + } + } catch (GSSException gsse) { + logger.warn("Caught GSSException trying to authenticate the client", gsse); + } + return null; + } +} + http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java index a21977f..01206c9 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java @@ -38,9 +38,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<RolePrincipal> ADMIN_PRINCIPALS = + ImmutableList.of(new RolePrincipal(AUTHENTICATED_ROLE), new RolePrincipal(ADMIN_ROLE)); - public static final List<RolePrincipal> NON_ADMIN_PRINCIPALS = ImmutableList.of(new RolePrincipal(AUTHENTICATED_ROLE)); + public static final List<RolePrincipal> NON_ADMIN_PRINCIPALS = + ImmutableList.of(new RolePrincipal(AUTHENTICATED_ROLE)); private final String userName; http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/FormSecurityHanlder.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/FormSecurityHanlder.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/FormSecurityHanlder.java new file mode 100644 index 0000000..31d7cec --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/FormSecurityHanlder.java @@ -0,0 +1,49 @@ +/* + * 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 org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.rpc.security.plain.PlainFactory; +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 FormSecurityHanlder extends DrillHttpConstraintSecurityHandler { + //private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FormSecurityHanlder.class); + + @Override + public String getImplName() { + return Constraint.__FORM_AUTH; + } + + @Override + public void doSetup(DrillbitContext dbContext) throws DrillException { + + // Check if PAMAuthenticator is available or not which is required for FORM authentication + if (!dbContext.getAuthProvider().containsFactory(PlainFactory.SIMPLE_NAME)) { + throw new DrillException("FORM mechanism was configured but PLAIN mechanism is not enabled to provide an " + + "authenticator. Please configure user authentication with PLAIN mechanism and authenticator to use " + + "FORM authentication"); + } + + setup(new FormAuthenticator(WebServerConstants.FORM_LOGIN_RESOURCE_PATH, + WebServerConstants.FORM_LOGIN_RESOURCE_PATH, true), new DrillRestLoginService(dbContext)); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoConfig.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoConfig.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoConfig.java new file mode 100644 index 0000000..a64d7de --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoConfig.java @@ -0,0 +1,112 @@ +/* + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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 org.apache.drill.common.config.DrillConfig; +import org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.ExecConstants; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.CommonConfigurationKeys; +import org.apache.hadoop.security.UserGroupInformation; + +public class SpnegoConfig { + + private UserGroupInformation loggedInUgi; + + private final String principal; + + private final String keytab; + + public SpnegoConfig(DrillConfig config) { + + keytab = config.hasPath(ExecConstants.HTTP_SPNEGO_KEYTAB) ? + config.getString(ExecConstants.HTTP_SPNEGO_KEYTAB) : + null; + + principal = config.hasPath(ExecConstants.HTTP_SPNEGO_PRINCIPAL) ? + config.getString(ExecConstants.HTTP_SPNEGO_PRINCIPAL) : + null; + } + + //Reads the SPNEGO principal from the config file + public String getSpnegoPrincipal() { + return principal; + } + + public void validateSpnegoConfig() throws DrillException { + + StringBuilder errorMsg = new StringBuilder(); + + if (principal != null && keytab != null) { + return; + } + + if (principal == null) { + errorMsg.append("\nConfiguration "); + errorMsg.append(ExecConstants.HTTP_SPNEGO_PRINCIPAL); + errorMsg.append(" is not found"); + } + + if (keytab == null) { + errorMsg.append("\nConfiguration "); + errorMsg.append(ExecConstants.HTTP_SPNEGO_KEYTAB); + errorMsg.append(" is not found"); + } + + throw new DrillException(errorMsg.toString()); + } + + public UserGroupInformation getLoggedInUgi() throws DrillException { + + if (loggedInUgi != null) { + return loggedInUgi; + } + loggedInUgi = loginAndReturnUgi(); + return loggedInUgi; + } + + //Performs the Server login to KDC for SPNEGO + private UserGroupInformation loginAndReturnUgi() throws DrillException { + + validateSpnegoConfig(); + + UserGroupInformation ugi; + try { + // Check if security is not enabled and try to set the security parameter to login the principal. + // After the login is performed reset the static UGI state. + if (!UserGroupInformation.isSecurityEnabled()) { + final Configuration newConfig = new Configuration(); + newConfig.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION, + UserGroupInformation.AuthenticationMethod.KERBEROS.toString()); + + UserGroupInformation.setConfiguration(newConfig); + ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(principal, keytab); + + // Reset the original configuration for static UGI + UserGroupInformation.setConfiguration(new Configuration()); + } else { + ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(principal, keytab); + } + } catch (Exception e) { + throw new DrillException(String.format("Login failed for %s with given keytab", principal), e); + } + return ugi; + } +} http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoSecurityHandler.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoSecurityHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoSecurityHandler.java new file mode 100644 index 0000000..9e6acb1 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/SpnegoSecurityHandler.java @@ -0,0 +1,36 @@ +/* + * 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 org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.server.DrillbitContext; +import org.eclipse.jetty.util.security.Constraint; + +public class SpnegoSecurityHandler extends DrillHttpConstraintSecurityHandler { + //private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SpnegoSecurityHandler.class); + + @Override + public String getImplName() { + return Constraint.__SPNEGO_AUTH; + } + + @Override + public void doSetup(DrillbitContext dbContext) throws DrillException { + setup(new DrillSpnegoAuthenticator(getImplName()), new DrillSpnegoLoginService(dbContext)); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/resources/drill-module.conf ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/resources/drill-module.conf b/exec/java-exec/src/main/resources/drill-module.conf index c923e4f..a0cf643 100644 --- a/exec/java-exec/src/main/resources/drill-module.conf +++ b/exec/java-exec/src/main/resources/drill-module.conf @@ -27,6 +27,7 @@ drill { org.apache.drill.exec.physical.impl.RootCreator, org.apache.drill.exec.rpc.user.security.UserAuthenticator, org.apache.drill.exec.rpc.security.AuthenticatorFactory, + org.apache.drill.exec.server.rest.auth.DrillHttpConstraintSecurityHandler, org.apache.drill.exec.store.dfs.FormatPlugin, org.apache.drill.exec.store.StoragePlugin ], @@ -38,7 +39,8 @@ drill { org.apache.drill.exec.physical, org.apache.drill.exec.store, org.apache.drill.exec.rpc.user.security, - org.apache.drill.exec.rpc.security + org.apache.drill.exec.rpc.security, + org.apache.drill.exec.server.rest.auth ] } } http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/resources/rest/generic.ftl ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/resources/rest/generic.ftl b/exec/java-exec/src/main/resources/rest/generic.ftl index 9025adb..d04414f 100644 --- a/exec/java-exec/src/main/resources/rest/generic.ftl +++ b/exec/java-exec/src/main/resources/rest/generic.ftl @@ -77,7 +77,7 @@ </#if> <li><a href="http://drill.apache.org/docs/">Documentation</a> <#if showLogin == true > - <li><a href="/login">Log In</a> + <li><a href="/mainLogin">Log In</a> </#if> <#if showLogout == true > <li><a href="/logout">Log Out (${loggedInUserName})</a> http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/main/resources/rest/mainLogin.ftl ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/resources/rest/mainLogin.ftl b/exec/java-exec/src/main/resources/rest/mainLogin.ftl new file mode 100644 index 0000000..8deb156 --- /dev/null +++ b/exec/java-exec/src/main/resources/rest/mainLogin.ftl @@ -0,0 +1,32 @@ +<#-- 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. --> +<#include "*/generic.ftl"> +<#macro page_head> +</#macro> + +<#macro page_body> + <div class="page-header"> + </div> + <div class="container container-table"> + <div align="center" class="table-responsive"> + <#if model?? && model.isFormEnabled()> + <a href ="/login" class="btn btn-primary"> Login using FORM AUTHENTICATION </a> + </#if> + <#if model?? && model.isSpnegoEnabled()> + <a href = "/spnegoLogin" class="btn btn-primary"> Login using SPNEGO </a> + </#if> + <#if model?? && model.getError()??> + <p style="color:red">${model.getError()}</p></br> + </#if> + </div> + </div> +</#macro> +<@page_html/> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/data/TestBitBitKerberos.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/data/TestBitBitKerberos.java b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/data/TestBitBitKerberos.java index 81b027f..a24b0db 100644 --- a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/data/TestBitBitKerberos.java +++ b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/data/TestBitBitKerberos.java @@ -95,7 +95,8 @@ public class TestBitBitKerberos extends BaseTestQuery { public static void setupTest() throws Exception { final Config config = DrillConfig.create(cloneDefaultTestConfigProperties()); - krbHelper = new KerberosHelper(TestBitBitKerberos.class.getSimpleName()); + + krbHelper = new KerberosHelper(TestBitBitKerberos.class.getSimpleName(), null); krbHelper.setupKdc(dirTestWatcher.getTmpDir()); newConfig = new DrillConfig( http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/security/KerberosHelper.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/security/KerberosHelper.java b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/security/KerberosHelper.java index 451e0aa..8ba4d18 100644 --- a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/security/KerberosHelper.java +++ b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/security/KerberosHelper.java @@ -49,10 +49,14 @@ public class KerberosHelper { private boolean kdcStarted; - public KerberosHelper(final String testName) { + public KerberosHelper(final String testName, String serverShortName) { final String realm = "EXAMPLE.COM"; CLIENT_PRINCIPAL = CLIENT_SHORT_NAME + "@" + realm; - final String serverShortName = System.getProperty("user.name"); + + if (serverShortName == null) { + serverShortName = System.getProperty("user.name"); + } + SERVER_PRINCIPAL = serverShortName + "/" + HOSTNAME + "@" + realm; this.testName = testName; } http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java index d6495e9..065746c 100644 --- a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java +++ b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java @@ -56,7 +56,7 @@ public class TestUserBitKerberos extends BaseTestQuery { @BeforeClass public static void setupTest() throws Exception { - krbHelper = new KerberosHelper(TestUserBitKerberos.class.getSimpleName()); + krbHelper = new KerberosHelper(TestUserBitKerberos.class.getSimpleName(), null); krbHelper.setupKdc(dirTestWatcher.getTmpDir()); // Create a new DrillConfig which has user authentication enabled and authenticator set to http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java index 4f411ae..ac60880 100644 --- a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java +++ b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java @@ -60,7 +60,7 @@ public class TestUserBitKerberosEncryption extends BaseTestQuery { @BeforeClass public static void setupTest() throws Exception { - krbHelper = new KerberosHelper(TestUserBitKerberosEncryption.class.getSimpleName()); + krbHelper = new KerberosHelper(TestUserBitKerberosEncryption.class.getSimpleName(), null); krbHelper.setupKdc(dirTestWatcher.getTmpDir()); // Create a new DrillConfig which has user authentication enabled and authenticator set to