http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetSolrHostsCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetSolrHostsCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetSolrHostsCommand.java
new file mode 100644
index 0000000..5a14a44
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetSolrHostsCommand.java
@@ -0,0 +1,53 @@
+/*
+  * 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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.zookeeper.ZooKeeper;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class GetSolrHostsCommand extends 
AbstractRetryCommand<Collection<String>> {
+
+  public GetSolrHostsCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  public Collection<String> createAndProcessRequest(AmbariSolrCloudClient 
solrCloudClient) throws Exception {
+    List<String> solrHosts = new ArrayList<>();
+
+    ZooKeeper zk = new ZooKeeper(solrCloudClient.getZkConnectString(), 10000, 
null);
+    List<String> ids = zk.getChildren("/live_nodes", false);
+    for (String id : ids) {
+      if (id.endsWith("_solr")) {
+        String hostAndPort = id.substring(0, id.length() - 5);
+        String[] tokens = hostAndPort.split(":");
+        String host = InetAddress.getByName(tokens[0]).getHostName();
+
+        solrHosts.add(host);
+      }
+    }
+
+    return solrHosts;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetStateFileZkCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetStateFileZkCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetStateFileZkCommand.java
new file mode 100644
index 0000000..10a8daa
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/GetStateFileZkCommand.java
@@ -0,0 +1,43 @@
+/*
+ * 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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.ambari.infra.solr.domain.AmbariSolrState;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.cloud.SolrZooKeeper;
+
+public class GetStateFileZkCommand extends AbstractStateFileZkCommand {
+  private String unsecureZnode;
+
+  public GetStateFileZkCommand(int maxRetries, int interval, String 
unsecureZnode) {
+    super(maxRetries, interval);
+    this.unsecureZnode = unsecureZnode;
+  }
+
+  @Override
+  protected AmbariSolrState executeZkCommand(AmbariSolrCloudClient client, 
SolrZkClient zkClient, SolrZooKeeper solrZooKeeper) throws Exception {
+    AmbariSolrState result = AmbariSolrState.UNSECURE;
+    String stateFile = String.format("%s/%s", unsecureZnode, 
AbstractStateFileZkCommand.STATE_FILE);
+    if (zkClient.exists(stateFile, true)) {
+      result = getStateFromJson(client, stateFile);
+    }
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/ListCollectionCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/ListCollectionCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/ListCollectionCommand.java
new file mode 100644
index 0000000..41094c7
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/ListCollectionCommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+
+import java.util.List;
+
+public class ListCollectionCommand extends 
AbstractSolrRetryCommand<CollectionAdminRequest.List, List<String>> {
+
+  public ListCollectionCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  public List<String> handleResponse(CollectionAdminResponse response, 
AmbariSolrCloudClient client) throws Exception {
+    List<String> allCollectionList = (List<String>) response
+      .getResponse().get("collections");
+    return allCollectionList;
+  }
+
+  @Override
+  public CollectionAdminRequest.List createRequest(AmbariSolrCloudClient 
client) {
+    return new CollectionAdminRequest.List();
+  }
+
+  @Override
+  public String errorMessage(AmbariSolrCloudClient client) {
+    return "Cannot get collections.";
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureSolrZNodeZkCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureSolrZNodeZkCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureSolrZNodeZkCommand.java
new file mode 100644
index 0000000..6958623
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureSolrZNodeZkCommand.java
@@ -0,0 +1,86 @@
+/*
+ * 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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.ambari.infra.solr.util.AclUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.cloud.SolrZooKeeper;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.data.Stat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class SecureSolrZNodeZkCommand extends 
AbstractZookeeperRetryCommand<Boolean> {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(SecureSolrZNodeZkCommand.class);
+
+  public SecureSolrZNodeZkCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  protected Boolean executeZkCommand(AmbariSolrCloudClient client, 
SolrZkClient zkClient, SolrZooKeeper solrZooKeeper) throws Exception {
+    String zNode = client.getZnode();
+    List<ACL> newAclList = new ArrayList<>();
+    List<ACL> saslUserList = 
AclUtils.createAclListFromSaslUsers(client.getSaslUsers().split(","));
+    newAclList.addAll(saslUserList);
+    newAclList.add(new ACL(ZooDefs.Perms.READ, new Id("world", "anyone")));
+
+    String configsPath = String.format("%s/%s", zNode, "configs");
+    String collectionsPath = String.format("%s/%s", zNode, "collections");
+    String aliasesPath = String.format("%s/%s", zNode, "aliases.json"); // 
TODO: protect this later somehow
+    List<String> excludePaths = Arrays.asList(configsPath, collectionsPath, 
aliasesPath);
+
+    createZnodeIfNeeded(configsPath, client.getSolrZkClient());
+    createZnodeIfNeeded(collectionsPath, client.getSolrZkClient());
+
+    AclUtils.setRecursivelyOn(client.getSolrZkClient().getSolrZooKeeper(), 
zNode, newAclList, excludePaths);
+
+    List<ACL> commonConfigAcls = new ArrayList<>();
+    commonConfigAcls.addAll(saslUserList);
+    commonConfigAcls.add(new ACL(ZooDefs.Perms.READ | ZooDefs.Perms.CREATE, 
new Id("world", "anyone")));
+
+    LOG.info("Set sasl users for znode '{}' : {}", client.getZnode(), 
StringUtils.join(saslUserList, ","));
+    LOG.info("Skip {}/configs and {}/collections", client.getZnode(), 
client.getZnode());
+    solrZooKeeper.setACL(configsPath, 
AclUtils.mergeAcls(solrZooKeeper.getACL(configsPath, new Stat()), 
commonConfigAcls), -1);
+    solrZooKeeper.setACL(collectionsPath, 
AclUtils.mergeAcls(solrZooKeeper.getACL(collectionsPath, new Stat()), 
commonConfigAcls), -1);
+
+    LOG.info("Set world:anyone to 'cr' on  {}/configs and {}/collections", 
client.getZnode(), client.getZnode());
+    AclUtils.setRecursivelyOn(solrZooKeeper, configsPath, saslUserList);
+    AclUtils.setRecursivelyOn(solrZooKeeper, collectionsPath, saslUserList);
+
+    return true;
+  }
+
+  private void createZnodeIfNeeded(String configsPath, SolrZkClient zkClient) 
throws KeeperException, InterruptedException {
+    if (!zkClient.exists(configsPath, true)) {
+      LOG.info("'{}' does not exist. Creating it ...", configsPath);
+      zkClient.makePath(configsPath, true);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureZNodeZkCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureZNodeZkCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureZNodeZkCommand.java
new file mode 100644
index 0000000..a96dc5d
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SecureZNodeZkCommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.ambari.infra.solr.util.AclUtils;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.cloud.SolrZooKeeper;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SecureZNodeZkCommand extends 
AbstractZookeeperRetryCommand<Boolean> {
+
+  public SecureZNodeZkCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  protected Boolean executeZkCommand(AmbariSolrCloudClient client, 
SolrZkClient zkClient, SolrZooKeeper solrZooKeeper) throws Exception {
+    String zNode = client.getZnode();
+    List<ACL> newAclList = new ArrayList<>();
+    List<ACL> saslUserList = 
AclUtils.createAclListFromSaslUsers(client.getSaslUsers().split(","));
+    newAclList.addAll(saslUserList);
+    newAclList.add(new ACL(ZooDefs.Perms.READ, new Id("world", "anyone")));
+    AclUtils.setRecursivelyOn(client.getSolrZkClient().getSolrZooKeeper(), 
zNode, newAclList);
+    return true;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SetClusterPropertyZkCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SetClusterPropertyZkCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SetClusterPropertyZkCommand.java
new file mode 100644
index 0000000..34597c6
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/SetClusterPropertyZkCommand.java
@@ -0,0 +1,40 @@
+/*
+ * 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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.cloud.SolrZooKeeper;
+import org.apache.solr.common.cloud.ZkStateReader;
+
+public class SetClusterPropertyZkCommand extends 
AbstractZookeeperRetryCommand<String>{
+
+  public SetClusterPropertyZkCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  protected String executeZkCommand(AmbariSolrCloudClient client, SolrZkClient 
zkClient, SolrZooKeeper solrZooKeeper) throws Exception {
+    String propertyName = client.getPropName();
+    String propertyValue = client.getPropValue();
+    ZkStateReader reader = new ZkStateReader(zkClient);
+    reader.setClusterProperty(propertyName, propertyValue);
+    return propertyValue;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UpdateStateFileZkCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UpdateStateFileZkCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UpdateStateFileZkCommand.java
new file mode 100644
index 0000000..2b360fb
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UpdateStateFileZkCommand.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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.ambari.infra.solr.domain.AmbariSolrState;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.cloud.SolrZooKeeper;
+import org.apache.zookeeper.CreateMode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+public class UpdateStateFileZkCommand extends AbstractStateFileZkCommand {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(UpdateStateFileZkCommand.class);
+
+  private String unsecureZnode;
+
+  public UpdateStateFileZkCommand(int maxRetries, int interval, String 
unsecureZnode) {
+    super(maxRetries, interval);
+    this.unsecureZnode = unsecureZnode;
+  }
+
+  @Override
+  protected AmbariSolrState executeZkCommand(AmbariSolrCloudClient client, 
SolrZkClient zkClient, SolrZooKeeper solrZooKeeper) throws Exception {
+    boolean secure = client.isSecure();
+    String stateFile = String.format("%s/%s", unsecureZnode, 
AbstractStateFileZkCommand.STATE_FILE);
+    AmbariSolrState result = null;
+    if (secure) {
+      LOG.info("Update state file in secure mode.");
+      updateStateFile(client, zkClient, AmbariSolrState.SECURE, stateFile);
+      result = AmbariSolrState.SECURE;
+    } else {
+      LOG.info("Update state file in unsecure mode.");
+      updateStateFile(client, zkClient, AmbariSolrState.UNSECURE, stateFile);
+      result = AmbariSolrState.UNSECURE;
+    }
+    return result;
+  }
+
+  private void updateStateFile(AmbariSolrCloudClient client, SolrZkClient 
zkClient, AmbariSolrState stateToUpdate,
+                               String stateFile) throws Exception {
+    if (!zkClient.exists(stateFile, true)) {
+      LOG.info("State file does not exits. Initializing it as '{}'", 
stateToUpdate);
+      zkClient.create(stateFile, 
createStateJson(stateToUpdate).getBytes(StandardCharsets.UTF_8),
+        CreateMode.PERSISTENT, true);
+    } else {
+      AmbariSolrState stateOnSecure = getStateFromJson(client, stateFile);
+      if (stateToUpdate.equals(stateOnSecure)) {
+        LOG.info("State file is in '{}' mode. No update.", stateOnSecure);
+      } else {
+        LOG.info("State file is in '{}' mode. Updating it to '{}'", 
stateOnSecure, stateToUpdate);
+        zkClient.setData(stateFile, 
createStateJson(stateToUpdate).getBytes(StandardCharsets.UTF_8), true);
+      }
+    }
+  }
+
+  private String createStateJson(AmbariSolrState state) throws Exception {
+    Map<String, String> secStateMap = new HashMap<>();
+    secStateMap.put(AbstractStateFileZkCommand.STATE_FIELD, state.toString());
+    return new ObjectMapper().writeValueAsString(secStateMap);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UploadConfigZkCommand.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UploadConfigZkCommand.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UploadConfigZkCommand.java
new file mode 100644
index 0000000..fc7482d
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/commands/UploadConfigZkCommand.java
@@ -0,0 +1,41 @@
+/*
+ * 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.ambari.infra.solr.commands;
+
+import org.apache.ambari.infra.solr.AmbariSolrCloudClient;
+import org.apache.solr.common.cloud.ZkConfigManager;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class UploadConfigZkCommand extends 
AbstractZookeeperConfigCommand<String> {
+
+  public UploadConfigZkCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  protected String executeZkConfigCommand(ZkConfigManager zkConfigManager, 
AmbariSolrCloudClient client) throws Exception {
+    Path configDir = Paths.get(client.getConfigDir());
+    String configSet = client.getConfigSet();
+    zkConfigManager.uploadConfigDir(configDir, configSet);
+    return configSet;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/domain/AmbariSolrState.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/domain/AmbariSolrState.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/domain/AmbariSolrState.java
new file mode 100644
index 0000000..489d3f1
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/domain/AmbariSolrState.java
@@ -0,0 +1,26 @@
+/*
+ * 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.ambari.infra.solr.domain;
+
+/**
+ * Enum state values for storing security status in unsecure znode
+ */
+public enum AmbariSolrState {
+  SECURE, UNSECURE
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/AclUtils.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/AclUtils.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/AclUtils.java
new file mode 100644
index 0000000..dd5d6c8
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/AclUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.ambari.infra.solr.util;
+
+import org.apache.solr.common.cloud.SolrZooKeeper;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.data.Stat;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AclUtils {
+
+  public static List<ACL> mergeAcls(List<ACL> originalAcls, List<ACL> 
updateAcls) {
+    Map<String, ACL> aclMap = new HashMap<>();
+    List<ACL> acls = new ArrayList<>();
+    if (originalAcls != null) {
+      for (ACL acl : originalAcls) {
+        aclMap.put(acl.getId().getId(), acl);
+      }
+    }
+
+    if (updateAcls != null) {
+      for (ACL acl : updateAcls) {
+        aclMap.put(acl.getId().getId(), acl);
+      }
+    }
+
+    for (Map.Entry<String, ACL> aclEntry : aclMap.entrySet()) {
+      acls.add(aclEntry.getValue());
+    }
+    return acls;
+  }
+
+  public static List<ACL> createAclListFromSaslUsers(String[] saslUsers) {
+    List<ACL> saslUserList = new ArrayList<>();
+    for (String saslUser : saslUsers) {
+      ACL acl = new ACL();
+      acl.setId(new Id("sasl", saslUser));
+      acl.setPerms(ZooDefs.Perms.ALL);
+      saslUserList.add(acl);
+    }
+    return saslUserList;
+  }
+
+  public static void setRecursivelyOn(SolrZooKeeper solrZooKeeper, String 
node, List<ACL> acls) throws KeeperException, InterruptedException {
+    setRecursivelyOn(solrZooKeeper, node, acls, new ArrayList<String>());
+  }
+
+  public static void setRecursivelyOn(SolrZooKeeper solrZooKeeper, String 
node, List<ACL> acls, List<String> excludePaths)
+    throws KeeperException, InterruptedException {
+    if (!excludePaths.contains(node)) {
+      List<ACL> newAcls = AclUtils.mergeAcls(solrZooKeeper.getACL(node, new 
Stat()), acls);
+      solrZooKeeper.setACL(node, newAcls, -1);
+      for (String child : solrZooKeeper.getChildren(node, null)) {
+        setRecursivelyOn(solrZooKeeper, path(node, child), acls, excludePaths);
+      }
+    }
+  }
+
+  private static String path(String node, String child) {
+    return node.endsWith("/") ? node + child : node + "/" + child;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/ShardUtils.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/ShardUtils.java
 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/ShardUtils.java
new file mode 100644
index 0000000..f46565b
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/main/java/org/apache/ambari/infra/solr/util/ShardUtils.java
@@ -0,0 +1,71 @@
+/*
+ * 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.ambari.infra.solr.util;
+
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+public class ShardUtils {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ShardUtils.class);
+
+  public static String generateShardListStr(int maxShardsPerNode) {
+    String shardsListStr = "";
+    for (int i = 0; i < maxShardsPerNode; i++) {
+      if (i != 0) {
+        shardsListStr += ",";
+      }
+      String shard = "shard" + i;
+      shardsListStr += shard;
+    }
+    return shardsListStr;
+  }
+
+  public static List<String> generateShardList(int maxShardsPerNode) {
+    List<String> shardsList = new ArrayList<>();
+    for (int i = 0; i < maxShardsPerNode; i++) {
+      shardsList.add("shard" + i);
+    }
+    return shardsList;
+  }
+
+  public static Collection<String> getShardNamesFromSlices(Collection<Slice> 
slices, String collection) {
+    Collection<String> result = new HashSet<String>();
+    Iterator<Slice> iter = slices.iterator();
+    while (iter.hasNext()) {
+      Slice slice = iter.next();
+      for (Replica replica : slice.getReplicas()) {
+        LOG.info("collectionName=" + collection + ", slice.name="
+          + slice.getName() + ", slice.state=" + slice.getState()
+          + ", replica.core=" + replica.getStr("core")
+          + ", replica.state=" + replica.getStr("state"));
+        result.add(slice.getName());
+      }
+    }
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/resources/log4j.properties 
b/ambari-infra/ambari-infra-solr-client/src/main/resources/log4j.properties
new file mode 100644
index 0000000..e8dca12
--- /dev/null
+++ b/ambari-infra/ambari-infra-solr-client/src/main/resources/log4j.properties
@@ -0,0 +1,31 @@
+# Copyright 2011 The Apache Software Foundation
+#
+# 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.
+
+log4j.rootLogger=INFO,stdout,stderr
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Threshold=INFO
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%m%n
+
+log4j.appender.stderr=org.apache.log4j.ConsoleAppender
+log4j.appender.stderr.Threshold=ERROR
+log4j.appender.stderr.Target=System.err
+log4j.appender.stderr.layout=org.apache.log4j.PatternLayout
+log4j.appender.stderr.layout.ConversionPattern=%m%n
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/main/resources/solrCloudCli.sh
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/main/resources/solrCloudCli.sh 
b/ambari-infra/ambari-infra-solr-client/src/main/resources/solrCloudCli.sh
new file mode 100644
index 0000000..cd47f06
--- /dev/null
+++ b/ambari-infra/ambari-infra-solr-client/src/main/resources/solrCloudCli.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# 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.
+
+JVM="java"
+sdir="`dirname \"$0\"`"
+
+PATH=$JAVA_HOME/bin:$PATH $JVM -classpath "$sdir:$sdir/libs/*" 
org.apache.ambari.logsearch.solr.AmbariSolrCloudCLI ${1+"$@"}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-client/src/test/java/org/apache/ambari/infra/solr/AmbariSolrCloudClientTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-client/src/test/java/org/apache/ambari/infra/solr/AmbariSolrCloudClientTest.java
 
b/ambari-infra/ambari-infra-solr-client/src/test/java/org/apache/ambari/infra/solr/AmbariSolrCloudClientTest.java
new file mode 100644
index 0000000..44f3ec5
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-client/src/test/java/org/apache/ambari/infra/solr/AmbariSolrCloudClientTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.ambari.infra.solr;
+
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.util.NamedList;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class AmbariSolrCloudClientTest {
+
+  private AmbariSolrCloudClient underTest;
+
+  private CloudSolrClient mockedSolrClient;
+
+  private SolrZkClient mockedSolrZkClient;
+
+  private CollectionAdminResponse mockedResponse;
+
+  @Before
+  public void setUp() {
+    AmbariSolrCloudClientBuilder builder = new AmbariSolrCloudClientBuilder();
+
+    mockedSolrClient = createMock(CloudSolrClient.class);
+    mockedSolrZkClient = createMock(SolrZkClient.class);
+    mockedResponse = createMock(CollectionAdminResponse.class);
+
+    builder.solrCloudClient = mockedSolrClient;
+    builder.solrZkClient = mockedSolrZkClient;
+
+    underTest = builder
+      .withZkConnectString("localhost1:2181,localhost2:2182")
+      .withCollection("collection1")
+      .withConfigSet("configSet")
+      .withShards(1)
+      .withReplication(1)
+      .withMaxShardsPerNode(2)
+      .withInterval(1)
+      .withRetry(2)
+      .withRouterName("routerName")
+      .withRouterField("routerField")
+      .build();
+  }
+
+  @Test
+  public void testCreateCollectionWhenCollectionDoesNotExist() throws 
Exception {
+    // GIVEN
+    NamedList<Object> namedList = new NamedList<>();
+    namedList.add("collections", Arrays.asList("collection1", "collection2"));
+
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), 
anyString())).andReturn(namedList).times(1);
+    replay(mockedSolrClient);
+
+    // WHEN
+    String result = underTest.createCollection();
+    // THEN
+    assertEquals("collection1", result);
+    verify(mockedSolrClient);
+  }
+
+  @Test
+  public void testCreateCollectionWhenCollectionExists() throws Exception {
+    // GIVEN
+    NamedList<Object> namedList = new NamedList<>();
+    namedList.add("collections", Arrays.asList("collection2", "collection3"));
+
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), 
anyString())).andReturn(namedList).times(2);
+    replay(mockedSolrClient);
+
+    // WHEN
+    String result = underTest.createCollection();
+    // THEN
+    assertEquals("collection1", result);
+    verify(mockedSolrClient);
+  }
+
+  @Test
+  public void testListCollections() throws Exception {
+    // GIVEN
+    NamedList<Object> namedList = new NamedList<>();
+    namedList.add("collections", Arrays.asList("collection1", "collection2"));
+
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), 
anyString())).andReturn(namedList);
+
+    replay(mockedSolrClient);
+    // WHEN
+    List<String> result = underTest.listCollections();
+
+    // THEN
+    assertTrue(result.contains("collection1"));
+    assertTrue(result.contains("collection2"));
+    assertEquals(2, result.size());
+  }
+
+  @Test(expected = AmbariSolrCloudClientException.class)
+  public void testRetries() throws Exception {
+    // GIVEN
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), 
anyString())).andThrow(new RuntimeException("ex")).times(2);
+    replay(mockedSolrClient);
+    // WHEN
+    underTest.listCollections();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-solr-plugin/pom.xml 
b/ambari-infra/ambari-infra-solr-plugin/pom.xml
new file mode 100644
index 0000000..c890cec
--- /dev/null
+++ b/ambari-infra/ambari-infra-solr-plugin/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <parent>
+    <artifactId>ambari-infra</artifactId>
+    <groupId>org.apache.ambari</groupId>
+    <version>2.0.0.0-SNAPSHOT</version>
+  </parent>
+  <name>Ambari Infra Solr Plugin</name>
+  <url>http://maven.apache.org</url>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>ambari-infra-solr-plugin</artifactId>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.solr</groupId>
+      <artifactId>solr-core</artifactId>
+      <version>${solr.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.solr</groupId>
+      <artifactId>solr-test-framework</artifactId>
+      <version>${solr.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.3</version>
+        <configuration>
+          <source>1.7</source>
+          <target>1.7</target>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java
 
b/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java
new file mode 100644
index 0000000..4a47a89
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.ambari.infra.security;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Validate that the user has the right access based on the hostname in the 
kerberos principal
+ */
+public class InfraKerberosHostValidator {
+
+  public boolean validate(Principal principal, Map<String, Set<String>> 
userVsHosts, Map<String, String> userVsHostRegex) {
+    if (principal instanceof AuthenticationToken) {
+      AuthenticationToken authenticationToken = (AuthenticationToken) 
principal;
+      KerberosName kerberosName = new 
KerberosName(authenticationToken.getName());
+      String hostname = kerberosName.getHostName();
+      String serviceUserName = kerberosName.getServiceName();
+      if (MapUtils.isNotEmpty(userVsHostRegex)) {
+        String regex = userVsHostRegex.get(serviceUserName);
+        return hostname.matches(regex);
+      }
+      if (MapUtils.isNotEmpty(userVsHosts)) {
+        Set<String> hosts = userVsHosts.get(serviceUserName);
+        if (CollectionUtils.isNotEmpty(hosts)) {
+          return hosts.contains(hostname);
+        }
+      }
+    }
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java
 
b/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java
new file mode 100644
index 0000000..2f1a558
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java
@@ -0,0 +1,542 @@
+/*
+ * 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.ambari.infra.security;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CollectionParams;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.AuthorizationPlugin;
+import org.apache.solr.security.AuthorizationResponse;
+import org.apache.solr.security.ConfigEditablePlugin;
+import org.apache.solr.util.CommandOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Collections.singleton;
+import static org.apache.solr.common.params.CommonParams.NAME;
+import static org.apache.solr.common.util.Utils.getDeepCopy;
+import static org.apache.solr.handler.admin.SecurityConfHandler.getListValue;
+import static org.apache.solr.handler.admin.SecurityConfHandler.getMapValue;
+
+/**
+ * Modified copy of solr.RuleBasedAuthorizationPlugin to handle role - 
permission mappings with KereberosPlugin
+ * Added 2 new JSON map: (precedence: user-host-regex > user-host)
+ * 1. "user-host": user host mappings (array) for hostname validation
+ * 2. "user-host-regex": user host regex mapping (string) for hostname 
validation
+ */
+public class InfraRuleBasedAuthorizationPlugin implements AuthorizationPlugin, 
ConfigEditablePlugin {
+
+  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private final Map<String, Set<String>> usersVsRoles = new HashMap<>();
+  private final Map<String, WildCardSupportMap> mapping = new HashMap<>();
+  private final List<Permission> permissions = new ArrayList<>();
+  private final Map<String, Set<String>> userVsHosts = new HashMap<>();
+  private final Map<String, String> userVsHostRegex = new HashMap<>();
+
+  private final InfraUserRolesLookupStrategy infraUserRolesLookupStrategy = 
new InfraUserRolesLookupStrategy();
+  private final InfraKerberosHostValidator infraKerberosDomainValidator = new 
InfraKerberosHostValidator();
+
+  private static class WildCardSupportMap extends HashMap<String, 
List<Permission>> {
+    final Set<String> wildcardPrefixes = new HashSet<>();
+
+    @Override
+    public List<Permission> put(String key, List<Permission> value) {
+      if (key != null && key.endsWith("/*")) {
+        key = key.substring(0, key.length() - 2);
+        wildcardPrefixes.add(key);
+      }
+      return super.put(key, value);
+    }
+
+    @Override
+    public List<Permission> get(Object key) {
+      List<Permission> result = super.get(key);
+      if (key == null || result != null) return result;
+      if (!wildcardPrefixes.isEmpty()) {
+        for (String s : wildcardPrefixes) {
+          if (key.toString().startsWith(s)) {
+            List<Permission> l = super.get(s);
+            if (l != null) {
+              result = result == null ? new ArrayList<Permission>() : new 
ArrayList<Permission>(result);
+              result.addAll(l);
+            }
+          }
+        }
+      }
+      return result;
+    }
+  }
+
+  @Override
+  public AuthorizationResponse authorize(AuthorizationContext context) {
+    List<AuthorizationContext.CollectionRequest> collectionRequests = 
context.getCollectionRequests();
+    if (context.getRequestType() == AuthorizationContext.RequestType.ADMIN) {
+      MatchStatus flag = checkCollPerm(mapping.get(null), context);
+      return flag.rsp;
+    }
+
+    for (AuthorizationContext.CollectionRequest collreq : collectionRequests) {
+      //check permissions for each collection
+      MatchStatus flag = checkCollPerm(mapping.get(collreq.collectionName), 
context);
+      if (flag != MatchStatus.NO_PERMISSIONS_FOUND) return flag.rsp;
+    }
+    //check wildcard (all=*) permissions.
+    MatchStatus flag = checkCollPerm(mapping.get("*"), context);
+    return flag.rsp;
+  }
+
+  private MatchStatus checkCollPerm(Map<String, List<Permission>> pathVsPerms,
+                                    AuthorizationContext context) {
+    if (pathVsPerms == null) return MatchStatus.NO_PERMISSIONS_FOUND;
+
+    String path = context.getResource();
+    MatchStatus flag = checkPathPerm(pathVsPerms.get(path), context);
+    if (flag != MatchStatus.NO_PERMISSIONS_FOUND) return flag;
+    return checkPathPerm(pathVsPerms.get(null), context);
+  }
+
+  private MatchStatus checkPathPerm(List<Permission> permissions, 
AuthorizationContext context) {
+    if (permissions == null || permissions.isEmpty()) return 
MatchStatus.NO_PERMISSIONS_FOUND;
+    Principal principal = context.getUserPrincipal();
+    loopPermissions:
+    for (int i = 0; i < permissions.size(); i++) {
+      Permission permission = permissions.get(i);
+      if (permission.method != null && 
!permission.method.contains(context.getHttpMethod())) {
+        //this permissions HTTP method does not match this rule. try other 
rules
+        continue;
+      }
+      if(permission.predicate != null){
+        if(!permission.predicate.test(context)) continue ;
+      }
+
+      if (permission.params != null) {
+        for (Map.Entry<String, Object> e : permission.params.entrySet()) {
+          String paramVal = context.getParams().get(e.getKey());
+          Object val = e.getValue();
+          if (val instanceof List) {
+            if (!((List) val).contains(paramVal)) continue loopPermissions;
+          } else if (!Objects.equals(val, paramVal)) continue loopPermissions;
+        }
+      }
+
+      if (permission.role == null) {
+        //no role is assigned permission.That means everybody is allowed to 
access
+        return MatchStatus.PERMITTED;
+      }
+      if (principal == null) {
+        log.info("request has come without principal. failed permission {} 
",permission);
+        //this resource needs a principal but the request has come without
+        //any credential.
+        return MatchStatus.USER_REQUIRED;
+      } else if (permission.role.contains("*")) {
+        return MatchStatus.PERMITTED;
+      }
+
+      for (String role : permission.role) {
+        Set<String> userRoles = 
infraUserRolesLookupStrategy.getUserRolesFromPrincipal(usersVsRoles, principal);
+        boolean validHostname = 
infraKerberosDomainValidator.validate(principal, userVsHosts, userVsHostRegex);
+        if (!validHostname) {
+          log.warn("Hostname is not valid for principal {}", principal);
+          return MatchStatus.FORBIDDEN;
+        }
+        if (userRoles != null && userRoles.contains(role)) return 
MatchStatus.PERMITTED;
+      }
+      log.info("This resource is configured to have a permission {}, The 
principal {} does not have the right role ", permission, principal);
+      return MatchStatus.FORBIDDEN;
+    }
+    log.debug("No permissions configured for the resource {} . So allowed to 
access", context.getResource());
+    return MatchStatus.NO_PERMISSIONS_FOUND;
+  }
+
+  @Override
+  public void init(Map<String, Object> initInfo) {
+    mapping.put(null, new WildCardSupportMap());
+    Map<String, Object> map = getMapValue(initInfo, "user-role");
+    for (Object o : map.entrySet()) {
+      Map.Entry e = (Map.Entry) o;
+      String roleName = (String) e.getKey();
+      usersVsRoles.put(roleName, readValueAsSet(map, roleName));
+    }
+    List<Map> perms = getListValue(initInfo, "permissions");
+    for (Map o : perms) {
+      Permission p;
+      try {
+        p = Permission.load(o);
+      } catch (Exception exp) {
+        log.error("Invalid permission ", exp);
+        continue;
+      }
+      permissions.add(p);
+      add2Mapping(p);
+    }
+    // adding user-host
+    Map<String, Object> userHostsMap = getMapValue(initInfo, "user-host");
+    for (Object userHost : userHostsMap.entrySet()) {
+      Map.Entry e = (Map.Entry) userHost;
+      String roleName = (String) e.getKey();
+      userVsHosts.put(roleName, readValueAsSet(userHostsMap, roleName));
+    }
+    // adding user-host-regex
+    Map<String, Object> userHostRegexMap = getMapValue(initInfo, 
"user-host-regex");
+    for (Map.Entry<String, Object> entry : userHostRegexMap.entrySet()) {
+      userVsHostRegex.put(entry.getKey(), entry.getValue().toString());
+    }
+
+  }
+
+  //this is to do optimized lookup of permissions for a given collection/path
+  private void add2Mapping(Permission permission) {
+    for (String c : permission.collections) {
+      WildCardSupportMap m = mapping.get(c);
+      if (m == null) mapping.put(c, m = new WildCardSupportMap());
+      for (String path : permission.path) {
+        List<Permission> perms = m.get(path);
+        if (perms == null) m.put(path, perms = new ArrayList<>());
+        perms.add(permission);
+      }
+    }
+  }
+
+  /**
+   * read a key value as a set. if the value is a single string ,
+   * return a singleton set
+   *
+   * @param m   the map from which to lookup
+   * @param key the key with which to do lookup
+   */
+  static Set<String> readValueAsSet(Map m, String key) {
+    Set<String> result = new HashSet<>();
+    Object val = m.get(key);
+    if (val == null) {
+      if("collection".equals(key)){
+        //for collection collection: null means a core admin/ collection admin 
request
+        // otherwise it means a request where collection name is ignored
+        return m.containsKey(key) ? singleton((String) null) : singleton("*");
+      }
+      return null;
+    }
+    if (val instanceof Collection) {
+      Collection list = (Collection) val;
+      for (Object o : list) result.add(String.valueOf(o));
+    } else if (val instanceof String) {
+      result.add((String) val);
+    } else {
+      throw new RuntimeException("Bad value for : " + key);
+    }
+    return result.isEmpty() ? null : Collections.unmodifiableSet(result);
+  }
+
+  @Override
+  public void close() throws IOException { }
+
+  static class Permission {
+    String name;
+    Set<String> path, role, collections, method;
+    Map<String, Object> params;
+    Predicate<AuthorizationContext> predicate;
+    Map originalConfig;
+
+    private Permission() {
+    }
+
+    static Permission load(Map m) {
+      Permission p = new Permission();
+      p.originalConfig = new LinkedHashMap<>(m);
+      String name = (String) m.get(NAME);
+      if (!m.containsKey("role")) throw new 
SolrException(SolrException.ErrorCode.BAD_REQUEST, "role not specified");
+      p.role = readValueAsSet(m, "role");
+      if (well_known_permissions.containsKey(name)) {
+        HashSet<String> disAllowed = new HashSet<>(knownKeys);
+        disAllowed.remove("role");//these are the only
+        disAllowed.remove(NAME);//allowed keys for well-known permissions
+        for (String s : disAllowed) {
+          if (m.containsKey(s))
+            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, s + " 
is not a valid key for the permission : " + name);
+        }
+        p.predicate = (Predicate<AuthorizationContext>) ((Map) 
well_known_permissions.get(name)).get(Predicate.class.getName());
+        m = well_known_permissions.get(name);
+      }
+      p.name = name;
+      p.path = readSetSmart(name, m, "path");
+      p.collections = readSetSmart(name, m, "collection");
+      p.method = readSetSmart(name, m, "method");
+      p.params = (Map<String, Object>) m.get("params");
+      return p;
+    }
+
+    @Override
+    public String toString() {
+      return Utils.toJSONString(originalConfig);
+    }
+
+    static final Set<String> knownKeys = ImmutableSet.of("collection", "role", 
"params", "path", "method", NAME);
+  }
+
+  enum MatchStatus {
+    USER_REQUIRED(AuthorizationResponse.PROMPT),
+    NO_PERMISSIONS_FOUND(AuthorizationResponse.OK),
+    PERMITTED(AuthorizationResponse.OK),
+    FORBIDDEN(AuthorizationResponse.FORBIDDEN);
+
+    final AuthorizationResponse rsp;
+
+    MatchStatus(AuthorizationResponse rsp) {
+      this.rsp = rsp;
+    }
+  }
+
+  /**
+   * This checks for the defaults available other rules for the keys
+   */
+  private static Set<String> readSetSmart(String permissionName, Map m, String 
key) {
+    Set<String> set = readValueAsSet(m, key);
+    if (set == null && well_known_permissions.containsKey(permissionName)) {
+      set = readValueAsSet((Map) well_known_permissions.get(permissionName), 
key);
+    }
+    if ("method".equals(key)) {
+      if (set != null) {
+        for (String s : set) if (!HTTP_METHODS.contains(s)) return null;
+      }
+      return set;
+    }
+    return set == null ? singleton((String)null) : set;
+  }
+
+  @Override
+  public Map<String, Object> edit(Map<String, Object> latestConf, 
List<CommandOperation> commands) {
+    for (CommandOperation op : commands) {
+      OPERATION operation = null;
+      for (OPERATION o : OPERATION.values()) {
+        if (o.name.equals(op.name)) {
+          operation = o;
+          break;
+        }
+      }
+      if (operation == null) {
+        op.unknownOperation();
+        return null;
+      }
+      latestConf = operation.edit(latestConf, op);
+      if (latestConf == null) return null;
+
+    }
+    return latestConf;
+  }
+
+  enum OPERATION {
+    SET_USER_ROLE("set-user-role") {
+      @Override
+      public Map<String, Object> edit(Map<String, Object> latestConf, 
CommandOperation op) {
+        Map<String, Object> roleMap = getMapValue(latestConf, "user-role");
+        Map<String, Object> map = op.getDataMap();
+        if (op.hasError()) return null;
+        for (Map.Entry<String, Object> e : map.entrySet()) {
+          if (e.getValue() == null) {
+            roleMap.remove(e.getKey());
+            continue;
+          }
+          if (e.getValue() instanceof String || e.getValue() instanceof List) {
+            roleMap.put(e.getKey(), e.getValue());
+          } else {
+            op.addError("Unexpected value ");
+            return null;
+          }
+        }
+        return latestConf;
+      }
+    },
+    SET_PERMISSION("set-permission") {
+      @Override
+      public Map<String, Object> edit(Map<String, Object> latestConf, 
CommandOperation op) {
+        String name = op.getStr(NAME);
+        Map<String, Object> dataMap = op.getDataMap();
+        if (op.hasError()) return null;
+        dataMap = getDeepCopy(dataMap, 3);
+        String before = (String) dataMap.remove("before");
+        for (String key : dataMap.keySet()) {
+          if (!Permission.knownKeys.contains(key)) op.addError("Unknown key, " 
+ key);
+        }
+        try {
+          Permission.load(dataMap);
+        } catch (Exception e) {
+          op.addError(e.getMessage());
+          return null;
+        }
+        List<Map> permissions = getListValue(latestConf, "permissions");
+        List<Map> permissionsCopy = new ArrayList<>();
+        boolean added = false;
+        for (Map e : permissions) {
+          Object n = e.get(NAME);
+          if (n.equals(before) || n.equals(name)) {
+            added = true;
+            permissionsCopy.add(dataMap);
+          }
+          if (!n.equals(name)) permissionsCopy.add(e);
+        }
+        if (!added && before != null) {
+          op.addError("Invalid 'before' :" + before);
+          return null;
+        }
+        if (!added) permissionsCopy.add(dataMap);
+        latestConf.put("permissions", permissionsCopy);
+        return latestConf;
+      }
+    },
+    UPDATE_PERMISSION("update-permission") {
+      @Override
+      public Map<String, Object> edit(Map<String, Object> latestConf, 
CommandOperation op) {
+        String name = op.getStr(NAME);
+        if (op.hasError()) return null;
+        for (Map permission : (List<Map>) getListValue(latestConf, 
"permissions")) {
+          if (name.equals(permission.get(NAME))) {
+            LinkedHashMap copy = new LinkedHashMap<>(permission);
+            copy.putAll(op.getDataMap());
+            op.setCommandData(copy);
+            return SET_PERMISSION.edit(latestConf, op);
+          }
+        }
+        op.addError("No such permission " + name);
+        return null;
+      }
+    },
+    DELETE_PERMISSION("delete-permission") {
+      @Override
+      public Map<String, Object> edit(Map<String, Object> latestConf, 
CommandOperation op) {
+        List<String> names = op.getStrs("");
+        if (names == null || names.isEmpty()) {
+          op.addError("Invalid command");
+          return null;
+        }
+        names = new ArrayList<>(names);
+        List<Map> copy = new ArrayList<>();
+        List<Map> p = getListValue(latestConf, "permissions");
+        for (Map map : p) {
+          Object n = map.get(NAME);
+          if (names.contains(n)) {
+            names.remove(n);
+            continue;
+          } else {
+            copy.add(map);
+          }
+        }
+        if (!names.isEmpty()) {
+          op.addError("Unknown permission name(s) " + names);
+          return null;
+        }
+        latestConf.put("permissions", copy);
+        return latestConf;
+      }
+    };
+
+    public abstract Map<String, Object> edit(Map<String, Object> latestConf, 
CommandOperation op);
+
+    public final String name;
+
+    OPERATION(String s) {
+      this.name = s;
+    }
+
+    public static OPERATION get(String name) {
+      for (OPERATION o : values()) if (o.name.equals(name)) return o;
+      return null;
+    }
+  }
+
+  public static final Set<String> HTTP_METHODS = ImmutableSet.of("GET", 
"POST", "DELETE", "PUT", "HEAD");
+
+  private static final Map<String, Map<String,Object>> well_known_permissions 
= (Map) Utils.fromJSONString(
+    "    { " +
+      "    security-edit :{" +
+      "      path:['/admin/authentication','/admin/authorization']," +
+      "      collection:null," +
+      "      method:POST }," +
+      "    security-read :{" +
+      "      path:['/admin/authentication','/admin/authorization']," +
+      "      collection:null," +
+      "      method:GET}," +
+      "    schema-edit :{" +
+      "      method:POST," +
+      "      path:'/schema/*'}," +
+      "    collection-admin-edit :{" +
+      "  collection:null," +
+      "      path:'/admin/collections'}," +
+      "    collection-admin-read :{" +
+      "      collection:null," +
+      "      path:'/admin/collections'}," +
+      "    schema-read :{" +
+      "      method:GET," +
+      "      path:'/schema/*'}," +
+      "    config-read :{" +
+      "      method:GET," +
+      "      path:'/config/*'}," +
+      "    update :{" +
+      "      path:'/update/*'}," +
+      "    read :{" +
+      "      path:['/select', 
'/get','/browse','/tvrh','/terms','/clustering','/elevate', 
'/export','/spell','/clustering']}," +
+      "    config-edit:{" +
+      "      method:POST," +
+      "      path:'/config/*'}," +
+      "    all:{collection:['*', null]}" +
+      "}");
+
+  static {
+    ((Map) 
well_known_permissions.get("collection-admin-edit")).put(Predicate.class.getName(),
 getCollectionActionPredicate(true));
+    ((Map) 
well_known_permissions.get("collection-admin-read")).put(Predicate.class.getName(),
 getCollectionActionPredicate(false));
+  }
+
+  private static Predicate<AuthorizationContext> 
getCollectionActionPredicate(final boolean isEdit) {
+    return new Predicate<AuthorizationContext>() {
+      @Override
+      public boolean test(AuthorizationContext context) {
+        String action = context.getParams().get("action");
+        if (action == null) return false;
+        CollectionParams.CollectionAction collectionAction = 
CollectionParams.CollectionAction.get(action);
+        if (collectionAction == null) return false;
+        return isEdit ? collectionAction.isWrite : !collectionAction.isWrite;
+      }
+    };
+  }
+
+
+  public static void main(String[] args) {
+    System.out.println(Utils.toJSONString(well_known_permissions));
+
+  }
+
+  public interface Predicate<T> {
+
+    boolean test(T t);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java
 
b/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java
new file mode 100644
index 0000000..a54e4ad
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java
@@ -0,0 +1,49 @@
+/*
+ * 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.ambari.infra.security;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Strategy class to get roles with the principal name (in a specific format 
e.g.: 'name@DOMAIN')
+ * in case of KerberosPlugin is used for authentication
+ */
+public class InfraUserRolesLookupStrategy {
+
+  public Set<String> getUserRolesFromPrincipal(Map<String, Set<String>> 
usersVsRoles, Principal principal) {
+    if (principal instanceof AuthenticationToken) {
+      AuthenticationToken authenticationToken = (AuthenticationToken) 
principal;
+      KerberosName kerberosName = new 
KerberosName(authenticationToken.getName());
+      Set<String> rolesResult = usersVsRoles.get(String.format("%s@%s", 
kerberosName.getServiceName(), kerberosName.getRealm()));
+      if (CollectionUtils.isEmpty(rolesResult)) {
+        rolesResult = usersVsRoles.get(principal.getName());
+      }
+      return rolesResult;
+    } else {
+      return usersVsRoles.get(principal.getName());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java
 
b/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java
new file mode 100644
index 0000000..828f09a
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.ambari.infra.security;
+
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class InfraKerberosHostValidatorTest {
+
+  private static final String DEFAULT_SERVICE_USER = "logsearch";
+
+  private InfraKerberosHostValidator underTest = new 
InfraKerberosHostValidator();
+  private AuthenticationToken principal;
+
+
+  @Before
+  public void setUp() {
+    principal = new AuthenticationToken(DEFAULT_SERVICE_USER, 
DEFAULT_SERVICE_USER + "/c6401.ambari.apache....@example.com", "kerberos");
+  }
+
+  @Test
+  public void testValidateHosts() {
+    // GIVEN
+    Map<String, Set<String>> userHostsMap = 
generateUserHostMap("c6401.ambari.apache.org");
+    // WHEN
+    boolean result = underTest.validate(principal, userHostsMap, new 
HashMap<String, String>());
+    // THEN
+    assertTrue(result);
+  }
+
+  @Test
+  public void testValidateHostsValid() {
+    // GIVEN
+    Map<String, Set<String>> userHostsMap = 
generateUserHostMap("c6402.ambari.apache.org");
+    // WHEN
+    boolean result = underTest.validate(principal, userHostsMap, new 
HashMap<String, String>());
+    // THEN
+    assertFalse(result);
+
+  }
+
+  @Test
+  public void testValidateHostRegex() {
+    // GIVEN
+    Map<String, String> userHostRegex = generateRegexMap("c\\d+.*.apache.org");
+    // WHEN
+    boolean result = underTest.validate(principal, new HashMap<String, 
Set<String>>(), userHostRegex);
+    // THEN
+    assertTrue(result);
+
+  }
+
+  @Test
+  public void testValidateHostRegexInvalid() {
+    // GIVEN
+    Map<String, String> userHostRegex = generateRegexMap("c\\d+.*.org.apache");
+    // WHEN
+    boolean result = underTest.validate(principal, new HashMap<String, 
Set<String>>(), userHostRegex);
+    // THEN
+    assertFalse(result);
+  }
+
+  @Test
+  public void testPrecedence() {
+    // GIVEN
+    Map<String, Set<String>> userHostsMap = 
generateUserHostMap("c6402.ambari.apache.org");
+    Map<String, String> userHostRegex = generateRegexMap("c\\d+.*.apache.org");
+    // WHEN
+    boolean result = underTest.validate(principal, userHostsMap, 
userHostRegex);
+    // THEN
+    assertTrue(result);
+  }
+
+  private Map<String, Set<String>> generateUserHostMap(String... hosts) {
+    Map<String, Set<String>> map = new HashMap<>();
+    Set<String> hostSet = new HashSet<>();
+    for (String host : hosts) {
+      hostSet.add(host);
+    }
+    map.put(DEFAULT_SERVICE_USER, hostSet);
+    return map;
+  }
+
+  private Map<String, String> generateRegexMap(String regex) {
+    Map<String, String> map = new HashMap<>();
+    map.put(DEFAULT_SERVICE_USER, regex);
+    return map;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java
 
b/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java
new file mode 100644
index 0000000..ee84969
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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.ambari.infra.security;
+
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.solr.common.params.MapSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.AuthorizationContext.RequestType;
+import org.apache.solr.security.AuthorizationResponse;
+import org.junit.Test;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.apache.solr.common.util.Utils.makeMap;
+import static org.junit.Assert.assertEquals;
+
+public class InfraRuleBasedAuthorizationPluginTest {
+
+  private static final String PERMISSIONS = "{" +
+    "  user-host : {" +
+    "    'infra-s...@example.com': [hostname, hostname2]" +
+    "  }," +
+    "  user-role : {" +
+    "    'infra-s...@example.com': [admin]," +
+    "    'logsea...@example.com': [logsearch_role,dev]," +
+    "    'logfee...@example.com': [logsearch_role,dev]," +
+    "    'at...@example.com': [atlas_role, audit_role, dev]," +
+    "    'k...@example.com': [audit_role,dev]," +
+    "    'h...@example.com': [audit_role,dev]," +
+    "    'hb...@example.com': [audit_role,dev]," +
+    "    'y...@example.com': [audit_role,dev]," +
+    "    'k...@example.com': [audit_role,dev]," +
+    "    'ka...@example.com': [audit_role,dev]," +
+    "    'k...@example.com': [audit_role,dev]," +
+    "    'st...@example.com': [audit_role,dev]," +
+    "    'rangerad...@example.com':[ranger_role, audit_role, dev]" +
+    "  }," +
+    "  permissions : [" +
+    "    {name:'collection-admin-read'," +
+    "    role:null}," +
+    "    {name:collection-admin-edit ," +
+    "    role:[logsearch_role, atlas_role, ranger_role, admin]}," +
+    "    {name:mycoll_update," +
+    "      collection:mycoll," +
+    "      path:'/*'," +
+    "      role:[logsearch_role,admin]" +
+    "    }," +
+    "    {name:mycoll2_update," +
+    "      collection:mycoll2," +
+    "      path:'/*'," +
+    "      role:[ranger_role, audit_role, admin]" +
+    "    }," +
+    "{name:read , role:dev }]}";
+
+  @Test
+  public void testPermissions() {
+    int STATUS_OK = 200;
+    int FORBIDDEN = 403;
+    int PROMPT_FOR_CREDENTIALS = 401;
+
+    checkRules(makeMap("resource", "/#",
+      "httpMethod", "POST",
+      "userPrincipal", "unknownuser",
+      "collectionRequests", "freeforall" )
+      , STATUS_OK);
+
+    checkRules(makeMap("resource", "/update/json/docs",
+      "httpMethod", "POST",
+      "userPrincipal", "tim",
+      "collectionRequests", "mycoll")
+      , FORBIDDEN);
+
+    checkRules(makeMap("resource", "/update/json/docs",
+      "httpMethod", "POST",
+      "userPrincipal", "logsearch",
+      "collectionRequests", "mycoll")
+      , STATUS_OK);
+
+    checkRules(makeMap("resource", "/update/json/docs",
+      "httpMethod", "GET",
+      "userPrincipal", "rangeradmin",
+      "collectionRequests", "mycoll")
+      , FORBIDDEN);
+
+    checkRules(makeMap("resource", "/update/json/docs",
+      "httpMethod", "GET",
+      "userPrincipal", "rangeradmin",
+      "collectionRequests", "mycoll2")
+      , STATUS_OK);
+
+    checkRules(makeMap("resource", "/update/json/docs",
+      "httpMethod", "GET",
+      "userPrincipal", "logsearch",
+      "collectionRequests", "mycoll2")
+      , FORBIDDEN);
+
+    checkRules(makeMap("resource", "/update/json/docs",
+      "httpMethod", "POST",
+      "userPrincipal", "kms",
+      "collectionRequests", "mycoll2")
+      , STATUS_OK);
+
+    checkRules(makeMap("resource", "/admin/collections",
+      "userPrincipal", "tim",
+      "requestType", RequestType.ADMIN,
+      "collectionRequests", null,
+      "params", new MapSolrParams(singletonMap("action", "CREATE")))
+      , FORBIDDEN);
+
+    checkRules(makeMap("resource", "/admin/collections",
+      "userPrincipal", null,
+      "requestType", RequestType.ADMIN,
+      "collectionRequests", null,
+      "params", new MapSolrParams(singletonMap("action", "CREATE")))
+      , PROMPT_FOR_CREDENTIALS);
+
+    checkRules(makeMap("resource", "/admin/collections",
+      "userPrincipal", "rangeradmin",
+      "requestType", RequestType.ADMIN,
+      "collectionRequests", null,
+      "params", new MapSolrParams(singletonMap("action", "CREATE")))
+      , STATUS_OK);
+
+    checkRules(makeMap("resource", "/admin/collections",
+      "userPrincipal", "kms",
+      "requestType", RequestType.ADMIN,
+      "collectionRequests", null,
+      "params", new MapSolrParams(singletonMap("action", "CREATE")))
+      , FORBIDDEN);
+
+    checkRules(makeMap("resource", "/admin/collections",
+      "userPrincipal", "kms",
+      "requestType", RequestType.ADMIN,
+      "collectionRequests", null,
+      "params", new MapSolrParams(singletonMap("action", "LIST")))
+      , STATUS_OK);
+
+    checkRules(makeMap("resource", "/admin/collections",
+      "userPrincipal", "rangeradmin",
+      "requestType", RequestType.ADMIN,
+      "collectionRequests", null,
+      "params", new MapSolrParams(singletonMap("action", "LIST")))
+      , STATUS_OK);
+  }
+
+  private void checkRules(Map<String, Object> values, int expected) {
+    checkRules(values,expected,(Map) Utils.fromJSONString(PERMISSIONS));
+  }
+
+  private void checkRules(Map<String, Object> values, int expected, Map<String 
,Object> permissions) {
+    AuthorizationContext context = new MockAuthorizationContext(values);
+    InfraRuleBasedAuthorizationPlugin plugin = new 
InfraRuleBasedAuthorizationPlugin();
+    plugin.init(permissions);
+    AuthorizationResponse authResp = plugin.authorize(context);
+    assertEquals(expected, authResp.statusCode);
+  }
+
+  private static class MockAuthorizationContext extends AuthorizationContext {
+    private final Map<String,Object> values;
+
+    private MockAuthorizationContext(Map<String, Object> values) {
+      this.values = values;
+    }
+
+    @Override
+    public SolrParams getParams() {
+      SolrParams params = (SolrParams) values.get("params");
+      return params == null ?  new MapSolrParams(new HashMap<String, 
String>()) : params;
+    }
+
+    @Override
+    public Principal getUserPrincipal() {
+      Object userPrincipal = values.get("userPrincipal");
+      return userPrincipal == null ? null :
+        new AuthenticationToken(String.valueOf(userPrincipal), 
String.format("%s%s", String.valueOf(userPrincipal), "/hostn...@example.com"), 
"kerberos");
+    }
+
+    @Override
+    public String getHttpHeader(String header) {
+      return null;
+    }
+
+    @Override
+    public Enumeration getHeaderNames() {
+      return null;
+    }
+
+    @Override
+    public String getRemoteAddr() {
+      return null;
+    }
+
+    @Override
+    public String getRemoteHost() {
+      return null;
+    }
+
+    @Override
+    public List<CollectionRequest> getCollectionRequests() {
+      Object collectionRequests = values.get("collectionRequests");
+      if (collectionRequests instanceof String) {
+        return singletonList(new 
CollectionRequest((String)collectionRequests));
+      }
+      return (List<CollectionRequest>) collectionRequests;
+    }
+
+    @Override
+    public RequestType getRequestType() {
+      return (RequestType) values.get("requestType");
+    }
+
+    @Override
+    public String getHttpMethod() {
+      return (String) values.get("httpMethod");
+    }
+
+    @Override
+    public String getResource() {
+      return (String) values.get("resource");
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8842be0c/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java
 
b/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java
new file mode 100644
index 0000000..c1a47d1
--- /dev/null
+++ 
b/ambari-infra/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.ambari.infra.security;
+
+import com.google.common.collect.Sets;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.http.auth.BasicUserPrincipal;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class InfraUserRolesLookupStrategyTest {
+
+  private InfraUserRolesLookupStrategy underTest = new 
InfraUserRolesLookupStrategy();
+
+  @Test
+  public void testLookupRolesForPrincipalName() {
+    // GIVEN
+    Map<String, Set<String>> usersVsRoles = generateUserRolesMap();
+    AuthenticationToken principal = new AuthenticationToken(
+      "logsearch", "logsearch/c6401.ambari.apache....@example.com", 
"kerberos");
+    // WHEN
+    Set<String> result = underTest.getUserRolesFromPrincipal(usersVsRoles, 
principal);
+    // THEN
+    assertTrue(result.contains("logsearch_user"));
+    assertTrue(result.contains("ranger_user"));
+    assertFalse(result.contains("admin"));
+  }
+
+  @Test
+  public void testLookupRolesForNonKerberosPrincipalName() {
+    // GIVEN
+    Map<String, Set<String>> usersVsRoles = generateUserRolesMap();
+    BasicUserPrincipal principal = new BasicUserPrincipal("infra-solr");
+    // WHEN
+    Set<String> result = underTest.getUserRolesFromPrincipal(usersVsRoles, 
principal);
+    // THEN
+    assertTrue(result.contains("admin"));
+    assertTrue(result.contains("logsearch_user"));
+  }
+
+  @Test
+  public void testLookupRolesWithNonKerberosPrincipalWithoutRoles() {
+    // GIVEN
+    Map<String, Set<String>> usersVsRoles = generateUserRolesMap();
+    BasicUserPrincipal principal = new BasicUserPrincipal("unknownuser");
+    // WHEN
+    Set<String> result = underTest.getUserRolesFromPrincipal(usersVsRoles, 
principal);
+    // THEN
+    assertTrue(result.isEmpty());
+  }
+
+  private Map<String, Set<String>> generateUserRolesMap() {
+    Map<String, Set<String>> usersVsRoles = new HashMap<>();
+    usersVsRoles.put("logsea...@example.com", 
Sets.newHashSet("logsearch_user", "ranger_user"));
+    usersVsRoles.put("infra-s...@example.com", Sets.newHashSet("admin"));
+    usersVsRoles.put("infra-solr", Sets.newHashSet("admin", "logsearch_user"));
+    usersVsRoles.put("unknownuser", new HashSet<String>());
+    return usersVsRoles;
+  }
+}

Reply via email to