Skip to content

Commit

Permalink
DRILL-4354: Remove sessions in anonymous (auth disabled) WebUI access
Browse files Browse the repository at this point in the history
  • Loading branch information
vkorukanti committed Mar 2, 2016
1 parent ae46d26 commit c98edba
Show file tree
Hide file tree
Showing 13 changed files with 148 additions and 200 deletions.
Expand Up @@ -20,6 +20,7 @@
import org.apache.drill.exec.ExecConstants;
import org.apache.drill.exec.server.rest.auth.AuthDynamicFeature;
import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal.AnonDrillUserPrincipal;
import org.apache.drill.exec.server.rest.profile.ProfileResources;
import org.apache.drill.exec.store.StoragePluginRegistry;
import org.apache.drill.exec.store.sys.PersistentStoreProvider;
Expand All @@ -29,6 +30,7 @@
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
Expand Down Expand Up @@ -57,7 +59,10 @@ public DrillRestServer(final WorkManager workManager) {
register(MultiPartFeature.class);
property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);

if (workManager.getContext().getConfig().getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED)) {
final boolean isAuthEnabled =
workManager.getContext().getConfig().getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED);

if (isAuthEnabled) {
register(LogInLogOutResources.class);
register(AuthDynamicFeature.class);
register(RolesAllowedDynamicFeature.class);
Expand All @@ -82,7 +87,12 @@ protected void configure() {
bind(workManager.getContext().getLpPersistence().getMapper()).to(ObjectMapper.class);
bind(workManager.getContext().getStoreProvider()).to(PersistentStoreProvider.class);
bind(workManager.getContext().getStorage()).to(StoragePluginRegistry.class);
bindFactory(DrillUserPrincipalProvider.class).to(DrillUserPrincipal.class);
bind(new UserAuthEnabled(isAuthEnabled)).to(UserAuthEnabled.class);
if (isAuthEnabled) {
bindFactory(DrillUserPrincipalProvider.class).to(DrillUserPrincipal.class);
} else {
bindFactory(AnonDrillUserPrincipalProvider.class).to(DrillUserPrincipal.class);
}
}
});
}
Expand All @@ -102,4 +112,34 @@ public void dispose(DrillUserPrincipal principal) {
// No-Op
}
}

// Provider which creates and cleanups DrillUserPrincipal for anonymous (auth disabled) mode
public static class AnonDrillUserPrincipalProvider implements Factory<DrillUserPrincipal> {
@Inject WorkManager workManager;

@RequestScoped
@Override
public DrillUserPrincipal provide() {
return new AnonDrillUserPrincipal(workManager.getContext());
}

@Override
public void dispose(DrillUserPrincipal principal) {
// If this worked it would have been clean to free the resources here, but there are various scenarios
// where dispose never gets called due to bugs in jersey.
}
}

// Returns whether auth is enabled or not in config
public static class UserAuthEnabled {
private boolean value;

public UserAuthEnabled(boolean value) {
this.value = value;
}

public boolean get() {
return value;
}
}
}
Expand Up @@ -30,6 +30,7 @@

import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.exec.proto.CoordinationProtos;
import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
import org.apache.drill.exec.work.WorkManager;
import org.glassfish.jersey.server.mvc.Viewable;

Expand All @@ -41,13 +42,14 @@
public class DrillRoot {
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillRoot.class);

@Inject UserAuthEnabled authEnabled;
@Inject WorkManager work;
@Inject SecurityContext sc;

@GET
@Produces(MediaType.TEXT_HTML)
public Viewable getStats() {
return ViewableWithPermissions.create("/rest/index.ftl", sc, getStatsJSON());
return ViewableWithPermissions.create(authEnabled.get(), "/rest/index.ftl", sc, getStatsJSON());
}

@GET
Expand Down
Expand Up @@ -25,6 +25,7 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.SecurityContext;

import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
import org.glassfish.jersey.server.mvc.Viewable;

Expand All @@ -33,12 +34,13 @@
public class MetricsResources {
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MetricsResources.class);

@Inject UserAuthEnabled authEnabled;
@Inject SecurityContext sc;

@GET
@Produces(MediaType.TEXT_HTML)
public Viewable getMetrics() {
return ViewableWithPermissions.create("/rest/metrics/metrics.ftl", sc);
return ViewableWithPermissions.create(authEnabled.get(), "/rest/metrics/metrics.ftl", sc);
}

}
Expand Up @@ -34,7 +34,9 @@
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.drill.exec.client.DrillClient;
import org.apache.drill.exec.memory.BufferAllocator;
import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
import org.apache.drill.exec.work.WorkManager;
import org.glassfish.jersey.server.mvc.Viewable;
Expand All @@ -44,6 +46,7 @@
public class QueryResources {
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(QueryResources.class);

@Inject UserAuthEnabled authEnabled;
@Inject WorkManager work;
@Inject SecurityContext sc;
@Inject DrillUserPrincipal principal;
Expand All @@ -52,16 +55,23 @@ public class QueryResources {
@Path("/query")
@Produces(MediaType.TEXT_HTML)
public Viewable getQuery() {
return ViewableWithPermissions.create("/rest/query/query.ftl", sc);
return ViewableWithPermissions.create(authEnabled.get(), "/rest/query/query.ftl", sc);
}

@POST
@Path("/query.json")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public QueryWrapper.QueryResult submitQueryJSON(QueryWrapper query) throws Exception {
final BufferAllocator allocator = work.getContext().getAllocator();
return query.run(principal.getDrillClient(), allocator);
DrillClient drillClient = null;

try {
final BufferAllocator allocator = work.getContext().getAllocator();
drillClient = principal.getDrillClient();
return query.run(drillClient, allocator);
} finally {
principal.recycleDrillClient(drillClient);
}
}

@POST
Expand All @@ -72,10 +82,10 @@ public Viewable submitQuery(@FormParam("query") String query, @FormParam("queryT
try {
final String trimmedQueryString = CharMatcher.is(';').trimTrailingFrom(query.trim());
final QueryWrapper.QueryResult result = submitQueryJSON(new QueryWrapper(trimmedQueryString, queryType));
return ViewableWithPermissions.create("/rest/query/result.ftl", sc, new TabularResult(result));
return ViewableWithPermissions.create(authEnabled.get(), "/rest/query/result.ftl", sc, new TabularResult(result));
} catch(Exception | Error e) {
logger.error("Query from Web UI Failed", e);
return ViewableWithPermissions.create("/rest/query/errorMessage.ftl", sc, e);
return ViewableWithPermissions.create(authEnabled.get(), "/rest/query/errorMessage.ftl", sc, e);
}
}

Expand Down
Expand Up @@ -35,6 +35,7 @@

import org.apache.drill.exec.server.options.OptionValue;
import org.apache.drill.exec.server.options.OptionValue.Kind;
import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
import org.apache.drill.exec.work.WorkManager;
import org.glassfish.jersey.server.mvc.Viewable;
Expand All @@ -47,14 +48,15 @@
public class StatusResources {
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(StatusResources.class);

@Inject UserAuthEnabled authEnabled;
@Inject WorkManager work;
@Inject SecurityContext sc;

@GET
@Path("/status")
@Produces(MediaType.TEXT_HTML)
public Viewable getStatus() {
return ViewableWithPermissions.create("/rest/status.ftl", sc, "Running!");
return ViewableWithPermissions.create(authEnabled.get(), "/rest/status.ftl", sc, "Running!");
}

@GET
Expand All @@ -74,7 +76,7 @@ public List<OptionWrapper> getSystemOptionsJSON() {
@RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
@Produces(MediaType.TEXT_HTML)
public Viewable getSystemOptions() {
return ViewableWithPermissions.create("/rest/options.ftl", sc, getSystemOptionsJSON());
return ViewableWithPermissions.create(authEnabled.get(), "/rest/options.ftl", sc, getSystemOptionsJSON());
}

@POST
Expand Down
Expand Up @@ -40,6 +40,7 @@

import org.apache.drill.common.exceptions.ExecutionSetupException;
import org.apache.drill.common.logical.StoragePluginConfig;
import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
import org.apache.drill.exec.store.StoragePlugin;
import org.apache.drill.exec.store.StoragePluginRegistry;
import org.glassfish.jersey.server.mvc.Viewable;
Expand All @@ -56,6 +57,7 @@
public class StorageResources {
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(StorageResources.class);

@Inject UserAuthEnabled authEnabled;
@Inject StoragePluginRegistry storage;
@Inject ObjectMapper mapper;
@Inject SecurityContext sc;
Expand Down Expand Up @@ -88,7 +90,7 @@ public List<PluginConfigWrapper> getStoragePluginsJSON() {
@Produces(MediaType.TEXT_HTML)
public Viewable getStoragePlugins() {
List<PluginConfigWrapper> list = getStoragePluginsJSON();
return ViewableWithPermissions.create("/rest/storage/list.ftl", sc, list);
return ViewableWithPermissions.create(authEnabled.get(), "/rest/storage/list.ftl", sc, list);
}

@GET
Expand All @@ -111,7 +113,7 @@ public PluginConfigWrapper getStoragePluginJSON(@PathParam("name") String name)
@Produces(MediaType.TEXT_HTML)
public Viewable getStoragePlugin(@PathParam("name") String name) {
PluginConfigWrapper plugin = getStoragePluginJSON(name);
return ViewableWithPermissions.create("/rest/storage/update.ftl", sc, plugin);
return ViewableWithPermissions.create(authEnabled.get(), "/rest/storage/update.ftl", sc, plugin);
}

@GET
Expand Down
Expand Up @@ -25,6 +25,7 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.SecurityContext;

import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
import org.glassfish.jersey.server.mvc.Viewable;

Expand All @@ -33,12 +34,13 @@
public class ThreadsResources {
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MetricsResources.class);

@Inject UserAuthEnabled authEnabled;
@Inject SecurityContext sc;

@GET
@Produces(MediaType.TEXT_HTML)
public Viewable getMetrics() {
return ViewableWithPermissions.create("/rest/threads/threads.ftl", sc);
return ViewableWithPermissions.create(authEnabled.get(), "/rest/threads/threads.ftl", sc);
}

}
Expand Up @@ -18,7 +18,6 @@
package org.apache.drill.exec.server.rest;

import com.google.common.collect.ImmutableMap;
import org.apache.drill.exec.server.rest.auth.AnonymousAuthenticator;
import org.apache.drill.exec.server.rest.auth.AuthDynamicFeature;
import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
import org.glassfish.jersey.server.mvc.Viewable;
Expand All @@ -37,8 +36,8 @@ public class ViewableWithPermissions extends Viewable {
* @param sc
* @return
*/
public static Viewable create(final String templateName, final SecurityContext sc) {
return new ViewableWithPermissions(templateName, sc, true, null);
public static Viewable create(final boolean authEnabled, final String templateName, final SecurityContext sc) {
return new ViewableWithPermissions(authEnabled, templateName, sc, true, null);
}

/**
Expand All @@ -48,8 +47,9 @@ public static Viewable create(final String templateName, final SecurityContext s
* @param model
* @return
*/
public static Viewable create(final String templateName, final SecurityContext sc, final Object model) {
return new ViewableWithPermissions(templateName, sc, true, 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);
}

/**
Expand All @@ -58,28 +58,30 @@ public static Viewable create(final String templateName, final SecurityContext s
* @return
*/
public static Viewable createLoginPage(final String errorMsg) {
return new ViewableWithPermissions("/rest/login.ftl", null, false, errorMsg);
return new ViewableWithPermissions(true, "/rest/login.ftl", null, false, errorMsg);
}

private ViewableWithPermissions(final String templateName, final SecurityContext sc, final boolean showControls,
final Object model) throws IllegalArgumentException {
super(templateName, createModel(sc, showControls, model));
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 SecurityContext sc, final boolean showControls,
final Object pageModel) {
final boolean isAdmin = showControls && sc.isUserInRole(DrillUserPrincipal.ADMIN_ROLE);
private static Map<String, Object> createModel(final boolean authEnabled, final SecurityContext sc,
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));

final boolean isUserLoggedIn = AuthDynamicFeature.isUserLoggedIn(sc);

final ImmutableMap.Builder<String, Object> mapBuilder = ImmutableMap.<String, Object>builder()
.put("showStorage", isAdmin)
.put("showOptions", isAdmin)
.put("showThreads", isAdmin)
.put("showLogin", showControls && !isUserLoggedIn)
.put("showLogout", showControls && isUserLoggedIn &&
!AnonymousAuthenticator.METHOD.equals(sc.getAuthenticationScheme()))
.put("loggedInUserName", showControls && isUserLoggedIn ? sc.getUserPrincipal().getName() : "anonymous")
.put("showLogin", authEnabled && showControls && !isUserLoggedIn)
.put("showLogout", authEnabled && showControls && isUserLoggedIn)
.put("loggedInUserName", authEnabled && showControls &&
isUserLoggedIn ? sc.getUserPrincipal().getName() : DrillUserPrincipal.ANONYMOUS_USER)
.put("showControls", showControls);

if (pageModel != null) {
Expand Down
Expand Up @@ -25,9 +25,6 @@
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.exec.ExecConstants;
import org.apache.drill.exec.server.DrillbitContext;
import org.apache.drill.exec.server.rest.auth.AnonymousAuthenticator;
import org.apache.drill.exec.server.rest.auth.AnonymousLoginService;
import org.apache.drill.exec.server.rest.auth.DrillRestLoginService;
import org.apache.drill.exec.work.WorkManager;
import org.bouncycastle.asn1.x500.X500NameBuilder;
Expand Down Expand Up @@ -149,8 +146,10 @@ public void start() throws Exception {
staticHolder.setInitParameter("pathInfoOnly", "true");
servletContextHandler.addServlet(staticHolder, "/static/*");

servletContextHandler.setSecurityHandler(createSecurityHandler());
servletContextHandler.setSessionHandler(createSessionHandler(servletContextHandler.getSecurityHandler()));
if (config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED)) {
servletContextHandler.setSecurityHandler(createSecurityHandler());
servletContextHandler.setSessionHandler(createSessionHandler(servletContextHandler.getSecurityHandler()));
}

embeddedJetty.start();
}
Expand Down Expand Up @@ -190,25 +189,13 @@ public void sessionDestroyed(HttpSessionEvent se) {
* @return {@link SecurityHandler} with appropriate {@link LoginService}, {@link Authenticator} and constraints.
*/
private ConstraintSecurityHandler createSecurityHandler() {
final LoginService loginService;
final Authenticator authenticator;

final DrillbitContext drillbitContext = workManager.getContext();
if (config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED)) {
loginService = new DrillRestLoginService(drillbitContext);
authenticator = new FormAuthenticator("/login", "/login", true);
} else {
loginService = new AnonymousLoginService(drillbitContext);
authenticator = new AnonymousAuthenticator();
}

ConstraintSecurityHandler security = new ConstraintSecurityHandler();

Set<String> knownRoles = ImmutableSet.of(AUTHENTICATED_ROLE, ADMIN_ROLE);
security.setConstraintMappings(Collections.<ConstraintMapping>emptyList(), knownRoles);

security.setAuthenticator(authenticator);
security.setLoginService(loginService);
security.setAuthenticator(new FormAuthenticator("/login", "/login", true));
security.setLoginService(new DrillRestLoginService(workManager.getContext()));

return security;
}
Expand Down

0 comments on commit c98edba

Please sign in to comment.