/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package controllers.tenant;
import static com.emc.vipr.client.core.util.ResourceUtils.*;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import models.ACLs;
import models.RoleAssignmentType;
import models.datatable.ProjectsDataTable;
import org.apache.commons.lang.StringUtils;
import play.Logger;
import play.data.binding.As;
import play.data.validation.MaxSize;
import play.data.validation.MinSize;
import play.data.validation.Required;
import play.data.validation.Validation;
import play.mvc.Util;
import play.mvc.With;
import util.*;
import util.datatable.DataTablesSupport;
import com.emc.storageos.model.auth.ACLAssignmentChanges;
import com.emc.storageos.model.auth.ACLEntry;
import com.emc.storageos.model.project.ProjectParam;
import com.emc.storageos.model.project.ProjectRestRep;
import com.emc.storageos.model.project.ProjectUpdateParam;
import com.emc.storageos.model.quota.QuotaInfo;
import com.emc.storageos.security.validator.StorageOSPrincipal;
import com.emc.storageos.security.validator.Validator;
import com.emc.vipr.client.exceptions.ServiceErrorException;
import com.emc.vipr.client.exceptions.ViPRException;
import com.google.common.collect.Lists;
import controllers.Common;
import controllers.deadbolt.Restrict;
import controllers.deadbolt.Restrictions;
import controllers.security.Security;
import controllers.util.ViprResourceController;
import controllers.util.FlashException;
import controllers.util.Models;
@With(Common.class)
@Restrictions({ @Restrict("PROJECT_ADMIN"), @Restrict("TENANT_ADMIN") })
public class Projects extends ViprResourceController {
protected static final String UNKNOWN = "projects.unknown";
private static final String VIPR_START_GUIDE = "VIPR_START_GUIDE";
private static final String GUIDE_DATA = "GUIDE_DATA";
private static final String GUIDE_VISIBLE = "guideVisible";
private static final String GUIDE_COMPLETED_STEP = "completedSteps";
public static void list() {
ProjectsDataTable dataTable = new ProjectsDataTable();
TenantSelector.addRenderArgs();
render(dataTable);
}
@FlashException(value = "list", keep = true)
public static void listJson() {
String userId = Security.getUserInfo().getIdentifier();
List<ProjectRestRep> viprProjects = ProjectUtils.getProjects(Models.currentAdminTenant());
List<ProjectsDataTable.Project> projects = Lists.newArrayList();
for (ProjectRestRep viprProject : viprProjects) {
if (Security.isTenantAdmin()
|| (Security.isProjectAdmin() && StringUtils.equalsIgnoreCase(userId, viprProject.getOwner()))) {
projects.add(new ProjectsDataTable.Project(viprProject));
}
}
renderJSON(DataTablesSupport.createJSON(projects, params));
}
@FlashException(value = "list", keep = true)
public static void create() {
ProjectForm project = new ProjectForm();
project.tenantId = Models.currentAdminTenant();
addRenderArgs();
render("@edit", project);
}
@FlashException(value = "list", keep = true)
public static void edit(String id) {
ProjectRestRep viprProject = ProjectUtils.getProject(id);
if (viprProject == null) {
flash.error(MessagesUtils.get(UNKNOWN, id));
list();
}
QuotaInfo quota = null;
if (Security.isTenantAdmin()) {
quota = ProjectUtils.getQuota(id);
}
if (viprProject != null) {
ProjectForm project = new ProjectForm().from(viprProject, quota);
project.aclEntries = AclEntryForm.loadAclEntryForms(ProjectUtils.getACLs(id));
addRenderArgs();
render(project);
}
else {
flash.error(MessagesUtils.get("projects.unknown", id));
list();
}
}
@FlashException(keep = true, referrer = { "create", "edit" })
public static void save(ProjectForm project) {
if (project == null) {
Logger.error("No project parameters passed");
badRequest("No project parameters passed");
return;
}
project.validate("project");
if (Validation.hasErrors()) {
Common.handleError();
}
if (project.isNew()) {
project.id = stringId(ProjectUtils.create(project.tenantId, new ProjectParam(project.name)));
saveProjectQuota(project);
saveProjectACLs(project.id, project.aclEntries);
}
else {
ProjectRestRep currentProject = ProjectUtils.getProject(project.id);
if (currentProject != null) {
saveProjectQuota(project);
saveProjectACLs(project.id, project.aclEntries);
ProjectUtils.update(project.id, new ProjectUpdateParam(project.name, project.owner));
}
}
flash.success(MessagesUtils.get("projects.saved", project.name));
JsonObject jobject = getCookieAsJson(VIPR_START_GUIDE);
if (jobject != null && jobject.get(GUIDE_COMPLETED_STEP) != null && jobject.get(GUIDE_VISIBLE) != null) {
if (jobject.get("completedSteps").getAsInt() == 7 && jobject.get("guideVisible").getAsBoolean()) {
JsonObject dataObject = getCookieAsJson(GUIDE_DATA);
JsonArray projects = dataObject.getAsJsonArray("projects");
if (projects == null) {
projects = new JsonArray();
}
boolean addToCookie = true;
for(Object projectObject: projects) {
JsonObject projectjson = (JsonObject)projectObject;
if(projectjson.get("id") != null) {
String projectId = projectjson.get("id").getAsString();
if(StringUtils.equals(projectId, project.id)) {
addToCookie = false; //update case, don't add in cookie
break;
}
}
}
if (addToCookie) {
JsonObject projectObject = new JsonObject();
projectObject.addProperty("id", project.id);
projectObject.addProperty("name", project.name);
projects.add(projectObject);
dataObject.add("projects", projects);
saveJsonAsCookie("GUIDE_DATA", dataObject);
}
}
}
if (StringUtils.isNotBlank(project.referrerUrl)) {
redirect(project.referrerUrl);
}
else {
list();
}
}
@Util
private static void saveProjectQuota(ProjectForm project) {
if (Security.isTenantAdmin()) {
if (project.enableQuota) {
ProjectUtils.enableQuota(project.id, project.quota);
}
else {
ProjectUtils.disableQuota(project.id);
}
}
}
@Util
private static void saveProjectACLs(String projectId, List<AclEntryForm> aclEntries) {
List<ACLEntry> currentProjectAcls = ProjectUtils.getACLs(projectId);
ACLAssignmentChanges changes = new ACLAssignmentChanges();
changes.getAdd().addAll(AclEntryForm.getAddedAcls(currentProjectAcls, aclEntries));
changes.getRemove().addAll(AclEntryForm.getRemovedAcls(currentProjectAcls, aclEntries));
try {
ProjectUtils.updateACLs(projectId, changes);
} catch (ViPRException e) {
Logger.error(e, "Failed to update Project ACLs");
String errorDesc = e.getMessage();
if (e instanceof ServiceErrorException) {
errorDesc = ((ServiceErrorException) e).getDetailedMessage();
}
flash.error(MessagesUtils.get("projects.updateProjectACLs.failed", errorDesc));
}
}
@FlashException("list")
public static void delete(@As(",") String[] ids) {
String tenantId = null;
if (ids != null && ids.length > 0) {
ProjectRestRep project = ProjectUtils.getProject(ids[0]);
if (project != null) {
tenantId = project.getTenant().toString();
}
for (String id : ids) {
ProjectUtils.deactivate(uri(id));
}
flash.success(MessagesUtils.get("projects.deleted"));
}
list();
}
@Util
private static void addRenderArgs() {
renderArgs.put("acls", ACLs.options(ACLs.ALL, ACLs.BACKUP));
Set<EnumOption> aclTypes = new TreeSet<EnumOption>();
aclTypes.addAll(Arrays.asList(EnumOption.options(RoleAssignmentType.values(), "RoleAssignmentType")));
renderArgs.put("aclTypes", aclTypes);
}
public static class ProjectForm {
public String id;
public String tenantId;
@Required
@MaxSize(128)
@MinSize(2)
public String name;
public String owner;
public Boolean enableQuota = Boolean.FALSE;
public Long quota;
public List<AclEntryForm> aclEntries = Lists.newArrayList();
public String referrerUrl;
public ProjectForm from(ProjectRestRep from, QuotaInfo quota) {
this.id = from.getId().toString();
this.tenantId = from.getTenant().getId().toString();
this.name = from.getName();
this.owner = from.getOwner();
if (quota != null) {
this.enableQuota = quota.getEnabled();
if (isTrue(quota.getEnabled())) {
this.quota = quota.getQuotaInGb();
}
else {
this.quota = null;
}
}
else {
this.enableQuota = Boolean.FALSE;
this.quota = null;
}
return this;
}
public boolean isNew() {
return StringUtils.isBlank(id);
}
public void validate(String formName) {
Validation.valid(formName, this);
boolean ownerChanged = false;
if (isNew()) {
ownerChanged = StringUtils.isNotBlank(this.owner);
}
else {
// Require an owner for editing projects
Validation.required(formName + ".owner", owner);
ProjectRestRep existingProject = ProjectUtils.getProject(this.id);
if (existingProject != null) {
ownerChanged = StringUtils.equalsIgnoreCase(existingProject.getOwner(), this.owner) == false;
}
}
if (ownerChanged && StringUtils.isNotBlank(owner)) {
if (LocalUser.isLocalUser(this.owner) && ownerChanged) {
Validation.addError(formName + ".owner", MessagesUtils.get("Projects.localUsersNotPermitted"));
}
else if (isValidPrincipal(RoleAssignmentType.USER, this.owner) == false) {
Validation.addError(formName + ".owner", MessagesUtils.get("project.owner.error.invalid"));
}
}
String fieldName = formName + ".quota";
if (enableQuota) {
if (this.quota == null) {
Validation.addError(fieldName, MessagesUtils.get("project.quota.error.required"));
}
else {
String quotaParamName = formName + ".quota";
String quotaParamString = params.get(quotaParamName);
try {
Integer.parseInt(quotaParamString);
} catch (NumberFormatException e) {
Validation.addError(quotaParamName, MessagesUtils.get("project.quota.error.invalid"));
}
}
}
if (Security.isSecurityAdminOrRestrictedSecurityAdmin()) {
ACLUtils.validateAclEntries(formName + ".aclEntries", this.aclEntries);
}
}
private boolean isValidPrincipal(RoleAssignmentType type, String name) {
StorageOSPrincipal principal = new StorageOSPrincipal();
principal.setName(name);
if (RoleAssignmentType.GROUP.equals(type)) {
principal.setType(StorageOSPrincipal.Type.Group);
}
else if (RoleAssignmentType.USER.equals(type)) {
principal.setType(StorageOSPrincipal.Type.User);
}
String tenant = Models.currentAdminTenant();
return Validator.isValidPrincipal(principal, URI.create(tenant));
}
}
}