/* * 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.ObjectBucketACL; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.model.object.BucketACE; import com.emc.storageos.model.object.BucketACL; import com.emc.storageos.model.object.BucketACLUpdateParams; import com.emc.storageos.model.object.BucketACLUpdateParams.BucketACLOperationErrorType; import com.emc.storageos.model.object.BucketACLUpdateParams.BucketACLOperationType; import com.emc.storageos.model.object.BucketACLUpdateParams.BucketPermissions; import com.emc.storageos.svcs.errorhandling.resources.APIException; /** * Utility class for BUCKET ACL Operations. * */ public class BucketACLUtility { private final static Logger _log = LoggerFactory .getLogger(BucketACLUtility.class); private DbClient dbClient; private String bucketName; private URI bucketId; private List<String> usersGroupsCustomGroups; public static final String REQUEST_PARAM_PERMISSIONS = "permissions"; // 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 static final String REQUEST_PARAM_CUSTOM_GROUP = "customgroup"; // NOSONAR // ("Suppressing Sonar violation for variable name should be in camel case") public BucketACLUtility(DbClient dbClient, String bucketName, URI bucketId) { this.dbClient = dbClient; this.bucketName = bucketName; this.bucketId = bucketId; this.usersGroupsCustomGroups = new ArrayList<String>(); } public void verifyBucketACL(BucketACLUpdateParams param) { BucketACL bucketAcl = null; // Add Payload bucketAcl = param.getAclToAdd(); validateBucketACL(bucketAcl, BucketACLOperationType.ADD); reportErrors(param, BucketACLOperationType.ADD); // Modify Payload bucketAcl = param.getAclToModify(); validateBucketACL(bucketAcl, BucketACLOperationType.MODIFY); reportErrors(param, BucketACLOperationType.MODIFY); // Delete Payload bucketAcl = param.getAclToDelete(); validateBucketACL(bucketAcl, BucketACLOperationType.DELETE); reportErrors(param, BucketACLOperationType.DELETE); } private void reportErrors(BucketACLUpdateParams param, BucketACLOperationType 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(BucketACLUpdateParams param) { String opName = BucketACLOperationType.DELETE.name(); // Report Add ACL Errors BucketACL bucketACL = param.getAclToDelete(); if (bucketACL == null || bucketACL.getBucketACL().isEmpty()) { return; } List<BucketACE> bucketACELits = bucketACL.getBucketACL(); for (BucketACE bucketACE : bucketACELits) { if (!bucketACE.canProceedToNextStep()) { BucketACLOperationErrorType error = bucketACE.getErrorType(); switch (error) { case USER_AND_GROUP_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), bucketACE.getGroup(), bucketACE.getCustomGroup()); } case USER_AND_GROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), bucketACE.getGroup(), null); } case USER_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), null, bucketACE.getCustomGroup()); } case GROUP_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( null, bucketACE.getGroup(), bucketACE.getCustomGroup()); } case USER_OR_GROUP_OR_CUSTOMGROUP_NOT_PROVIDED: { throw APIException.badRequests .missingUserOrGroupOrCustomGroupInACE(opName); } case MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP_CUSTOMGROUP: { String userOrGroupOrCustomgroup = bucketACE.getUser(); if (userOrGroupOrCustomgroup == null) { userOrGroupOrCustomgroup = bucketACE.getGroup() != null ? bucketACE.getGroup() : bucketACE.getCustomGroup(); } throw APIException.badRequests .multipleACLsWithUserOrGroupOrCustomGroupFound(opName, userOrGroupOrCustomgroup); } case MULTIPLE_DOMAINS_FOUND: { String domain1 = bucketACE.getDomain(); String userOrGroupOrCustomgroup = bucketACE.getUser(); if (userOrGroupOrCustomgroup == null) { userOrGroupOrCustomgroup = bucketACE.getGroup() != null ? bucketACE.getGroup() : bucketACE.getCustomGroup(); } String domain2 = userOrGroupOrCustomgroup.substring(0, userOrGroupOrCustomgroup.indexOf("\\")); throw APIException.badRequests.multipleDomainsFound(opName, domain1, domain2); } case ACL_NOT_FOUND: { throw APIException.badRequests.bucketACLNotFound( opName, bucketACE.toString()); } default: break; } } } } private void reportModifyErrors(BucketACLUpdateParams param) { String opName = BucketACLOperationType.MODIFY.name(); // Report Add ACL Errors BucketACL bucketACL = param.getAclToModify(); if (bucketACL == null || bucketACL.getBucketACL().isEmpty()) { return; } List<BucketACE> bucketACEList = bucketACL.getBucketACL(); for (BucketACE bucketACE : bucketACEList) { if (!bucketACE.canProceedToNextStep()) { BucketACLOperationErrorType error = bucketACE.getErrorType(); switch (error) { case INVALID_PERMISSIONS: { if (bucketACE.getPermissions() != null) { throw APIException.badRequests .invalidPermissionForBucketACL(bucketACE.getPermissions()); } else { throw APIException.badRequests.missingValueInACE( opName, REQUEST_PARAM_PERMISSIONS); } } case USER_AND_GROUP_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), bucketACE.getGroup(), bucketACE.getCustomGroup()); } case USER_AND_GROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), bucketACE.getGroup(), null); } case USER_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), null, bucketACE.getCustomGroup()); } case GROUP_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( null, bucketACE.getGroup(), bucketACE.getCustomGroup()); } case USER_OR_GROUP_OR_CUSTOMGROUP_NOT_PROVIDED: { throw APIException.badRequests .missingUserOrGroupOrCustomGroupInACE(opName); } case MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP_CUSTOMGROUP: { String userOrGroupOrCustomgroup = bucketACE.getUser(); if (userOrGroupOrCustomgroup == null) { userOrGroupOrCustomgroup = bucketACE.getGroup() != null ? bucketACE.getGroup() : bucketACE.getCustomGroup(); } throw APIException.badRequests .multipleACLsWithUserOrGroupOrCustomGroupFound(opName, userOrGroupOrCustomgroup); } case ACL_NOT_FOUND: { throw APIException.badRequests.bucketACLNotFound( opName, bucketACE.toString()); } case MULTIPLE_DOMAINS_FOUND: { String domain1 = bucketACE.getDomain(); String userOrGroupOrCustomgroup = bucketACE.getUser(); if (userOrGroupOrCustomgroup == null) { userOrGroupOrCustomgroup = bucketACE.getGroup() != null ? bucketACE.getGroup() : bucketACE.getCustomGroup(); } String domain2 = userOrGroupOrCustomgroup.substring(0, userOrGroupOrCustomgroup.indexOf("\\")); throw APIException.badRequests.multipleDomainsFound(opName, domain1, domain2); } case ACL_EXISTS: default: break; } } } } private void reportAddErrors(BucketACLUpdateParams param) { String opName = BucketACLOperationType.ADD.name(); // Report Add ACL Errors BucketACL bucketAcl = param.getAclToAdd(); if (bucketAcl == null || bucketAcl.getBucketACL().isEmpty()) { return; } List<BucketACE> bucketACEList = bucketAcl.getBucketACL(); for (BucketACE bucketACE : bucketACEList) { if (!bucketACE.canProceedToNextStep()) { BucketACLOperationErrorType error = bucketACE.getErrorType(); switch (error) { case INVALID_PERMISSIONS: { if (bucketACE.getPermissions() != null) { throw APIException.badRequests .invalidPermissionForBucketACL(bucketACE.getPermissions()); } else { throw APIException.badRequests.missingValueInACE( opName, REQUEST_PARAM_PERMISSIONS); } } case USER_AND_GROUP_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), bucketACE.getGroup(), bucketACE.getCustomGroup()); } case USER_AND_GROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), bucketACE.getGroup(), null); } case USER_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( bucketACE.getUser(), null, bucketACE.getCustomGroup()); } case GROUP_AND_CUSTOMGROUP_PROVIDED: { throw APIException.badRequests.userGroupAndCustomGroupInACLFound( null, bucketACE.getGroup(), bucketACE.getCustomGroup()); } case USER_OR_GROUP_OR_CUSTOMGROUP_NOT_PROVIDED: { throw APIException.badRequests .missingUserOrGroupOrCustomGroupInACE(opName); } case MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP_CUSTOMGROUP: { String userOrGroupOrCustomgroup = bucketACE.getUser(); if (userOrGroupOrCustomgroup == null) { userOrGroupOrCustomgroup = bucketACE.getGroup() != null ? bucketACE.getGroup() : bucketACE.getCustomGroup(); } throw APIException.badRequests .multipleACLsWithUserOrGroupOrCustomGroupFound(opName, userOrGroupOrCustomgroup); } case MULTIPLE_DOMAINS_FOUND: { String domain1 = bucketACE.getDomain(); String userOrGroupOrCustomgroup = bucketACE.getUser(); if (userOrGroupOrCustomgroup == null) { userOrGroupOrCustomgroup = bucketACE.getGroup() != null ? bucketACE.getGroup() : bucketACE.getCustomGroup(); } String domain2 = userOrGroupOrCustomgroup.substring(0, userOrGroupOrCustomgroup.indexOf("\\")); throw APIException.badRequests.multipleDomainsFound(opName, domain1, domain2); } case ACL_EXISTS: { throw APIException.badRequests.bucketACLAlreadyExists( opName, bucketACE.toString()); } // case ACL_NOT_FOUND: default: break; } } } } private void validateBucketACL(BucketACL bucketACL, BucketACLOperationType type) { if (bucketACL == null) { _log.info("Missing ACLs - Ignoring the operation type {} ", type.name()); return; } switch (type) { case ADD: { verifyAddBucketACL(bucketACL.getBucketACL()); break; } case MODIFY: { verifyModifyBucketACL(bucketACL.getBucketACL()); break; } case DELETE: { verifyDeleteBucketACL(bucketACL.getBucketACL()); break; } } } private void verifyAddBucketACL(List<BucketACE> bucketACEList) { if (bucketACEList == null) { return; } _log.info("Number of bucket ACE(s) to add {} ", bucketACEList.size()); for (BucketACE ace : bucketACEList) { ace.proceedToNextStep(); _log.info("Verifying ACL {}", ace.toString()); // Are there same user or group found in other acls. If so, report // error verifyUserGroupCustomgroup(ace); if (!ace.canProceedToNextStep()) { break; } validatePermissions(ace); if (!ace.canProceedToNextStep()) { break; } // Verify with existing ACL ObjectBucketACL dbBucketAcl = getExistingACL(ace); // If same acl exists, don't allow to add again. if (dbBucketAcl != null) { _log.error( "Duplicate ACL in add request. User/group/customgroup in ACL for bucket already exists: {}", dbBucketAcl); ace.cancelNextStep(BucketACLOperationErrorType.ACL_EXISTS); break; } // If not found proceed for further verifications. else { if (ace.canProceedToNextStep()) { _log.info("No existing ACL found in DB {}", ace); } } } } private void verifyModifyBucketACL(List<BucketACE> bucketACEList) { if (bucketACEList == null) { return; } _log.info("Number of bucket ACE(s) to modify {} ", bucketACEList.size()); for (BucketACE ace : bucketACEList) { ace.proceedToNextStep(); _log.info("Verifying ACL {}", ace.toString()); // Are there same user or group found in other acls. If so, report // error verifyUserGroupCustomgroup(ace); if (!ace.canProceedToNextStep()) { break; } validatePermissions(ace); if (!ace.canProceedToNextStep()) { break; } // Verify with existing ACL ObjectBucketACL dbBucketAcl = getExistingACL(ace); // If same acl exists, allow to modify if (dbBucketAcl != null) { _log.info("Existing ACL in modify request: {}", dbBucketAcl); ace.proceedToNextStep(); } else { // If not found, don't allow to proceed further if (ace.canProceedToNextStep()) { _log.error("No existing ACL found in DB to modify {}", ace); ace.cancelNextStep(BucketACLOperationErrorType.ACL_NOT_FOUND); } } } } private void verifyDeleteBucketACL(List<BucketACE> bucketACEList) { if (bucketACEList == null) { return; } _log.info("Number of bucket ACE(s) to delete {} ", bucketACEList.size()); for (BucketACE ace : bucketACEList) { ace.proceedToNextStep(); _log.info("Verifying ACL {}", ace.toString()); // Are there same user or group found in other acls. If so, report // error verifyUserGroupCustomgroup(ace); if (!ace.canProceedToNextStep()) { break; } // Verify with existing ACL ObjectBucketACL dbBucketAcl = getExistingACL(ace); // If same acl exists, allow to modify if (dbBucketAcl != null) { _log.info("Existing ACL found in delete request: {}", dbBucketAcl); ace.proceedToNextStep(); } else { // If not found, don't allow to proceed further if (ace.canProceedToNextStep()) { _log.error("No existing ACL found in DB to delete {}", ace); ace.cancelNextStep(BucketACLOperationErrorType.ACL_NOT_FOUND); } } } } public List<BucketACE> queryExistingBucketACL() { List<BucketACE> bucketACEList = new ArrayList<BucketACE>(); List<ObjectBucketACL> dbBucketACL = queryDbBucketACL(); if (dbBucketACL != null) { Iterator<ObjectBucketACL> dbAclIterator = dbBucketACL.iterator(); while (dbAclIterator.hasNext()) { ObjectBucketACL dbBucketAce = dbAclIterator.next(); if (bucketId.equals(dbBucketAce.getBucketId())) { BucketACE ace = new BucketACE(); ace.setBucketName(dbBucketAce.getBucketName()); ace.setDomain(dbBucketAce.getDomain()); ace.setUser(dbBucketAce.getUser()); ace.setGroup(dbBucketAce.getGroup()); ace.setPermissions(dbBucketAce.getPermissions()); ace.setCustomGroup(dbBucketAce.getCustomGroup()); ace.setNamespace(dbBucketAce.getNamespace()); bucketACEList.add(ace); } } } return bucketACEList; } private List<ObjectBucketACL> queryDbBucketACL() { try { ContainmentConstraint containmentConstraint = null; _log.info("Querying DB for ACL of bucket {} ", this.bucketName); containmentConstraint = ContainmentConstraint.Factory .getBucketAclsConstraint(this.bucketId); List<ObjectBucketACL> dbBucketBucketAcl = CustomQueryUtility .queryActiveResourcesByConstraint(this.dbClient, ObjectBucketACL.class, containmentConstraint); return dbBucketBucketAcl; } catch (Exception e) { _log.error("Error while querying DB for ACL of a bucket {}", e); } return null; } private ObjectBucketACL getExistingACL(BucketACE requestAcl) { ObjectBucketACL acl = null; String domainOfReqAce = requestAcl.getDomain(); if (domainOfReqAce == null) { domainOfReqAce = ""; } String userOrGroupOrCustomGroup = requestAcl.getUser(); String type = "user"; if (userOrGroupOrCustomGroup == null) { userOrGroupOrCustomGroup = requestAcl.getGroup() != null ? requestAcl .getGroup() : requestAcl.getCustomGroup(); type = requestAcl.getGroup() != null ? "group" : "customgroup"; } // Construct ACL Index StringBuffer aclIndex = new StringBuffer(); aclIndex.append(this.bucketId).append(domainOfReqAce.toLowerCase()).append(userOrGroupOrCustomGroup.toLowerCase()).append(type); acl = this.queryACLByIndex(aclIndex.toString()); return acl; } private ObjectBucketACL queryACLByIndex(String index) { _log.info("Querying ACL in DB by alternate Id: {}", index); URIQueryResultList result = new URIQueryResultList(); ObjectBucketACL acl = null; dbClient.queryByConstraint(AlternateIdConstraint.Factory .getBucketACLConstraint(index), result); Iterator<URI> it = result.iterator(); while (it.hasNext()) { if (result.iterator().hasNext()) { acl = dbClient.queryObject(ObjectBucketACL.class, it.next()); if (acl != null && !acl.getInactive()) { _log.info("Existing ACE found in DB: {}", acl); break; } } } return acl; } private void validatePermissions(BucketACE bucketACE) { if (bucketACE == null) { return; } String permissionsValue = bucketACE.getPermissions(); if (permissionsValue != null) { String[] permissionsArray = permissionsValue.split("\\|"); for (String permission : permissionsArray) { if (isValidEnum(permission, BucketPermissions.class)) { bucketACE.proceedToNextStep(); } else { _log.error("Invalid value for permission: {}", permissionsValue); bucketACE.cancelNextStep(BucketACLOperationErrorType.INVALID_PERMISSIONS); return; } } } else { _log.error("permissions are not provided: {}", permissionsValue); bucketACE.cancelNextStep(BucketACLOperationErrorType.INVALID_PERMISSIONS); return; } } /** * 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 method verifies user or group or customgroup. * ACE accepts any one entry from group, user and custom group. * */ private void verifyUserGroupCustomgroup(BucketACE bucketACE) { String userOrGroupOrCustomgroup = null; String USER = "user"; String GROUP = "group"; String CUSTOMGROUP = "customgroup"; if (bucketACE == null) { return; } if (bucketACE.getUser() == null && bucketACE.getGroup() == null && bucketACE.getCustomGroup() == null) { bucketACE.cancelNextStep(BucketACLOperationErrorType.USER_OR_GROUP_OR_CUSTOMGROUP_NOT_PROVIDED); _log.error("User or Group or Customgroup is missing."); } else if (bucketACE.getUser() != null && bucketACE.getGroup() != null && bucketACE.getCustomGroup() != null) { bucketACE.cancelNextStep(BucketACLOperationErrorType.USER_AND_GROUP_AND_CUSTOMGROUP_PROVIDED); _log.error("Either user or group or customgroup should be provided. Never all of them."); } else if (bucketACE.getUser() != null && bucketACE.getGroup() != null) { bucketACE.cancelNextStep(BucketACLOperationErrorType.USER_AND_GROUP_PROVIDED); _log.error("Either user or group or customgroup should be provided. Never all of them."); } else if (bucketACE.getUser() != null && bucketACE.getCustomGroup() != null) { bucketACE.cancelNextStep(BucketACLOperationErrorType.USER_AND_CUSTOMGROUP_PROVIDED); _log.error("Either user or group or customgroup should be provided. Never all of them."); } else if (bucketACE.getGroup() != null && bucketACE.getCustomGroup() != null) { bucketACE.cancelNextStep(BucketACLOperationErrorType.GROUP_AND_CUSTOMGROUP_PROVIDED); _log.error("Either user or group or customgroup should be provided. Never all of them."); } else { String domain = bucketACE.getDomain(); if (domain == null) { domain = ""; } domain = domain.toLowerCase(); if (bucketACE.getUser() != null) { userOrGroupOrCustomgroup = domain + bucketACE.getUser().toLowerCase() + USER; } else if (bucketACE.getGroup() != null) { userOrGroupOrCustomgroup = domain + bucketACE.getGroup().toLowerCase() + GROUP; } else { userOrGroupOrCustomgroup = domain + bucketACE.getCustomGroup().toLowerCase() + CUSTOMGROUP; } } if (userOrGroupOrCustomgroup != null) { if (!usersGroupsCustomGroups.contains(userOrGroupOrCustomgroup)) { usersGroupsCustomGroups.add(userOrGroupOrCustomgroup); } else { bucketACE.cancelNextStep(BucketACLOperationErrorType.MULTIPLE_ACES_WITH_SAME_USER_OR_GROUP_CUSTOMGROUP); _log.error("There are multiple ACEs with same user or group or customgroup."); } } // below code is to validate domain by splitting backslash if (bucketACE.getDomain() != null && bucketACE.getUser() != null) { if (bucketACE.getUser().contains("@")) { bucketACE.cancelNextStep(BucketACLOperationErrorType.MULTIPLE_DOMAINS_FOUND); _log.error("Multiple Domains found. Please provide either in user or in domain field."); } } if (bucketACE.getDomain() != null && bucketACE.getGroup() != null) { if (bucketACE.getGroup().contains("@")) { bucketACE.cancelNextStep(BucketACLOperationErrorType.MULTIPLE_DOMAINS_FOUND); _log.error("Multiple Domains found. Please provide either in group or in domain field."); } } if (bucketACE.getDomain() != null && bucketACE.getCustomGroup() != null) { if (bucketACE.getCustomGroup().contains("@")) { bucketACE.cancelNextStep(BucketACLOperationErrorType.MULTIPLE_DOMAINS_FOUND); _log.error("Multiple Domains found. Please provide either in customgroup or in domain field."); } } } }