I am thinking of the other way, when folks check sentry ha related stuffs into master could merge into redesign-sentry-ha branch. There are also new tests we might want to have to validate the feature branch.
Best, Anne On Wed, Jun 22, 2016 at 11:18 AM, Colin McCabe <[email protected]> wrote: > Let's try to focus on getting our branch done, and then worry about > merging back to master. > > People move stuff around all the time, and following the latest churn > takes time. > > Colin > > On Wed, Jun 22, 2016 at 9:45 AM, Anne Yu <[email protected]> wrote: > >> Hi Colin and Team, >> >> We are implementing Sentry-ha redesign in the branch >> *redesign-sentry-ha*, do you think this commit need also go into that >> branch. From now on, we might want to check in ha fixes in both trunk and >> branch. >> >> Thanks, >> Anne >> On Jun 21, 2016 10:27 PM, <[email protected]> wrote: >> >>> Repository: sentry >>> Updated Branches: >>> refs/heads/SENTRY-1205 aef769858 -> 4767ec38e >>> >>> >>> SENTRY-1348: Move HA related class from sentry-provider-db to >>> sentry-service-common(Colin Ma, reviewed by Dapeng Sun) >>> >>> >>> Project: http://git-wip-us.apache.org/repos/asf/sentry/repo >>> Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/4767ec38 >>> Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/4767ec38 >>> Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/4767ec38 >>> >>> Branch: refs/heads/SENTRY-1205 >>> Commit: 4767ec38ef2a5d967d37539c64c787c986afe5cb >>> Parents: aef7698 >>> Author: Colin Ma <[email protected]> >>> Authored: Wed Jun 22 14:11:57 2016 +0800 >>> Committer: Colin Ma <[email protected]> >>> Committed: Wed Jun 22 14:11:57 2016 +0800 >>> >>> ---------------------------------------------------------------------- >>> sentry-provider/sentry-provider-db/pom.xml | 17 -- >>> .../persistent/FixedJsonInstanceSerializer.java | 163 ------------ >>> .../db/service/persistent/HAContext.java | 262 >>> ------------------- >>> .../service/thrift/JaasConfiguration.java | 133 ---------- >>> sentry-service/sentry-service-common/pom.xml | 17 ++ >>> .../persistent/FixedJsonInstanceSerializer.java | 163 ++++++++++++ >>> .../db/service/persistent/HAContext.java | 262 >>> +++++++++++++++++++ >>> .../service/thrift/JaasConfiguration.java | 133 ++++++++++ >>> 8 files changed, 575 insertions(+), 575 deletions(-) >>> ---------------------------------------------------------------------- >>> >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/pom.xml >>> ---------------------------------------------------------------------- >>> diff --git a/sentry-provider/sentry-provider-db/pom.xml >>> b/sentry-provider/sentry-provider-db/pom.xml >>> index f3029fa..3d76198 100644 >>> --- a/sentry-provider/sentry-provider-db/pom.xml >>> +++ b/sentry-provider/sentry-provider-db/pom.xml >>> @@ -38,11 +38,6 @@ limitations under the License. >>> </dependency> >>> <dependency> >>> <groupId>org.apache.hadoop</groupId> >>> - <artifactId>hadoop-common</artifactId> >>> - <scope>provided</scope> >>> - </dependency> >>> - <dependency> >>> - <groupId>org.apache.hadoop</groupId> >>> <artifactId>hadoop-mapreduce-client-jobclient</artifactId> >>> </dependency> >>> <dependency> >>> @@ -166,18 +161,6 @@ limitations under the License. >>> <scope>test</scope> >>> </dependency> >>> <dependency> >>> - <groupId>org.apache.curator</groupId> >>> - <artifactId>curator-recipes</artifactId> >>> - </dependency> >>> - <dependency> >>> - <groupId>org.apache.curator</groupId> >>> - <artifactId>curator-x-discovery</artifactId> >>> - </dependency> >>> - <dependency> >>> - <groupId>org.apache.curator</groupId> >>> - <artifactId>curator-test</artifactId> >>> - </dependency> >>> - <dependency> >>> <groupId>org.apache.commons</groupId> >>> <artifactId>commons-pool2</artifactId> >>> </dependency> >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> ---------------------------------------------------------------------- >>> diff --git >>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> deleted file mode 100644 >>> index 476bf6a..0000000 >>> --- >>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> +++ /dev/null >>> @@ -1,163 +0,0 @@ >>> -/** >>> - * 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.sentry.provider.db.service.persistent; >>> - >>> -import java.io.ByteArrayInputStream; >>> -import java.io.ByteArrayOutputStream; >>> -import java.io.IOException; >>> - >>> -import org.codehaus.jackson.JsonNode; >>> -import org.codehaus.jackson.JsonParseException; >>> -import org.codehaus.jackson.map.DeserializationConfig; >>> -import org.codehaus.jackson.map.JsonMappingException; >>> -import org.codehaus.jackson.map.ObjectMapper; >>> - >>> -import com.google.common.base.Preconditions; >>> -import org.apache.curator.x.discovery.ServiceInstance; >>> -import org.apache.curator.x.discovery.ServiceInstanceBuilder; >>> -import org.apache.curator.x.discovery.ServiceType; >>> -import org.apache.curator.x.discovery.UriSpec; >>> -import org.apache.curator.x.discovery.details.InstanceSerializer; >>> - >>> -// TODO: Workaround for CURATOR-5 ( >>> https://issues.apache.org/jira/browse/CURATOR-5) >>> -// Remove this class (code from pull request listed on JIRA) and use >>> regular JsonInstanceSerializer once fixed >>> -// (Otherwise we can't properly serialize objects for the ZK Service >>> Discovery) >>> -public class FixedJsonInstanceSerializer<T> implements >>> InstanceSerializer<T> >>> -{ >>> - >>> - private final ObjectMapper mMapper; >>> - private final Class<T> mPayloadClass; >>> - >>> - /** >>> - * @param payloadClass >>> - * used to validate payloads when deserializing >>> - */ >>> - public FixedJsonInstanceSerializer(final Class<T> payloadClass) { >>> - this(payloadClass, new ObjectMapper()); >>> - } >>> - >>> - public FixedJsonInstanceSerializer(final Class<T> pPayloadClass, >>> final ObjectMapper pMapper) { >>> - mPayloadClass = pPayloadClass; >>> - mMapper = pMapper; >>> - >>> mMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, >>> false); >>> - } >>> - >>> - @Override >>> - public byte[] serialize(final ServiceInstance<T> pInstance) throws >>> Exception { >>> - final ByteArrayOutputStream out = new ByteArrayOutputStream(); >>> - mMapper.writeValue(out, pInstance); >>> - return out.toByteArray(); >>> - >>> - } >>> - >>> - private String getTextField(final JsonNode pNode, final String >>> pFieldName) { >>> - Preconditions.checkNotNull(pNode); >>> - Preconditions.checkNotNull(pFieldName); >>> - return pNode.get(pFieldName) != null ? >>> pNode.get(pFieldName).getTextValue() : null; >>> - } >>> - >>> - private Integer getIntegerField(final JsonNode pNode, final String >>> pFieldName) { >>> - Preconditions.checkNotNull(pNode); >>> - Preconditions.checkNotNull(pFieldName); >>> - return pNode.get(pFieldName) != null && >>> pNode.get(pFieldName).isNumber() ? pNode.get(pFieldName) >>> - .getIntValue() : null; >>> - } >>> - >>> - private Long getLongField(final JsonNode pNode, final String >>> pFieldName) { >>> - Preconditions.checkNotNull(pNode); >>> - Preconditions.checkNotNull(pFieldName); >>> - return pNode.get(pFieldName) != null && >>> pNode.get(pFieldName).isLong() ? pNode.get(pFieldName).getLongValue() >>> - : null; >>> - } >>> - >>> - private <O> O getObject(final JsonNode pNode, final String >>> pFieldName, final Class<O> pObjectClass) >>> - throws JsonParseException, JsonMappingException, IOException { >>> - Preconditions.checkNotNull(pNode); >>> - Preconditions.checkNotNull(pFieldName); >>> - Preconditions.checkNotNull(pObjectClass); >>> - if (pNode.get(pFieldName) != null && >>> pNode.get(pFieldName).isObject()) { >>> - return mMapper.readValue(pNode.get(pFieldName), >>> pObjectClass); >>> - } else { >>> - return null; >>> - } >>> - } >>> - >>> - @Override >>> - public ServiceInstance<T> deserialize(final byte[] pBytes) throws >>> Exception { >>> - final ByteArrayInputStream bais = new >>> ByteArrayInputStream(pBytes); >>> - final JsonNode rootNode = mMapper.readTree(bais); >>> - final ServiceInstanceBuilder<T> builder = >>> ServiceInstance.builder(); >>> - { >>> - final String address = getTextField(rootNode, "address"); >>> - if (address != null) { >>> - builder.address(address); >>> - } >>> - } >>> - { >>> - final String id = getTextField(rootNode, "id"); >>> - if (id != null) { >>> - builder.id(id); >>> - } >>> - } >>> - { >>> - final String name = getTextField(rootNode, "name"); >>> - if (name != null) { >>> - builder.name(name); >>> - } >>> - } >>> - { >>> - final Integer port = getIntegerField(rootNode, "port"); >>> - if (port != null) { >>> - builder.port(port); >>> - } >>> - } >>> - { >>> - final Integer sslPort = getIntegerField(rootNode, >>> "sslPort"); >>> - if (sslPort != null) { >>> - builder.sslPort(sslPort); >>> - } >>> - } >>> - { >>> - final Long registrationTimeUTC = getLongField(rootNode, >>> "registrationTimeUTC"); >>> - if (registrationTimeUTC != null) { >>> - builder.registrationTimeUTC(registrationTimeUTC); >>> - } >>> - } >>> - { >>> - final T payload = getObject(rootNode, "payload", >>> mPayloadClass); >>> - if (payload != null) { >>> - builder.payload(payload); >>> - } >>> - } >>> - { >>> - final ServiceType serviceType = getObject(rootNode, >>> "serviceType", ServiceType.class); >>> - if (serviceType != null) { >>> - builder.serviceType(serviceType); >>> - } >>> - } >>> - { >>> - final UriSpec uriSpec = getObject(rootNode, "uriSpec", >>> UriSpec.class); >>> - if (uriSpec != null) { >>> - builder.uriSpec(uriSpec); >>> - } >>> - } >>> - return builder.build(); >>> - } >>> - >>> -} >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> ---------------------------------------------------------------------- >>> diff --git >>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> deleted file mode 100644 >>> index cacc29f..0000000 >>> --- >>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> +++ /dev/null >>> @@ -1,262 +0,0 @@ >>> -/** >>> - * 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.sentry.provider.db.service.persistent; >>> - >>> -import java.io.IOException; >>> -import java.util.Arrays; >>> -import java.util.List; >>> - >>> -import org.apache.curator.RetryPolicy; >>> -import org.apache.curator.framework.CuratorFramework; >>> -import org.apache.curator.framework.CuratorFrameworkFactory; >>> -import org.apache.curator.framework.api.ACLProvider; >>> -import org.apache.curator.framework.imps.CuratorFrameworkState; >>> -import org.apache.curator.framework.imps.DefaultACLProvider; >>> -import org.apache.curator.retry.RetryNTimes; >>> -import org.apache.hadoop.conf.Configuration; >>> -import org.apache.hadoop.security.SecurityUtil; >>> -import org.apache.sentry.service.thrift.JaasConfiguration; >>> -import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig; >>> -import org.apache.zookeeper.ZooDefs.Perms; >>> -import org.apache.zookeeper.client.ZooKeeperSaslClient; >>> -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 com.google.common.annotations.VisibleForTesting; >>> -import com.google.common.base.Preconditions; >>> -import com.google.common.base.Strings; >>> -import com.google.common.collect.Lists; >>> - >>> -/** >>> - * Stores the HA related context >>> - */ >>> -public class HAContext { >>> - >>> - private static final Logger LOGGER = >>> LoggerFactory.getLogger(HAContext.class); >>> - private static volatile HAContext serverHAContext = null; >>> - private static boolean aclChecked = false; >>> - >>> - public final static String SENTRY_SERVICE_REGISTER_NAMESPACE = >>> "sentry-service"; >>> - public static final String SENTRY_ZK_JAAS_NAME = "SentryClient"; >>> - private final String zookeeperQuorum; >>> - private final int retriesMaxCount; >>> - private final int sleepMsBetweenRetries; >>> - private final String namespace; >>> - >>> - private final boolean zkSecure; >>> - private List<ACL> saslACL; >>> - >>> - private final CuratorFramework curatorFramework; >>> - private final RetryPolicy retryPolicy; >>> - >>> - protected HAContext(Configuration conf) throws Exception { >>> - this.zookeeperQuorum = >>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM, >>> - ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM_DEFAULT); >>> - this.retriesMaxCount = >>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT, >>> - ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT_DEFAULT); >>> - this.sleepMsBetweenRetries = >>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS, >>> - >>> ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS_DEFAULT); >>> - this.namespace = >>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE, >>> - ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE_DEFAULT); >>> - this.zkSecure = >>> conf.getBoolean(ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY, >>> - ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY_DEFAULT); >>> - ACLProvider aclProvider; >>> - validateConf(); >>> - if (zkSecure) { >>> - LOGGER.info("Connecting to ZooKeeper with SASL/Kerberos and using >>> 'sasl' ACLs"); >>> - setJaasConfiguration(conf); >>> - System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, >>> - SENTRY_ZK_JAAS_NAME); >>> - saslACL = Lists.newArrayList(); >>> - saslACL.add(new ACL(Perms.ALL, new Id("sasl", >>> getServicePrincipal(conf, >>> - ServerConfig.PRINCIPAL)))); >>> - saslACL.add(new ACL(Perms.ALL, new Id("sasl", >>> getServicePrincipal(conf, >>> - ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL)))); >>> - aclProvider = new SASLOwnerACLProvider(); >>> - String allowConnect = conf.get(ServerConfig.ALLOW_CONNECT); >>> - >>> - if (!Strings.isNullOrEmpty(allowConnect)) { >>> - for (String principal : >>> Arrays.asList(allowConnect.split("\\s*,\\s*"))) { >>> - LOGGER.info("Adding acls for " + principal); >>> - saslACL.add(new ACL(Perms.ALL, new Id("sasl", principal))); >>> - } >>> - } >>> - } else { >>> - LOGGER.info("Connecting to ZooKeeper without authentication"); >>> - aclProvider = new DefaultACLProvider(); >>> - } >>> - >>> - retryPolicy = new RetryNTimes(retriesMaxCount, >>> sleepMsBetweenRetries); >>> - this.curatorFramework = CuratorFrameworkFactory.builder() >>> - .namespace(this.namespace) >>> - .connectString(this.zookeeperQuorum) >>> - .retryPolicy(retryPolicy) >>> - .aclProvider(aclProvider) >>> - .build(); >>> - startCuratorFramework(); >>> - } >>> - >>> - /** >>> - * Use common HAContext (ie curator framework connection to ZK) >>> - * >>> - * @param conf >>> - * @throws Exception >>> - */ >>> - public static HAContext getHAContext(Configuration conf) throws >>> Exception { >>> - if (serverHAContext == null) { >>> - serverHAContext = new HAContext(conf); >>> - Runtime.getRuntime().addShutdownHook(new Thread() { >>> - @Override >>> - public void run() { >>> - LOGGER.info("ShutdownHook closing curator framework"); >>> - try { >>> - clearServerContext(); >>> - } catch (Throwable t) { >>> - LOGGER.error("Error stopping SentryService", t); >>> - } >>> - } >>> - }); >>> - >>> - } >>> - return serverHAContext; >>> - } >>> - >>> - // HA context for server which verifies the ZK ACLs on namespace >>> - public static HAContext getHAServerContext(Configuration conf) throws >>> Exception { >>> - HAContext serverContext = getHAContext(conf); >>> - serverContext.checkAndSetACLs(); >>> - return serverContext; >>> - } >>> - >>> - @VisibleForTesting >>> - public static synchronized void clearServerContext() { >>> - if (serverHAContext != null) { >>> - serverHAContext.getCuratorFramework().close(); >>> - serverHAContext = null; >>> - } >>> - } >>> - >>> - public void startCuratorFramework() { >>> - if (curatorFramework.getState() != CuratorFrameworkState.STARTED) { >>> - curatorFramework.start(); >>> - } >>> - } >>> - >>> - public CuratorFramework getCuratorFramework() { >>> - return this.curatorFramework; >>> - } >>> - >>> - public String getZookeeperQuorum() { >>> - return zookeeperQuorum; >>> - } >>> - >>> - public static boolean isHaEnabled(Configuration conf) { >>> - return conf.getBoolean(ServerConfig.SENTRY_HA_ENABLED, >>> ServerConfig.SENTRY_HA_ENABLED_DEFAULT); >>> - } >>> - >>> - public String getNamespace() { >>> - return namespace; >>> - } >>> - >>> - public RetryPolicy getRetryPolicy() { >>> - return retryPolicy; >>> - } >>> - >>> - private void validateConf() { >>> - Preconditions.checkNotNull(zookeeperQuorum, "Zookeeper Quorum >>> should not be null."); >>> - Preconditions.checkNotNull(namespace, "Zookeeper namespace should >>> not be null."); >>> - } >>> - >>> - protected String getServicePrincipal(Configuration conf, String >>> confProperty) >>> - throws IOException { >>> - String principal = conf.get(confProperty); >>> - Preconditions.checkNotNull(principal); >>> - Preconditions.checkArgument(principal.length() != 0, "Server >>> principal is not right."); >>> - return principal.split("[/@]")[0]; >>> - } >>> - >>> - private void checkAndSetACLs() throws Exception { >>> - if (zkSecure && !aclChecked) { >>> - // If znodes were previously created without security enabled, >>> and now it is, we need to go through all existing znodes >>> - // and set the ACLs for them. This is done just once at the >>> startup >>> - // We can't get the namespace znode through curator; have to go >>> through zk client >>> - startCuratorFramework(); >>> - String newNamespace = "/" + curatorFramework.getNamespace(); >>> - if >>> (curatorFramework.getZookeeperClient().getZooKeeper().exists(newNamespace, >>> null) != null) { >>> - List<ACL> acls = >>> curatorFramework.getZookeeperClient().getZooKeeper().getACL(newNamespace, >>> new Stat()); >>> - if (acls.isEmpty() || >>> !acls.get(0).getId().getScheme().equals("sasl")) { >>> - LOGGER.info("'sasl' ACLs not set; setting..."); >>> - List<String> children = >>> curatorFramework.getZookeeperClient().getZooKeeper().getChildren(newNamespace, >>> null); >>> - for (String child : children) { >>> - checkAndSetACLs("/" + child); >>> - } >>> - >>> curatorFramework.getZookeeperClient().getZooKeeper().setACL(newNamespace, >>> saslACL, -1); >>> - } >>> - } >>> - aclChecked = true; >>> - >>> - } >>> - } >>> - >>> - private void checkAndSetACLs(String path) throws Exception { >>> - LOGGER.info("Setting acls on " + path); >>> - List<String> children = >>> curatorFramework.getChildren().forPath(path); >>> - for (String child : children) { >>> - checkAndSetACLs(path + "/" + child); >>> - } >>> - curatorFramework.setACL().withACL(saslACL).forPath(path); >>> - } >>> - >>> - // This gets ignored during most tests, see >>> ZKXTestCaseWithSecurity#setupZKServer() >>> - private void setJaasConfiguration(Configuration conf) throws >>> IOException { >>> - if ("false".equalsIgnoreCase(conf.get( >>> - ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE, >>> - >>> ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE_DEFAULT))) { >>> - String keytabFile = >>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_KEYTAB); >>> - Preconditions.checkArgument(keytabFile.length() != 0, "Keytab >>> File is not right."); >>> - String principal = >>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL); >>> - principal = SecurityUtil.getServerPrincipal(principal, >>> - conf.get(ServerConfig.RPC_ADDRESS, >>> ServerConfig.RPC_ADDRESS_DEFAULT)); >>> - Preconditions.checkArgument(principal.length() != 0, "Kerberos >>> principal is not right."); >>> - >>> - // This is equivalent to writing a jaas.conf file and setting the >>> system property, "java.security.auth.login.config", to >>> - // point to it (but this way we don't have to write a file, and >>> it works better for the tests) >>> - JaasConfiguration.addEntryForKeytab(SENTRY_ZK_JAAS_NAME, >>> principal, keytabFile); >>> - } else { >>> - // Create jaas conf for ticket cache >>> - JaasConfiguration.addEntryForTicketCache(SENTRY_ZK_JAAS_NAME); >>> - } >>> - >>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance()); >>> - } >>> - >>> - public class SASLOwnerACLProvider implements ACLProvider { >>> - @Override >>> - public List<ACL> getDefaultAcl() { >>> - return saslACL; >>> - } >>> - >>> - @Override >>> - public List<ACL> getAclForPath(String path) { >>> - return saslACL; >>> - } >>> - } >>> -} >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> ---------------------------------------------------------------------- >>> diff --git >>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> deleted file mode 100644 >>> index a79ce5f..0000000 >>> --- >>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> +++ /dev/null >>> @@ -1,133 +0,0 @@ >>> -/** >>> - * 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.sentry.service.thrift; >>> - >>> -import java.util.HashMap; >>> -import java.util.Map; >>> - >>> -import javax.security.auth.login.AppConfigurationEntry; >>> -import javax.security.auth.login.Configuration; >>> - >>> -/** >>> - * Creates a programmatic version of a jaas.conf file. This can be >>> used instead of writing a jaas.conf file and setting >>> - * the system property, "java.security.auth.login.config", to point to >>> that file. It is meant to be used for connecting to >>> - * ZooKeeper. >>> - * <p> >>> - * example usage: >>> - * JaasConfiguration.addEntry("Client", principal, keytabFile); >>> - * >>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance()); >>> - */ >>> -public final class JaasConfiguration extends Configuration { >>> - private static Map<String, AppConfigurationEntry> entries = new >>> HashMap<String, AppConfigurationEntry>(); >>> - private static JaasConfiguration me = null; >>> - private static final String krb5LoginModuleName; >>> - >>> - static { >>> - if (System.getProperty("java.vendor").contains("IBM")) { >>> - krb5LoginModuleName = >>> "com.ibm.security.auth.module.Krb5LoginModule"; >>> - } >>> - else { >>> - krb5LoginModuleName = >>> "com.sun.security.auth.module.Krb5LoginModule"; >>> - } >>> - } >>> - >>> - private JaasConfiguration() { >>> - // don't need to do anything here but we want to make it private >>> - } >>> - >>> - /** >>> - * Return the singleton. You'd typically use it only to do this: >>> - * <p> >>> - * >>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance()); >>> - * >>> - * @return >>> - */ >>> - public static Configuration getInstance() { >>> - if (me == null) { >>> - me = new JaasConfiguration(); >>> - } >>> - return me; >>> - } >>> - >>> - /** >>> - * Add an entry to the jaas configuration with the passed in name, >>> principal, and keytab. The other necessary options will be >>> - * set for you. >>> - * >>> - * @param name The name of the entry (e.g. "Client") >>> - * @param principal The principal of the user >>> - * @param keytab The location of the keytab >>> - */ >>> - public static void addEntryForKeytab(String name, String principal, >>> String keytab) { >>> - Map<String, String> options = new HashMap<String, String>(); >>> - options.put("keyTab", keytab); >>> - options.put("principal", principal); >>> - options.put("useKeyTab", "true"); >>> - options.put("storeKey", "true"); >>> - options.put("useTicketCache", "false"); >>> - AppConfigurationEntry entry = new >>> AppConfigurationEntry(krb5LoginModuleName, >>> - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); >>> - entries.put(name, entry); >>> - } >>> - >>> - /** >>> - * Add an entry to the jaas configuration with the passed in name. >>> The other >>> - * necessary options will be set for you. >>> - * >>> - * @param name The name of the entry (e.g. "Client") >>> - */ >>> - public static void addEntryForTicketCache(String sectionName) { >>> - Map<String, String> options = new HashMap<String, String>(); >>> - options.put("useKeyTab", "false"); >>> - options.put("storeKey", "false"); >>> - options.put("useTicketCache", "true"); >>> - AppConfigurationEntry entry = new >>> AppConfigurationEntry(krb5LoginModuleName, >>> - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); >>> - entries.put(sectionName, entry); >>> - } >>> - >>> - /** >>> - * Removes the specified entry. >>> - * >>> - * @param name The name of the entry to remove >>> - */ >>> - public static void removeEntry(String name) { >>> - entries.remove(name); >>> - } >>> - >>> - /** >>> - * Clears all entries. >>> - */ >>> - public static void clearEntries() { >>> - entries.clear(); >>> - } >>> - >>> - /** >>> - * Returns the entries map. >>> - * >>> - * @return the entries map >>> - */ >>> - public static Map<String, AppConfigurationEntry> getEntries() { >>> - return entries; >>> - } >>> - >>> - @Override >>> - public AppConfigurationEntry[] getAppConfigurationEntry(String name) { >>> - return new AppConfigurationEntry[]{entries.get(name)}; >>> - } >>> -} >>> - >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/pom.xml >>> ---------------------------------------------------------------------- >>> diff --git a/sentry-service/sentry-service-common/pom.xml >>> b/sentry-service/sentry-service-common/pom.xml >>> index d47cafa..512c341 100644 >>> --- a/sentry-service/sentry-service-common/pom.xml >>> +++ b/sentry-service/sentry-service-common/pom.xml >>> @@ -52,6 +52,23 @@ limitations under the License. >>> <groupId>org.apache.sentry</groupId> >>> <artifactId>sentry-core-common</artifactId> >>> </dependency> >>> + <dependency> >>> + <groupId>org.apache.curator</groupId> >>> + <artifactId>curator-recipes</artifactId> >>> + </dependency> >>> + <dependency> >>> + <groupId>org.apache.curator</groupId> >>> + <artifactId>curator-x-discovery</artifactId> >>> + </dependency> >>> + <dependency> >>> + <groupId>org.apache.curator</groupId> >>> + <artifactId>curator-test</artifactId> >>> + </dependency> >>> + <dependency> >>> + <groupId>org.apache.hadoop</groupId> >>> + <artifactId>hadoop-common</artifactId> >>> + <scope>provided</scope> >>> + </dependency> >>> </dependencies> >>> >>> <build> >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> ---------------------------------------------------------------------- >>> diff --git >>> a/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> new file mode 100644 >>> index 0000000..476bf6a >>> --- /dev/null >>> +++ >>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java >>> @@ -0,0 +1,163 @@ >>> +/** >>> + * 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.sentry.provider.db.service.persistent; >>> + >>> +import java.io.ByteArrayInputStream; >>> +import java.io.ByteArrayOutputStream; >>> +import java.io.IOException; >>> + >>> +import org.codehaus.jackson.JsonNode; >>> +import org.codehaus.jackson.JsonParseException; >>> +import org.codehaus.jackson.map.DeserializationConfig; >>> +import org.codehaus.jackson.map.JsonMappingException; >>> +import org.codehaus.jackson.map.ObjectMapper; >>> + >>> +import com.google.common.base.Preconditions; >>> +import org.apache.curator.x.discovery.ServiceInstance; >>> +import org.apache.curator.x.discovery.ServiceInstanceBuilder; >>> +import org.apache.curator.x.discovery.ServiceType; >>> +import org.apache.curator.x.discovery.UriSpec; >>> +import org.apache.curator.x.discovery.details.InstanceSerializer; >>> + >>> +// TODO: Workaround for CURATOR-5 ( >>> https://issues.apache.org/jira/browse/CURATOR-5) >>> +// Remove this class (code from pull request listed on JIRA) and use >>> regular JsonInstanceSerializer once fixed >>> +// (Otherwise we can't properly serialize objects for the ZK Service >>> Discovery) >>> +public class FixedJsonInstanceSerializer<T> implements >>> InstanceSerializer<T> >>> +{ >>> + >>> + private final ObjectMapper mMapper; >>> + private final Class<T> mPayloadClass; >>> + >>> + /** >>> + * @param payloadClass >>> + * used to validate payloads when deserializing >>> + */ >>> + public FixedJsonInstanceSerializer(final Class<T> payloadClass) { >>> + this(payloadClass, new ObjectMapper()); >>> + } >>> + >>> + public FixedJsonInstanceSerializer(final Class<T> pPayloadClass, >>> final ObjectMapper pMapper) { >>> + mPayloadClass = pPayloadClass; >>> + mMapper = pMapper; >>> + >>> mMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, >>> false); >>> + } >>> + >>> + @Override >>> + public byte[] serialize(final ServiceInstance<T> pInstance) throws >>> Exception { >>> + final ByteArrayOutputStream out = new ByteArrayOutputStream(); >>> + mMapper.writeValue(out, pInstance); >>> + return out.toByteArray(); >>> + >>> + } >>> + >>> + private String getTextField(final JsonNode pNode, final String >>> pFieldName) { >>> + Preconditions.checkNotNull(pNode); >>> + Preconditions.checkNotNull(pFieldName); >>> + return pNode.get(pFieldName) != null ? >>> pNode.get(pFieldName).getTextValue() : null; >>> + } >>> + >>> + private Integer getIntegerField(final JsonNode pNode, final String >>> pFieldName) { >>> + Preconditions.checkNotNull(pNode); >>> + Preconditions.checkNotNull(pFieldName); >>> + return pNode.get(pFieldName) != null && >>> pNode.get(pFieldName).isNumber() ? pNode.get(pFieldName) >>> + .getIntValue() : null; >>> + } >>> + >>> + private Long getLongField(final JsonNode pNode, final String >>> pFieldName) { >>> + Preconditions.checkNotNull(pNode); >>> + Preconditions.checkNotNull(pFieldName); >>> + return pNode.get(pFieldName) != null && >>> pNode.get(pFieldName).isLong() ? pNode.get(pFieldName).getLongValue() >>> + : null; >>> + } >>> + >>> + private <O> O getObject(final JsonNode pNode, final String >>> pFieldName, final Class<O> pObjectClass) >>> + throws JsonParseException, JsonMappingException, IOException { >>> + Preconditions.checkNotNull(pNode); >>> + Preconditions.checkNotNull(pFieldName); >>> + Preconditions.checkNotNull(pObjectClass); >>> + if (pNode.get(pFieldName) != null && >>> pNode.get(pFieldName).isObject()) { >>> + return mMapper.readValue(pNode.get(pFieldName), >>> pObjectClass); >>> + } else { >>> + return null; >>> + } >>> + } >>> + >>> + @Override >>> + public ServiceInstance<T> deserialize(final byte[] pBytes) throws >>> Exception { >>> + final ByteArrayInputStream bais = new >>> ByteArrayInputStream(pBytes); >>> + final JsonNode rootNode = mMapper.readTree(bais); >>> + final ServiceInstanceBuilder<T> builder = >>> ServiceInstance.builder(); >>> + { >>> + final String address = getTextField(rootNode, "address"); >>> + if (address != null) { >>> + builder.address(address); >>> + } >>> + } >>> + { >>> + final String id = getTextField(rootNode, "id"); >>> + if (id != null) { >>> + builder.id(id); >>> + } >>> + } >>> + { >>> + final String name = getTextField(rootNode, "name"); >>> + if (name != null) { >>> + builder.name(name); >>> + } >>> + } >>> + { >>> + final Integer port = getIntegerField(rootNode, "port"); >>> + if (port != null) { >>> + builder.port(port); >>> + } >>> + } >>> + { >>> + final Integer sslPort = getIntegerField(rootNode, >>> "sslPort"); >>> + if (sslPort != null) { >>> + builder.sslPort(sslPort); >>> + } >>> + } >>> + { >>> + final Long registrationTimeUTC = getLongField(rootNode, >>> "registrationTimeUTC"); >>> + if (registrationTimeUTC != null) { >>> + builder.registrationTimeUTC(registrationTimeUTC); >>> + } >>> + } >>> + { >>> + final T payload = getObject(rootNode, "payload", >>> mPayloadClass); >>> + if (payload != null) { >>> + builder.payload(payload); >>> + } >>> + } >>> + { >>> + final ServiceType serviceType = getObject(rootNode, >>> "serviceType", ServiceType.class); >>> + if (serviceType != null) { >>> + builder.serviceType(serviceType); >>> + } >>> + } >>> + { >>> + final UriSpec uriSpec = getObject(rootNode, "uriSpec", >>> UriSpec.class); >>> + if (uriSpec != null) { >>> + builder.uriSpec(uriSpec); >>> + } >>> + } >>> + return builder.build(); >>> + } >>> + >>> +} >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> ---------------------------------------------------------------------- >>> diff --git >>> a/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> new file mode 100644 >>> index 0000000..cacc29f >>> --- /dev/null >>> +++ >>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java >>> @@ -0,0 +1,262 @@ >>> +/** >>> + * 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.sentry.provider.db.service.persistent; >>> + >>> +import java.io.IOException; >>> +import java.util.Arrays; >>> +import java.util.List; >>> + >>> +import org.apache.curator.RetryPolicy; >>> +import org.apache.curator.framework.CuratorFramework; >>> +import org.apache.curator.framework.CuratorFrameworkFactory; >>> +import org.apache.curator.framework.api.ACLProvider; >>> +import org.apache.curator.framework.imps.CuratorFrameworkState; >>> +import org.apache.curator.framework.imps.DefaultACLProvider; >>> +import org.apache.curator.retry.RetryNTimes; >>> +import org.apache.hadoop.conf.Configuration; >>> +import org.apache.hadoop.security.SecurityUtil; >>> +import org.apache.sentry.service.thrift.JaasConfiguration; >>> +import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig; >>> +import org.apache.zookeeper.ZooDefs.Perms; >>> +import org.apache.zookeeper.client.ZooKeeperSaslClient; >>> +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 com.google.common.annotations.VisibleForTesting; >>> +import com.google.common.base.Preconditions; >>> +import com.google.common.base.Strings; >>> +import com.google.common.collect.Lists; >>> + >>> +/** >>> + * Stores the HA related context >>> + */ >>> +public class HAContext { >>> + >>> + private static final Logger LOGGER = >>> LoggerFactory.getLogger(HAContext.class); >>> + private static volatile HAContext serverHAContext = null; >>> + private static boolean aclChecked = false; >>> + >>> + public final static String SENTRY_SERVICE_REGISTER_NAMESPACE = >>> "sentry-service"; >>> + public static final String SENTRY_ZK_JAAS_NAME = "SentryClient"; >>> + private final String zookeeperQuorum; >>> + private final int retriesMaxCount; >>> + private final int sleepMsBetweenRetries; >>> + private final String namespace; >>> + >>> + private final boolean zkSecure; >>> + private List<ACL> saslACL; >>> + >>> + private final CuratorFramework curatorFramework; >>> + private final RetryPolicy retryPolicy; >>> + >>> + protected HAContext(Configuration conf) throws Exception { >>> + this.zookeeperQuorum = >>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM, >>> + ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM_DEFAULT); >>> + this.retriesMaxCount = >>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT, >>> + ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT_DEFAULT); >>> + this.sleepMsBetweenRetries = >>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS, >>> + >>> ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS_DEFAULT); >>> + this.namespace = >>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE, >>> + ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE_DEFAULT); >>> + this.zkSecure = >>> conf.getBoolean(ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY, >>> + ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY_DEFAULT); >>> + ACLProvider aclProvider; >>> + validateConf(); >>> + if (zkSecure) { >>> + LOGGER.info("Connecting to ZooKeeper with SASL/Kerberos and using >>> 'sasl' ACLs"); >>> + setJaasConfiguration(conf); >>> + System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, >>> + SENTRY_ZK_JAAS_NAME); >>> + saslACL = Lists.newArrayList(); >>> + saslACL.add(new ACL(Perms.ALL, new Id("sasl", >>> getServicePrincipal(conf, >>> + ServerConfig.PRINCIPAL)))); >>> + saslACL.add(new ACL(Perms.ALL, new Id("sasl", >>> getServicePrincipal(conf, >>> + ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL)))); >>> + aclProvider = new SASLOwnerACLProvider(); >>> + String allowConnect = conf.get(ServerConfig.ALLOW_CONNECT); >>> + >>> + if (!Strings.isNullOrEmpty(allowConnect)) { >>> + for (String principal : >>> Arrays.asList(allowConnect.split("\\s*,\\s*"))) { >>> + LOGGER.info("Adding acls for " + principal); >>> + saslACL.add(new ACL(Perms.ALL, new Id("sasl", principal))); >>> + } >>> + } >>> + } else { >>> + LOGGER.info("Connecting to ZooKeeper without authentication"); >>> + aclProvider = new DefaultACLProvider(); >>> + } >>> + >>> + retryPolicy = new RetryNTimes(retriesMaxCount, >>> sleepMsBetweenRetries); >>> + this.curatorFramework = CuratorFrameworkFactory.builder() >>> + .namespace(this.namespace) >>> + .connectString(this.zookeeperQuorum) >>> + .retryPolicy(retryPolicy) >>> + .aclProvider(aclProvider) >>> + .build(); >>> + startCuratorFramework(); >>> + } >>> + >>> + /** >>> + * Use common HAContext (ie curator framework connection to ZK) >>> + * >>> + * @param conf >>> + * @throws Exception >>> + */ >>> + public static HAContext getHAContext(Configuration conf) throws >>> Exception { >>> + if (serverHAContext == null) { >>> + serverHAContext = new HAContext(conf); >>> + Runtime.getRuntime().addShutdownHook(new Thread() { >>> + @Override >>> + public void run() { >>> + LOGGER.info("ShutdownHook closing curator framework"); >>> + try { >>> + clearServerContext(); >>> + } catch (Throwable t) { >>> + LOGGER.error("Error stopping SentryService", t); >>> + } >>> + } >>> + }); >>> + >>> + } >>> + return serverHAContext; >>> + } >>> + >>> + // HA context for server which verifies the ZK ACLs on namespace >>> + public static HAContext getHAServerContext(Configuration conf) throws >>> Exception { >>> + HAContext serverContext = getHAContext(conf); >>> + serverContext.checkAndSetACLs(); >>> + return serverContext; >>> + } >>> + >>> + @VisibleForTesting >>> + public static synchronized void clearServerContext() { >>> + if (serverHAContext != null) { >>> + serverHAContext.getCuratorFramework().close(); >>> + serverHAContext = null; >>> + } >>> + } >>> + >>> + public void startCuratorFramework() { >>> + if (curatorFramework.getState() != CuratorFrameworkState.STARTED) { >>> + curatorFramework.start(); >>> + } >>> + } >>> + >>> + public CuratorFramework getCuratorFramework() { >>> + return this.curatorFramework; >>> + } >>> + >>> + public String getZookeeperQuorum() { >>> + return zookeeperQuorum; >>> + } >>> + >>> + public static boolean isHaEnabled(Configuration conf) { >>> + return conf.getBoolean(ServerConfig.SENTRY_HA_ENABLED, >>> ServerConfig.SENTRY_HA_ENABLED_DEFAULT); >>> + } >>> + >>> + public String getNamespace() { >>> + return namespace; >>> + } >>> + >>> + public RetryPolicy getRetryPolicy() { >>> + return retryPolicy; >>> + } >>> + >>> + private void validateConf() { >>> + Preconditions.checkNotNull(zookeeperQuorum, "Zookeeper Quorum >>> should not be null."); >>> + Preconditions.checkNotNull(namespace, "Zookeeper namespace should >>> not be null."); >>> + } >>> + >>> + protected String getServicePrincipal(Configuration conf, String >>> confProperty) >>> + throws IOException { >>> + String principal = conf.get(confProperty); >>> + Preconditions.checkNotNull(principal); >>> + Preconditions.checkArgument(principal.length() != 0, "Server >>> principal is not right."); >>> + return principal.split("[/@]")[0]; >>> + } >>> + >>> + private void checkAndSetACLs() throws Exception { >>> + if (zkSecure && !aclChecked) { >>> + // If znodes were previously created without security enabled, >>> and now it is, we need to go through all existing znodes >>> + // and set the ACLs for them. This is done just once at the >>> startup >>> + // We can't get the namespace znode through curator; have to go >>> through zk client >>> + startCuratorFramework(); >>> + String newNamespace = "/" + curatorFramework.getNamespace(); >>> + if >>> (curatorFramework.getZookeeperClient().getZooKeeper().exists(newNamespace, >>> null) != null) { >>> + List<ACL> acls = >>> curatorFramework.getZookeeperClient().getZooKeeper().getACL(newNamespace, >>> new Stat()); >>> + if (acls.isEmpty() || >>> !acls.get(0).getId().getScheme().equals("sasl")) { >>> + LOGGER.info("'sasl' ACLs not set; setting..."); >>> + List<String> children = >>> curatorFramework.getZookeeperClient().getZooKeeper().getChildren(newNamespace, >>> null); >>> + for (String child : children) { >>> + checkAndSetACLs("/" + child); >>> + } >>> + >>> curatorFramework.getZookeeperClient().getZooKeeper().setACL(newNamespace, >>> saslACL, -1); >>> + } >>> + } >>> + aclChecked = true; >>> + >>> + } >>> + } >>> + >>> + private void checkAndSetACLs(String path) throws Exception { >>> + LOGGER.info("Setting acls on " + path); >>> + List<String> children = >>> curatorFramework.getChildren().forPath(path); >>> + for (String child : children) { >>> + checkAndSetACLs(path + "/" + child); >>> + } >>> + curatorFramework.setACL().withACL(saslACL).forPath(path); >>> + } >>> + >>> + // This gets ignored during most tests, see >>> ZKXTestCaseWithSecurity#setupZKServer() >>> + private void setJaasConfiguration(Configuration conf) throws >>> IOException { >>> + if ("false".equalsIgnoreCase(conf.get( >>> + ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE, >>> + >>> ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE_DEFAULT))) { >>> + String keytabFile = >>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_KEYTAB); >>> + Preconditions.checkArgument(keytabFile.length() != 0, "Keytab >>> File is not right."); >>> + String principal = >>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL); >>> + principal = SecurityUtil.getServerPrincipal(principal, >>> + conf.get(ServerConfig.RPC_ADDRESS, >>> ServerConfig.RPC_ADDRESS_DEFAULT)); >>> + Preconditions.checkArgument(principal.length() != 0, "Kerberos >>> principal is not right."); >>> + >>> + // This is equivalent to writing a jaas.conf file and setting the >>> system property, "java.security.auth.login.config", to >>> + // point to it (but this way we don't have to write a file, and >>> it works better for the tests) >>> + JaasConfiguration.addEntryForKeytab(SENTRY_ZK_JAAS_NAME, >>> principal, keytabFile); >>> + } else { >>> + // Create jaas conf for ticket cache >>> + JaasConfiguration.addEntryForTicketCache(SENTRY_ZK_JAAS_NAME); >>> + } >>> + >>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance()); >>> + } >>> + >>> + public class SASLOwnerACLProvider implements ACLProvider { >>> + @Override >>> + public List<ACL> getDefaultAcl() { >>> + return saslACL; >>> + } >>> + >>> + @Override >>> + public List<ACL> getAclForPath(String path) { >>> + return saslACL; >>> + } >>> + } >>> +} >>> >>> >>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> ---------------------------------------------------------------------- >>> diff --git >>> a/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> new file mode 100644 >>> index 0000000..a79ce5f >>> --- /dev/null >>> +++ >>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java >>> @@ -0,0 +1,133 @@ >>> +/** >>> + * 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.sentry.service.thrift; >>> + >>> +import java.util.HashMap; >>> +import java.util.Map; >>> + >>> +import javax.security.auth.login.AppConfigurationEntry; >>> +import javax.security.auth.login.Configuration; >>> + >>> +/** >>> + * Creates a programmatic version of a jaas.conf file. This can be >>> used instead of writing a jaas.conf file and setting >>> + * the system property, "java.security.auth.login.config", to point to >>> that file. It is meant to be used for connecting to >>> + * ZooKeeper. >>> + * <p> >>> + * example usage: >>> + * JaasConfiguration.addEntry("Client", principal, keytabFile); >>> + * >>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance()); >>> + */ >>> +public final class JaasConfiguration extends Configuration { >>> + private static Map<String, AppConfigurationEntry> entries = new >>> HashMap<String, AppConfigurationEntry>(); >>> + private static JaasConfiguration me = null; >>> + private static final String krb5LoginModuleName; >>> + >>> + static { >>> + if (System.getProperty("java.vendor").contains("IBM")) { >>> + krb5LoginModuleName = >>> "com.ibm.security.auth.module.Krb5LoginModule"; >>> + } >>> + else { >>> + krb5LoginModuleName = >>> "com.sun.security.auth.module.Krb5LoginModule"; >>> + } >>> + } >>> + >>> + private JaasConfiguration() { >>> + // don't need to do anything here but we want to make it private >>> + } >>> + >>> + /** >>> + * Return the singleton. You'd typically use it only to do this: >>> + * <p> >>> + * >>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance()); >>> + * >>> + * @return >>> + */ >>> + public static Configuration getInstance() { >>> + if (me == null) { >>> + me = new JaasConfiguration(); >>> + } >>> + return me; >>> + } >>> + >>> + /** >>> + * Add an entry to the jaas configuration with the passed in name, >>> principal, and keytab. The other necessary options will be >>> + * set for you. >>> + * >>> + * @param name The name of the entry (e.g. "Client") >>> + * @param principal The principal of the user >>> + * @param keytab The location of the keytab >>> + */ >>> + public static void addEntryForKeytab(String name, String principal, >>> String keytab) { >>> + Map<String, String> options = new HashMap<String, String>(); >>> + options.put("keyTab", keytab); >>> + options.put("principal", principal); >>> + options.put("useKeyTab", "true"); >>> + options.put("storeKey", "true"); >>> + options.put("useTicketCache", "false"); >>> + AppConfigurationEntry entry = new >>> AppConfigurationEntry(krb5LoginModuleName, >>> + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); >>> + entries.put(name, entry); >>> + } >>> + >>> + /** >>> + * Add an entry to the jaas configuration with the passed in name. >>> The other >>> + * necessary options will be set for you. >>> + * >>> + * @param name The name of the entry (e.g. "Client") >>> + */ >>> + public static void addEntryForTicketCache(String sectionName) { >>> + Map<String, String> options = new HashMap<String, String>(); >>> + options.put("useKeyTab", "false"); >>> + options.put("storeKey", "false"); >>> + options.put("useTicketCache", "true"); >>> + AppConfigurationEntry entry = new >>> AppConfigurationEntry(krb5LoginModuleName, >>> + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); >>> + entries.put(sectionName, entry); >>> + } >>> + >>> + /** >>> + * Removes the specified entry. >>> + * >>> + * @param name The name of the entry to remove >>> + */ >>> + public static void removeEntry(String name) { >>> + entries.remove(name); >>> + } >>> + >>> + /** >>> + * Clears all entries. >>> + */ >>> + public static void clearEntries() { >>> + entries.clear(); >>> + } >>> + >>> + /** >>> + * Returns the entries map. >>> + * >>> + * @return the entries map >>> + */ >>> + public static Map<String, AppConfigurationEntry> getEntries() { >>> + return entries; >>> + } >>> + >>> + @Override >>> + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { >>> + return new AppConfigurationEntry[]{entries.get(name)}; >>> + } >>> +} >>> + >>> >>> > > > -- > Celebrating a decade of community accomplishments > cloudera.com/hadoop10 > #hadoop10 > -- Anne
