问题
I am working with zookeeper 3.4.6, I'm using acl
in order to authenticate with zookeeper server. I have my own implementation ZooKeeperSupport
, it's a support for create, remove and verify znode. I am triying to create a znode
using acl
, but fail throwning InvalidACLException
in this part of the code zooKeeperSupport.create("/f", DATA_F);
I'm basing this project to do it zookeeper-acl-sample, but I want to use digest
auth because use user
and password
BasicMockZookeeperSecurity
public class BasicMockZookeeperSecurity {
@Resource (name = "zooKeeperSupportFactory")
protected ZooKeeperSupportFactory zooKeeperSupportFactory;
public static final byte[] DATA_F = "data for znode /f".getBytes(Charsets.UTF_8);
public static final byte[] DATA_B = "data for znode /b".getBytes(Charsets.UTF_8);
@Before
public void setup() throws Exception {
System.setProperty("curator-dont-log-connection-problems", "true");
System.setProperty("zookeeper.security.user", "user");
System.setProperty("zookeeper.security.password", "pass");
System.setProperty("zookeeper.authProvider.1","com.equifax.product.fraud.common.zookeeper.security.CustomUserAuthenticationProvider");
zooKeeperSupport = CuratorFrameworkFactory.builder().connectString(connectionPath).retryPolicy(retryPolicy).aclProvider(new ACLProvider()).build();
zooKeeperSupport.create("/f", DATA_F);
zooKeeperSupport.create("/b", DATA_B);
}
}
ZookeeperSecurtyTest
public class ZookeeperSecurtyTest extends BasicMockZookeeperSecurity {
@Test
public void securityTester() throws Exception {
final CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().connectString(connectionPath).retryPolicy(retryPolicy).aclProvider(new ACLProvider()).authorization(ZookeeperSecurityUtil.getCredentialsFromSystemProperties()).build();
curatorFramework.getData().forPath("/f");
}
@After
public void teardown() throws Exception {
System.clearProperty("zookeeeper.authProvider.1");
System.clearProperty("zookeeper.security.user");
System.clearProperty("zookeeper.security.password");
}
}
ZooKeeperSupport
@SuppressWarnings ("unchecked")
public class ZooKeeperSupport implements AutoCloseable {
private CuratorFramework curatorClient;
private Builder builder;
public ZooKeeperSupport(final Builder builder) {
this.setBuilder(builder);
this.curatorClient = builder.build();
}
public <T> T getData(final String path) throws Exception {
startConnection();
return (T) curatorClient.getData().forPath(path);
}
public <T> T create(final String path, final byte[] data) throws Exception {
startConnection();
return (T) curatorClient.create().forPath(path, data);
}
public <T> T create(final String path) throws Exception {
startConnection();
return (T) curatorClient.create().forPath(path);
}
public <T> T delete(final String path) throws Exception {
startConnection();
return (T) curatorClient.delete().forPath(path);
}
public boolean exists(final String path) throws Exception {
startConnection();
final Stat stat = curatorClient.checkExists().forPath(StringUtils.trim(path));
return stat != null ? true : false;
}
public void startConnection() {
if (curatorClient.getState() == CuratorFrameworkState.LATENT) {
curatorClient.start();
}
}
public List<String> getChildrenNames(final String path) throws Exception {
startConnection();
return curatorClient.getChildren().forPath(path);
}
@Override
public void close() throws Exception {
curatorClient.close();
curatorClient = null;
}
public Builder getBuilder() {
return builder;
}
public void setBuilder(final Builder builder) {
this.builder = builder;
}
public CuratorFramework getCuratorClient() {
return curatorClient;
}
public void setCuratorClient(final CuratorFramework curatorClient) {
this.curatorClient = curatorClient;
}
}
ZookeeperSecurityUtil
public class ZookeeperSecurityUtil {
public static List<AuthInfo> getCredentialsFromSystemProperties() {
final List<AuthInfo> authInfo = new ArrayList<>();
final String user = System.getProperty("zookeeper.security.user");
final String password = System.getProperty("zookeeper.security.password");
authInfo.add(new AuthInfo("digest", new String(user + ":" + password).getBytes(Charsets.UTF_8)));
return authInfo;
}
}
CustomUserAuthenticationProvider
public class CustomUserAuthenticationProvider implements AuthenticationProvider {
@Override
public String getScheme() {
return "digest";
}
@Override
public Code handleAuthentication(final ServerCnxn cnxn, final byte[] authData) {
final String userName = new String(authData, Charsets.UTF_8);
if (StringUtils.isNotBlank(userName)) {
cnxn.addAuthInfo(new Id(getScheme(), userName));
return Code.OK;
}
return Code.AUTHFAILED;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public boolean isValid(final String arg0) {
return true;
}
@Override
public boolean matches(final String id, final String aclExpr) {
if (Strings.isNullOrEmpty(id) || Strings.isNullOrEmpty(aclExpr)) {
return false;
}
return id.charAt(0) == aclExpr.charAt(0);
}
}
ACLProvider
public class ACLProvider implements org.apache.curator.framework.api.ACLProvider {
@Override
public List<ACL> getAclForPath(final String path) {
final String firstLetter = String.valueOf(path.charAt(1));
final Id FIRST_USER_LETTER = new Id("digest", firstLetter);
final ACL acl = new ACL(Perms.ALL, FIRST_USER_LETTER);
return Collections.singletonList(acl);
}
@Override
public List<ACL> getDefaultAcl() {
throw new NotImplementedException();
}
}
Stack
org.apache.zookeeper.KeeperException$InvalidACLException: KeeperErrorCode = InvalidACL for /f
at org.apache.zookeeper.KeeperException.create(KeeperException.java:121)
at org.apache.zookeeper.KeeperException.create(KeeperException.java:51)
at org.apache.zookeeper.ZooKeeper.create(ZooKeeper.java:783)
at org.apache.curator.framework.imps.CreateBuilderImpl$11.call(CreateBuilderImpl.java:696)
at org.apache.curator.framework.imps.CreateBuilderImpl$11.call(CreateBuilderImpl.java:679)
at org.apache.curator.RetryLoop.callWithRetry(RetryLoop.java:107)
at org.apache.curator.framework.imps.CreateBuilderImpl.pathInForeground(CreateBuilderImpl.java:676)
at org.apache.curator.framework.imps.CreateBuilderImpl.protectedPathInForeground(CreateBuilderImpl.java:453)
at org.apache.curator.framework.imps.CreateBuilderImpl.forPath(CreateBuilderImpl.java:443)
at org.apache.curator.framework.imps.CreateBuilderImpl.forPath(CreateBuilderImpl.java:44)
at com.equifax.product.fraud.common.zookeeper.support.ZooKeeperSupport.create(ZooKeeperSupport.java:35)
at com.equifax.product.fraud.common.zookeeper.authentication.BasicMockZookeeperSecurity.setup(BasicMockZookeeperSecurity.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
What is wrong?
回答1:
I have found the solution. Basically the problem is the way to create the user with password using digest
schemee. According to zookeeper documentation, digest auth use an ID and password in base64 encode SHA1, apparently ACL doesn't make that automatically when you add the use to ACL so I had to do by myself.
public class ZookeeperSecurityUtil {
private static final String SHA1 = "SHA1";
private static final String COLON = ":";
private static final String DIGEST_SCHEME = "digest";
public static List<AuthInfo> getCredentialsFromSystemProperties() {
final List<AuthInfo> authInfo = new ArrayList<>();
final String user = System.getProperty("zookeeper.security.user");
final String password = System.getProperty("zookeeper.security.password");
authInfo.add(new AuthInfo(DIGEST_SCHEME, new String(user + COLON + password).getBytes(Charsets.UTF_8)));
return authInfo;
}
public static String generateDigest(final String idPassword) throws NoSuchAlgorithmException {
final String parts[] = idPassword.split(COLON, 2);
final byte digest[] = MessageDigest.getInstance(SHA1).digest(idPassword.getBytes());
return parts[0] + COLON + base64Encode(digest);
}
private static String base64Encode(final byte byteDigest[]) {
return new String(Base64.getEncoder().encode(byteDigest));
}
}
and
public class ACLProvider implements org.apache.curator.framework.api.ACLProvider {
private static final String ZK_DIGEST_SCHEME = "digest";
@Override
public List<ACL> getAclForPath(final String path) {
final String user = System.getProperty("zookeeper.security.user");
final String password = System.getProperty("zookeeper.security.password");
Id zkId = null;
try {
zkId = new Id(ZK_DIGEST_SCHEME, ZookeeperSecurityUtil.generateDigest(user + ":" + password));
} catch(final NoSuchAlgorithmException e) {
e.printStackTrace();
}
final ACL acl = new ACL(Perms.ALL, zkId);
return Collections.singletonList(acl);
}
@Override
public List<ACL> getDefaultAcl() {
throw new NotImplementedException();
}
}
来源:https://stackoverflow.com/questions/36584568/org-apache-zookeeper-keeperexceptioninvalidaclexception-keepererrorcode-inva