/*
* 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.solr.common.cloud;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.cloud.AbstractZkTestCase;
import org.apache.solr.cloud.ZkTestServer;
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.server.auth.DigestAuthenticationProvider;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SolrZkClientTest extends SolrTestCaseJ4 {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final String ROOT = "/";
private static final String PATH = "/collections/collection1";
protected ZkTestServer zkServer;
SolrZkClient aclClient;
SolrZkClient credentialsClient;
SolrZkClient defaultClient;
@Override
public void setUp() throws Exception {
super.setUp();
final String SCHEME = "digest";
final String AUTH = "user:pass";
String zkDir = createTempDir().toString();
log.info("ZooKeeper dataDir:" + zkDir);
zkServer = new ZkTestServer(zkDir);
zkServer.run();
try (SolrZkClient client = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT)) {
// Set up chroot
client.makePath("/solr", false, true);
}
defaultClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
defaultClient.makePath(PATH, true);
aclClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT) {
@Override
protected ZkACLProvider createZkACLProvider() {
return new DefaultZkACLProvider() {
@Override
protected List<ACL> createGlobalACLsToAdd() {
try {
Id id = new Id(SCHEME, DigestAuthenticationProvider.generateDigest(AUTH));
return Collections.singletonList(new ACL(ZooDefs.Perms.ALL, id));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
};
}
};
credentialsClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT) {
@Override
protected ZkCredentialsProvider createZkCredentialsToAddAutomatically() {
return new DefaultZkCredentialsProvider() {
@Override
protected Collection<ZkCredentials> createCredentials() {
return Collections.singleton(new ZkCredentials(SCHEME, AUTH.getBytes(StandardCharsets.UTF_8)));
}
};
}
};
}
@Override
public void tearDown() throws Exception {
aclClient.close();
credentialsClient.close();
defaultClient.close();
zkServer.shutdown();
super.tearDown();
}
@Test
public void testSimpleUpdateACLs() throws KeeperException, InterruptedException {
assertTrue("Initial create was in secure mode; please check the test", canRead(defaultClient, PATH));
assertTrue("Credentialed client should always be able to read", canRead(credentialsClient, PATH));
// convert to secure
aclClient.updateACLs(ROOT);
assertFalse("Default client should not be able to read root in secure mode", canRead(defaultClient, ROOT));
assertFalse("Default client should not be able to read children in secure mode", canRead(defaultClient, PATH));
assertTrue("Credentialed client should always be able to read root in secure mode", canRead(credentialsClient, ROOT));
assertTrue("Credentialed client should always be able to read in secure mode", canRead(credentialsClient, PATH));
// convert to non-secure
credentialsClient.updateACLs(ROOT);
assertTrue("Default client should work again after clearing ACLs", canRead(defaultClient, PATH));
assertTrue("Credentialed client should always be able to read", canRead(credentialsClient, PATH));
// convert a subtree to secure
aclClient.updateACLs("/collections");
assertTrue("Default client should read unaffected paths", canRead(defaultClient, ROOT));
assertFalse("Default client should not read secure children", canRead(defaultClient, PATH));
}
private static boolean canRead(SolrZkClient zkClient, String path) throws KeeperException, InterruptedException {
try {
zkClient.getData(path, null, null, true);
return true;
} catch (KeeperException.NoAuthException e) {
return false;
}
}
}