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

svn commit: r1679316 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/core/ core/src/java/org/apache/solr/security/ core/src/java/org/apache/solr/servlet/ core/src/test/org/apache/solr/security/ solrj/src/java/org/apache/solr/common/cloud/

Author: anshum
Date: Thu May 14 06:20:04 2015
New Revision: 1679316

URL: http://svn.apache.org/r1679316
Log:
SOLR-7275: Authorization framework for Solr. It defines an interface and a mechanism to create, load and use an Authorization plugin.

Added:
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java   (with props)
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationPlugin.java   (with props)
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationResponse.java   (with props)
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/package-info.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/TestAuthorizationFramework.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
    lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1679316&r1=1679315&r2=1679316&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Thu May 14 06:20:04 2015
@@ -180,6 +180,8 @@ New Features
 * SOLR-7522: Facet Module - Implement field/terms faceting over single-valued
   numeric fields. (yonik)
 
+* SOLR-7275: Authorization framework for Solr. It defines an interface and a mechanism to create, 
+  load, and use an Authorization plugin. (Noble Paul, Ishan Chattopadhyaya, Anshum Gupta)
 
 Bug Fixes
 ----------------------

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java?rev=1679316&r1=1679315&r2=1679316&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java Thu May 14 06:20:04 2015
@@ -17,9 +17,8 @@
 
 package org.apache.solr.core;
 
-import static com.google.common.base.Preconditions.*;
-
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -30,8 +29,9 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import org.apache.solr.cloud.ZkController;
-import org.apache.solr.cloud.ZkSolrResourceLoader;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.util.ExecutorUtil;
@@ -43,6 +43,7 @@ import org.apache.solr.handler.admin.Inf
 import org.apache.solr.handler.component.ShardHandlerFactory;
 import org.apache.solr.logging.LogWatcher;
 import org.apache.solr.request.SolrRequestHandler;
+import org.apache.solr.security.AuthorizationPlugin;
 import org.apache.solr.update.UpdateShardHandler;
 import org.apache.solr.util.DefaultSolrThreadFactory;
 import org.apache.solr.util.FileUtils;
@@ -50,8 +51,7 @@ import org.apache.zookeeper.KeeperExcept
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 
 /**
@@ -64,6 +64,8 @@ public class CoreContainer {
 
   final SolrCores solrCores = new SolrCores(this);
 
+  protected AuthorizationPlugin authorizationPlugin;
+
   public static class CoreLoadFailure {
 
     public final CoreDescriptor cd;
@@ -176,6 +178,27 @@ public class CoreContainer {
     this.containerProperties = new Properties(properties);
   }
 
+  private void intializeAuthorizationPlugin() {
+    //Initialize the Authorization module
+    Map securityProps = getZkController().getZkStateReader().getSecurityProps();
+    if(securityProps != null) {
+      Map authorizationConf = (Map) securityProps.get("authorization");
+      if(authorizationConf == null) return;
+      String klas = (String) authorizationConf.get("class");
+      if(klas == null){
+        throw new SolrException(ErrorCode.SERVER_ERROR, "class is required for authorization plugin");
+      }
+      log.info("Initializing authorization plugin: " + klas);
+      authorizationPlugin = getResourceLoader().newInstance((String) klas,
+          AuthorizationPlugin.class);
+
+      // Read and pass the authorization context to the plugin
+      authorizationPlugin.init(authorizationConf);
+    } else {
+      log.info("Security conf doesn't exist. Skipping setup for authorization module.");
+    }
+  }
+
   /**
    * This method allows subclasses to construct a CoreContainer
    * without any default init behavior.
@@ -246,6 +269,9 @@ public class CoreContainer {
     log.info("Node Name: " + hostName);
 
     zkSys.initZooKeeper(this, solrHome, cfg.getCloudConfig());
+    if (isZooKeeperAware()) {
+      intializeAuthorizationPlugin();
+    }
 
     collectionsHandler = createHandler(cfg.getCollectionsHandlerClass(), CollectionsHandler.class);
     containerHandlers.put(COLLECTIONS_HANDLER_PATH, collectionsHandler);
@@ -396,6 +422,16 @@ public class CoreContainer {
         }
       }
     }
+    
+    // It should be safe to close the authorization plugin at this point.
+    try {
+      if(authorizationPlugin != null) {
+        authorizationPlugin.close();
+      }
+    } catch (IOException e) {
+      log.warn("Exception while closing authorization plugin.", e);
+    }
+    
     org.apache.lucene.util.IOUtils.closeWhileHandlingException(loader); // best effort
   }
 
@@ -448,7 +484,7 @@ public class CoreContainer {
       solrCores.putDynamicDescriptor(name, cd);
     }
 
-    SolrCore old = null;
+    SolrCore old;
 
     if (isShutDown) {
       core.close();
@@ -640,7 +676,7 @@ public class CoreContainer {
 
     coresLocator.swap(this, solrCores.getCoreDescriptor(n0), solrCores.getCoreDescriptor(n1));
 
-    log.info("swapped: "+n0 + " with " + n1);
+    log.info("swapped: " + n0 + " with " + n1);
   }
 
   /**
@@ -689,8 +725,7 @@ public class CoreContainer {
       // cancel recovery in cloud mode
       core.getSolrCoreState().cancelRecovery();
     }
-    String configSetZkPath =  core.getResourceLoader() instanceof ZkSolrResourceLoader ?  ((ZkSolrResourceLoader)core.getResourceLoader()).getConfigSetZkPath() : null;
-
+    
     core.unloadOnClose(deleteIndexDir, deleteDataDir, deleteInstanceDir);
     if (close)
       core.close();
@@ -705,7 +740,6 @@ public class CoreContainer {
         throw new SolrException(ErrorCode.SERVER_ERROR, "Error unregistering core [" + name + "] from cloud state", e);
       }
     }
-
   }
 
   public void rename(String name, String toName) {
@@ -885,6 +919,11 @@ public class CoreContainer {
   public SolrResourceLoader getResourceLoader() {
     return loader;
   }
+
+  public AuthorizationPlugin getAuthorizationPlugin() {
+    return authorizationPlugin;
+  }
+  
 }
 
 class CloserThread extends Thread {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java?rev=1679316&r1=1679315&r2=1679316&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java Thu May 14 06:20:04 2015
@@ -17,33 +17,6 @@
 
 package org.apache.solr.core;
 
-import org.apache.lucene.analysis.util.CharFilterFactory;
-import org.apache.lucene.analysis.util.ResourceLoader;
-import org.apache.lucene.analysis.util.ResourceLoaderAware;
-import org.apache.lucene.analysis.util.TokenFilterFactory;
-import org.apache.lucene.analysis.util.TokenizerFactory;
-import org.apache.lucene.analysis.util.WordlistLoader;
-import org.apache.lucene.codecs.Codec;
-import org.apache.lucene.codecs.DocValuesFormat;
-import org.apache.lucene.codecs.PostingsFormat;
-import org.apache.lucene.util.IOUtils;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.handler.admin.CoreAdminHandler;
-import org.apache.solr.handler.component.SearchComponent;
-import org.apache.solr.handler.component.ShardHandlerFactory;
-import org.apache.solr.request.SolrRequestHandler;
-import org.apache.solr.response.QueryResponseWriter;
-import org.apache.solr.rest.RestManager;
-import org.apache.solr.schema.FieldType;
-import org.apache.solr.schema.ManagedIndexSchemaFactory;
-import org.apache.solr.schema.SimilarityFactory;
-import org.apache.solr.search.QParserPlugin;
-import org.apache.solr.update.processor.UpdateRequestProcessorFactory;
-import org.apache.solr.util.FileUtils;
-import org.apache.solr.util.plugin.SolrCoreAware;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
@@ -56,7 +29,6 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.OutputStreamWriter;
 import java.lang.reflect.Constructor;
 import java.net.MalformedURLException;
 import java.net.URI;
@@ -76,6 +48,33 @@ import java.util.concurrent.ConcurrentHa
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.lucene.analysis.util.CharFilterFactory;
+import org.apache.lucene.analysis.util.ResourceLoader;
+import org.apache.lucene.analysis.util.ResourceLoaderAware;
+import org.apache.lucene.analysis.util.TokenFilterFactory;
+import org.apache.lucene.analysis.util.TokenizerFactory;
+import org.apache.lucene.analysis.util.WordlistLoader;
+import org.apache.lucene.codecs.Codec;
+import org.apache.lucene.codecs.DocValuesFormat;
+import org.apache.lucene.codecs.PostingsFormat;
+import org.apache.lucene.util.IOUtils;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.handler.admin.CoreAdminHandler;
+import org.apache.solr.handler.component.SearchComponent;
+import org.apache.solr.handler.component.ShardHandlerFactory;
+import org.apache.solr.request.SolrRequestHandler;
+import org.apache.solr.response.QueryResponseWriter;
+import org.apache.solr.rest.RestManager;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.ManagedIndexSchemaFactory;
+import org.apache.solr.schema.SimilarityFactory;
+import org.apache.solr.search.QParserPlugin;
+import org.apache.solr.update.processor.UpdateRequestProcessorFactory;
+import org.apache.solr.util.FileUtils;
+import org.apache.solr.util.plugin.SolrCoreAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
  * @since solr 1.3
  */ 
@@ -88,7 +87,7 @@ public class SolrResourceLoader implemen
   static final String[] packages = {
       "", "analysis.", "schema.", "handler.", "search.", "update.", "core.", "response.", "request.",
       "update.processor.", "util.", "spelling.", "handler.component.", "handler.dataimport.",
-      "spelling.suggest.", "spelling.suggest.fst.", "rest.schema.analysis."
+      "spelling.suggest.", "spelling.suggest.fst.", "rest.schema.analysis.", "security."
   };
 
   protected URLClassLoader classLoader;

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java?rev=1679316&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java Thu May 14 06:20:04 2015
@@ -0,0 +1,55 @@
+package org.apache.solr.security;
+
+/*
+ * 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.
+ */
+
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.solr.common.params.SolrParams;
+
+/**
+ * Request context for Solr to be used by Authorization plugin.
+ */
+public abstract class AuthorizationContext {
+
+  public static class CollectionRequest {
+    final public String collectionName;
+
+    public CollectionRequest(String collectionName) {
+      this.collectionName = collectionName;
+    }
+  }
+  
+  public abstract SolrParams getParams() ;
+  
+  public abstract Principal getUserPrincipal() ;
+
+  public abstract String getHttpHeader(String header);
+  
+  public abstract Enumeration getHeaderNames();
+
+  public abstract List<CollectionRequest> getCollectionRequests() ;
+  
+  public abstract RequestType getRequestType();
+  
+  public abstract String getResource();
+
+  public enum RequestType {READ, WRITE, ADMIN, UNKNOWN}
+
+}
\ No newline at end of file

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationPlugin.java?rev=1679316&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationPlugin.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationPlugin.java Thu May 14 06:20:04 2015
@@ -0,0 +1,31 @@
+package org.apache.solr.security;
+
+/*
+ * 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.
+ */
+
+import java.io.Closeable;
+import java.util.Map;
+
+/**
+ * Authorization interface that needs to be implemented to write an authorization
+ * plugin.
+ */
+public interface AuthorizationPlugin extends Closeable {
+  AuthorizationResponse authorize(AuthorizationContext context);
+
+  void init(Map<String, Object> initInfo);
+}

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationResponse.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationResponse.java?rev=1679316&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationResponse.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/AuthorizationResponse.java Thu May 14 06:20:04 2015
@@ -0,0 +1,38 @@
+package org.apache.solr.security;
+
+/*
+ * 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.
+ */
+
+/* This class currently only stores an int statusCode (HttpStatus) value and a message but can 
+   be used to return ACLs and other information from the authorization plugin.
+ */
+public class AuthorizationResponse {
+  public final int statusCode;
+  String message;
+
+  public AuthorizationResponse(int httpStatusCode) {
+    this.statusCode = httpStatusCode;
+  }
+  
+  public String getMessage() {
+    return message;
+  }
+
+  public void setMessage(String message) {
+    this.message = message;
+  }
+}

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/package-info.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/package-info.java?rev=1679316&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/package-info.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/security/package-info.java Thu May 14 06:20:04 2015
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Commonly used classes for Solr security framework.
+ */
+package org.apache.solr.security;
+

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java?rev=1679316&r1=1679315&r2=1679316&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java Thu May 14 06:20:04 2015
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
+import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -42,6 +43,7 @@ import org.apache.http.HeaderIterator;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
 import org.apache.http.client.methods.HttpDelete;
 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
 import org.apache.http.client.methods.HttpGet;
@@ -79,16 +81,26 @@ import org.apache.solr.request.SolrReque
 import org.apache.solr.response.QueryResponseWriter;
 import org.apache.solr.response.QueryResponseWriterUtil;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.AuthorizationContext.CollectionRequest;
+import org.apache.solr.security.AuthorizationContext.RequestType;
+import org.apache.solr.security.AuthorizationResponse;
 import org.apache.solr.servlet.cache.HttpCacheHeaderUtil;
 import org.apache.solr.servlet.cache.Method;
 import org.apache.solr.update.processor.DistributingUpdateProcessorFactory;
 import org.apache.solr.util.RTimer;
 import org.apache.zookeeper.KeeperException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.slf4j.MDC;
 
 import static org.apache.solr.common.cloud.ZkStateReader.BASE_URL_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
 import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
 import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
+import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATE;
+import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE;
+import static org.apache.solr.common.params.CollectionParams.CollectionAction.RELOAD;
 import static org.apache.solr.servlet.SolrDispatchFilter.Action;
 import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN;
 import static org.apache.solr.servlet.SolrDispatchFilter.Action.FORWARD;
@@ -101,7 +113,9 @@ import static org.apache.solr.servlet.So
 /**
  * This class represents a call made to Solr
  **/
-class HttpSolrCall {
+public class HttpSolrCall {
+  private static Logger log = LoggerFactory.getLogger(HttpSolrCall.class);
+
   private final SolrDispatchFilter solrDispatchFilter;
   private final CoreContainer cores;
   private final HttpServletRequest req;
@@ -117,6 +131,19 @@ class HttpSolrCall {
   private SolrConfig config;
   private Map<String, Integer> invalidStates;
 
+  public RequestType getRequestType() {
+    return requestType;
+  }
+
+  private RequestType requestType;
+
+
+  public List<String> getCollectionsList() {
+    return collectionsList;
+  }
+
+  private List<String> collectionsList;
+
   HttpSolrCall(SolrDispatchFilter solrDispatchFilter, CoreContainer cores,
                HttpServletRequest request, HttpServletResponse response, boolean retry) {
     this.solrDispatchFilter = solrDispatchFilter;
@@ -124,6 +151,7 @@ class HttpSolrCall {
     this.req = request;
     this.response = response;
     this.retry = retry;
+    this.requestType = RequestType.UNKNOWN;
     queryParams = SolrRequestParsers.parseQueryString(req.getQueryString());
   }
 
@@ -131,7 +159,20 @@ class HttpSolrCall {
     return path;
   }
 
-  private void setContext() throws Exception {
+
+  public HttpServletRequest getReq() {
+    return req;
+  }
+
+  public SolrCore getCore() {
+    return core;
+  }
+
+  public SolrParams getQueryParams() {
+    return queryParams;
+  }
+  
+  private void init() throws Exception {
     //The states of client that is invalid in this request
     Aliases aliases = null;
     String corename = "";
@@ -158,13 +199,13 @@ class HttpSolrCall {
     }
 
     boolean usingAliases = false;
-    List<String> collectionsList = null;
 
     // Check for container handlers
     handler = cores.getRequestHandler(path);
     if (handler != null) {
       solrReq = SolrRequestParsers.DEFAULT.parse(null, path, req);
       solrReq.getContext().put(CoreContainer.class.getName(), cores);
+      requestType = RequestType.ADMIN;
       action = ADMIN;
       return;
     } else {
@@ -210,6 +251,9 @@ class HttpSolrCall {
         // we found a core, update the path
         path = path.substring(idx);
         addMDCValues();
+        if (collectionsList == null)
+          collectionsList = new ArrayList<>();
+        collectionsList.add(corename);
       }
 
       // if we couldn't find it locally, look on other nodes
@@ -351,8 +395,28 @@ class HttpSolrCall {
     }
 
     try {
-      setContext();
-      
+      init();
+      /* Authorize the request if
+       1. Authorization is enabled, and
+       2. The requested resource is not a known static file
+        */
+      // TODO: There should be a better way to ignore the static files.
+      if (cores.getAuthorizationPlugin() != null &&
+          !(req.getRequestURI().endsWith(".html") 
+              || req.getRequestURI().endsWith(".png")
+              || req.getRequestURI().endsWith(".ico")
+              || req.getRequestURI().endsWith(".css")
+          )) {
+        AuthorizationContext context = getAuthCtx();
+        log.info(context.toString());
+        AuthorizationResponse authResponse = cores.getAuthorizationPlugin().authorize(context);
+        if (!(authResponse.statusCode == HttpStatus.SC_ACCEPTED) && !(authResponse.statusCode == HttpStatus.SC_OK)) {
+          sendError(authResponse.statusCode,
+              "Unauthorized request, Response code: " + authResponse.statusCode);
+          return RETURN;
+        }
+      }
+
       HttpServletResponse resp = response;
       switch (action) {
         case ADMIN:
@@ -593,7 +657,7 @@ class HttpSolrCall {
 
   private void processAliases(Aliases aliases,
                               List<String> collectionsList) {
-    String collection = solrReq.getParams().get("collection");
+    String collection = solrReq.getParams().get(COLLECTION_PROP);
     if (collection != null) {
       collectionsList = StrUtils.splitSmart(collection, ",", true);
     }
@@ -621,7 +685,7 @@ class HttpSolrCall {
         }
         ModifiableSolrParams params = new ModifiableSolrParams(
             solrReq.getParams());
-        params.set("collection", collectionString.toString());
+        params.set(COLLECTION_PROP, collectionString.toString());
         solrReq.setParams(params);
       }
     }
@@ -760,6 +824,10 @@ class HttpSolrCall {
       return null;
     }
 
+    if (collectionsList == null)
+      collectionsList = new ArrayList<>();
+
+    collectionsList.add(collectionName);
     String coreUrl = getCoreUrl(collectionName, origCorename, clusterState,
         slices, byCoreName, true);
 
@@ -807,6 +875,105 @@ class HttpSolrCall {
     return null;
   }
 
+  private AuthorizationContext getAuthCtx() {
+
+    String resource = getPath();
+
+    SolrParams params = getQueryParams();
+    final ArrayList<CollectionRequest> collectionRequests = new ArrayList<>();
+    if (getCollectionsList() != null) {
+      for (String collection : getCollectionsList()) {
+        collectionRequests.add(new CollectionRequest(collection));
+      }
+    }
+
+    // Extract collection name from the params in case of a Collection Admin request
+    if (getPath().equals("/admin/collections")) {
+      if (CREATE.isEqual(params.get("action"))||
+          RELOAD.isEqual(params.get("action"))||
+          DELETE.isEqual(params.get("action")))
+        collectionRequests.add(new CollectionRequest(params.get("name")));
+      else if (params.get(COLLECTION_PROP) != null)
+        collectionRequests.add(new CollectionRequest(params.get(COLLECTION_PROP)));
+    }
+    
+    // Handle the case when it's a /select request and collections are specified as a param
+    if(resource.equals("/select") && params.get("collection") != null) {
+      collectionRequests.clear();
+      for(String collection:params.get("collection").split(",")) {
+        collectionRequests.add(new CollectionRequest(collection));
+      }
+    }
+    
+    // Populate the request type if the request is select or update
+    if(requestType == RequestType.UNKNOWN) {
+      if(resource.startsWith("/select"))
+        requestType = RequestType.READ;
+      if(resource.startsWith("/update"))
+        requestType = RequestType.WRITE;
+    }
+
+    // There's no collection explicitly mentioned, let's try and extract it from the core if one exists for
+    // the purpose of processing this request.
+    if (getCore() != null && (getCollectionsList() == null || getCollectionsList().size() == 0)) {
+      collectionRequests.add(new CollectionRequest(getCore().getCoreDescriptor().getCollectionName()));
+    }
+
+    if (getQueryParams().get(COLLECTION_PROP) != null)
+      collectionRequests.add(new CollectionRequest(getQueryParams().get(COLLECTION_PROP)));
+
+    return new AuthorizationContext() {
+      @Override
+      public SolrParams getParams() {
+        return getQueryParams();
+      }
+
+      @Override
+      public Principal getUserPrincipal() {
+        return getReq().getUserPrincipal();
+      }
+
+      @Override
+      public String getHttpHeader(String s) {
+        return getReq().getHeader(s);
+      }
+      
+      @Override
+      public Enumeration getHeaderNames() {
+        return getReq().getHeaderNames();
+      }
+
+      @Override
+      public List<CollectionRequest> getCollectionRequests() {
+        return collectionRequests;
+      }
+
+      @Override
+      public RequestType getRequestType() {
+        return requestType;
+      }
+      
+      public String getResource() {
+        return path;
+      }
+      
+      @Override
+      public String toString() {
+        StringBuilder response = new StringBuilder("userPrincipal: [").append(getUserPrincipal()).append("]")
+            .append(" type: [").append(requestType.toString()).append("], collections: [");
+        for (CollectionRequest collectionRequest : collectionRequests) {
+          response.append(collectionRequest.collectionName).append(", ");
+        }
+        if(collectionRequests.size() > 0)
+          response.delete(response.length() - 1, response.length());
+        
+        response.append("], Path: [").append(resource).append("]");
+        return response.toString();
+      }
+    };
+
+  }
+
   static final String CONNECTION_HEADER = "Connection";
   static final String TRANSFER_ENCODING_HEADER = "Transfer-Encoding";
   static final String CONTENT_LENGTH_HEADER = "Content-Length";

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java?rev=1679316&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/MockAuthorizationPlugin.java Thu May 14 06:20:04 2015
@@ -0,0 +1,52 @@
+package org.apache.solr.security;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MockAuthorizationPlugin implements AuthorizationPlugin{
+
+  private Logger log = LoggerFactory.getLogger(MockAuthorizationPlugin.class);
+  HashSet<String> denyUsers;
+  
+  @Override
+  public AuthorizationResponse authorize(AuthorizationContext context) {
+    log.info("User request: " + context.getParams().get("uname"));
+    if(denyUsers.contains(context.getParams().get("uname")))
+      return new AuthorizationResponse(403);
+    else
+      return new AuthorizationResponse(200);
+  }
+
+  @Override
+  public void init(Map<String, Object> initInfo) {
+    denyUsers = new HashSet();
+    denyUsers.add("user1");
+    denyUsers.add("user2");
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+}
\ No newline at end of file

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/TestAuthorizationFramework.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/TestAuthorizationFramework.java?rev=1679316&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/TestAuthorizationFramework.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/security/TestAuthorizationFramework.java Thu May 14 06:20:04 2015
@@ -0,0 +1,63 @@
+package org.apache.solr.security;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.io.Charsets;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.zookeeper.CreateMode;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@LuceneTestCase.Slow
+public class TestAuthorizationFramework extends AbstractFullDistribZkTestBase {
+  final private Logger log = LoggerFactory.getLogger(TestAuthorizationFramework.class);
+
+  static final int TIMEOUT = 10000;
+
+  public void distribSetUp() throws Exception {
+    super.distribSetUp();
+    try (ZkStateReader zkStateReader = new ZkStateReader(zkServer.getZkAddress(),
+        TIMEOUT, TIMEOUT)) {
+      zkStateReader.getZkClient().create(ZkStateReader.SOLR_SECURITY_CONF_PATH,
+          "{\"authorization\":{\"class\":\"org.apache.solr.security.MockAuthorizationPlugin\"}}".getBytes(Charsets.UTF_8),
+          CreateMode.PERSISTENT, true);
+    }
+  }
+
+  @Test
+  public void authorizationFrameworkTest() throws Exception {
+    waitForThingsToLevelOut(10);
+    log.info("Starting test");
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.add("q", "*:*");
+    // This should work fine.
+    cloudClient.query(params);
+    
+    // This user is blacklisted in the mock. The request should return a 403.
+    params.add("uname", "user1");
+    try {
+      cloudClient.query(params);
+      fail("This should have failed");
+    } catch (Exception e) {}
+    log.info("Ending test");
+  }
+}

Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java?rev=1679316&r1=1679315&r2=1679316&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java Thu May 14 06:20:04 2015
@@ -84,6 +84,7 @@ public class ZkStateReader implements Cl
   public static final String CLUSTER_STATE = "/clusterstate.json";
   public static final String CLUSTER_PROPS = "/clusterprops.json";
   public static final String REJOIN_AT_HEAD_PROP = "rejoinAtHead";
+  public static final String SOLR_SECURITY_CONF_PATH = "/security.json";
 
   public static final String REPLICATION_FACTOR = "replicationFactor";
   public static final String MAX_SHARDS_PER_NODE = "maxShardsPerNode";
@@ -830,6 +831,23 @@ public class ZkStateReader implements Cl
     }
   }
 
+
+
+  /**
+   * Returns the content of /security.json from ZooKeeper as a Map
+   * If the files doesn't exist, it returns null.
+   */
+  public Map getSecurityProps() {
+    try {
+      if(getZkClient().exists(SOLR_SECURITY_CONF_PATH, true)) {
+        return (Map) ZkStateReader.fromJSON(getZkClient()
+            .getData(ZkStateReader.SOLR_SECURITY_CONF_PATH, null, new Stat(), true)) ;
+      }
+    } catch (KeeperException | InterruptedException e) {
+      throw new SolrException(ErrorCode.SERVER_ERROR,"Error reading security properties",e) ;
+    }
+    return null;
+  }
   /**
    * Returns the baseURL corresponding to a given node's nodeName --
    * NOTE: does not (currently) imply that the nodeName (or resulting