/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.model.FileShare;
import com.emc.storageos.db.client.model.NFSShareACL;
import com.emc.storageos.db.client.model.Snapshot;
import com.emc.storageos.db.client.util.CustomQueryUtility;
import com.emc.storageos.model.file.NfsACE;
import com.emc.storageos.model.file.NfsACE.NfsPermission;
import com.emc.storageos.model.file.NfsACE.NfsPermissionType;
import com.emc.storageos.model.file.NfsACE.NfsUserType;
import com.emc.storageos.model.file.NfsACL;
import com.emc.storageos.model.file.NfsACLUpdateParams;
import com.emc.storageos.model.file.NfsACLs;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.volumecontroller.FileControllerConstants;
/**
* NfsACLUtility class will provide the utility methods for NFS ACL support.
*
* @author sauraa
*
*/
public class NfsACLUtility {
private final static Logger _log = LoggerFactory
.getLogger(NfsACLUtility.class);
private final DbClient dbClient;
private final FileShare fs;
private final Snapshot snapShot;
private final String subDir;
public static final String REQUEST_PARAM_PERMISSION_TYPE = "permission_type";
public static final String REQUEST_PARAM_PERMISSION = "permission";
public static final String REQUEST_PARAM_USER = "user";
public static final String REQUEST_PARAM_GROUP = "group";
public NfsACLUtility(DbClient dbClient, FileShare fs, Snapshot snapShot, String subDir) {
this.dbClient = dbClient;
this.fs = fs;
this.snapShot = snapShot;
this.subDir = subDir;
}
/**
* Check the provided String value is a valid type of enum or not
*
* @param value String need to be checked
* @param enumClass the enum class for which it need to be checked.
* @return true/false
*/
public <T extends Enum<T>> boolean isValidEnum(String value, Class<T> enumClass) {
for (T e : enumClass.getEnumConstants()) {
if (e.name().equalsIgnoreCase(value)) {
return true;
}
}
return false;
}
/**
* This function verify the NfsACLUpdateParams data provided by user to set ACL is valid or not
* it throw exception for invalid NfsACLUpdateParams data
*
* @param param : input ACLs to be updated.
*/
public void verifyNfsACLs(NfsACLUpdateParams param) {
List<NfsACE> addList = param.getAcesToAdd();
List<NfsACE> modifyList = param.getAcesToModify();
List<NfsACE> deleteList = param.getAcesToDelete();
List<NFSShareACL> dbACLList = queryDBSFileNfsACLs(false);
Set<String> userSetDB = new HashSet<String>();
for (NFSShareACL dbAcl : dbACLList) {
userSetDB.add(dbAcl.getUser());
}
if (addList != null && !addList.isEmpty()) {
verifyNfsACLsAddList(addList, userSetDB);
}
if (modifyList != null && !modifyList.isEmpty()) {
verifyNfsACLsModifyOrDeleteList(modifyList, userSetDB);
}
if (deleteList != null && !deleteList.isEmpty()) {
verifyNfsACLsModifyOrDeleteList(deleteList, userSetDB);
}
}
/**
* To verify the syntax of payload
*
* @param nfsAces list of the ACE need to be validated
*/
private void validateNfsAceSyntax(List<NfsACE> nfsAces) {
for (NfsACE ace : nfsAces) {
// PermissionType is optional , if provided check it is proper
if (ace.getPermissionType() != null && !ace.getPermissionType().isEmpty()) {
if (!isValidEnum(ace.getPermissionType(), NfsPermissionType.class)) {
throw APIException.badRequests.invalidPermissionType(ace.getPermissionType());
}
}
// user type is optional , if provided check it is proper
if (ace.getType() != null && !ace.getType().isEmpty()) {
if (!isValidEnum(ace.getType(), NfsUserType.class)) {
throw APIException.badRequests.invalidUserType(ace.getType());
}
}
for (String permission : ace.getPermissionSet()) {
if (!isValidEnum(permission, NfsPermission.class)) {
throw APIException.badRequests.invalidNFSPermission(permission);
}
}
// check if two times domain is provided
int index = ace.getUser().indexOf("\\");
if (index >= 0) {
if (ace.getDomain() != null && !ace.getDomain().isEmpty()) {
throw APIException.badRequests.multipleDomainsFound("update", ace.getDomain(), ace.getUser().substring(0, index));
} else {
// verify the username provided with domain and user
String domainAndUser[] = ace.getUser().split("\\");
if (domainAndUser.length > 2) {
throw APIException.badRequests.multipleDomainsFound("update", domainAndUser[0], domainAndUser[1]);
}
// split the user and domain form user field and store it separately
// this is required to store value in DB in one format
if (domainAndUser.length == 2) {
ace.setDomain(domainAndUser[0]);
ace.setUser(domainAndUser[1]);
}
}
}
}
}
/**
* Verify the new ACE which need to be added exist in DB or not
*
* @param addList list which need to be added
* @param userSet list already in the DB
*/
private void verifyNfsACLsAddList(List<NfsACE> addList, Set<String> userSet) {
validateNfsAceSyntax(addList);
for (NfsACE ace : addList) {
if (userSet.contains(ace.getUser())) {
throw APIException.badRequests.nfsACLAlreadyExists("add",
ace.getUser());
}
}
}
/**
* Verify the modify ACE which need to be updated is in DB or not
*
* @param changeList list which need to be updated
* @param userSet list already in the DB
*/
private void verifyNfsACLsModifyOrDeleteList(List<NfsACE> changeList, Set<String> userSet) {
validateNfsAceSyntax(changeList);
for (NfsACE ace : changeList) {
if (!userSet.contains(ace.getUser())) {
throw APIException.badRequests.nfsACLNotFound("modify or delete",
ace.getUser());
}
}
}
/**
* Get the list of DB Object for current file System
*
* @param allDirs if true function will return complete list of ACL including its SubDir
* @return list of NFS ACLs
*/
private List<NFSShareACL> queryDBSFileNfsACLs(boolean allDirs) {
try {
ContainmentConstraint containmentConstraint = null;
if (this.fs != null) {
_log.info(
"Querying DB for Nfs ACLs of fs{} of filesystemId {} ",
this.fs.getPath(), fs.getId());
containmentConstraint = ContainmentConstraint.Factory
.getFileNfsAclsConstraint(this.fs.getId());
} else {
// Snapshot
_log.info(
"Querying DB for Nfs ACLs of fs {} of snapshotId {} ",
this.snapShot.getPath(), this.snapShot.getId());
containmentConstraint = ContainmentConstraint.Factory
.getSnapshotNfsAclsConstraint(this.snapShot
.getId());
}
List<NFSShareACL> nfsAclList = CustomQueryUtility
.queryActiveResourcesByConstraint(this.dbClient,
NFSShareACL.class, containmentConstraint);
if (allDirs) {
return nfsAclList;
}
List<NFSShareACL> rootAclList = new ArrayList<NFSShareACL>();
List<NFSShareACL> subDirAclList = new ArrayList<NFSShareACL>();
String absoluteSubdir = "";
if (this.subDir != null && !this.subDir.isEmpty()) {
absoluteSubdir = this.fs.getPath() + "/" + subDir;
}
for (NFSShareACL nfsAcl : nfsAclList) {
if (nfsAcl.getFileSystemPath().equals(fs.getPath())) {
rootAclList.add(nfsAcl);
}
if (!absoluteSubdir.isEmpty()) {
if (nfsAcl.getFileSystemPath().equals(absoluteSubdir)) {
subDirAclList.add(nfsAcl);
}
}
}
if (!absoluteSubdir.isEmpty()) {
_log.info("Found {} Nfs ACLs for subdir {} ", subDirAclList.size(),
this.subDir);
return subDirAclList;
}
_log.info("Found {} Nfs ACLs ", rootAclList.size());
return rootAclList;
} catch (Exception e) {
_log.error("Error while querying DB for ACL of a NFS {}", e);
}
return null;
}
/**
* This function return all the ACL in the DB in the format of xml Object
*
* @param allDirs if true function will return complete list of ACL including its SubDir
* @return list of NFS ACLs.
*/
public NfsACLs getNfsAclFromDB(boolean allDirs) {
NfsACLs acls = new NfsACLs();
List<NfsACL> nfsAclList = new ArrayList<NfsACL>();
Map<String, List<NfsACE>> nfsAclMap = new HashMap<String, List<NfsACE>>();
_log.info("Sub-directory {} and allDirs {}", this.subDir, allDirs);
// Query All ACl Specific to a File System.
List<NFSShareACL> nfsAcls = queryDBSFileNfsACLs(allDirs);
_log.info("Number of existing ACL found : {} ", nfsAcls.size());
// Group the ACLs based on file system path!!!
for (NFSShareACL nfsAcl : nfsAcls) {
String fsPath = nfsAcl.getFileSystemPath();
List<NfsACE> nfsAceList = nfsAclMap.get(fsPath);
if (nfsAceList == null) {
nfsAceList = new ArrayList<NfsACE>();
}
NfsACE ace = getNFSAce(nfsAcl);
nfsAceList.add(ace);
nfsAclMap.put(fsPath, nfsAceList);
}
// Convert all ACE to ACLs!!
for (Map.Entry<String, List<NfsACE>> pathAcls : nfsAclMap.entrySet()) {
String mountPath = pathAcls.getKey();
NfsACL nfsAcl = new NfsACL(mountPath, pathAcls.getValue());
if (mountPath.length() > fs.getPath().length()) {
nfsAcl.setSubDir(mountPath.substring(fs.getPath().length() + 1));
}
nfsAclList.add(nfsAcl);
}
if (!nfsAclList.isEmpty()) {
acls.setNfsACLs(nfsAclList);
}
return acls;
}
/**
* This function is to convert DB object into NfsACE object
*
* @param orig provided DB object
* @return the REST representation of NFS ACE
*/
private NfsACE getNFSAce(NFSShareACL orig) {
NfsACE dest = new NfsACE();
dest.setDomain(orig.getDomain());
dest.setPermissions(orig.getPermissions());
dest.setPermissionType(FileControllerConstants.NFS_FILE_PERMISSION_TYPE_ALLOW);
if (orig.getPermissionType() != null && !orig.getPermissionType().isEmpty()) {
dest.setPermissionType(orig.getPermissionType());
}
dest.setType(REQUEST_PARAM_USER);
if (orig.getType() != null && !orig.getType().isEmpty()) {
dest.setType(orig.getType());
}
dest.setUser(orig.getUser());
return dest;
}
}