package com.thinkbiganalytics.datalake.authorization.hdfs;
/*-
* #%L
* thinkbig-sentry-client
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* Licensed 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.
* #L%
*/
import com.google.common.collect.Lists;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
/**
*/
public class HDFSUtil {
private static final Logger log = LoggerFactory.getLogger(HDFSUtil.class);
final private static String READ = "read";
final private static String WRITE = "write";
final private static String EXECUTE = "execute";
final private static String ALL = "all";
final private static String NONE = "none";
private static String modelDBPath = "/model.db/";
private static String appBasePath = "/app/warehouse/";
private static String etlBasePath = "/etl/";
private static String archiveBasePath = "/archive/";
/**
* @param configResources : Hadoop configuration resource
*/
public static Configuration getConfigurationFromResources(String configResources) throws IOException {
boolean foundResources = false;
final Configuration config = new Configuration();
if (null != configResources) {
String[] resources = configResources.split(",");
for (String resource : resources) {
config.addResource(new Path(resource.trim()));
foundResources = true;
}
}
if (!foundResources) {
// check that at least 1 non-default resource is available on the classpath
String configStr = config.toString();
for (String resource : configStr.substring(configStr.indexOf(":") + 1).split(",")) {
if (!resource.contains("default") && config.getResource(resource.trim()) != null) {
foundResources = true;
break;
}
}
}
if (!foundResources) {
throw new IOException("Could not find any of the " + "hadoop conf" + " on the classpath");
}
return config;
}
/**
* @param category_name : Category Name
* @param feed_name : Feed Name
* @param permission_level : Level At Which Permission Needs to be granted.
* @return : Returns list of HDFS of Path
*/
public String constructResourceforPermissionHDFS(String category_name, String feed_name, String permission_level) {
String final_resource_path = "";
//Check level at which permission needs to be defined.
if (permission_level.equalsIgnoreCase("category")) {
String modeldb = modelDBPath + category_name;
String appPath = appBasePath + category_name;
String etlPath = etlBasePath + category_name;
String archivePath = archiveBasePath + category_name;
final_resource_path = modeldb + "," + appPath + "," + etlPath + "," + archivePath;
} else {
String modeldb = modelDBPath + category_name + "/" + feed_name;
String appPath = appBasePath + category_name + "/" + feed_name;
String etlPath = etlBasePath + category_name + "/" + feed_name;
String archivePath = archiveBasePath + category_name + "/" + feed_name;
final_resource_path = modeldb + "," + appPath + "," + etlPath + "," + archivePath;
}
return final_resource_path;
}
/**
* @param allPathForAclCreation : Each Kylo internal path
* @param conf : Hadoop configuration resources
* @param fileSystem : HDFS filesystem
* @param groupList : List of group for granting permission
*/
public void splitPathListAndApplyPolicy(String allPathForAclCreation, Configuration conf, FileSystem fileSystem, String groupList, String hdfs_permission) throws IOException {
String allKyloIntermediatePath[] = allPathForAclCreation.split(",");
for (int pathCounter = 0; pathCounter < allKyloIntermediatePath.length; pathCounter++) {
try {
individualIntermediatePathApplyPolicy(conf, fileSystem, allKyloIntermediatePath[pathCounter], groupList, hdfs_permission);
} catch (IOException e) {
throw new IOException("Unable to iterate on HDFS directories " + e.getMessage());
}
}
}
public void splitPathListAndFlushPolicy(String allPathForAclCreation, Configuration conf, FileSystem fileSystem) throws IOException {
String allKyloIntermediatePath[] = allPathForAclCreation.split(",");
for (int pathCounter = 0; pathCounter < allKyloIntermediatePath.length; pathCounter++) {
try {
individualIntermediatePathFlushPolicy(conf, fileSystem, allKyloIntermediatePath[pathCounter]);
} catch (IOException e) {
throw new IOException("Unable to iterate on HDFS directories " + e.getMessage());
}
}
}
public void individualIntermediatePathApplyPolicy(Configuration conf, FileSystem fileSystem, String kyloPath, String groupList, String hdfs_permission) throws IOException {
Path path = new Path(kyloPath);
fileSystem = path.getFileSystem(conf);
listAllDirAndApplyPolicy(fileSystem, path, groupList, hdfs_permission);
}
public void individualIntermediatePathFlushPolicy(Configuration conf, FileSystem fileSystem, String kyloPath) throws IOException {
Path path = new Path(kyloPath);
fileSystem = path.getFileSystem(conf);
listAllDirAndFlushPolicy(fileSystem, path);
}
private void listAllDirAndFlushPolicy(FileSystem fileSystem, Path path) throws FileNotFoundException, IOException {
FileStatus[] fileStatus = fileSystem.listStatus(path);
for (FileStatus status : fileStatus) {
// Apply ACL recursively on each file/directory.
if (status.isDirectory()) {
// Flush ACL before creating new one.
flushAcl(fileSystem, status.getPath());
listAllDirAndFlushPolicy(fileSystem, status.getPath());
} else {
// Flush ACL before creating new one.
flushAcl(fileSystem, status.getPath());
}
}
}
/**
* @param fileSystem : HDFS fileSystem object
* @param path : Path on which ACL needs to be created
* @param groups : List of group to which permission needs to be granted.
*/
public void listAllDirAndApplyPolicy(FileSystem fileSystem, Path path, String groups, String hdfsPermission) throws FileNotFoundException, IOException {
FsAction fsActionObject = getFinalPermission(hdfsPermission);
FileStatus[] fileStatus = fileSystem.listStatus(path);
for (FileStatus status : fileStatus) {
// Flush ACL before creating new one.
flushAcl(fileSystem, status.getPath());
// Apply ACL recursively on each file/directory.
if (status.isDirectory()) {
String groupListForPermission[] = groups.split(",");
for (int groupCounter = 0; groupCounter < groupListForPermission.length; groupCounter++) {
// Create HDFS ACL for each for each Path on HDFS
AclEntry aclEntryOwner = new AclEntry.Builder().setName(groupListForPermission[groupCounter])
.setPermission(fsActionObject).setScope(AclEntryScope.ACCESS).setType(AclEntryType.GROUP).build();
AclEntry aclEntryOther = new AclEntry.Builder().setPermission(FsAction.NONE).setScope(AclEntryScope.ACCESS).setType(AclEntryType.OTHER).build();
// Apply ACL on Path
applyAcl(fileSystem, status.getPath(), aclEntryOwner);
applyAcl(fileSystem, status.getPath(), aclEntryOther);
}
// Recursive call made to apply acl on each sub directory
listAllDirAndApplyPolicy(fileSystem, status.getPath(), groups, hdfsPermission);
} else {
String groupListForPermission[] = groups.split(",");
for (int groupCounter = 0; groupCounter < groupListForPermission.length; groupCounter++) {
// Create HDFS ACL for each for each Path on HDFS
AclEntry aclEntryOwner = new AclEntry.Builder().setName(groupListForPermission[groupCounter])
.setPermission(fsActionObject).setScope(AclEntryScope.ACCESS).setType(AclEntryType.GROUP).build();
AclEntry aclEntryOther = new AclEntry.Builder().setPermission(FsAction.NONE).setScope(AclEntryScope.ACCESS).setType(AclEntryType.OTHER).build();
// Apply ACL on Path
applyAcl(fileSystem, status.getPath(), aclEntryOwner);
applyAcl(fileSystem, status.getPath(), aclEntryOther);
}
}
}
}
/**
* @param fileSystem : HDFS FileSystem Object
* @param path : HDFS Path
* @param aclEntry : ACL for HDFS Path
*/
public void applyAcl(FileSystem fileSystem, Path path, AclEntry aclEntry) throws IOException {
try {
fileSystem.modifyAclEntries(path, Lists.newArrayList(aclEntry));
} catch (IOException e) {
throw new IOException("Unable to apply HDFS Policy for " + path.toString() + " " + e.getMessage());
}
}
/**
* @param fileSystem : HDFS FileSystem Object
* @param path : HDFS Path
*/
public void flushAcl(FileSystem fileSystem, Path path) throws IOException {
try {
fileSystem.removeAcl(path);
} catch (IOException e) {
throw new IOException("Unable to flush HDFS Policy for " + path.toString() + " " + e.getMessage());
}
}
/**
* @param hdfsPermission : Permission assgined by user.
* @return : Final Permission to be set for creating ACL
*/
private FsAction getFinalPermission(String hdfsPermission) {
HashMap<String, Integer> standardPermissionMap = new HashMap<String, Integer>();
String permissions[] = hdfsPermission.split(",");
standardPermissionMap.put(READ, 0);
standardPermissionMap.put(WRITE, 0);
standardPermissionMap.put(EXECUTE, 0);
standardPermissionMap.put(NONE, 0);
standardPermissionMap.put(ALL, 0);
for (String permission : permissions) {
permission = permission.toLowerCase();
switch (permission) {
case READ:
standardPermissionMap.put(READ, 1);
break;
case WRITE:
standardPermissionMap.put(WRITE, 1);
break;
case EXECUTE:
standardPermissionMap.put(EXECUTE, 1);
break;
case ALL:
return FsAction.ALL;
case NONE:
return FsAction.NONE;
default:
standardPermissionMap.put(NONE, 1);
}
}
if (standardPermissionMap.get(READ) == 1 && standardPermissionMap.get(WRITE) == 1 && standardPermissionMap.get(EXECUTE) == 1) {
return FsAction.ALL;
}
if (standardPermissionMap.get(READ) == 1 && standardPermissionMap.get(WRITE) == 1) {
return FsAction.READ_WRITE;
}
if (standardPermissionMap.get(READ) == 1 && standardPermissionMap.get(EXECUTE) == 1) {
return FsAction.READ_EXECUTE;
}
if (standardPermissionMap.get(WRITE) == 1 && standardPermissionMap.get(EXECUTE) == 1) {
return FsAction.WRITE_EXECUTE;
}
if (standardPermissionMap.get(WRITE) == 1) {
return FsAction.WRITE;
}
if (standardPermissionMap.get(READ) == 1) {
return FsAction.READ;
}
if (standardPermissionMap.get(EXECUTE) == 1) {
return FsAction.EXECUTE;
}
// Default Permission - None
return FsAction.NONE;
}
}