drill-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject drill git commit: DRILL-3201: Support security for access through Web UI
Date Tue, 12 Jan 2016 01:26:32 GMT
Repository: drill
Updated Branches:
  refs/heads/master 392d1f7e9 -> 9dad9da6d


DRILL-3201: Support security for access through Web UI


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/9dad9da6
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/9dad9da6
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/9dad9da6

Branch: refs/heads/master
Commit: 9dad9da6d012c1ad913ebf99755dc6cb8ae1e3bb
Parents: 392d1f7
Author: vkorukanti <venki.korukanti@gmail.com>
Authored: Sun Sep 13 00:44:56 2015 -0700
Committer: vkorukanti <venki@stealthsec.com>
Committed: Mon Jan 11 08:05:54 2016 -0800

----------------------------------------------------------------------
 .../src/resources/drill-override-example.conf   |   1 +
 .../org/apache/drill/exec/ExecConstants.java    |   1 +
 .../drill/exec/server/rest/DrillRestServer.java |  29 ++++++
 .../drill/exec/server/rest/DrillRoot.java       |   9 +-
 .../exec/server/rest/LogInLogOutResources.java  |  91 +++++++++++++++++
 .../exec/server/rest/MetricsResources.java      |   9 +-
 .../drill/exec/server/rest/QueryResources.java  |  28 +++--
 .../drill/exec/server/rest/QueryWrapper.java    |  33 +++---
 .../drill/exec/server/rest/StatusResources.java |  19 +++-
 .../exec/server/rest/StorageResources.java      |  20 ++--
 .../exec/server/rest/ThreadsResources.java      |   9 +-
 .../server/rest/ViewableWithPermissions.java    |  91 +++++++++++++++++
 .../drill/exec/server/rest/WebServer.java       |  67 +++++++++++-
 .../rest/auth/AbstractDrillLoginService.java    |  95 +++++++++++++++++
 .../rest/auth/AnonymousAuthenticator.java       |  84 +++++++++++++++
 .../server/rest/auth/AnonymousLoginService.java |  62 +++++++++++
 .../server/rest/auth/AuthDynamicFeature.java    | 102 +++++++++++++++++++
 .../server/rest/auth/DrillRestLoginService.java |  88 ++++++++++++++++
 .../server/rest/auth/DrillUserPrincipal.java    |  93 +++++++++++++++++
 .../server/rest/profile/ProfileResources.java   |  77 ++++++++++----
 .../src/main/resources/drill-module.conf        |   1 +
 .../src/main/resources/rest/generic.ftl         |  14 +++
 .../java-exec/src/main/resources/rest/login.ftl |  37 +++++++
 .../rest/static/img/apache-drill-logo.png       | Bin 0 -> 29802 bytes
 24 files changed, 979 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/distribution/src/resources/drill-override-example.conf
----------------------------------------------------------------------
diff --git a/distribution/src/resources/drill-override-example.conf b/distribution/src/resources/drill-override-example.conf
index 6dbab3d..bc77245 100644
--- a/distribution/src/resources/drill-override-example.conf
+++ b/distribution/src/resources/drill-override-example.conf
@@ -84,6 +84,7 @@ drill.exec: {
     enabled: true,
     ssl_enabled: false,
     port: 8047
+    session_max_idle_secs: 3600 # Default value 1hr
   },
   functions: ["org.apache.drill.expr.fn.impl"],
   network: {

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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 0198da8..c5a5063 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
@@ -75,6 +75,7 @@ public interface ExecConstants {
   String HTTP_ENABLE = "drill.exec.http.enabled";
   String HTTP_PORT = "drill.exec.http.port";
   String HTTP_ENABLE_SSL = "drill.exec.http.ssl_enabled";
+  String HTTP_SESSION_MAX_IDLE_SECS = "drill.exec.http.session_max_idle_secs";
   String HTTP_KEYSTORE_PATH = "javax.net.ssl.keyStore";
   String HTTP_KEYSTORE_PASSWORD = "javax.net.ssl.keyStorePassword";
   String HTTP_TRUSTSTORE_PATH = "javax.net.ssl.trustStore";

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
index 7d2dfe8..300c617 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
@@ -17,16 +17,21 @@
  */
 package org.apache.drill.exec.server.rest;
 
+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.profile.ProfileResources;
 import org.apache.drill.exec.store.StoragePluginRegistry;
 import org.apache.drill.exec.store.sys.PStoreProvider;
 import org.apache.drill.exec.work.WorkManager;
+import org.glassfish.hk2.api.Factory;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
 import org.glassfish.jersey.media.multipart.MultiPartFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
 import org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -34,6 +39,9 @@ import com.fasterxml.jackson.jaxrs.base.JsonMappingExceptionMapper;
 import com.fasterxml.jackson.jaxrs.base.JsonParseExceptionMapper;
 import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
 
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+
 public class DrillRestServer extends ResourceConfig {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillRestServer.class);
 
@@ -49,6 +57,11 @@ public class DrillRestServer extends ResourceConfig {
     register(MultiPartFeature.class);
     property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
 
+    if (workManager.getContext().getConfig().getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED)) {
+      register(LogInLogOutResources.class);
+      register(AuthDynamicFeature.class);
+      register(RolesAllowedDynamicFeature.class);
+    }
 
     //disable moxy so it doesn't conflict with jackson.
     final String disableMoxy = PropertiesHelper.getPropertyNameForRuntime(CommonProperties.MOXY_JSON_FEATURE_DISABLE, getConfiguration().getRuntimeType());
@@ -69,8 +82,24 @@ public class DrillRestServer extends ResourceConfig {
         bind(workManager.getContext().getLpPersistence().getMapper()).to(ObjectMapper.class);
         bind(workManager.getContext().getPersistentStoreProvider()).to(PStoreProvider.class);
         bind(workManager.getContext().getStorage()).to(StoragePluginRegistry.class);
+        bindFactory(DrillUserPrincipalProvider.class).to(DrillUserPrincipal.class);
       }
     });
   }
 
+  // Provider which injects DrillUserPrincipal directly instead of getting it from SecurityContext and typecasting
+  public static class DrillUserPrincipalProvider implements Factory<DrillUserPrincipal> {
+
+    @Inject HttpServletRequest request;
+
+    @Override
+    public DrillUserPrincipal provide() {
+      return (DrillUserPrincipal) request.getUserPrincipal();
+    }
+
+    @Override
+    public void dispose(DrillUserPrincipal principal) {
+      // No-Op
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
index 3e972b4..2e18a74 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
@@ -19,11 +19,13 @@ package org.apache.drill.exec.server.rest;
 
 import java.util.List;
 
+import javax.annotation.security.PermitAll;
 import javax.inject.Inject;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.apache.drill.common.config.DrillConfig;
@@ -35,16 +37,17 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.google.common.collect.Lists;
 
 @Path("/")
+@PermitAll
 public class DrillRoot {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillRoot.class);
 
-  @Inject
-  WorkManager work;
+  @Inject WorkManager work;
+  @Inject SecurityContext sc;
 
   @GET
   @Produces(MediaType.TEXT_HTML)
   public Viewable getStats() {
-    return new Viewable("/rest/index.ftl", getStatsJSON());
+    return ViewableWithPermissions.create("/rest/index.ftl", sc, getStatsJSON());
   }
 
   @GET

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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
new file mode 100644
index 0000000..20cd6da
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/LogInLogOutResources.java
@@ -0,0 +1,91 @@
+/**
+ * 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;
+
+import javax.annotation.security.PermitAll;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+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;
+
+@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;
+    }
+
+    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.
+      final HttpSession session = request.getSession(true);
+      final URI destURI = UriBuilder.fromUri(URLDecoder.decode(redirect, "UTF-8")).build();
+      session.setAttribute(FormAuthenticator.__J_URI, destURI.toString());
+    }
+
+    return ViewableWithPermissions.createLoginPage(null);
+  }
+
+  // Request type is POST because POST request which contains the login credentials are invalid and the request is
+  // dispatched here directly.
+  @POST
+  @Path("/login")
+  @Produces(MediaType.TEXT_HTML)
+  public Viewable getLoginPageAfterValidationError() {
+    return ViewableWithPermissions.createLoginPage("Invalid username/password credentials.");
+  }
+
+  @GET
+  @Path("/logout")
+  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);
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/MetricsResources.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/MetricsResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/MetricsResources.java
index 28a292b..de8523f 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/MetricsResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/MetricsResources.java
@@ -17,21 +17,28 @@
  */
 package org.apache.drill.exec.server.rest;
 
+import javax.annotation.security.RolesAllowed;
+import javax.inject.Inject;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 
+import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
 import org.glassfish.jersey.server.mvc.Viewable;
 
 @Path("/metrics")
+@RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
 public class MetricsResources {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MetricsResources.class);
 
+  @Inject SecurityContext sc;
+
   @GET
   @Produces(MediaType.TEXT_HTML)
   public Viewable getMetrics() {
-    return new Viewable("/rest/metrics/metrics.ftl");
+    return ViewableWithPermissions.create("/rest/metrics/metrics.ftl", sc);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
index 1978cd8..c7f450e 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
@@ -20,6 +20,7 @@ package org.apache.drill.exec.server.rest;
 import java.util.List;
 import java.util.Map;
 
+import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
@@ -28,28 +29,30 @@ import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 
 import com.google.common.base.CharMatcher;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
-import org.apache.drill.common.config.DrillConfig;
-import org.apache.drill.exec.coord.ClusterCoordinator;
 import org.apache.drill.exec.memory.BufferAllocator;
+import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
 import org.apache.drill.exec.work.WorkManager;
 import org.glassfish.jersey.server.mvc.Viewable;
 
 @Path("/")
+@RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
 public class QueryResources {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(QueryResources.class);
 
-  @Inject
-  WorkManager work;
+  @Inject WorkManager work;
+  @Inject SecurityContext sc;
+  @Inject DrillUserPrincipal principal;
 
   @GET
   @Path("/query")
   @Produces(MediaType.TEXT_HTML)
   public Viewable getQuery() {
-    return new Viewable("/rest/query/query.ftl");
+    return ViewableWithPermissions.create("/rest/query/query.ftl", sc);
   }
 
   @POST
@@ -57,10 +60,8 @@ public class QueryResources {
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   public QueryWrapper.QueryResult submitQueryJSON(QueryWrapper query) throws Exception {
-    final DrillConfig config = work.getContext().getConfig();
-    final ClusterCoordinator coordinator = work.getContext().getClusterCoordinator();
     final BufferAllocator allocator = work.getContext().getAllocator();
-    return query.run(config, coordinator, allocator);
+    return query.run(principal.getDrillClient(), allocator);
   }
 
   @POST
@@ -69,12 +70,12 @@ public class QueryResources {
   @Produces(MediaType.TEXT_HTML)
   public Viewable submitQuery(@FormParam("query") String query, @FormParam("queryType") String queryType) throws Exception {
     try {
-      String trimmedQueryString = CharMatcher.is(';').trimTrailingFrom(query.trim());
+      final String trimmedQueryString = CharMatcher.is(';').trimTrailingFrom(query.trim());
       final QueryWrapper.QueryResult result = submitQueryJSON(new QueryWrapper(trimmedQueryString, queryType));
-      return new Viewable("/rest/query/result.ftl", new TabularResult(result));
+      return ViewableWithPermissions.create("/rest/query/result.ftl", sc, new TabularResult(result));
     } catch(Exception | Error e) {
       logger.error("Query from Web UI Failed", e);
-      return new Viewable("/rest/query/errorMessage.ftl", e);
+      return ViewableWithPermissions.create("/rest/query/errorMessage.ftl", sc, e);
     }
   }
 
@@ -96,11 +97,6 @@ public class QueryResources {
       this.rows = rows;
     }
 
-    public TabularResult(List<String> columns, List<List<String>> rows) {
-      this.columns = columns;
-      this.rows = rows;
-    }
-
     public boolean isEmpty() {
       return columns.isEmpty();
     }

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryWrapper.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryWrapper.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryWrapper.java
index 0ca8e74..f67c002 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryWrapper.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryWrapper.java
@@ -30,10 +30,8 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
-import org.apache.drill.common.config.DrillConfig;
 import org.apache.drill.common.exceptions.UserException;
 import org.apache.drill.exec.client.DrillClient;
-import org.apache.drill.exec.coord.ClusterCoordinator;
 import org.apache.drill.exec.exception.SchemaChangeException;
 import org.apache.drill.exec.memory.BufferAllocator;
 import org.apache.drill.exec.proto.UserBitShared;
@@ -80,27 +78,22 @@ public class QueryWrapper {
     return type;
   }
 
-  public QueryResult run(DrillConfig config, ClusterCoordinator coordinator, BufferAllocator allocator)
-    throws Exception {
-    try(DrillClient client = new DrillClient(config, coordinator, allocator)){
-      Listener listener = new Listener(allocator);
-
-      client.connect();
-      client.runQuery(getType(), query, listener);
+  public QueryResult run(final DrillClient client, final BufferAllocator allocator) throws Exception {
+    Listener listener = new Listener(allocator);
+    client.runQuery(getType(), query, listener);
+    listener.waitForCompletion();
+    if (listener.results.isEmpty()) {
+      listener.results.add(Maps.<String, String>newHashMap());
+    }
 
-      listener.waitForCompletion();
-      if (listener.results.isEmpty()) {
-        listener.results.add(Maps.<String, String>newHashMap());
-      }
-      final Map<String, String> first = listener.results.get(0);
-      for (String columnName : listener.columns) {
-        if (!first.containsKey(columnName)) {
-          first.put(columnName, null);
-        }
+    final Map<String, String> first = listener.results.get(0);
+    for (String columnName : listener.columns) {
+      if (!first.containsKey(columnName)) {
+        first.put(columnName, null);
       }
-
-      return new QueryResult(listener.columns, listener.results);
     }
+
+    return new QueryResult(listener.columns, listener.results);
   }
 
   public static class QueryResult {

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
index c99c49b..d3e6107 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
@@ -20,6 +20,8 @@ package org.apache.drill.exec.server.rest;
 import java.util.LinkedList;
 import java.util.List;
 
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
@@ -28,33 +30,38 @@ import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.apache.drill.exec.server.options.OptionValue;
 import org.apache.drill.exec.server.options.OptionValue.Kind;
+import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
 import org.apache.drill.exec.work.WorkManager;
 import org.glassfish.jersey.server.mvc.Viewable;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
+import static org.apache.drill.exec.server.rest.auth.DrillUserPrincipal.ADMIN_ROLE;
+
 @Path("/")
+@PermitAll
 public class StatusResources {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(StatusResources.class);
 
-  @Inject
-  WorkManager work;
+  @Inject WorkManager work;
+  @Inject SecurityContext sc;
 
   @GET
   @Path("/status")
   @Produces(MediaType.TEXT_HTML)
   public Viewable getStatus() {
-    String status = "Running!";
-    return new Viewable("/rest/status.ftl", status);
+    return ViewableWithPermissions.create("/rest/status.ftl", sc, "Running!");
   }
 
   @GET
   @Path("/options.json")
+  @RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
   @Produces(MediaType.APPLICATION_JSON)
   public List getSystemOptionsJSON() {
     List<OptionWrapper> options = new LinkedList<>();
@@ -66,13 +73,15 @@ public class StatusResources {
 
   @GET
   @Path("/options")
+  @RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
   @Produces(MediaType.TEXT_HTML)
   public Viewable getSystemOptions() {
-    return new Viewable("/rest/options.ftl", getSystemOptionsJSON());
+    return ViewableWithPermissions.create("/rest/options.ftl", sc, getSystemOptionsJSON());
   }
 
   @POST
   @Path("/option/{optionName}")
+  @RolesAllowed(DrillUserPrincipal.ADMIN_ROLE)
   @Consumes("application/x-www-form-urlencoded")
   @Produces(MediaType.TEXT_HTML)
   public Viewable updateSystemOption(@FormParam("name") String name, @FormParam("value") String value,

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java
index 1cff961..2af9cac 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java
@@ -24,6 +24,7 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 
+import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -34,6 +35,7 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.apache.drill.common.exceptions.ExecutionSetupException;
@@ -47,18 +49,18 @@ import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.Lists;
 
+import static org.apache.drill.exec.server.rest.auth.DrillUserPrincipal.ADMIN_ROLE;
+
 @Path("/")
+@RolesAllowed(ADMIN_ROLE)
 public class StorageResources {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(StorageResources.class);
 
-  @Inject
-  StoragePluginRegistry storage;
-//  @Inject
-//  PStoreProvider storeProvider;
-  @Inject
-  ObjectMapper mapper;
+  @Inject StoragePluginRegistry storage;
+  @Inject ObjectMapper mapper;
+  @Inject SecurityContext sc;
 
-  static final Comparator<PluginConfigWrapper> PLUGIN_COMPARATOR = new Comparator<PluginConfigWrapper>() {
+  private static final Comparator<PluginConfigWrapper> PLUGIN_COMPARATOR = new Comparator<PluginConfigWrapper>() {
     @Override
     public int compare(PluginConfigWrapper o1, PluginConfigWrapper o2) {
       return o1.getName().compareTo(o2.getName());
@@ -86,7 +88,7 @@ public class StorageResources {
   @Produces(MediaType.TEXT_HTML)
   public Viewable getStoragePlugins() {
     List<PluginConfigWrapper> list = getStoragePluginsJSON();
-    return new Viewable("/rest/storage/list.ftl", list);
+    return ViewableWithPermissions.create("/rest/storage/list.ftl", sc, list);
   }
 
   @GET
@@ -109,7 +111,7 @@ public class StorageResources {
   @Produces(MediaType.TEXT_HTML)
   public Viewable getStoragePlugin(@PathParam("name") String name) {
     PluginConfigWrapper plugin = getStoragePluginJSON(name);
-    return new Viewable("/rest/storage/update.ftl", plugin);
+    return ViewableWithPermissions.create("/rest/storage/update.ftl", sc, plugin);
   }
 
   @GET

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ThreadsResources.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ThreadsResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ThreadsResources.java
index def5acb..ae2e3a0 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ThreadsResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ThreadsResources.java
@@ -17,21 +17,28 @@
  */
 package org.apache.drill.exec.server.rest;
 
+import javax.annotation.security.RolesAllowed;
+import javax.inject.Inject;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 
+import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
 import org.glassfish.jersey.server.mvc.Viewable;
 
 @Path("/threads")
+@RolesAllowed(DrillUserPrincipal.ADMIN_ROLE)
 public class ThreadsResources {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MetricsResources.class);
 
+  @Inject SecurityContext sc;
+
   @GET
   @Produces(MediaType.TEXT_HTML)
   public Viewable getMetrics() {
-    return new Viewable("/rest/threads/threads.ftl");
+    return ViewableWithPermissions.create("/rest/threads/threads.ftl", sc);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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
new file mode 100644
index 0000000..7e20e5c
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ViewableWithPermissions.java
@@ -0,0 +1,91 @@
+/**
+ * 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;
+
+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;
+
+import javax.ws.rs.core.SecurityContext;
+import java.util.Map;
+
+/**
+ * Overrides {@link Viewable} to create a model which contains additional info of what control to display in menubar.
+ */
+public class ViewableWithPermissions extends Viewable {
+
+  /**
+   * Create the web page using the given template and {@link SecurityContext} after authentication is done.
+   * @param templateName
+   * @param sc
+   * @return
+   */
+  public static Viewable create(final String templateName, final SecurityContext sc) {
+    return new ViewableWithPermissions(templateName, sc, true, null);
+  }
+
+  /**
+   * Create a web page using the given template, {@link SecurityContext} and model data.
+   * @param templateName
+   * @param sc
+   * @param model
+   * @return
+   */
+  public static Viewable create(final String templateName, final SecurityContext sc, final Object model) {
+    return new ViewableWithPermissions(templateName, sc, true, model);
+  }
+
+  /**
+   * Create a login page.
+   * @param errorMsg Optional error messages to be shown in case when the page is requested after login attempt failure.
+   * @return
+   */
+  public static Viewable createLoginPage(final String errorMsg) {
+    return new ViewableWithPermissions("/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 static Map<String, Object> createModel(final SecurityContext sc, final boolean showControls,
+      final Object pageModel) {
+    final boolean isAdmin = 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("showControls", showControls);
+
+    if (pageModel != null) {
+      mapBuilder.put("model", pageModel);
+    }
+
+    return mapBuilder.build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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 802d5cd..358e847 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
@@ -21,9 +21,14 @@ import com.codahale.metrics.MetricRegistry;
 import com.codahale.metrics.servlets.MetricsServlet;
 import com.codahale.metrics.servlets.ThreadDumpServlet;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
 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;
 import org.bouncycastle.asn1.x500.style.BCStyle;
@@ -33,13 +38,22 @@ import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
 import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.security.Authenticator;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.LoginService;
+import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.security.authentication.FormAuthenticator;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SessionManager;
 import org.eclipse.jetty.server.SslConnectionFactory;
 import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.server.session.HashSessionManager;
+import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
@@ -54,7 +68,12 @@ import java.security.KeyPairGenerator;
 import java.security.KeyStore;
 import java.security.SecureRandom;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
 import java.util.Date;
+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;
 
 /**
  * Wrapper class around jetty based webserver.
@@ -107,8 +126,7 @@ public class WebServer implements AutoCloseable {
     errorHandler.setShowStacks(true);
     errorHandler.setShowMessageInTitle(true);
 
-    final ServletContextHandler servletContextHandler =
-        new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+    final ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
     servletContextHandler.setErrorHandler(errorHandler);
     servletContextHandler.setContextPath("/");
     embeddedJetty.setHandler(servletContextHandler);
@@ -123,14 +141,53 @@ public class WebServer implements AutoCloseable {
 
     final ServletHolder staticHolder = new ServletHolder("static", DefaultServlet.class);
     staticHolder.setInitParameter("resourceBase", Resource.newClassPathResource("/rest/static").toString());
-    staticHolder.setInitParameter("dirAllowed","false");
-    staticHolder.setInitParameter("pathInfoOnly","true");
-    servletContextHandler.addServlet(staticHolder,"/static/*");
+    staticHolder.setInitParameter("dirAllowed", "false");
+    staticHolder.setInitParameter("pathInfoOnly", "true");
+    servletContextHandler.addServlet(staticHolder, "/static/*");
+
+    servletContextHandler.setSessionHandler(createSessionHandler());
+    servletContextHandler.setSecurityHandler(createSecurityHandler());
 
     embeddedJetty.start();
   }
 
   /**
+   * @return A {@link SessionHandler} which contains a {@link HashSessionManager}
+   */
+  private SessionHandler createSessionHandler() {
+    SessionManager sessionManager = new HashSessionManager();
+    sessionManager.setMaxInactiveInterval(config.getInt(ExecConstants.HTTP_SESSION_MAX_IDLE_SECS));
+    return new SessionHandler(sessionManager);
+  }
+
+  /**
+   * @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);
+
+    return security;
+  }
+
+  /**
    * Create an HTTPS connector for given jetty server instance. If the admin has specified keystore/truststore settings
    * they will be used else a self-signed certificate is generated and used.
    *

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AbstractDrillLoginService.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AbstractDrillLoginService.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AbstractDrillLoginService.java
new file mode 100644
index 0000000..62ddca9
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AbstractDrillLoginService.java
@@ -0,0 +1,95 @@
+/**
+ * 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.AutoCloseables;
+import org.apache.drill.exec.client.DrillClient;
+import org.apache.drill.exec.server.DrillbitContext;
+import org.eclipse.jetty.security.DefaultIdentityService;
+import org.eclipse.jetty.security.IdentityService;
+import org.eclipse.jetty.security.LoginService;
+import org.eclipse.jetty.server.UserIdentity;
+
+import java.util.Properties;
+
+/**
+ * LoginService implementation which abstracts common functionality needed when user authentication is enabled or
+ * disabled.
+ */
+public abstract class AbstractDrillLoginService implements LoginService {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AbstractDrillLoginService.class);
+
+  protected final DrillbitContext drillbitContext;
+  protected IdentityService identityService = new DefaultIdentityService();
+
+  public AbstractDrillLoginService(final DrillbitContext drillbitContext) {
+    this.drillbitContext = drillbitContext;
+  }
+
+  protected DrillClient createDrillClient(final String userName, final String password) throws Exception {
+    DrillClient drillClient = null;
+
+    try {
+      // Create a DrillClient
+      drillClient = new DrillClient(drillbitContext.getConfig(),
+          drillbitContext.getClusterCoordinator(), drillbitContext.getAllocator());
+      final Properties props = new Properties();
+      props.setProperty("user", userName);
+      if (password != null) {
+        props.setProperty("password", password);
+      }
+      drillClient.connect(props);
+      return  drillClient;
+    } catch (final Exception e) {
+      AutoCloseables.close(e, drillClient);
+      throw e;
+    }
+  }
+
+  @Override
+  public boolean validate(UserIdentity user) {
+    // This is called for every request after authentication is complete to make sure the user is still valid.
+    // Once a user is authenticated we assume that the user is still valid. This behavior is similar to ODBC/JDBC where
+    // once a user is logged-in we don't recheck the credentials again in the same session.
+    return true;
+  }
+
+  @Override
+  public IdentityService getIdentityService() {
+    return identityService;
+  }
+
+  @Override
+  public void setIdentityService(IdentityService identityService) {
+    this.identityService = identityService;
+  }
+
+  /**
+   * This gets called whenever a session is invalidated (because of user logout) or timed out.
+   * @param user
+   */
+  @Override
+  public void logout(UserIdentity user) {
+    final DrillUserPrincipal principal = (DrillUserPrincipal) user.getUserPrincipal();
+    try {
+      principal.close();
+    } catch (final Exception e) {
+      logger.error("Failure in logging out.", e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousAuthenticator.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousAuthenticator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousAuthenticator.java
new file mode 100644
index 0000000..ff796e2
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousAuthenticator.java
@@ -0,0 +1,84 @@
+/**
+ * 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.eclipse.jetty.security.Authenticator;
+import org.eclipse.jetty.security.LoginService;
+import org.eclipse.jetty.security.ServerAuthException;
+import org.eclipse.jetty.security.authentication.FormAuthenticator;
+import org.eclipse.jetty.security.authentication.SessionAuthentication;
+import org.eclipse.jetty.server.Authentication;
+import org.eclipse.jetty.server.Authentication.User;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * Equivalent to {@link FormAuthenticator} used when user authentication is disabled. Purpose is to make sure
+ * a session is established even if the user is "anonymous".
+ */
+public class AnonymousAuthenticator implements Authenticator {
+  public static final String METHOD = "ANONYMOUS";
+
+  private LoginService loginService;
+
+  @Override
+  public void setConfiguration(AuthConfiguration configuration) {
+    loginService = configuration.getLoginService();
+  }
+
+  @Override
+  public String getAuthMethod() {
+    return METHOD;
+  }
+
+  @Override
+  public void prepareRequest(ServletRequest request) {
+    // No-op
+  }
+
+  @Override
+  public Authentication validateRequest(ServletRequest req, ServletResponse resp, boolean mandatory)
+      throws ServerAuthException {
+    final HttpServletRequest request = (HttpServletRequest)req;
+
+    // Create a session if one not exists already
+    final HttpSession session = request.getSession(true);
+
+    if (session.getAttribute(SessionAuthentication.__J_AUTHENTICATED) != null) {
+      // If the session already been authenticated, return the stored auth details
+      return (SessionAuthentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
+    }
+
+    // Create a new session authentication and set it in session.
+    final SessionAuthentication sessionAuth = new SessionAuthentication(getAuthMethod(),
+        loginService.login(null, null), null);
+
+    session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
+
+    return sessionAuth;
+  }
+
+  @Override
+  public boolean secureResponse(ServletRequest req, ServletResponse resp, boolean mandatory,
+      User validatedUser) throws ServerAuthException {
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousLoginService.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousLoginService.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousLoginService.java
new file mode 100644
index 0000000..f6cae51
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AnonymousLoginService.java
@@ -0,0 +1,62 @@
+/**
+ * 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.AutoCloseables;
+import org.apache.drill.exec.client.DrillClient;
+import org.apache.drill.exec.server.DrillbitContext;
+import org.eclipse.jetty.server.UserIdentity;
+
+import javax.security.auth.Subject;
+
+/**
+ * LoginService used when user authentication is disabled. This allows all users and establishes a session for each
+ * "anonymous" user.
+ */
+public class AnonymousLoginService extends AbstractDrillLoginService {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AnonymousLoginService.class);
+
+  public AnonymousLoginService(DrillbitContext drillbitContext) {
+    super(drillbitContext);
+  }
+
+  @Override
+  public String getName() {
+    return "AnonymousLoginService";
+  }
+
+  @Override
+  public UserIdentity login(String userName, Object credentials) {
+    DrillClient client = null;
+    try {
+      client = createDrillClient(DrillUserPrincipal.ANONYMOUS_USER, null);
+      final DrillUserPrincipal principal = new DrillUserPrincipal(DrillUserPrincipal.ANONYMOUS_USER,
+          true /* all users are admins when auth is disabled */, client);
+      final Subject subject = new Subject();
+      subject.getPrincipals().add(principal);
+      subject.getPrivateCredentials().add(credentials);
+
+      subject.getPrincipals().addAll(DrillUserPrincipal.ADMIN_PRINCIPALS);
+      return identityService.newUserIdentity(subject, principal, DrillUserPrincipal.ADMIN_USER_ROLES);
+    } catch (final Exception e) {
+      AutoCloseables.close(e, client);
+      logger.error("Login failed.", e);
+      return null;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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
new file mode 100644
index 0000000..bee0c9d
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/AuthDynamicFeature.java
@@ -0,0 +1,102 @@
+/**
+ * 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.LogInLogOutResources;
+import org.glassfish.jersey.server.model.AnnotatedMethod;
+
+import javax.annotation.Priority;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URLEncoder;
+
+/**
+ * Implementation of {@link DynamicFeature}. As part of the setup it adds the auth check filter {@link AuthCheckFilter}
+ * for resources that need to have user authenticated. If authentication is not done, request is forwarded to login
+ * page.
+ */
+public class AuthDynamicFeature implements DynamicFeature {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AuthDynamicFeature.class);
+
+  @Override
+  public void configure(final ResourceInfo resourceInfo, final FeatureContext configuration) {
+    AnnotatedMethod am = new AnnotatedMethod(resourceInfo.getResourceMethod());
+
+    // RolesAllowed on the method takes precedence over PermitAll
+    RolesAllowed ra = am.getAnnotation(RolesAllowed.class);
+    if (ra != null) {
+      configuration.register(AuthCheckFilter.INSTANCE);
+      return;
+    }
+
+    // PermitAll takes precedence over RolesAllowed on the class
+    if (am.isAnnotationPresent(PermitAll.class)) {
+      // Do nothing.
+      return;
+    }
+
+    // RolesAllowed on the class takes precedence over PermitAll
+    ra = resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
+    if (ra != null) {
+      configuration.register(AuthCheckFilter.INSTANCE);
+    }
+  }
+
+  @Priority(Priorities.AUTHENTICATION) // authentication filter - should go first before all other filters.
+  private static class AuthCheckFilter implements ContainerRequestFilter {
+    private static AuthCheckFilter INSTANCE = new AuthCheckFilter();
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+      final SecurityContext sc = requestContext.getSecurityContext();
+      if (!isUserLoggedIn(sc)) {
+        try {
+          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)
+              .build();
+          requestContext.abortWith(Response.temporaryRedirect(loginURI).build()
+          );
+        } catch (final Exception ex) {
+          final String errMsg = String.format("Failed to forward the request to login page: %s", ex.getMessage());
+          logger.error(errMsg, ex);
+          requestContext.abortWith(
+              Response.serverError()
+                  .entity(errMsg)
+                  .build());
+        }
+      }
+    }
+  }
+
+  public static boolean isUserLoggedIn(final SecurityContext sc) {
+    return sc != null && sc.getUserPrincipal() != null;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
new file mode 100644
index 0000000..d865e94
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
@@ -0,0 +1,88 @@
+/**
+ * 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.AutoCloseables;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.client.DrillClient;
+import org.apache.drill.exec.proto.UserProtos.HandshakeStatus;
+import org.apache.drill.exec.server.DrillbitContext;
+import org.apache.drill.exec.server.options.SystemOptionManager;
+import org.apache.drill.exec.util.ImpersonationUtil;
+import org.eclipse.jetty.server.UserIdentity;
+
+import javax.security.auth.Subject;
+import java.security.Principal;
+
+/**
+ * LoginService used when user authentication is enabled in Drillbit. It validates the user against the user
+ * authenticator set in BOOT config.
+ */
+public class DrillRestLoginService extends AbstractDrillLoginService {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillRestLoginService.class);
+
+  public DrillRestLoginService(final DrillbitContext drillbitContext) {
+    super(drillbitContext);
+  }
+
+  @Override
+  public String getName() {
+    return "DrillRestLoginService";
+  }
+
+  @Override
+  public UserIdentity login(String username, Object credentials) {
+    if (!(credentials instanceof String)) {
+      return null;
+    }
+
+    DrillClient drillClient = null;
+
+    try {
+      // Create a DrillClient
+      drillClient = createDrillClient(username, (String)credentials);
+
+      final SystemOptionManager sysOptions = drillbitContext.getOptionManager();
+      final boolean isAdmin = ImpersonationUtil.hasAdminPrivileges(username,
+          sysOptions.getOption(ExecConstants.ADMIN_USERS_KEY).string_val,
+          sysOptions.getOption(ExecConstants.ADMIN_USER_GROUPS_KEY).string_val);
+
+      final Principal userPrincipal = new DrillUserPrincipal(username, isAdmin, drillClient);
+
+      final Subject subject = new Subject();
+      subject.getPrincipals().add(userPrincipal);
+      subject.getPrivateCredentials().add(credentials);
+
+      if (isAdmin) {
+        subject.getPrincipals().addAll(DrillUserPrincipal.ADMIN_PRINCIPALS);
+        return identityService.newUserIdentity(subject, userPrincipal, DrillUserPrincipal.ADMIN_USER_ROLES);
+      } else {
+        subject.getPrincipals().addAll(DrillUserPrincipal.NON_ADMIN_PRINCIPALS);
+        return identityService.newUserIdentity(subject, userPrincipal, DrillUserPrincipal.NON_ADMIN_USER_ROLES);
+      }
+    } catch (final Exception e) {
+      AutoCloseables.close(e, drillClient);
+      if (e.getMessage().contains(HandshakeStatus.AUTH_FAILED.toString())) {
+        logger.trace("Authentication failed for user '{}'", username, e);
+      } else {
+        logger.error("Error while creating the DrillClient: user '{}'", username, e);
+      }
+      return null;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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
new file mode 100644
index 0000000..a7c3425
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java
@@ -0,0 +1,93 @@
+/**
+ * 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 com.google.common.collect.ImmutableList;
+import org.apache.drill.exec.client.DrillClient;
+import org.apache.drill.exec.proto.UserBitShared.QueryProfile;
+import org.eclipse.jetty.security.MappedLoginService.RolePrincipal;
+
+import java.security.Principal;
+import java.util.List;
+
+/**
+ * Captures Drill user credentials and resources in a session.
+ */
+public class DrillUserPrincipal implements Principal, AutoCloseable {
+  public static final String ANONYMOUS_USER = "anonymous";
+
+  public static final String AUTHENTICATED_ROLE = "authenticated";
+  public static final String ADMIN_ROLE = "admin";
+
+  public static final String[] ADMIN_USER_ROLES = new String[] { AUTHENTICATED_ROLE, ADMIN_ROLE };
+  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> NON_ADMIN_PRINCIPALS =
+      ImmutableList.of(new RolePrincipal(AUTHENTICATED_ROLE));
+
+  private final String userName;
+  private final boolean isAdmin;
+  private final DrillClient drillClient;
+
+  public DrillUserPrincipal(final String userName, final boolean isAdmin, final DrillClient drillClient) {
+    this.userName = userName;
+    this.isAdmin = isAdmin;
+    this.drillClient = drillClient;
+  }
+
+  @Override
+  public String getName() {
+    return userName;
+  }
+
+  /**
+   * @return Return {@link DrillClient} instanced with credentials of this user principal.
+   */
+  public DrillClient getDrillClient() {
+    return drillClient;
+  }
+
+  /**
+   * Is the user identified by this user principal can manage (read) the profile owned by the given user?
+   * @param profileOwner Owner of the profile.
+   * @return
+   */
+  public boolean canManageProfileOf(final String profileOwner) {
+    return isAdmin || userName.equals(profileOwner);
+  }
+
+  /**
+   * Is the user identified by this user principal can manage (cancel) the query issued by the given user?
+   * @param queryUser User who launched the query.
+   * @return
+   */
+  public boolean canManageQueryOf(final String queryUser) {
+    return isAdmin || userName.equals(queryUser);
+  }
+
+  @Override
+  public void close() throws Exception {
+    if (drillClient != null) {
+      drillClient.close();
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java
index 6656bf6..0c04c9e 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java
@@ -26,19 +26,24 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
+import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 import javax.xml.bind.annotation.XmlRootElement;
 
+import org.apache.drill.common.exceptions.UserException;
 import org.apache.drill.exec.proto.GeneralRPCProtos.Ack;
 import org.apache.drill.exec.proto.UserBitShared.QueryId;
 import org.apache.drill.exec.proto.UserBitShared.QueryInfo;
 import org.apache.drill.exec.proto.UserBitShared.QueryProfile;
 import org.apache.drill.exec.proto.helper.QueryIdHelper;
+import org.apache.drill.exec.server.rest.ViewableWithPermissions;
+import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
 import org.apache.drill.exec.store.sys.PStore;
 import org.apache.drill.exec.store.sys.PStoreProvider;
 import org.apache.drill.exec.work.WorkManager;
@@ -49,10 +54,13 @@ import org.glassfish.jersey.server.mvc.Viewable;
 import com.google.common.collect.Lists;
 
 @Path("/")
+@RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
 public class ProfileResources {
   static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ProfileResources.class);
 
   @Inject WorkManager work;
+  @Inject DrillUserPrincipal principal;
+  @Inject SecurityContext sc;
 
   public static class ProfileInfo implements Comparable<ProfileInfo> {
     public static final SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
@@ -152,18 +160,21 @@ public class ProfileResources {
 
     for (Map.Entry<String, QueryInfo> entry : running) {
       QueryInfo profile = entry.getValue();
-      runningQueries.add(new ProfileInfo(entry.getKey(), profile.getStart(), profile.getForeman().getAddress(), profile
-          .getQuery(), profile.getState().name(), profile.getUser()));
+      if (principal.canManageProfileOf(profile.getUser())) {
+        runningQueries.add(new ProfileInfo(entry.getKey(), profile.getStart(), profile.getForeman().getAddress(),
+            profile.getQuery(), profile.getState().name(), profile.getUser()));
+      }
     }
 
     Collections.sort(runningQueries, Collections.reverseOrder());
 
-
     List<ProfileInfo> finishedQueries = Lists.newArrayList();
     for (Map.Entry<String, QueryProfile> entry : completed) {
       QueryProfile profile = entry.getValue();
-      finishedQueries.add(new ProfileInfo(entry.getKey(), profile.getStart(), profile.getForeman().getAddress(),
-          profile.getQuery(), profile.getState().name(), profile.getUser()));
+      if (principal.canManageProfileOf(profile.getUser())) {
+        finishedQueries.add(new ProfileInfo(entry.getKey(), profile.getStart(), profile.getForeman().getAddress(),
+            profile.getQuery(), profile.getState().name(), profile.getUser()));
+      }
     }
 
     return new QProfiles(runningQueries, finishedQueries);
@@ -174,38 +185,48 @@ public class ProfileResources {
   @Produces(MediaType.TEXT_HTML)
   public Viewable getProfiles() {
     QProfiles profiles = getProfilesJSON();
-    return new Viewable("/rest/profile/list.ftl", profiles);
+    return ViewableWithPermissions.create("/rest/profile/list.ftl", sc, profiles);
   }
 
-  private QueryProfile getQueryProfile(String queryId) {
+  private QueryProfile getQueryProfile(String queryId) throws IOException {
     QueryId id = QueryIdHelper.getQueryIdFromString(queryId);
 
     // first check local running
     Foreman f = work.getBee().getForemanForQueryId(id);
     if(f != null){
-      return f.getQueryManager().getQueryProfile();
+      QueryProfile queryProfile = f.getQueryManager().getQueryProfile();
+      checkOrThrowProfileViewAuthorization(queryProfile);
+      return queryProfile;
     }
 
     // then check remote running
     try{
       PStore<QueryInfo> runningQueries = provider().getStore(QueryManager.RUNNING_QUERY_INFO);
       QueryInfo info = runningQueries.get(queryId);
-      return work.getContext().getController().getTunnel(info.getForeman()).requestQueryProfile(id).checkedGet(2, TimeUnit.SECONDS);
+      if (info != null) {
+        QueryProfile queryProfile = work.getContext()
+            .getController()
+            .getTunnel(info.getForeman())
+            .requestQueryProfile(id)
+            .checkedGet(2, TimeUnit.SECONDS);
+        checkOrThrowProfileViewAuthorization(queryProfile);
+        return queryProfile;
+      }
     }catch(Exception e){
       logger.trace("Failed to find query as running profile.", e);
     }
 
     // then check blob store
-    try{
-      PStore<QueryProfile> profiles = provider().getStore(QueryManager.QUERY_PROFILE);
-      return profiles.get(queryId);
-    }catch(Exception e){
-      logger.warn("Failure to load query profile for query {}", queryId, e);
+    PStore<QueryProfile> profiles = provider().getStore(QueryManager.QUERY_PROFILE);
+    QueryProfile queryProfile = profiles.get(queryId);
+    if (queryProfile != null) {
+      checkOrThrowProfileViewAuthorization(queryProfile);
+      return queryProfile;
     }
 
-    // TODO: Improve error messaging.
-    return QueryProfile.getDefaultInstance();
-
+    throw UserException.validationError()
+        .message("No profile with given query id '%s' exists. Please verify the query id.", queryId)
+        .build(logger);
   }
 
 
@@ -224,11 +245,9 @@ public class ProfileResources {
   @GET
   @Path("/profiles/{queryid}")
   @Produces(MediaType.TEXT_HTML)
-  public Viewable getProfile(@PathParam("queryid") String queryId) {
+  public Viewable getProfile(@PathParam("queryid") String queryId) throws IOException {
     ProfileWrapper wrapper = new ProfileWrapper(getQueryProfile(queryId));
-
-    return new Viewable("/rest/profile/profile.ftl", wrapper);
-
+    return ViewableWithPermissions.create("/rest/profile/profile.ftl", sc, wrapper);
   }
 
 
@@ -242,6 +261,7 @@ public class ProfileResources {
     // first check local running
     Foreman f = work.getBee().getForemanForQueryId(id);
     if(f != null){
+      checkOrThrowQueryCancelAuthorization(f.getQueryContext().getQueryUserName(), queryId);
       f.cancel();
       return String.format("Cancelled query %s on locally running node.", queryId);
     }
@@ -250,6 +270,7 @@ public class ProfileResources {
     try{
       PStore<QueryInfo> runningQueries = provider().getStore(QueryManager.RUNNING_QUERY_INFO);
       QueryInfo info = runningQueries.get(queryId);
+      checkOrThrowQueryCancelAuthorization(info.getUser(), queryId);
       Ack a = work.getContext().getController().getTunnel(info.getForeman()).requestCancelQuery(id).checkedGet(2, TimeUnit.SECONDS);
       if(a.getOk()){
         return String.format("Query %s canceled on node %s.", queryId, info.getForeman().getAddress());
@@ -260,7 +281,21 @@ public class ProfileResources {
       logger.debug("Failure to find query as running profile.", e);
       return String.format("Failure attempting to cancel query %s.  Unable to find information about where query is actively running.", queryId);
     }
+  }
 
+  private void checkOrThrowProfileViewAuthorization(final QueryProfile profile) {
+    if (!principal.canManageProfileOf(profile.getUser())) {
+      throw UserException.permissionError()
+          .message("Not authorized to view the profile of query '%s'", profile.getId())
+          .build(logger);
+    }
   }
 
+  private void checkOrThrowQueryCancelAuthorization(final String queryUser, final String queryId) {
+    if (!principal.canManageQueryOf(queryUser)) {
+      throw UserException.permissionError()
+          .message("Not authorized to cancel the query '%s'", queryId)
+          .build(logger);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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 6901a5f..10c5319 100644
--- a/exec/java-exec/src/main/resources/drill-module.conf
+++ b/exec/java-exec/src/main/resources/drill-module.conf
@@ -97,6 +97,7 @@ drill.exec: {
     enabled: true,
     ssl_enabled: false,
     port: 8047
+    session_max_idle_secs: 3600 # Default value 1hr
   },
   network: {
     start: 35000

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/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 9df2424..b3e249e 100644
--- a/exec/java-exec/src/main/resources/rest/generic.ftl
+++ b/exec/java-exec/src/main/resources/rest/generic.ftl
@@ -53,16 +53,30 @@
             <a class="navbar-brand" href="/">Apache Drill</a>
           </div>
           <div class="navbar-collapse collapse">
+            <#if showControls == true>
             <ul class="nav navbar-nav">
               <li><a href="/query">Query</a></li>
               <li><a href="/profiles">Profiles</a></li>
+              <#if showStorage == true>
               <li><a href="/storage">Storage</a></li>
+              </#if>
               <li><a href="/metrics">Metrics</a></li>
+              <#if showThreads == true>
               <li><a href="/threads">Threads</a></li>
+              </#if>
             </ul>
+            </#if>
             <ul class="nav navbar-nav navbar-right">
+              <#if showOptions == true>
               <li><a href="/options">Options</a></li>
+              </#if>
               <li><a href="http://drill.apache.org/docs/">Documentation</a>
+              <#if showLogin == true >
+              <li><a href="/login">Log In</a>
+              </#if>
+              <#if showLogout == true >
+              <li><a href="/logout">Log Out (${loggedInUserName})</a>
+              </#if>
             </ul>
           </div>
         </div>

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/resources/rest/login.ftl
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/login.ftl b/exec/java-exec/src/main/resources/rest/login.ftl
new file mode 100644
index 0000000..1588836
--- /dev/null
+++ b/exec/java-exec/src/main/resources/rest/login.ftl
@@ -0,0 +1,37 @@
+<#-- 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>
+    <a href="/queries">back</a><br/>
+    <div class="page-header"></div>
+    <div align="center" class="table-responsive">
+        <form role="form" name="input" action="/j_security_check" method="POST">
+            <fieldset>
+                <div class="form-group">
+                    <img src="/static/img/apache-drill-logo.png" alt="Apache Drill Logo">
+                    <#if model??>
+                    <p style="color:red">${model}</p></br>
+                    </#if>
+                    <h4>Log In to Drill Web Console</h4></br>
+                    <p><input type="text" size="30" name="j_username" placeholder="Username"></p>
+                    <p><input type="password" size="30" name="j_password" placeholder="Password"></p>
+                    <p><button type="submit" class="btn btn-default">Log In</button> </p>
+                </div>
+
+            </fieldset>
+        </form>
+    </div>
+</#macro>
+<@page_html/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/9dad9da6/exec/java-exec/src/main/resources/rest/static/img/apache-drill-logo.png
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/static/img/apache-drill-logo.png b/exec/java-exec/src/main/resources/rest/static/img/apache-drill-logo.png
new file mode 100644
index 0000000..bce39c0
Binary files /dev/null and b/exec/java-exec/src/main/resources/rest/static/img/apache-drill-logo.png differ


Mime
View raw message