You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ja...@apache.org on 2018/04/20 10:58:08 UTC

[1/2] lucene-solr:solr7896-login-page: WIP testing HTTP interceptors

Repository: lucene-solr
Updated Branches:
  refs/heads/solr7896-login-page [created] f70371bab


WIP testing HTTP interceptors


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/0fc0ff23
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/0fc0ff23
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/0fc0ff23

Branch: refs/heads/solr7896-login-page
Commit: 0fc0ff23ae91509cc135dfa425df469559dd76a0
Parents: 1c8ab33
Author: Jan Høydahl <ja...@apache.org>
Authored: Thu Apr 19 11:23:43 2018 +0200
Committer: Jan Høydahl <ja...@apache.org>
Committed: Thu Apr 19 11:23:43 2018 +0200

----------------------------------------------------------------------
 solr/bin/solr.in.sh               |  8 ++++++++
 solr/webapp/web/js/angular/app.js | 24 ++++++++++++++++++++++++
 2 files changed, 32 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fc0ff23/solr/bin/solr.in.sh
----------------------------------------------------------------------
diff --git a/solr/bin/solr.in.sh b/solr/bin/solr.in.sh
index 7cf6a84..04ac46c 100644
--- a/solr/bin/solr.in.sh
+++ b/solr/bin/solr.in.sh
@@ -171,3 +171,11 @@
 #SOLR_RECOMMENDED_MAX_PROCESSES=
 #SOLR_ULIMIT_CHECKS=
 
+
+# The following lines added by ./solr for enabling BasicAuth
+# SOLR_AUTH_TYPE="basic"
+# SOLR_AUTHENTICATION_OPTS="-Dsolr.httpclient.config=/Users/janhoy/git/lucene-solr/solr/server/solr/basicAuth.conf"
+
+# The following lines added by ./solr for enabling BasicAuth
+SOLR_AUTH_TYPE="basic"
+SOLR_AUTHENTICATION_OPTS="-Dsolr.httpclient.config=/Users/janhoy/git/lucene-solr/solr/server/solr/basicAuth.conf"

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fc0ff23/solr/webapp/web/js/angular/app.js
----------------------------------------------------------------------
diff --git a/solr/webapp/web/js/angular/app.js b/solr/webapp/web/js/angular/app.js
index ad96ce0..ee420d3 100644
--- a/solr/webapp/web/js/angular/app.js
+++ b/solr/webapp/web/js/angular/app.js
@@ -369,8 +369,32 @@ solrAdminApp.config([
 
   return {request: started, response: ended, responseError: failed};
 })
+.factory('authInterceptor', function($q, $rootScope, $timeout, $injector) {
+  var started = function(config) {
+    config.headers['Authorization'] = "Basic c29scjpTb2xyUm9ja3M="; // solr / solrRocks
+    console.log("Added authorization header");
+    return config || $q.when(config);
+  };
+
+  var ended = function(response) {
+    if (response.headers['WWW-Authenticate'] != null) {
+      console.log("Got WWW-Authenticate header");
+      alert("WWW-Authenticate: " + response.headers['WWW-Authenticate']);
+    }
+    return response || $q.when(response);
+  };
+
+  var failed = function(rejection) {
+    console.log("Failed with rejection " + rejection);
+    $rootScope.$broadcast('loadingStatusInactive');
+    return $q.reject(rejection);
+  };
+
+  return {request: started, response: ended, responseError: failed};
+})
 .config(function($httpProvider) {
   $httpProvider.interceptors.push("httpInterceptor");
+  $httpProvider.interceptors.push("authInterceptor");
 })
 .directive('fileModel', function ($parse) {
     return {


[2/2] lucene-solr:solr7896-login-page: Hard coded authentication with solr:solrRocks

Posted by ja...@apache.org.
Hard coded authentication with solr:solrRocks


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/f70371ba
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/f70371ba
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/f70371ba

Branch: refs/heads/solr7896-login-page
Commit: f70371bab49e3838955dbab9d2e8b85589817afe
Parents: 0fc0ff2
Author: Jan Høydahl <ja...@apache.org>
Authored: Fri Apr 20 12:36:35 2018 +0200
Committer: Jan Høydahl <ja...@apache.org>
Committed: Fri Apr 20 12:36:35 2018 +0200

----------------------------------------------------------------------
 .../apache/solr/security/BasicAuthPlugin.java   | 35 +++++++++++++----
 .../apache/solr/servlet/SolrDispatchFilter.java | 41 +++++++++++++-------
 solr/webapp/web/WEB-INF/web.xml                 |  2 +-
 solr/webapp/web/js/angular/app.js               | 17 +++++---
 4 files changed, 66 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f70371ba/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java b/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java
index eab89e3..3533b9e 100644
--- a/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java
@@ -34,12 +34,13 @@ import java.util.StringTokenizer;
 import com.google.common.collect.ImmutableSet;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.http.Header;
+import org.apache.http.HttpHeaders;
 import org.apache.http.auth.BasicUserPrincipal;
 import org.apache.http.message.BasicHeader;
 import org.apache.solr.common.SolrException;
-import org.apache.solr.common.util.ValidatingJsonMap;
-import org.apache.solr.common.util.CommandOperation;
 import org.apache.solr.common.SpecProvider;
+import org.apache.solr.common.util.CommandOperation;
+import org.apache.solr.common.util.ValidatingJsonMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,6 +48,7 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private AuthenticationProvider authenticationProvider;
   private final static ThreadLocal<Header> authHeader = new ThreadLocal<>();
+  private static final String X_REQUESTED_WITH_HEADER = "X-Requested-With";
   private boolean blockUnknown = false;
 
   public boolean authenticate(String username, String pwd) {
@@ -94,9 +96,17 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
     return provider;
   }
 
-  private void authenticationFailure(HttpServletResponse response, String message) throws IOException {
+  private void authenticationFailure(HttpServletResponse response, boolean isAjaxRequest, String message) throws IOException {
     for (Map.Entry<String, String> entry : authenticationProvider.getPromptHeaders().entrySet()) {
-      response.setHeader(entry.getKey(), entry.getValue());
+      String value = entry.getValue();
+      // Prevent browser from intercepting basic authentication header when reqeust from Admin UI
+      if (isAjaxRequest && HttpHeaders.WWW_AUTHENTICATE.equalsIgnoreCase(entry.getKey()) && value != null) {
+        if (value.startsWith("Basic ")) {
+          value = "x" + value;
+          log.debug("Prefixing {} header for Basic Auth with 'x' to prevent browser basic auth popup", HttpHeaders.WWW_AUTHENTICATE);
+        }
+      }
+      response.setHeader(entry.getKey(), value);
     }
     response.sendError(401, message);
   }
@@ -108,6 +118,8 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
     HttpServletResponse response = (HttpServletResponse) servletResponse;
 
     String authHeader = request.getHeader("Authorization");
+    boolean isAjaxRequest = isAjaxRequest(request);
+    
     if (authHeader != null) {
       BasicAuthPlugin.authHeader.set(new BasicHeader("Authorization", authHeader));
       StringTokenizer st = new StringTokenizer(authHeader);
@@ -122,7 +134,7 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
               String pwd = credentials.substring(p + 1).trim();
               if (!authenticate(username, pwd)) {
                 log.debug("Bad auth credentials supplied in Authorization header");
-                authenticationFailure(response, "Bad credentials");
+                authenticationFailure(response, isAjaxRequest, "Bad credentials");
               } else {
                 HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
                   @Override
@@ -135,7 +147,7 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
               }
 
             } else {
-              authenticationFailure(response, "Invalid authentication token");
+              authenticationFailure(response, isAjaxRequest, "Invalid authentication token");
             }
           } catch (UnsupportedEncodingException e) {
             throw new Error("Couldn't retrieve authentication", e);
@@ -144,7 +156,7 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
       }
     } else {
       if (blockUnknown) {
-        authenticationFailure(response, "require authentication");
+        authenticationFailure(response, isAjaxRequest, "require authentication");
       } else {
         request.setAttribute(AuthenticationPlugin.class.getName(), authenticationProvider.getPromptHeaders());
         filterChain.doFilter(request, response);
@@ -183,5 +195,12 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
   public static final String BLOCK_UNKNOWN = "blockUnknown";
   private static final Set<String> PROPS = ImmutableSet.of(BLOCK_UNKNOWN);
 
-
+  /**
+   * Check if the request is an AJAX request, i.e. from the Admin UI or other SPA front 
+   * @param request the servlet request
+   * @return true if the request is AJAX request
+   */
+  private boolean isAjaxRequest(HttpServletRequest request) {
+    return "XMLHttpRequest".equalsIgnoreCase(request.getHeader(X_REQUESTED_WITH_HEADER));
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f70371ba/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
index fc0c28f..5f8097c 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
@@ -358,18 +358,6 @@ public class SolrDispatchFilter extends BaseSolrFilter {
         }
       }
 
-      AtomicReference<HttpServletRequest> wrappedRequest = new AtomicReference<>();
-      if (!authenticateRequest(request, response, wrappedRequest)) { // the response and status code have already been sent
-        return;
-      }
-      if (wrappedRequest.get() != null) {
-        request = wrappedRequest.get();
-      }
-
-      if (cores.getAuthenticationPlugin() != null) {
-        log.debug("User principal: {}", request.getUserPrincipal());
-      }
-
       // No need to even create the HttpSolrCall object if this path is excluded.
       if (excludePatterns != null) {
         String requestPath = request.getServletPath();
@@ -387,6 +375,18 @@ public class SolrDispatchFilter extends BaseSolrFilter {
         }
       }
 
+      AtomicReference<HttpServletRequest> wrappedRequest = new AtomicReference<>();
+      if (!authenticateRequest(request, response, wrappedRequest)) { // the response and status code have already been sent
+        return;
+      }
+      if (wrappedRequest.get() != null) {
+        request = wrappedRequest.get();
+      }
+
+      if (cores.getAuthenticationPlugin() != null) {
+        log.debug("User principal: {}", request.getUserPrincipal());
+      }
+
       HttpSolrCall call = getHttpSolrCall(closeShield(request, retry), closeShield(response, retry), retry);
       ExecutorUtil.setServerThreadFlag(Boolean.TRUE);
       try {
@@ -453,11 +453,22 @@ public class SolrDispatchFilter extends BaseSolrFilter {
     if (authenticationPlugin == null) {
       return true;
     } else {
-      // /admin/info/key must be always open. see SOLR-9188
+      // /admin/info/key and /solr/ (Admin UI) must be always open. see SOLR-9188
       // tests work only w/ getPathInfo
       //otherwise it's just enough to have getServletPath()
-      if (PKIAuthenticationPlugin.PATH.equals(request.getServletPath()) ||
-          PKIAuthenticationPlugin.PATH.equals(request.getPathInfo())) return true;
+      String requestPath = request.getPathInfo();
+      if (requestPath == null) 
+        requestPath = request.getServletPath();
+      if (PKIAuthenticationPlugin.PATH.equals(requestPath)) {
+        if (log.isDebugEnabled())
+          log.debug("Pass through PKI authentication endpoint");
+        return true;
+      }
+      if ("/solr/".equals(requestPath) || "/".equals(requestPath)) {
+        if (log.isDebugEnabled())
+          log.debug("Pass through Admin UI entry point");
+        return true;
+      }
       String header = request.getHeader(PKIAuthenticationPlugin.HEADER);
       if (header != null && cores.getPkiAuthenticationPlugin() != null)
         authenticationPlugin = cores.getPkiAuthenticationPlugin();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f70371ba/solr/webapp/web/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/solr/webapp/web/WEB-INF/web.xml b/solr/webapp/web/WEB-INF/web.xml
index 8d86269..22082c8 100644
--- a/solr/webapp/web/WEB-INF/web.xml
+++ b/solr/webapp/web/WEB-INF/web.xml
@@ -56,7 +56,7 @@
     -->
     <init-param>
       <param-name>excludePatterns</param-name>
-      <param-value>/partials/.+,/libs/.+,/css/.+,/js/.+,/img/.+,/tpl/.+</param-value>
+      <param-value>/partials/.+,/libs/.+,/css/.+,/js/.+,/img/.+</param-value>
     </init-param>
   </filter>
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f70371ba/solr/webapp/web/js/angular/app.js
----------------------------------------------------------------------
diff --git a/solr/webapp/web/js/angular/app.js b/solr/webapp/web/js/angular/app.js
index ee420d3..3a2009a 100644
--- a/solr/webapp/web/js/angular/app.js
+++ b/solr/webapp/web/js/angular/app.js
@@ -371,21 +371,25 @@ solrAdminApp.config([
 })
 .factory('authInterceptor', function($q, $rootScope, $timeout, $injector) {
   var started = function(config) {
-    config.headers['Authorization'] = "Basic c29scjpTb2xyUm9ja3M="; // solr / solrRocks
-    console.log("Added authorization header");
+    var ah = "Basic c29scjpzb2xyUm9ja3M=";  // solr / solrRocks
+    config.headers['Authorization'] = ah;
+    console.log("Added authorization header " + ah);
     return config || $q.when(config);
   };
 
   var ended = function(response) {
+    console.log("Response headers: " + JSON.stringify(response.headers, undefined, 2));
     if (response.headers['WWW-Authenticate'] != null) {
-      console.log("Got WWW-Authenticate header");
-      alert("WWW-Authenticate: " + response.headers['WWW-Authenticate']);
+      console.log("Got WWW-Authenticate header: " + response.headers['WWW-Authenticate']);
     }
     return response || $q.when(response);
   };
 
   var failed = function(rejection) {
-    console.log("Failed with rejection " + rejection);
+    console.log("Failed with rejection " + JSON.stringify(rejection, undefined, 2));
+    if (rejection.status === 401) {
+      console.log("Status code is 401");
+    }
     $rootScope.$broadcast('loadingStatusInactive');
     return $q.reject(rejection);
   };
@@ -395,6 +399,9 @@ solrAdminApp.config([
 .config(function($httpProvider) {
   $httpProvider.interceptors.push("httpInterceptor");
   $httpProvider.interceptors.push("authInterceptor");
+  // Tell the BasicAuth plugin that we are Admin UI so it can serve us a 'Authorization: xBasic xxxx' header
+  // so that the browser will not intercept the login dialogue
+  $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
 })
 .directive('fileModel', function ($parse) {
     return {