/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package controllers.auth;
import com.emc.storageos.security.keystore.impl.KeyCertificatePairGenerator;
import com.emc.vipr.client.core.Truststore;
import com.emc.vipr.model.keystore.TrustedCertificate;
import com.emc.vipr.model.keystore.TrustedCertificateChanges;
import com.emc.vipr.model.keystore.TruststoreSettings;
import com.emc.vipr.model.keystore.TruststoreSettingsChanges;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import controllers.Common;
import controllers.Maintenance;
import controllers.deadbolt.Restrict;
import controllers.deadbolt.Restrictions;
import controllers.util.FlashException;
import jobs.SaveCertificateSettingsJob;
import models.datatable.CertificateDataTable;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import play.Logger;
import play.data.binding.As;
import play.data.validation.Validation;
import play.mvc.Before;
import play.mvc.Controller;
import play.mvc.With;
import sun.security.provider.X509Factory;
import util.BourneUtil;
import util.MessagesUtils;
import util.StringOption;
import util.datatable.DataTablesSupport;
import java.io.File;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import static controllers.Common.angularRenderArgs;
@With(Common.class)
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public class Certificates extends Controller {
private static Truststore api() {
return BourneUtil.getViprClient().truststore();
}
@Before(unless = { "list", "listJson" })
static void isClusterStable() {
if (!Common.isClusterStable()) {
flash.error(MessagesUtils.get("configProperties.error.clusterNotStable"));
list();
}
}
@FlashException
public static void list() {
CertificateDataTable dataTable = new CertificateDataTable();
List<StringOption> options = Lists.newArrayList();
options.add(new StringOption("true", MessagesUtils.get("common.yes")));
options.add(new StringOption("false", MessagesUtils.get("common.no")));
TruststoreSettings certificateSettings = null;
certificateSettings = api().getTruststoreSettings();
angularRenderArgs().putAll(ImmutableMap.of(
"options", options,
"certificateSettings", certificateSettings
));
render(dataTable);
}
public static void listJson() throws CertificateException {
List<CertificateDataTable.CertificateInfo> certs = Lists.newArrayList();
for (TrustedCertificate cert : api().getTrustedCertificates()) {
certs.add(new CertificateDataTable.CertificateInfo(cert));
}
renderJSON(DataTablesSupport.createJSON(certs, params));
}
public static void edit(String id) {
error("Not implemented");
}
public static void create() {
render();
}
@FlashException(value = "list")
public static void delete(@As(",") List<String> ids) {
TrustedCertificateChanges changes = new TrustedCertificateChanges();
changes.setRemove(ids);
api().updateTrustedCertificate(changes);
flash.success(MessagesUtils.get("certificateChanges.submittedReconfigure"));
list();
}
public static void saveSettings(TruststoreSettingsChanges certificateSettings) {
new SaveCertificateSettingsJob(api(), certificateSettings).in(3);
flash.success(MessagesUtils.get("certificateSettings.submitted"));
Maintenance.maintenance(Common.reverseRoute(Certificates.class, "list"));
}
@FlashException(value = "list")
public static void addCertificates(CertificateChangesForm certificateChanges) {
if (certificateChanges.validateAndExtractAdds("certificateChanges")) {
if (certificateChanges.hasChanges()) {
TrustedCertificateChanges changes = new TrustedCertificateChanges();
changes.setAdd(certificateChanges.adds);
api().updateTrustedCertificate(changes);
flash.success(MessagesUtils.get("certificateChanges.submittedReconfigure"));
list();
} else {
// shouldn't actually be possible.
// The save button is disabled when there are no changes.
flash.error(MessagesUtils.get("certificates.nothing"));
}
} else {
params.flash();
Validation.keep();
}
create();
}
public static void validCertificate(File file) {
try {
CertificateChangesForm.pemFromFile(file);
} catch (Exception e) {
response.status = 406;
renderText(MessagesUtils.get("certificateChanges.files.invalidAjax"));
}
}
public static class CertificateChangesForm {
public File[] files;
public List<String> adds = new ArrayList();
public static String pemFromFile(File file) throws Exception {
X509Certificate cert = (X509Certificate) KeyCertificatePairGenerator.getCertificateFromString(
FileUtils.readFileToString(file));
if (cert == null) {
throw new CertificateException("No certificate found in file " + file.getName());
}
// some characters (such as non printable characters) are ignored by the cert parser but
// trip up our XML encoding/decoding. Get the PEM to send to API from the validated certificate
// instead of the uploaded file.
return (X509Factory.BEGIN_CERT + System.lineSeparator() +
new Base64().encodeToString(cert.getEncoded()) + System.lineSeparator() + X509Factory.END_CERT);
}
public boolean validateAndExtractAdds(String formName) {
if (files != null) {
for (File file : files) {
try {
adds.add(pemFromFile(file));
} catch (Exception e) {
Logger.error(e, "Unable to parse certificate file " + file.getName());
Validation.addError(formName + ".files",
MessagesUtils.get("certificateChanges.files.invalid", file.getName()));
return false;
}
}
}
return true;
}
public boolean hasChanges() {
return !adds.isEmpty();
}
}
}