/* * Copyright (c) 2012-2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource.utils; import java.net.URI; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.CifsShareACL; import com.emc.storageos.db.client.model.FileObject; import com.emc.storageos.db.client.model.FileShare; import com.emc.storageos.db.client.model.SMBFileShare; import com.emc.storageos.db.client.model.SMBShareMap; import com.emc.storageos.db.client.model.Snapshot; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.model.file.CifsShareACLUpdateParams; import com.emc.storageos.model.file.CifsShareACLUpdateParams.ShareACLOperationErrorType; import com.emc.storageos.model.file.CifsShareACLUpdateParams.ShareACLOperationType; import com.emc.storageos.model.file.CifsShareACLUpdateParams.SharePermission; import com.emc.storageos.model.file.ShareACL; import com.emc.storageos.model.file.ShareACLs; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.volumecontroller.FileControllerConstants; public class CifsShareUtility { private final static Logger _log = LoggerFactory .getLogger(CifsShareUtility.class); private DbClient dbClient; private FileShare fs; private Snapshot snapshot; private String shareName; private String missingRequestParameterErrorString; private List<String> userGroupList; public static final String REQUEST_PARAM_PERMISSION_TYPE = "permission_type"; // NOSONAR // ("Suppressing Sonar violation for variable name should be in camel case") public static final String REQUEST_PARAM_PERMISSION = "permission"; // NOSONAR // ("Suppressing Sonar violation for variable name should be in camel case") public static final String REQUEST_PARAM_USER = "user"; // NOSONAR // ("Suppressing Sonar violation for variable name should be in camel case") public static final String REQUEST_PARAM_GROUP = "group"; // NOSONAR // ("Suppressing Sonar violation for variable name should be in camel case") public CifsShareUtility(DbClient dbClient, FileShare fs, Snapshot snapshot, String shareName) { super(); this.dbClient = dbClient; this.fs = fs; this.snapshot = snapshot; this.shareName = shareName; this.userGroupList = new ArrayList<String>(); } public void verifyShareACLs(CifsShareACLUpdateParams param) { ShareACLs shareAcls = null; // Add Payload shareAcls = param.getAclsToAdd(); validateShareACLs(shareAcls, ShareACLOperationType.ADD); reportErrors(param, ShareACLOperationType.ADD); // Modify Payload shareAcls = param.getAclsToModify(); validateShareACLs(shareAcls, ShareACLOperationType.MODIFY); reportErrors(param, ShareACLOperationType.MODIFY); // Delete Payload shareAcls = param.getAclsToDelete(); validateShareACLs(shareAcls, ShareACLOperationType.DELETE); reportErrors(param, ShareACLOperationType.DELETE); } private void reportErrors(CifsShareACLUpdateParams param, ShareACLOperationType type) { _log.info("Report errors if found"); switch (type) { case ADD: { reportAddErrors(param); break; } case MODIFY: { reportModifyErrors(param); break; } case DELETE: { reportDeleteErrors(param); break; } } } private void reportDeleteErrors(CifsShareACLUpdateParams param) { String opName = ShareACLOperationType.DELETE.name(); // Report Add ACL Errors ShareACLs shareAcls = param.getAclsToDelete(); if (shareAcls == null || shareAcls.getShareACLs().isEmpty()) { return; } List<ShareACL> shareAclList = shareAcls.getShareACLs(); for (ShareACL acl : shareAclList) { if (!acl.canProceedToNextStep()) { ShareACLOperationErrorType error = acl.getErrorType(); switch (error) { /* * case SNAPSHOT_EXPORT_SHOULD_BE_READ_ONLY: { throw * APIException.badRequests.snapshotExportPermissionReadOnly(); * } */ case USER_AND_GROUP_PROVIDED: { throw APIException.badRequests.bothUserAndGroupInACLFound( acl.getUser(), acl.getGroup()); } case USER_OR_GROUP_NOT_PROVIDED: { throw APIException.badRequests .missingUserOrGroupInACE(opName); } case MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP: { String userOrGroup = acl.getUser() == null ? acl.getGroup() : acl.getUser(); throw APIException.badRequests .multipleACLsWithUserOrGroupFound(opName, userOrGroup); } case MULTIPLE_DOMAINS_FOUND: { String domain1 = acl.getDomain(); String userOrGroup = acl.getUser() == null ? acl.getGroup() : acl.getUser(); String domain2 = userOrGroup.substring(0, userOrGroup.indexOf("\\")); throw APIException.badRequests.multipleDomainsFound(opName, domain1, domain2); } case ACL_NOT_FOUND: { throw APIException.badRequests.shareACLNotFoundFound( opName, acl.toString()); } default: break; } } } } private void reportModifyErrors(CifsShareACLUpdateParams param) { String opName = ShareACLOperationType.MODIFY.name(); // Report Add ACL Errors ShareACLs shareAcls = param.getAclsToModify(); if (shareAcls == null || shareAcls.getShareACLs().isEmpty()) { return; } List<ShareACL> shareAclList = shareAcls.getShareACLs(); for (ShareACL acl : shareAclList) { if (!acl.canProceedToNextStep()) { ShareACLOperationErrorType error = acl.getErrorType(); switch (error) { case SNAPSHOT_SHARE_SHOULD_BE_READ_ONLY: { throw APIException.badRequests .snapshotSMBSharePermissionReadOnly(); } case INVALID_PERMISSION: { if (acl.getPermission() != null) { throw APIException.badRequests .invalidPermissionForACL(acl.getPermission()); } else { throw APIException.badRequests.missingValueInACE( opName, REQUEST_PARAM_PERMISSION); } } case USER_AND_GROUP_PROVIDED: { throw APIException.badRequests.bothUserAndGroupInACLFound( acl.getUser(), acl.getGroup()); } case USER_OR_GROUP_NOT_PROVIDED: { throw APIException.badRequests .missingUserOrGroupInACE(opName); } case MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP: { String userOrGroup = acl.getUser() == null ? acl.getGroup() : acl.getUser(); throw APIException.badRequests .multipleACLsWithUserOrGroupFound(opName, userOrGroup); } case ACL_NOT_FOUND: { throw APIException.badRequests.shareACLNotFoundFound( opName, acl.toString()); } case MULTIPLE_DOMAINS_FOUND: { String domain1 = acl.getDomain(); String userOrGroup = acl.getUser() == null ? acl.getGroup() : acl.getUser(); String domain2 = userOrGroup.substring(0, userOrGroup.indexOf("\\")); throw APIException.badRequests.multipleDomainsFound(opName, domain1, domain2); } case ACL_EXISTS: default: break; } } } } private void reportAddErrors(CifsShareACLUpdateParams param) { String opName = ShareACLOperationType.ADD.name(); // Report Add ACL Errors ShareACLs shareAcls = param.getAclsToAdd(); if (shareAcls == null || shareAcls.getShareACLs().isEmpty()) { return; } List<ShareACL> shareAclList = shareAcls.getShareACLs(); for (ShareACL acl : shareAclList) { if (!acl.canProceedToNextStep()) { ShareACLOperationErrorType error = acl.getErrorType(); switch (error) { case SNAPSHOT_SHARE_SHOULD_BE_READ_ONLY: { throw APIException.badRequests .snapshotSMBSharePermissionReadOnly(); } case INVALID_PERMISSION: { if (acl.getPermission() != null) { throw APIException.badRequests .invalidPermissionForACL(acl.getPermission()); } else { throw APIException.badRequests.missingValueInACE( opName, REQUEST_PARAM_PERMISSION); } } case USER_AND_GROUP_PROVIDED: { throw APIException.badRequests.bothUserAndGroupInACLFound( acl.getUser(), acl.getGroup()); } case USER_OR_GROUP_NOT_PROVIDED: { throw APIException.badRequests .missingUserOrGroupInACE(opName); } case MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP: { String userOrGroup = acl.getUser() == null ? acl.getGroup() : acl.getUser(); throw APIException.badRequests .multipleACLsWithUserOrGroupFound(opName, userOrGroup); } case MULTIPLE_DOMAINS_FOUND: { String domain1 = acl.getDomain(); String userOrGroup = acl.getUser() == null ? acl.getGroup() : acl.getUser(); String domain2 = userOrGroup.substring(0, userOrGroup.indexOf("\\")); throw APIException.badRequests.multipleDomainsFound(opName, domain1, domain2); } case ACL_EXISTS: { throw APIException.badRequests.shareACLAlreadyExists( opName, acl.toString()); } // case ACL_NOT_FOUND: default: break; } } } } private void validateShareACLs(ShareACLs shareAcls, ShareACLOperationType type) { if (shareAcls == null) { _log.info("Missing ACLs - Ignoring the operation type {} ", type.name()); missingRequestParameterErrorString = "Missing ACLs - Ignoring the operation type " + type.name(); return; } switch (type) { case ADD: { verifyAddShareACLs(shareAcls.getShareACLs()); break; } case MODIFY: { verifyModifyShareACLs(shareAcls.getShareACLs()); break; } case DELETE: { verifyDeleteShareACLs(shareAcls.getShareACLs()); break; } } } private void verifyDeleteShareACLs(List<ShareACL> shareAclList) { if (shareAclList == null) { return; } _log.info("Number of share ACL(s) to delete {} ", shareAclList.size()); for (ShareACL acl : shareAclList) { acl.proceedToNextStep(); _log.info("Verifying ACL {}", acl.toString()); // Are there same user or group found in other acls. If so, report // error verifyUserGroup(acl); if (!acl.canProceedToNextStep()) { break; } // Verify with existing ACL CifsShareACL dbShareAcl = getExistingACL(acl); // If same acl exists, allow to modify if (dbShareAcl != null) { _log.info("Existing ACL found in delete request: {}", dbShareAcl); acl.proceedToNextStep(); } else { // If not found, don't allow to proceed further if (acl.canProceedToNextStep()) { _log.error("No existing ACL found in DB to delete {}", acl); acl.cancelNextStep(ShareACLOperationErrorType.ACL_NOT_FOUND); } } } } private void verifyModifyShareACLs(List<ShareACL> shareAclList) { if (shareAclList == null) { return; } _log.info("Number of share ACL(s) to modify {} ", shareAclList.size()); for (ShareACL acl : shareAclList) { acl.proceedToNextStep(); _log.info("Verifying ACL {}", acl.toString()); // Are there same user or group found in other acls. If so, report // error verifyUserGroup(acl); if (!acl.canProceedToNextStep()) { break; } validatePermissions(acl); if (!acl.canProceedToNextStep()) { break; } // Verify with existing ACL CifsShareACL dbShareAcl = getExistingACL(acl); // If same acl exists, allow to modify if (dbShareAcl != null) { _log.info("Existing ACL in modify request: {}", dbShareAcl); acl.proceedToNextStep(); } else { // If not found, don't allow to proceed further if (acl.canProceedToNextStep()) { _log.error("No existing ACL found in DB to modify {}", acl); acl.cancelNextStep(ShareACLOperationErrorType.ACL_NOT_FOUND); } } } } private void verifyAddShareACLs(List<ShareACL> shareAclList) { if (shareAclList == null) { return; } _log.info("Number of share ACL(s) to add {} ", shareAclList.size()); for (ShareACL acl : shareAclList) { acl.proceedToNextStep(); _log.info("Verifying ACL {}", acl.toString()); // Are there same user or group found in other acls. If so, report // error verifyUserGroup(acl); if (!acl.canProceedToNextStep()) { break; } validatePermissions(acl); if (!acl.canProceedToNextStep()) { break; } // Verify with existing ACL CifsShareACL dbShareAcl = getExistingACL(acl); // If same acl exists, don't allow to add again. if (dbShareAcl != null) { _log.error( "Duplicate ACL in add request. User/group in ACL for share already exists: {}", dbShareAcl); acl.cancelNextStep(ShareACLOperationErrorType.ACL_EXISTS); break; } // If not found proceed for further verifications. else { if (acl.canProceedToNextStep()) { _log.info("No existing ACL found in DB {}", acl); } } } } public List<ShareACL> queryExistingShareACLs() { List<ShareACL> aclList = new ArrayList<ShareACL>(); List<CifsShareACL> dbShareAclList = queryDBShareACLs(); if (dbShareAclList != null) { Iterator<CifsShareACL> shareAclIter = dbShareAclList.iterator(); while (shareAclIter.hasNext()) { CifsShareACL dbShareAcl = shareAclIter.next(); if (shareName.equals(dbShareAcl.getShareName())) { ShareACL acl = new ShareACL(); acl.setShareName(shareName); acl.setDomain(dbShareAcl.getDomain()); acl.setUser(dbShareAcl.getUser()); acl.setGroup(dbShareAcl.getGroup()); acl.setPermission(dbShareAcl.getPermission()); if (this.fs != null) { acl.setFileSystemId(this.fs.getId()); } else { acl.setSnapshotId(this.snapshot.getId()); } aclList.add(acl); } } } return aclList; } private List<CifsShareACL> queryDBShareACLs() { try { ContainmentConstraint containmentConstraint = null; if (this.fs != null) { _log.info( "Querying DB for Share ACLs of share {} of filesystemId {} ", this.shareName, fs.getId()); containmentConstraint = ContainmentConstraint.Factory .getFileCifsShareAclsConstraint(this.fs.getId()); } else { // Snapshot _log.info( "Querying DB for Share ACLs of share {} of snapshotId {} ", this.shareName, this.snapshot.getId()); containmentConstraint = ContainmentConstraint.Factory .getSnapshotCifsShareAclsConstraint(this.snapshot .getId()); } List<CifsShareACL> shareAclList = CustomQueryUtility .queryActiveResourcesByConstraint(this.dbClient, CifsShareACL.class, containmentConstraint); return shareAclList; } catch (Exception e) { _log.error("Error while querying DB for ACL of a share {}", e); } return null; } private CifsShareACL getExistingACL(ShareACL requestAcl) { CifsShareACL acl = null; String domainOfReqAce = requestAcl.getDomain(); if (domainOfReqAce == null) { domainOfReqAce = ""; } String userOrGroup = requestAcl.getUser() == null ? requestAcl .getGroup() : requestAcl.getUser(); // Construct ACL Index StringBuffer aclIndex = new StringBuffer(); aclIndex.append(this.fs == null ? this.snapshot.getId().toString() : this.fs.getId().toString()); aclIndex.append(this.shareName).append(domainOfReqAce) .append(userOrGroup); acl = this.queryACLByIndex(aclIndex.toString()); return acl; } private CifsShareACL queryACLByIndex(String index) { _log.info("Querying ACL in DB by alternate Id: {}", index); URIQueryResultList result = new URIQueryResultList(); CifsShareACL acl = null; if (this.fs != null) { dbClient.queryByConstraint(AlternateIdConstraint.Factory .getFileSystemShareACLConstraint(index), result); } else { dbClient.queryByConstraint(AlternateIdConstraint.Factory .getSnapshotShareACLConstraint(index), result); } Iterator<URI> it = result.iterator(); while (it.hasNext()) { if (result.iterator().hasNext()) { acl = dbClient.queryObject(CifsShareACL.class, it.next()); if (acl != null && !acl.getInactive()) { _log.info("Existing ACE found in DB: {}", acl); break; } } } return acl; } private void validatePermissions(ShareACL acl) { if (acl == null) { return; } /* * String permissionTypeValue = acl.getPermissionType(); try { * SharePermissionType permissionType = SharePermissionType * .valueOf(permissionTypeValue.toUpperCase()); if (permissionType != * null) { acl.proceedToNextStep(); } } catch (Exception e) { * _log.error("Invalid value for permission type: {}", * permissionTypeValue); * acl.cancelNextStep(ShareACLOperationErrorType.INVALID_PERMISSION_TYPE * ); return; } */ String permissionValue = acl.getPermission(); try { SharePermission permission = SharePermission .valueOf(permissionValue.toUpperCase()); if (permission != null) { acl.setPermission(getFormattedPermissionText(permission)); acl.proceedToNextStep(); } } catch (Exception e) { _log.error("Invalid value for permission: {}", permissionValue); acl.cancelNextStep(ShareACLOperationErrorType.INVALID_PERMISSION); return; } if (this.snapshot != null) { // Snapshot share permission must be read only if (!SharePermission.READ.name().equalsIgnoreCase( acl.getPermission())) { _log.error("Snapshot permission should be read only"); acl.cancelNextStep(ShareACLOperationErrorType.SNAPSHOT_SHARE_SHOULD_BE_READ_ONLY); } } } private void verifyUserGroup(ShareACL acl) { String userOrGroup = null; if (acl == null) { return; } if (acl.getUser() == null && acl.getGroup() == null) { acl.cancelNextStep(ShareACLOperationErrorType.USER_OR_GROUP_NOT_PROVIDED); _log.error("User or group is missing."); } else if (acl.getUser() != null && acl.getGroup() != null) { acl.cancelNextStep(ShareACLOperationErrorType.USER_AND_GROUP_PROVIDED); _log.error("Either user or group should be provided. Never both."); } else { String domain = acl.getDomain(); if (domain == null) { domain = ""; } domain = domain.toLowerCase(); if (acl.getUser() != null) { userOrGroup = domain + acl.getUser().toLowerCase(); } else { userOrGroup = domain + acl.getGroup().toLowerCase(); } } if (userOrGroup != null) { if (!userGroupList.contains(userOrGroup)) { userGroupList.add(userOrGroup); } else { acl.cancelNextStep(ShareACLOperationErrorType.MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP); _log.error("There are multiple ACEs with same user or group."); } } // below code is to validate domain if (acl.getDomain() != null && acl.getUser() != null) { if (acl.getUser().contains("\\")) { acl.cancelNextStep(ShareACLOperationErrorType.MULTIPLE_DOMAINS_FOUND); _log.error("Multiple Domains found. Please provide either in user or in domain field."); } } if (acl.getDomain() != null && acl.getGroup() != null) { if (acl.getGroup().contains("\\")) { acl.cancelNextStep(ShareACLOperationErrorType.MULTIPLE_DOMAINS_FOUND); _log.error("Multiple Domains found. Please provide either in group or in domain field."); } } } private String getFormattedPermissionText(SharePermission permission) { String permissionText = null; switch (permission) { case READ: permissionText = FileControllerConstants.CIFS_SHARE_PERMISSION_READ; break; case CHANGE: permissionText = FileControllerConstants.CIFS_SHARE_PERMISSION_CHANGE; break; case FULLCONTROL: permissionText = FileControllerConstants.CIFS_SHARE_PERMISSION_FULLCONTROL; break; } return permissionText; } public static void checkForUpdateShareACLOperationOnStorage( String storageType, String operation) { StorageSystem.Type storageSystemType = StorageSystem.Type.valueOf(storageType); if (storageSystemType.equals(StorageSystem.Type.vnxe) || storageSystemType.equals(StorageSystem.Type.vnxfile) || storageSystemType.equals(StorageSystem.Type.datadomain)) { throw APIException.badRequests.operationNotSupportedForSystemType( operation, storageType); } } /** * Checks whether share exists in the file object * * @param fileObject fileshare or snapshot object * @param shareName name of the share * @return true if share exists in the SMBShareMap, false otherwise */ public static boolean doesShareExist(FileObject fileObject, String shareName) { SMBShareMap existingShares = fileObject.getSMBFileShares(); if (existingShares != null && !existingShares.isEmpty()) { SMBFileShare existingShare = existingShares.get(shareName); if (existingShare != null) { _log.info("CIFS share: {}, exists in ", shareName, fileObject.getId()); return true; } } _log.info("CIFS share: {}, does not exist in {}", shareName, fileObject.getId()); return false; } }