/** * 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.hadoop.util; import java.io.File; import java.io.IOException; import java.util.List; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import com.google.common.base.Charsets; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.common.io.Files; /** * Utilities for working with ZooKeeper. */ @InterfaceAudience.Private public class ZKUtil { /** * Parse ACL permission string, partially borrowed from * ZooKeeperMain private method */ private static int getPermFromString(String permString) { int perm = 0; for (int i = 0; i < permString.length(); i++) { char c = permString.charAt(i); switch (c) { case 'r': perm |= ZooDefs.Perms.READ; break; case 'w': perm |= ZooDefs.Perms.WRITE; break; case 'c': perm |= ZooDefs.Perms.CREATE; break; case 'd': perm |= ZooDefs.Perms.DELETE; break; case 'a': perm |= ZooDefs.Perms.ADMIN; break; default: throw new BadAclFormatException( "Invalid permission '" + c + "' in permission string '" + permString + "'"); } } return perm; } /** * Parse comma separated list of ACL entries to secure generated nodes, e.g. * <code>sasl:hdfs/host1@MY.DOMAIN:cdrwa,sasl:hdfs/host2@MY.DOMAIN:cdrwa</code> * * @return ACL list * @throws {@link BadAclFormatException} if an ACL is invalid */ public static List<ACL> parseACLs(String aclString) throws BadAclFormatException { List<ACL> acl = Lists.newArrayList(); if (aclString == null) { return acl; } List<String> aclComps = Lists.newArrayList( Splitter.on(',').omitEmptyStrings().trimResults() .split(aclString)); for (String a : aclComps) { // from ZooKeeperMain private method int firstColon = a.indexOf(':'); int lastColon = a.lastIndexOf(':'); if (firstColon == -1 || lastColon == -1 || firstColon == lastColon) { throw new BadAclFormatException( "ACL '" + a + "' not of expected form scheme:id:perm"); } ACL newAcl = new ACL(); newAcl.setId(new Id(a.substring(0, firstColon), a.substring( firstColon + 1, lastColon))); newAcl.setPerms(getPermFromString(a.substring(lastColon + 1))); acl.add(newAcl); } return acl; } /** * Parse a comma-separated list of authentication mechanisms. Each * such mechanism should be of the form 'scheme:auth' -- the same * syntax used for the 'addAuth' command in the ZK CLI. * * @param authString the comma-separated auth mechanisms * @return a list of parsed authentications * @throws {@link BadAuthFormatException} if the auth format is invalid */ public static List<ZKAuthInfo> parseAuth(String authString) throws BadAuthFormatException{ List<ZKAuthInfo> ret = Lists.newArrayList(); if (authString == null) { return ret; } List<String> authComps = Lists.newArrayList( Splitter.on(',').omitEmptyStrings().trimResults() .split(authString)); for (String comp : authComps) { String parts[] = comp.split(":", 2); if (parts.length != 2) { throw new BadAuthFormatException( "Auth '" + comp + "' not of expected form scheme:auth"); } ret.add(new ZKAuthInfo(parts[0], parts[1].getBytes(Charsets.UTF_8))); } return ret; } /** * Because ZK ACLs and authentication information may be secret, * allow the configuration values to be indirected through a file * by specifying the configuration as "@/path/to/file". If this * syntax is used, this function will return the contents of the file * as a String. * * @param valInConf the value from the Configuration * @return either the same value, or the contents of the referenced * file if the configured value starts with "@" * @throws IOException if the file cannot be read */ public static String resolveConfIndirection(String valInConf) throws IOException { if (valInConf == null) return null; if (!valInConf.startsWith("@")) { return valInConf; } String path = valInConf.substring(1).trim(); return Files.toString(new File(path), Charsets.UTF_8).trim(); } /** * An authentication token passed to ZooKeeper.addAuthInfo */ @InterfaceAudience.Private public static class ZKAuthInfo { private final String scheme; private final byte[] auth; public ZKAuthInfo(String scheme, byte[] auth) { super(); this.scheme = scheme; this.auth = auth; } public String getScheme() { return scheme; } public byte[] getAuth() { return auth; } } @InterfaceAudience.Private public static class BadAclFormatException extends HadoopIllegalArgumentException { private static final long serialVersionUID = 1L; public BadAclFormatException(String message) { super(message); } } @InterfaceAudience.Private public static class BadAuthFormatException extends HadoopIllegalArgumentException { private static final long serialVersionUID = 1L; public BadAuthFormatException(String message) { super(message); } } }