LENS-1509 : Lens Server SPNEGO authentication
diff --git a/contrib/clients/python/lens/client/session.py b/contrib/clients/python/lens/client/session.py
index a1ccc4b..e63da1e 100644
--- a/contrib/clients/python/lens/client/session.py
+++ b/contrib/clients/python/lens/client/session.py
@@ -17,6 +17,7 @@
 
 import requests
 
+from .auth import SpnegoAuth
 from .models import WrappedJson
 from .utils import conf_to_xml
 
@@ -24,6 +25,8 @@
 class LensSessionClient(object):
     def __init__(self, base_url, username, password, database, conf):
         self.base_url = base_url + "session/"
+        self.keytab = conf.get('lens.client.authentication.kerberos.keytab')
+        self.principal = username or conf.get('lens.client.authentication.kerberos.principal')
         self.open(username, password, database, conf)
 
     def __getitem__(self, key):
@@ -41,10 +44,12 @@
         payload = [('username', username), ('password', password), ('sessionconf', conf_to_xml(conf))]
         if database:
             payload.append(('database', database))
-        r = requests.post(self.base_url, files=payload, headers={'accept': 'application/xml'})
+        r = requests.post(self.base_url, files=payload, headers={'accept': 'application/xml'},
+                          auth=SpnegoAuth(self.keytab, self.principal))
         r.raise_for_status()
         self._sessionid = r.text
 
     def close(self):
-        requests.delete(self.base_url, params={'sessionid': self._sessionid})
+        requests.delete(self.base_url, params={'sessionid': self._sessionid},
+                        auth=SpnegoAuth(self.keytab, self.principal))
 
diff --git a/contrib/clients/python/setup.py b/contrib/clients/python/setup.py
index de59d32..dc40bfa 100644
--- a/contrib/clients/python/setup.py
+++ b/contrib/clients/python/setup.py
@@ -57,7 +57,7 @@
     license='Apache Software License',
     author='Apache',
     tests_require=['tox'],
-    install_requires=['requests>=2.9.1', 'six>=1.10.0'],
+    install_requires=['requests>=2.9.1', 'six>=1.10.0', 'pykerberos=1.2.1'],
     cmdclass={'test': Tox},
     author_email='dev@lens.apache.org',
     description='Python Lens Client',
diff --git a/lens-client/src/main/java/org/apache/lens/client/LensClientConfig.java b/lens-client/src/main/java/org/apache/lens/client/LensClientConfig.java
index b703e13..eb12ee3 100644
--- a/lens-client/src/main/java/org/apache/lens/client/LensClientConfig.java
+++ b/lens-client/src/main/java/org/apache/lens/client/LensClientConfig.java
@@ -90,6 +90,12 @@
 
   public static final int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 60000; //60 secs
 
+  public static final String KERBEROS_PRINCIPAL = CLIENT_PFX + "authentication.kerberos.principal";
+
+  public static final String KERBEROS_REALM = CLIENT_PFX + "authentication.kerberos.realm";
+
+  public static final String KERBEROS_KEYTAB = CLIENT_PFX + "authentication.kerberos.keytab";
+
   /**
    * Get the username from config
    *
@@ -167,4 +173,5 @@
   public static String getWSFilterImplConfKey(String filterName) {
     return CLIENT_PFX + filterName + WS_FILTER_IMPL_SFX;
   }
+
 }
diff --git a/lens-client/src/main/java/org/apache/lens/client/LensConnection.java b/lens-client/src/main/java/org/apache/lens/client/LensConnection.java
index bb15b23..ab49831 100644
--- a/lens-client/src/main/java/org/apache/lens/client/LensConnection.java
+++ b/lens-client/src/main/java/org/apache/lens/client/LensConnection.java
@@ -185,6 +185,7 @@
       if (e.getCause() != null && e.getCause() instanceof ConnectException) {
         throw new LensClientServerConnectionException(e.getCause().getMessage(), e);
       }
+      throw e;
     }
 
     log.debug("Successfully switched to database {}", params.getDbName());
diff --git a/lens-client/src/main/java/org/apache/lens/client/LensConnectionParams.java b/lens-client/src/main/java/org/apache/lens/client/LensConnectionParams.java
index 3a5dcdb..90c70c8 100644
--- a/lens-client/src/main/java/org/apache/lens/client/LensConnectionParams.java
+++ b/lens-client/src/main/java/org/apache/lens/client/LensConnectionParams.java
@@ -53,6 +53,7 @@
   private Set<Class<?>> requestFilters = new HashSet<Class<?>>();
 
   private void setupRequestFilters() {
+    requestFilters.add(SpnegoClientFilter.class); // add default filter
     if (this.conf.get(LensClientConfig.SESSION_FILTER_NAMES) != null) {
       String[] filterNames = this.conf.getStrings(LensClientConfig.SESSION_FILTER_NAMES);
       for (String filterName : filterNames) {
diff --git a/lens-server-api/src/main/java/org/apache/lens/server/api/LensConfConstants.java b/lens-server-api/src/main/java/org/apache/lens/server/api/LensConfConstants.java
index f14ae44..bda995d 100644
--- a/lens-server-api/src/main/java/org/apache/lens/server/api/LensConfConstants.java
+++ b/lens-server-api/src/main/java/org/apache/lens/server/api/LensConfConstants.java
@@ -277,6 +277,11 @@
    */
   public static final String SESSION_LOGGEDIN_USER = SESSION_PFX + "loggedin.user";
 
+  /**
+   * constant for session proxy user
+   */
+  public static final String SESSION_PROXY_USER = SESSION_PFX + "proxy.user";
+
   // ldap user to cluster/hdfs accessing user resolver related configs
   /**
    * The Constant USER_RESOLVER_TYPE.
@@ -1297,4 +1302,16 @@
   public static final double DEFAULT_DRIVER_QUERY_COST = 0.0;
 
   public static final String DRIVER_COST_QUERY_DECIDER = DRIVER_PFX + "cost.query.decider.class";
+
+  public static final String AUTH_SCHEME = SERVER_PFX  + "authentication.scheme";
+
+  public static final String KERBEROS_PRINCIPAL = SERVER_PFX + "authentication.kerberos.principal";
+
+  public static final String KERBEROS_REALM = SERVER_PFX + "authentication.kerberos.realm";
+
+  public static final String KERBEROS_KEYTAB = SERVER_PFX + "authentication.kerberos.keytab";
+
+  public static final String ALLOWED_PROXY_USERS = SERVER_PFX + "authentication.allowed.proxy.users";
+
+
 }
diff --git a/lens-server/src/main/java/org/apache/lens/server/BaseLensService.java b/lens-server/src/main/java/org/apache/lens/server/BaseLensService.java
index b5248f3..006ee88 100644
--- a/lens-server/src/main/java/org/apache/lens/server/BaseLensService.java
+++ b/lens-server/src/main/java/org/apache/lens/server/BaseLensService.java
@@ -26,6 +26,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.ws.rs.BadRequestException;
@@ -36,6 +37,7 @@
 
 import org.apache.lens.api.LensConf;
 import org.apache.lens.api.LensSessionHandle;
+import org.apache.lens.api.auth.AuthScheme;
 import org.apache.lens.api.session.UserSessionInfo;
 import org.apache.lens.api.util.PathValidator;
 import org.apache.lens.server.api.LensConfConstants;
@@ -73,6 +75,9 @@
 @Slf4j
 public abstract class BaseLensService extends CompositeService implements Externalizable, LensService,
   SessionValidator {
+  public static final Configuration CONF = LensServerConf.getHiveConf();
+  public static final Optional<AuthScheme> AUTH_SCHEME =
+          AuthScheme.getFromString(CONF.get(LensConfConstants.AUTH_SCHEME));
 
   /** The cli service. */
   private final CLIService cliService;
@@ -161,7 +166,7 @@
    */
   public LensSessionHandle openSession(String username, String password, Map<String, String> configuration)
     throws LensException {
-    return openSession(username, password, configuration, true);
+    return openSession(username, password, configuration, !AUTH_SCHEME.isPresent());
   }
 
   public LensSessionHandle openSession(String username, String password, Map<String, String> configuration,
@@ -343,9 +348,9 @@
         } else {
           SESSIONS_PER_USER.put(userName, --sessionCount);
         }
-      }else {
+      } else {
         log.info("Trying to decrement session count for non existing session {} for user {}: ",
-          sessionHandle, userName);
+                sessionHandle, userName);
       }
     }
   }
diff --git a/lens-server/src/main/java/org/apache/lens/server/LensApplication.java b/lens-server/src/main/java/org/apache/lens/server/LensApplication.java
index c3f9952..1e26a9e 100644
--- a/lens-server/src/main/java/org/apache/lens/server/LensApplication.java
+++ b/lens-server/src/main/java/org/apache/lens/server/LensApplication.java
@@ -26,6 +26,7 @@
 
 import org.apache.lens.server.api.LensConfConstants;
 import org.apache.lens.server.error.GenericExceptionMapper;
+import org.apache.lens.server.error.NotAuthorizedExceptionMapper;
 
 import org.apache.hadoop.conf.Configuration;
 
@@ -67,6 +68,7 @@
       classes.add(wsListenerClass);
       log.info("Added listener {}", wsListenerClass);
     }
+
     for (String filterName : filterNames) {
       Class wsFilterClass = CONF.getClass(LensConfConstants.getWSFilterImplConfKey(filterName), null);
       classes.add(wsFilterClass);
@@ -74,6 +76,7 @@
     }
 
     classes.add(GenericExceptionMapper.class);
+    classes.add(NotAuthorizedExceptionMapper.class);
     return classes;
   }
 
diff --git a/lens-server/src/main/java/org/apache/lens/server/LogResource.java b/lens-server/src/main/java/org/apache/lens/server/LogResource.java
index d6d3348..38e33ce 100644
--- a/lens-server/src/main/java/org/apache/lens/server/LogResource.java
+++ b/lens-server/src/main/java/org/apache/lens/server/LogResource.java
@@ -32,11 +32,13 @@
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.StreamingOutput;
 
+import org.apache.lens.server.auth.Authenticate;
 import org.apache.lens.server.util.UtilityMethods;
 
 /**
  * The logs resource
  */
+@Authenticate
 @Path("/logs")
 public class LogResource {
 
diff --git a/lens-server/src/main/java/org/apache/lens/server/metastore/MetastoreResource.java b/lens-server/src/main/java/org/apache/lens/server/metastore/MetastoreResource.java
index 8818844..f7b3427 100644
--- a/lens-server/src/main/java/org/apache/lens/server/metastore/MetastoreResource.java
+++ b/lens-server/src/main/java/org/apache/lens/server/metastore/MetastoreResource.java
@@ -42,12 +42,14 @@
 import org.apache.hadoop.hive.ql.metadata.HiveException;
 
 import lombok.extern.slf4j.Slf4j;
+import org.apache.lens.server.auth.Authenticate;
 
-/**
+  /**
  * metastore resource api
  * <p> </p>
  * This provides api for all things metastore.
  */
+@Authenticate
 @Path("metastore")
 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
 @Slf4j
diff --git a/lens-server/src/main/java/org/apache/lens/server/query/QueryServiceResource.java b/lens-server/src/main/java/org/apache/lens/server/query/QueryServiceResource.java
index d53fbbf..d00087a 100644
--- a/lens-server/src/main/java/org/apache/lens/server/query/QueryServiceResource.java
+++ b/lens-server/src/main/java/org/apache/lens/server/query/QueryServiceResource.java
@@ -39,6 +39,7 @@
 import org.apache.lens.server.api.error.LensException;
 import org.apache.lens.server.api.query.QueryExecutionService;
 import org.apache.lens.server.api.query.cost.QueryCostTOBuilder;
+import org.apache.lens.server.auth.Authenticate;
 import org.apache.lens.server.error.UnSupportedOpException;
 import org.apache.lens.server.model.LogSegregationContext;
 import org.apache.lens.server.util.UtilityMethods;
@@ -54,6 +55,7 @@
  * <p></p>
  * This provides api for all things query.
  */
+@Authenticate
 @Slf4j
 @Path("/queryapi")
 public class QueryServiceResource {
diff --git a/lens-server/src/main/java/org/apache/lens/server/query/save/SavedQueryResource.java b/lens-server/src/main/java/org/apache/lens/server/query/save/SavedQueryResource.java
index 72748cd..961884b 100644
--- a/lens-server/src/main/java/org/apache/lens/server/query/save/SavedQueryResource.java
+++ b/lens-server/src/main/java/org/apache/lens/server/query/save/SavedQueryResource.java
@@ -45,6 +45,7 @@
 import org.apache.lens.server.api.query.save.*;
 import org.apache.lens.server.api.query.save.param.ParameterParser;
 import org.apache.lens.server.api.query.save.param.ParameterResolver;
+import org.apache.lens.server.auth.Authenticate;
 import org.apache.lens.server.model.LogSegregationContext;
 
 import org.apache.hadoop.hive.conf.HiveConf;
@@ -53,6 +54,7 @@
 import org.glassfish.jersey.media.multipart.FormDataParam;
 
 
+@Authenticate
 @Path("/queryapi")
 /**
  * Saved query resource
diff --git a/lens-server/src/main/java/org/apache/lens/server/quota/QuotaResource.java b/lens-server/src/main/java/org/apache/lens/server/quota/QuotaResource.java
index 2130191..18d2b20 100644
--- a/lens-server/src/main/java/org/apache/lens/server/quota/QuotaResource.java
+++ b/lens-server/src/main/java/org/apache/lens/server/quota/QuotaResource.java
@@ -18,6 +18,8 @@
  */
 package org.apache.lens.server.quota;
 
+import org.apache.lens.server.auth.Authenticate;
+
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -26,6 +28,7 @@
 /**
  * The Class QuotaResource.
  */
+@Authenticate
 @Path("/quota")
 public class QuotaResource {
 
diff --git a/lens-server/src/main/java/org/apache/lens/server/scheduler/ScheduleResource.java b/lens-server/src/main/java/org/apache/lens/server/scheduler/ScheduleResource.java
index 6c8b8ad..1d5959b 100644
--- a/lens-server/src/main/java/org/apache/lens/server/scheduler/ScheduleResource.java
+++ b/lens-server/src/main/java/org/apache/lens/server/scheduler/ScheduleResource.java
@@ -30,6 +30,7 @@
 import org.apache.lens.server.LensServices;
 import org.apache.lens.server.api.error.LensException;
 import org.apache.lens.server.api.scheduler.SchedulerService;
+import org.apache.lens.server.auth.Authenticate;
 import org.apache.lens.server.error.UnSupportedOpException;
 import org.apache.lens.server.model.LogSegregationContext;
 import org.apache.lens.server.util.UtilityMethods;
@@ -37,6 +38,7 @@
 /**
  * REST end point for all scheduler operations.
  */
+@Authenticate
 @Path("scheduler")
 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
 public class ScheduleResource {
diff --git a/lens-server/src/main/java/org/apache/lens/server/session/LensSessionImpl.java b/lens-server/src/main/java/org/apache/lens/server/session/LensSessionImpl.java
index 08a5cff..7d81375 100644
--- a/lens-server/src/main/java/org/apache/lens/server/session/LensSessionImpl.java
+++ b/lens-server/src/main/java/org/apache/lens/server/session/LensSessionImpl.java
@@ -115,6 +115,7 @@
     persistInfo.setPassword(getPassword());
     persistInfo.setLastAccessTime(System.currentTimeMillis());
     persistInfo.setSessionConf(sessionConf);
+    persistInfo.setProxyUser(sessionConf.get(LensConfConstants.SESSION_PROXY_USER));
     if (sessionConf != null) {
       for (Map.Entry<String, String> entry : sessionConf.entrySet()) {
         conf.set(entry.getKey(), entry.getValue());
@@ -637,6 +638,9 @@
     /** Whether it's marked for close */
     private boolean markedForClose;
 
+    /** The proxy user which is initiating the request. This could be null */
+    private String proxyUser;
+
     public void setSessionConf(Map<String, String> sessionConf) {
       UtilityMethods.mergeMaps(config, sessionConf, true);
     }
@@ -666,6 +670,7 @@
       }
       out.writeLong(lastAccessTime);
       out.writeBoolean(markedForClose);
+      out.writeUTF(proxyUser == null ? "" : proxyUser);
     }
 
     /*
@@ -697,6 +702,7 @@
       }
       lastAccessTime = in.readLong();
       markedForClose = in.readBoolean();
+      proxyUser = in.readUTF();
     }
   }
 
diff --git a/lens-server/src/main/java/org/apache/lens/server/session/SessionResource.java b/lens-server/src/main/java/org/apache/lens/server/session/SessionResource.java
index 63eea63..dfecba5 100644
--- a/lens-server/src/main/java/org/apache/lens/server/session/SessionResource.java
+++ b/lens-server/src/main/java/org/apache/lens/server/session/SessionResource.java
@@ -18,24 +18,47 @@
  */
 package org.apache.lens.server.session;
 
+import java.security.Principal;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
-import javax.ws.rs.*;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.SecurityContext;
 
 import org.apache.lens.api.APIResult;
 import org.apache.lens.api.APIResult.Status;
 import org.apache.lens.api.LensConf;
 import org.apache.lens.api.LensSessionHandle;
 import org.apache.lens.api.StringList;
+import org.apache.lens.api.auth.AuthScheme;
 import org.apache.lens.api.session.UserSessionInfo;
+import org.apache.lens.server.LensServerConf;
 import org.apache.lens.server.LensServices;
+import org.apache.lens.server.api.LensConfConstants;
 import org.apache.lens.server.api.error.LensException;
 import org.apache.lens.server.api.session.SessionService;
+import org.apache.lens.server.auth.Authenticate;
 import org.apache.lens.server.util.ScannedPaths;
 
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+
 import org.glassfish.jersey.media.multipart.FormDataParam;
 
 import lombok.extern.slf4j.Slf4j;
@@ -45,13 +68,20 @@
  * <p></p>
  * This provides api for all things in session.
  */
+@Authenticate
 @Path("session")
 @Slf4j
 public class SessionResource {
+  public static final Configuration CONF = LensServerConf.getHiveConf();
+  public static final Optional<AuthScheme> AUTH_SCHEME =
+          AuthScheme.getFromString(CONF.get(LensConfConstants.AUTH_SCHEME));
 
   /** The session service. */
   private SessionService sessionService;
 
+  @Context
+  private SecurityContext securityContext;
+
   /**
    * API to know if session service is up and running
    *
@@ -94,7 +124,24 @@
     } else {
       conf = new HashMap();
     }
-    return sessionService.openSession(username, password, database,   conf);
+
+    if (AUTH_SCHEME.isPresent()) {
+      Principal userPrincipal = securityContext.getUserPrincipal();
+      String userPrincipalName = userPrincipal.getName();
+      Collection<String> allowedProxyUsers = CONF.getTrimmedStringCollection(LensConfConstants.ALLOWED_PROXY_USERS);
+      if (allowedProxyUsers.contains(userPrincipalName)) {
+        String loggedInUser = conf.get(LensConfConstants.SESSION_LOGGEDIN_USER);
+        if (StringUtils.isBlank(loggedInUser)) {
+          throw new BadRequestException(LensConfConstants.SESSION_LOGGEDIN_USER + " is required in sessionconf");
+        }
+        username = loggedInUser;
+        conf.put(LensConfConstants.SESSION_PROXY_USER, userPrincipalName);
+      } else {
+        username = userPrincipalName;
+      }
+      password = "";
+    }
+    return sessionService.openSession(username, password, database, conf);
   }
 
   /**