Java tutorial
/** * 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 permission and * limitations under the License. */ package org.apache.sentry.hdfs; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.hadoop.conf.Configurable; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.UnresolvedLinkException; import org.apache.hadoop.fs.permission.AclEntry; import org.apache.hadoop.fs.permission.AclEntryScope; import org.apache.hadoop.fs.permission.AclEntryType; import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.server.namenode.AclFeature; import org.apache.hadoop.hdfs.server.namenode.AuthorizationProvider; import org.apache.hadoop.hdfs.server.namenode.DefaultAuthorizationProvider; import org.apache.hadoop.security.AccessControlException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; public class SentryAuthorizationProvider extends AuthorizationProvider implements Configurable { static class SentryAclFeature extends AclFeature { public SentryAclFeature(ImmutableList<AclEntry> entries) { super(entries); } } private static Logger LOG = LoggerFactory.getLogger(SentryAuthorizationProvider.class); private boolean started; private Configuration conf; private AuthorizationProvider defaultAuthzProvider; private String user; private String group; private FsPermission permission; private boolean originalAuthzAsAcl; private SentryAuthorizationInfo authzInfo; public SentryAuthorizationProvider() { this(null); } @VisibleForTesting SentryAuthorizationProvider(SentryAuthorizationInfo authzInfo) { this.authzInfo = authzInfo; } @Override public void setConf(Configuration conf) { this.conf = conf; } @Override public Configuration getConf() { return conf; } @Override public synchronized void start() { if (started) { throw new IllegalStateException("Provider already started"); } started = true; try { if (!conf.getBoolean(DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, false)) { throw new RuntimeException("HDFS ACLs must be enabled"); } defaultAuthzProvider = new DefaultAuthorizationProvider(); defaultAuthzProvider.start(); // Configuration is read from hdfs-sentry.xml and NN configuration, in // that order of precedence. Configuration conf = new Configuration(this.conf); conf.addResource(SentryAuthorizationConstants.CONFIG_FILE); user = conf.get(SentryAuthorizationConstants.HDFS_USER_KEY, SentryAuthorizationConstants.HDFS_USER_DEFAULT); group = conf.get(SentryAuthorizationConstants.HDFS_GROUP_KEY, SentryAuthorizationConstants.HDFS_GROUP_DEFAULT); permission = FsPermission .createImmutable((short) conf.getLong(SentryAuthorizationConstants.HDFS_PERMISSION_KEY, SentryAuthorizationConstants.HDFS_PERMISSION_DEFAULT)); originalAuthzAsAcl = conf.getBoolean(SentryAuthorizationConstants.INCLUDE_HDFS_AUTHZ_AS_ACL_KEY, SentryAuthorizationConstants.INCLUDE_HDFS_AUTHZ_AS_ACL_DEFAULT); LOG.info("Starting"); LOG.info("Config: hdfs-user[{}] hdfs-group[{}] hdfs-permission[{}] " + "include-hdfs-authz-as-acl[{}]", new Object[] { user, group, permission, originalAuthzAsAcl }); if (authzInfo == null) { authzInfo = new SentryAuthorizationInfo(conf); } authzInfo.start(); } catch (Exception ex) { throw new RuntimeException(ex); } } @Override public synchronized void stop() { LOG.debug("Stopping"); authzInfo.stop(); defaultAuthzProvider.stop(); defaultAuthzProvider = null; } @Override public void setSnaphottableDirs(Map<INodeAuthorizationInfo, Integer> snapshotableDirs) { defaultAuthzProvider.setSnaphottableDirs(snapshotableDirs); } @Override public void addSnapshottable(INodeAuthorizationInfo dir) { defaultAuthzProvider.addSnapshottable(dir); } @Override public void removeSnapshottable(INodeAuthorizationInfo dir) { defaultAuthzProvider.removeSnapshottable(dir); } @Override public void createSnapshot(INodeAuthorizationInfo dir, int snapshotId) throws IOException { defaultAuthzProvider.createSnapshot(dir, snapshotId); } @Override public void removeSnapshot(INodeAuthorizationInfo dir, int snapshotId) throws IOException { defaultAuthzProvider.removeSnapshot(dir, snapshotId); } @Override public void checkPermission(String user, Set<String> groups, INodeAuthorizationInfo[] inodes, int snapshotId, boolean doCheckOwner, FsAction ancestorAccess, FsAction parentAccess, FsAction access, FsAction subAccess, boolean ignoreEmptyDir) throws AccessControlException, UnresolvedLinkException { defaultAuthzProvider.checkPermission(user, groups, inodes, snapshotId, doCheckOwner, ancestorAccess, parentAccess, access, subAccess, ignoreEmptyDir); } private static final String[] EMPTY_STRING_ARRAY = new String[0]; private String[] getPathElements(INodeAuthorizationInfo node) { return getPathElements(node, 0); } private String[] getPathElements(INodeAuthorizationInfo node, int idx) { String[] paths; INodeAuthorizationInfo parent = node.getParent(); if (parent == null) { paths = (idx > 0) ? new String[idx] : EMPTY_STRING_ARRAY; } else { paths = getPathElements(parent, idx + 1); paths[paths.length - 1 - idx] = node.getLocalName(); } return paths; } @Override public void setUser(INodeAuthorizationInfo node, String user) { defaultAuthzProvider.setUser(node, user); } @Override public String getUser(INodeAuthorizationInfo node, int snapshotId) { String user; String[] pathElements = getPathElements(node); if (!authzInfo.isManaged(pathElements)) { user = defaultAuthzProvider.getUser(node, snapshotId); } else { if (!authzInfo.isStale()) { if (authzInfo.doesBelongToAuthzObject(pathElements)) { user = this.user; } else { user = defaultAuthzProvider.getUser(node, snapshotId); } } else { user = this.user; } } return user; } @Override public void setGroup(INodeAuthorizationInfo node, String group) { defaultAuthzProvider.setGroup(node, group); } @Override public String getGroup(INodeAuthorizationInfo node, int snapshotId) { String group; String[] pathElements = getPathElements(node); if (!authzInfo.isManaged(pathElements)) { group = getDefaultProviderGroup(node, snapshotId); } else { if (!authzInfo.isStale()) { if (authzInfo.doesBelongToAuthzObject(pathElements)) { group = this.group; } else { group = getDefaultProviderGroup(node, snapshotId); } } else { group = this.group; } } return group; } @Override public void setPermission(INodeAuthorizationInfo node, FsPermission permission) { defaultAuthzProvider.setPermission(node, permission); } @Override public FsPermission getFsPermission(INodeAuthorizationInfo node, int snapshotId) { FsPermission permission; String[] pathElements = getPathElements(node); if (!authzInfo.isManaged(pathElements)) { permission = defaultAuthzProvider.getFsPermission(node, snapshotId); } else { FsPermission returnPerm = this.permission; // Handle case when prefix directory is itself associated with an // authorizable object (default db directory in hive) // An executable permission needs to be set on the the prefix directory // in this case.. else, subdirectories (which map to other dbs) will // not be travesible. for (String[] prefixPath : authzInfo.getPathPrefixes()) { if (Arrays.equals(prefixPath, pathElements)) { returnPerm = FsPermission.createImmutable((short) (returnPerm.toShort() | 0x01)); break; } } if (!authzInfo.isStale()) { if (authzInfo.doesBelongToAuthzObject(pathElements)) { permission = returnPerm; } else { permission = defaultAuthzProvider.getFsPermission(node, snapshotId); } } else { permission = returnPerm; } } return permission; } private List<AclEntry> createAclEntries(String user, String group, FsPermission permission) { List<AclEntry> list = new ArrayList<AclEntry>(); AclEntry.Builder builder = new AclEntry.Builder(); FsPermission fsPerm = new FsPermission(permission); builder.setName(user); builder.setType(AclEntryType.USER); builder.setScope(AclEntryScope.ACCESS); builder.setPermission(fsPerm.getUserAction()); list.add(builder.build()); builder.setName(group); builder.setType(AclEntryType.GROUP); builder.setScope(AclEntryScope.ACCESS); builder.setPermission(fsPerm.getGroupAction()); list.add(builder.build()); builder.setName(null); return list; } @Override public AclFeature getAclFeature(INodeAuthorizationInfo node, int snapshotId) { AclFeature f = null; String[] pathElements = getPathElements(node); String p = Arrays.toString(pathElements); boolean isManaged = false; boolean isStale = false; boolean hasAuthzObj = false; Map<String, AclEntry> aclMap = null; if (!authzInfo.isManaged(pathElements)) { isManaged = false; f = defaultAuthzProvider.getAclFeature(node, snapshotId); } else { isManaged = true; aclMap = new HashMap<String, AclEntry>(); if (originalAuthzAsAcl) { String user = defaultAuthzProvider.getUser(node, snapshotId); String group = getDefaultProviderGroup(node, snapshotId); FsPermission perm = defaultAuthzProvider.getFsPermission(node, snapshotId); addToACLMap(aclMap, createAclEntries(user, group, perm)); } else { addToACLMap(aclMap, createAclEntries(this.user, this.group, this.permission)); } if (!authzInfo.isStale()) { isStale = false; if (authzInfo.doesBelongToAuthzObject(pathElements)) { hasAuthzObj = true; addToACLMap(aclMap, authzInfo.getAclEntries(pathElements)); f = new SentryAclFeature(ImmutableList.copyOf(aclMap.values())); } else { hasAuthzObj = false; f = defaultAuthzProvider.getAclFeature(node, snapshotId); } } else { isStale = true; f = new SentryAclFeature(ImmutableList.copyOf(aclMap.values())); } } if (LOG.isDebugEnabled()) { LOG.debug("### getAclEntry \n[" + (p == null ? "null" : p) + "] : [" + "isManaged=" + isManaged + ", isStale=" + isStale + ", hasAuthzObj=" + hasAuthzObj + ", origAuthzAsAcl=" + originalAuthzAsAcl + "]\n" + "[" + (aclMap == null ? "null" : aclMap) + "]\n" + "[" + (f == null ? "null" : f.getEntries()) + "]\n"); } return f; } private void addToACLMap(Map<String, AclEntry> map, Collection<AclEntry> entries) { for (AclEntry ent : entries) { String key = (ent.getName() == null ? "" : ent.getName()) + ent.getScope() + ent.getType(); AclEntry aclEntry = map.get(key); if (aclEntry == null) { map.put(key, ent); } else { map.put(key, new AclEntry.Builder().setName(ent.getName()).setScope(ent.getScope()) .setType(ent.getType()) .setPermission(ent.getPermission().or(aclEntry.getPermission())).build()); } } } private String getDefaultProviderGroup(INodeAuthorizationInfo node, int snapshotId) { String group = defaultAuthzProvider.getGroup(node, snapshotId); INodeAuthorizationInfo pNode = node.getParent(); while (group == null && pNode != null) { group = defaultAuthzProvider.getGroup(pNode, snapshotId); pNode = pNode.getParent(); } return group; } @Override public void removeAclFeature(INodeAuthorizationInfo node) { AclFeature aclFeature = node.getAclFeature(CURRENT_STATE_ID); if (aclFeature.getClass() != SentryAclFeature.class) { defaultAuthzProvider.removeAclFeature(node); } } @Override public void addAclFeature(INodeAuthorizationInfo node, AclFeature f) { String[] pathElements = getPathElements(node); if (!authzInfo.isManaged(pathElements)) { defaultAuthzProvider.addAclFeature(node, f); } } }