/************************************************************************* * Copyright 2009-2013 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.cloudformation.resources.standard.actions; import com.eucalyptus.auth.euare.DeleteGroupPolicyResponseType; import com.eucalyptus.auth.euare.DeleteGroupPolicyType; import com.eucalyptus.auth.euare.DeleteRolePolicyResponseType; import com.eucalyptus.auth.euare.DeleteRolePolicyType; import com.eucalyptus.auth.euare.DeleteUserPolicyResponseType; import com.eucalyptus.auth.euare.DeleteUserPolicyType; import com.eucalyptus.auth.euare.PutGroupPolicyResponseType; import com.eucalyptus.auth.euare.PutGroupPolicyType; import com.eucalyptus.auth.euare.PutRolePolicyResponseType; import com.eucalyptus.auth.euare.PutRolePolicyType; import com.eucalyptus.auth.euare.PutUserPolicyResponseType; import com.eucalyptus.auth.euare.PutUserPolicyType; import com.eucalyptus.cloudformation.resources.IAMHelper; import com.eucalyptus.cloudformation.resources.ResourceAction; import com.eucalyptus.cloudformation.resources.ResourceInfo; import com.eucalyptus.cloudformation.resources.ResourceProperties; import com.eucalyptus.cloudformation.resources.standard.info.AWSIAMPolicyResourceInfo; import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSIAMPolicyProperties; import com.eucalyptus.cloudformation.template.JsonHelper; import com.eucalyptus.cloudformation.util.MessageHelper; import com.eucalyptus.cloudformation.workflow.steps.Step; import com.eucalyptus.cloudformation.workflow.steps.StepBasedResourceAction; import com.eucalyptus.cloudformation.workflow.steps.UpdateStep; import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateType; import com.eucalyptus.component.ServiceConfiguration; import com.eucalyptus.component.Topology; import com.eucalyptus.component.id.Euare; import com.eucalyptus.util.async.AsyncRequests; import com.fasterxml.jackson.databind.node.TextNode; import com.google.common.collect.Sets; import javax.annotation.Nullable; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; /** * Created by ethomas on 2/3/14. */ public class AWSIAMPolicyResourceAction extends StepBasedResourceAction { private AWSIAMPolicyProperties properties = new AWSIAMPolicyProperties(); private AWSIAMPolicyResourceInfo info = new AWSIAMPolicyResourceInfo(); public AWSIAMPolicyResourceAction() { super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), fromUpdateEnum(UpdateNoInterruptionSteps.class), null); } @Override public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) { UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE; AWSIAMPolicyResourceAction otherAction = (AWSIAMPolicyResourceAction) resourceAction; if (!Objects.equals(properties.getGroups(), otherAction.properties.getGroups())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getPolicyDocument(), otherAction.properties.getPolicyDocument())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getPolicyName(), otherAction.properties.getPolicyName())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getRoles(), otherAction.properties.getRoles())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } if (!Objects.equals(properties.getUsers(), otherAction.properties.getUsers())) { updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION); } return updateType; } private enum CreateSteps implements Step { CREATE_POLICY { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSIAMPolicyResourceAction action = (AWSIAMPolicyResourceAction) resourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); // just get fields action.info.setPhysicalResourceId(action.getDefaultPhysicalResourceId()); action.info.setCreatedEnoughToDelete(true); action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId()))); return action; } }, ATTACH_TO_GROUPS { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSIAMPolicyResourceAction action = (AWSIAMPolicyResourceAction) resourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); if (action.properties.getGroups() != null) { for (String groupName: action.properties.getGroups()) { PutGroupPolicyType putGroupPolicyType = MessageHelper.createMessage(PutGroupPolicyType.class, action.info.getEffectiveUserId()); putGroupPolicyType.setGroupName(groupName); putGroupPolicyType.setPolicyName(action.properties.getPolicyName()); putGroupPolicyType.setPolicyDocument(action.properties.getPolicyDocument().toString()); AsyncRequests.<PutGroupPolicyType,PutGroupPolicyResponseType> sendSync(configuration, putGroupPolicyType); } } return action; } }, ATTACH_TO_USERS { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSIAMPolicyResourceAction action = (AWSIAMPolicyResourceAction) resourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); if (action.properties.getUsers() != null) { for (String userName: action.properties.getUsers()) { PutUserPolicyType putUserPolicyType = MessageHelper.createMessage(PutUserPolicyType.class, action.info.getEffectiveUserId()); putUserPolicyType.setUserName(userName); putUserPolicyType.setPolicyName(action.properties.getPolicyName()); putUserPolicyType.setPolicyDocument(action.properties.getPolicyDocument().toString()); AsyncRequests.<PutUserPolicyType,PutUserPolicyResponseType> sendSync(configuration, putUserPolicyType); } } return action; } }, ATTACH_TO_ROLES { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSIAMPolicyResourceAction action = (AWSIAMPolicyResourceAction) resourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); if (action.properties.getRoles() != null) { for (String roleName: action.properties.getRoles()) { PutRolePolicyType putRolePolicyType = MessageHelper.createMessage(PutRolePolicyType.class, action.info.getEffectiveUserId()); putRolePolicyType.setRoleName(roleName); putRolePolicyType.setPolicyName(action.properties.getPolicyName()); putRolePolicyType.setPolicyDocument(action.properties.getPolicyDocument().toString()); AsyncRequests.<PutRolePolicyType,PutRolePolicyResponseType> sendSync(configuration, putRolePolicyType); } } return action; } }; @Nullable @Override public Integer getTimeout() { return null; } } private enum DeleteSteps implements Step { DELETE_POLICY { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSIAMPolicyResourceAction action = (AWSIAMPolicyResourceAction) resourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action; // find all roles that still exist from the list and remove the policy Set<String> passedInRoles = IAMHelper.collectionToSetAndNullToEmpty(action.properties.getRoles()); List<String> realRolesToRemovePolicyFrom = IAMHelper.getExistingRoles(configuration, passedInRoles, action.info.getEffectiveUserId()); for (String role: realRolesToRemovePolicyFrom) { DeleteRolePolicyType deleteRolePolicyType = MessageHelper.createMessage(DeleteRolePolicyType.class, action.info.getEffectiveUserId()); deleteRolePolicyType.setRoleName(role); deleteRolePolicyType.setPolicyName(action.properties.getPolicyName()); AsyncRequests.<DeleteRolePolicyType,DeleteRolePolicyResponseType> sendSync(configuration, deleteRolePolicyType); } // find all users that still exist from the list and remove the policy Set<String> passedInUsers = action.properties.getUsers() == null ? new HashSet<String>() : Sets.newHashSet(action.properties.getUsers()); List<String> realUsersToRemovePolicyFrom = IAMHelper.getExistingUsers(configuration, passedInUsers, action.info.getEffectiveUserId()); for (String user: realUsersToRemovePolicyFrom) { DeleteUserPolicyType deleteUserPolicyType = MessageHelper.createMessage(DeleteUserPolicyType.class, action.info.getEffectiveUserId()); deleteUserPolicyType.setUserName(user); deleteUserPolicyType.setPolicyName(action.properties.getPolicyName()); AsyncRequests.<DeleteUserPolicyType,DeleteUserPolicyResponseType> sendSync(configuration, deleteUserPolicyType); } // find all groups that still exist from the list and remove the policy Set<String> passedInGroups = IAMHelper.collectionToSetAndNullToEmpty(action.properties.getGroups()); List<String> realGroupsToRemovePolicyFrom = IAMHelper.getExistingGroups(configuration, passedInGroups, action.info.getEffectiveUserId()); for (String group: realGroupsToRemovePolicyFrom) { DeleteGroupPolicyType deleteGroupPolicyType = MessageHelper.createMessage(DeleteGroupPolicyType.class, action.info.getEffectiveUserId()); deleteGroupPolicyType.setGroupName(group); deleteGroupPolicyType.setPolicyName(action.properties.getPolicyName()); AsyncRequests.<DeleteGroupPolicyType,DeleteGroupPolicyResponseType> sendSync(configuration, deleteGroupPolicyType); } return action; } }; @Nullable @Override public Integer getTimeout() { return null; } } @Override public ResourceProperties getResourceProperties() { return properties; } @Override public void setResourceProperties(ResourceProperties resourceProperties) { properties = (AWSIAMPolicyProperties) resourceProperties; } @Override public ResourceInfo getResourceInfo() { return info; } @Override public void setResourceInfo(ResourceInfo resourceInfo) { info = (AWSIAMPolicyResourceInfo) resourceInfo; } private enum UpdateNoInterruptionSteps implements UpdateStep { UPDATE_ATTACHMENT_TO_GROUPS { @Override public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception { AWSIAMPolicyResourceAction oldAction = (AWSIAMPolicyResourceAction) oldResourceAction; AWSIAMPolicyResourceAction newAction = (AWSIAMPolicyResourceAction) newResourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); Set<String> oldGroupNames = IAMHelper.collectionToSetAndNullToEmpty(oldAction.properties.getGroups()); Set<String> newGroupNames = IAMHelper.collectionToSetAndNullToEmpty(newAction.properties.getGroups()); // add the policy to the new group for (String groupName : newGroupNames) { PutGroupPolicyType putGroupPolicyType = MessageHelper.createMessage(PutGroupPolicyType.class, newAction.info.getEffectiveUserId()); putGroupPolicyType.setGroupName(groupName); putGroupPolicyType.setPolicyName(newAction.properties.getPolicyName()); putGroupPolicyType.setPolicyDocument(newAction.properties.getPolicyDocument().toString()); AsyncRequests.<PutGroupPolicyType, PutGroupPolicyResponseType>sendSync(configuration, putGroupPolicyType); } // if the policy name has changed, remove it from all the old groups // otherwise remove it from just the old groups that are not new groups Collection<String> targetOldGroupNames = oldAction.properties.getPolicyName().equals(newAction.properties.getPolicyName()) ? Sets.difference(oldGroupNames, newGroupNames) : oldGroupNames; for (String groupName : targetOldGroupNames) { DeleteGroupPolicyType deleteGroupPolicyType = MessageHelper.createMessage(DeleteGroupPolicyType.class, newAction.info.getEffectiveUserId()); deleteGroupPolicyType.setGroupName(groupName); deleteGroupPolicyType.setPolicyName(oldAction.properties.getPolicyName()); AsyncRequests.<DeleteGroupPolicyType, DeleteGroupPolicyResponseType>sendSync(configuration, deleteGroupPolicyType); } return newAction; } }, UPDATE_ATTACHMENT_TO_USERS { @Override public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception { AWSIAMPolicyResourceAction oldAction = (AWSIAMPolicyResourceAction) oldResourceAction; AWSIAMPolicyResourceAction newAction = (AWSIAMPolicyResourceAction) newResourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); Set<String> oldUserNames = IAMHelper.collectionToSetAndNullToEmpty(oldAction.properties.getUsers()); Set<String> newUserNames = IAMHelper.collectionToSetAndNullToEmpty(newAction.properties.getUsers()); // add the policy to the new user for (String userName: newUserNames) { PutUserPolicyType putUserPolicyType = MessageHelper.createMessage(PutUserPolicyType.class, newAction.info.getEffectiveUserId()); putUserPolicyType.setUserName(userName); putUserPolicyType.setPolicyName(newAction.properties.getPolicyName()); putUserPolicyType.setPolicyDocument(newAction.properties.getPolicyDocument().toString()); AsyncRequests.<PutUserPolicyType,PutUserPolicyResponseType> sendSync(configuration, putUserPolicyType); } // if the policy name has changed, remove it from all the old groups // otherwise remove it from just the old groups that are not new groups Collection<String> targetOldUserNames = oldAction.properties.getPolicyName().equals(newAction.properties.getPolicyName()) ? Sets.difference(oldUserNames, newUserNames) : oldUserNames; for (String userName: targetOldUserNames) { DeleteUserPolicyType deleteUserPolicyType = MessageHelper.createMessage(DeleteUserPolicyType.class, newAction.info.getEffectiveUserId()); deleteUserPolicyType.setUserName(userName); deleteUserPolicyType.setPolicyName(oldAction.properties.getPolicyName()); AsyncRequests.<DeleteUserPolicyType,DeleteUserPolicyResponseType> sendSync(configuration, deleteUserPolicyType); } return newAction; } }, UPDATE_ATTACHMENT_TO_ROLES { @Override public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception { AWSIAMPolicyResourceAction oldAction = (AWSIAMPolicyResourceAction) oldResourceAction; AWSIAMPolicyResourceAction newAction = (AWSIAMPolicyResourceAction) newResourceAction; ServiceConfiguration configuration = Topology.lookup(Euare.class); Set<String> oldRoleNames = IAMHelper.collectionToSetAndNullToEmpty(oldAction.properties.getRoles()); Set<String> newRoleNames = IAMHelper.collectionToSetAndNullToEmpty(newAction.properties.getRoles()); // add the policy to the new role for (String roleName: newRoleNames) { PutRolePolicyType putRolePolicyType = MessageHelper.createMessage(PutRolePolicyType.class, newAction.info.getEffectiveUserId()); putRolePolicyType.setRoleName(roleName); putRolePolicyType.setPolicyName(newAction.properties.getPolicyName()); putRolePolicyType.setPolicyDocument(newAction.properties.getPolicyDocument().toString()); AsyncRequests.<PutRolePolicyType,PutRolePolicyResponseType> sendSync(configuration, putRolePolicyType); } // if the policy name has changed, remove it from all the old roles // otherwise remove it from just the old roles that are not new roles Collection<String> targetOldRoleNames = oldAction.properties.getPolicyName().equals(newAction.properties.getPolicyName()) ? Sets.difference(oldRoleNames, newRoleNames) : oldRoleNames; for (String roleName: targetOldRoleNames) { DeleteRolePolicyType deleteRolePolicyType = MessageHelper.createMessage(DeleteRolePolicyType.class, newAction.info.getEffectiveUserId()); deleteRolePolicyType.setRoleName(roleName); deleteRolePolicyType.setPolicyName(oldAction.properties.getPolicyName()); AsyncRequests.<DeleteRolePolicyType,DeleteRolePolicyResponseType> sendSync(configuration, deleteRolePolicyType); } return newAction; } }; @Nullable @Override public Integer getTimeout() { return null; } } }