/*************************************************************************
* 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.AddUserToGroupResponseType;
import com.eucalyptus.auth.euare.AddUserToGroupType;
import com.eucalyptus.cloudformation.ValidationErrorException;
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.AWSIAMUserToGroupAdditionResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSIAMUserToGroupAdditionProperties;
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.Objects;
import java.util.Set;
/**
* Created by ethomas on 2/3/14.
*/
public class AWSIAMUserToGroupAdditionResourceAction extends StepBasedResourceAction {
private AWSIAMUserToGroupAdditionProperties properties = new AWSIAMUserToGroupAdditionProperties();
private AWSIAMUserToGroupAdditionResourceInfo info = new AWSIAMUserToGroupAdditionResourceInfo();
public AWSIAMUserToGroupAdditionResourceAction() {
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;
AWSIAMUserToGroupAdditionResourceAction otherAction = (AWSIAMUserToGroupAdditionResourceAction) resourceAction;
if (!Objects.equals(properties.getGroupName(), otherAction.properties.getGroupName())) {
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 {
ADD_USER_TO_GROUP {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSIAMUserToGroupAdditionResourceAction action = (AWSIAMUserToGroupAdditionResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Euare.class);
if (!IAMHelper.groupExists(configuration, action.properties.getGroupName(), action.info.getEffectiveUserId())) {
throw new ValidationErrorException("No such group " + action.properties.getGroupName());
}
Collection<String> nonexistantUsers = IAMHelper.nonexistantUsers(configuration, action.properties.getUsers(), action.info.getEffectiveUserId());
if (!nonexistantUsers.isEmpty()) {
throw new ValidationErrorException("No such user(s) " + nonexistantUsers.toString());
}
// only add new users (AddUserToGroup not idempotent)
Set<String> existingUsersInGroup = IAMHelper.getUserNamesForGroup(configuration, action.properties.getGroupName(), action.info.getEffectiveUserId());
Set<String> passedInUsers = Sets.newLinkedHashSet();
if (action.properties.getUsers() != null) {
passedInUsers.addAll(action.properties.getUsers());
}
for (String userName: Sets.difference(passedInUsers, existingUsersInGroup)) {
AddUserToGroupType addUserToGroupType = MessageHelper.createMessage(AddUserToGroupType.class, action.info.getEffectiveUserId());
addUserToGroupType.setGroupName(action.properties.getGroupName());
addUserToGroupType.setUserName(userName);
AsyncRequests.<AddUserToGroupType,AddUserToGroupResponseType> sendSync(configuration, addUserToGroupType);
}
action.info.setPhysicalResourceId(action.getDefaultPhysicalResourceId());
action.info.setCreatedEnoughToDelete(true);
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum DeleteSteps implements Step {
REMOVE_USER_FROM_GROUP {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSIAMUserToGroupAdditionResourceAction action = (AWSIAMUserToGroupAdditionResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Euare.class);
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
IAMHelper.removeUsersFromGroup(configuration, action.properties.getUsers(), action.properties.getGroupName(), action.info.getEffectiveUserId());
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSIAMUserToGroupAdditionProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSIAMUserToGroupAdditionResourceInfo) resourceInfo;
}
private enum UpdateNoInterruptionSteps implements UpdateStep {
UPDATE_USERS_IN_GROUP {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSIAMUserToGroupAdditionResourceAction oldAction = (AWSIAMUserToGroupAdditionResourceAction) oldResourceAction;
AWSIAMUserToGroupAdditionResourceAction newAction = (AWSIAMUserToGroupAdditionResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Euare.class);
if (!IAMHelper.groupExists(configuration, newAction.properties.getGroupName(), newAction.info.getEffectiveUserId())) {
throw new ValidationErrorException("No such group " + newAction.properties.getGroupName());
}
Collection<String> nonexistantUsers = IAMHelper.nonexistantUsers(configuration, newAction.properties.getUsers(), newAction.info.getEffectiveUserId());
if (!nonexistantUsers.isEmpty()) {
throw new ValidationErrorException("No such user(s) " + nonexistantUsers.toString());
}
// First only add the 'new' users to the group (AddUserToGroup is not idempotent currently)
Set<String> existingUsersForGroup = IAMHelper.getUserNamesForGroup(configuration, newAction.properties.getGroupName(), newAction.info.getEffectiveUserId());
Set<String> passedInUsers = Sets.newLinkedHashSet();
if (newAction.properties.getUsers() != null) {
passedInUsers.addAll(newAction.properties.getUsers());
}
for (String userName: Sets.difference(passedInUsers, existingUsersForGroup)) {
AddUserToGroupType addUserToGroupType = MessageHelper.createMessage(AddUserToGroupType.class, newAction.info.getEffectiveUserId());
addUserToGroupType.setGroupName(newAction.properties.getGroupName());
addUserToGroupType.setUserName(userName);
AsyncRequests.<AddUserToGroupType,AddUserToGroupResponseType> sendSync(configuration, addUserToGroupType);
}
if (!Objects.equals(oldAction.properties.getGroupName(), newAction.properties.getGroupName())) {
IAMHelper.removeUsersFromGroup(
configuration,
oldAction.properties.getUsers(),
oldAction.properties.getGroupName(),
oldAction.info.getEffectiveUserId()
);
} else {
IAMHelper.removeUsersFromGroup(
configuration,
Sets.difference(
Sets.newLinkedHashSet(oldAction.properties.getUsers()),
Sets.newLinkedHashSet(newAction.properties.getUsers())
),
newAction.properties.getGroupName(),
newAction.info.getEffectiveUserId()
);
}
return newAction;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
}