/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.coordinator.client.model; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import static com.emc.storageos.coordinator.client.model.Constants.*; import com.emc.storageos.coordinator.client.service.PropertyInfoUtil; import com.emc.storageos.coordinator.exceptions.DecodingException; import com.emc.storageos.coordinator.exceptions.CoordinatorException; import com.emc.storageos.model.property.PropertiesMetadata; import com.emc.storageos.model.property.PropertyInfo; import com.emc.storageos.model.property.PropertyInfoRestRep; import com.emc.storageos.model.property.PropertyMetadata; import static com.emc.storageos.model.property.PropertyConstants.ENCRYPTEDSTRING; /** * PropertyInfoExt is only published as a shared object * According to CoordinatorClassInfo's requirement, only id, kind are necessary. * To comply with other similar classes, we gave it a dummy attribute as "targetInfoExt" */ public class PropertyInfoExt extends PropertyInfoRestRep implements CoordinatorSerializable { public static final String CONFIG_VERSION = "config_version"; public static final String CONNECTEMC_TRANSPORT = "system_connectemc_transport"; // property constants public static final String ENCODING_SEPARATOR = "\0"; public static final String ENCODING_EQUAL = "="; public static final String ENCODING_NEWLINE = "\n"; public static final String TARGET_PROPERTY = "upgradetargetpropertyoverride"; public static final String TARGET_PROPERTY_ID = "global"; public static final String TARGET_INFO = "targetInfo"; private static final List<String> SECRET_KEY_PROPS = new ArrayList<>(Arrays.asList( "ssh_host_ecdsa_key", "ssh_host_dsa_key", "ssh_host_rsa_key", "root_id_rsa", "root_id_dsa", "root_id_ecdsa", "storageos_id_rsa", "storageos_id_dsa", "storageos_id_ecdsa", "svcuser_id_rsa", "svcuser_id_dsa", "svcuser_id_ecdsa", "ipsec_key" )); public PropertyInfoExt() { } public PropertyInfoExt(Map<String, String> properties) { super(properties); } public PropertyInfoExt(String[] properties) { setProperties(PropertyInfoUtil.splitKeyValue(properties)); } @Override public String toString() { return toString(true); } /** * * @param withMask if true, replace all encrypted string with HIDDEN_TEXT_MASK, * otherwise always print the real content. * @return */ public String toString(boolean withMask) { StringBuffer sb = new StringBuffer(); for (Map.Entry<String, String> entry : getProperties().entrySet()) { sb.append(entry.getKey()); sb.append(ENCODING_EQUAL); // Hide encrypted string in audit log if (withMask && (PropertyInfoExt.isEncryptedProperty(entry.getKey()) || PropertyInfoExt.isSecretProperty(entry.getKey()))) { sb.append(HIDDEN_TEXT_MASK); } else { sb.append(entry.getValue()); } sb.append(ENCODING_NEWLINE); } return sb.toString(); } public boolean hasRebootProperty() { Map<String, PropertyMetadata> metadata = PropertiesMetadata.getGlobalMetadata(); if (getProperties() == null || metadata == null) { return false; } for (Map.Entry<String, String> entry : getProperties().entrySet()) { final String key = entry.getKey(); final PropertyMetadata propertyMetadata = metadata.get(key); if (propertyMetadata != null && propertyMetadata.getRebootRequired() != null && propertyMetadata.getRebootRequired().booleanValue()) { return true; } } return false; } public boolean hasReconfigProperty() { Map<String, PropertyMetadata> metadata = PropertiesMetadata.getGlobalMetadata(); if (getProperties() == null || metadata == null) { return false; } for (Map.Entry<String, String> entry : getProperties().entrySet()) { final String key = entry.getKey(); final PropertyMetadata propertyMetadata = metadata.get(key); if (propertyMetadata != null && propertyMetadata.getReconfigRequired() != null && propertyMetadata.getReconfigRequired().booleanValue()) { return true; } } return false; } public List<String> getNotifierTags() { return getNotifierTags(false); } public List<String> getNotifierTags(boolean forReconfig) { Map<String, PropertyMetadata> metadata = PropertiesMetadata.getGlobalMetadata(); List<String> ret = new ArrayList<>(); if (getProperties() == null || metadata == null) { return ret; } for (Map.Entry<String, String> entry : getProperties().entrySet()) { final String key = entry.getKey(); final PropertyMetadata propertyMetadata = metadata.get(key); if (propertyMetadata != null && propertyMetadata.getNotifiers() != null) { // skip those properties that have notifiters but don't require reconfig if (forReconfig && (propertyMetadata.getReconfigRequired() == null || !propertyMetadata.getReconfigRequired())) { continue; } String[] notifierTags = propertyMetadata.getNotifiers(); // TODO: Note that the ordering is not deterministic across properties now for (String notifierTag : notifierTags) { if (!ret.contains(notifierTag)) { ret.add(notifierTag); } } } } return ret; } public boolean hasReconfigAttributeWithoutNotifiers() { Map<String, PropertyMetadata> metadata = PropertiesMetadata.getGlobalMetadata(); List<String> ret = new ArrayList<>(); if (getProperties() == null || metadata == null) { return true; // call the old-fashion reconfig() in this case } for (Map.Entry<String, String> entry : getProperties().entrySet()) { final String key = entry.getKey(); final PropertyMetadata propertyMetadata = metadata.get(key); if (propertyMetadata != null && propertyMetadata.getReconfigRequired() != null && propertyMetadata.getReconfigRequired().booleanValue() && (propertyMetadata.getNotifiers() == null || propertyMetadata.getNotifiers().length == 0)) { return true; } } return false; } /** * Check if the given property is encryptedstring. * * @param key property name * @return True if yes. */ public static boolean isEncryptedProperty(String key) { Map<String, PropertyMetadata> metadata = PropertiesMetadata.getGlobalMetadata(); if (metadata != null) { PropertyMetadata propertyMetadata = metadata.get(key); // check property type if (propertyMetadata != null && ENCRYPTEDSTRING.equals(propertyMetadata.getType())) { return true; } } return false; } public static boolean isSecretProperty(String key) { return SECRET_KEY_PROPS.contains(key); } @Override public String encodeAsString() { final StringBuilder s = new StringBuilder(); Map<String, String> targetProps = getAllProperties(); for (Map.Entry<String, String> entry : targetProps.entrySet()) { s.append(entry.getKey()); s.append(ENCODING_EQUAL); s.append(entry.getValue()); s.append(ENCODING_SEPARATOR); } return s.toString(); } @SuppressWarnings("unchecked") @Override public PropertyInfoExt decodeFromString(String infoStr) throws DecodingException { try { PropertyInfo targetProps = constructPropertyObj(infoStr); if (targetProps != null) { return new PropertyInfoExt(targetProps.getAllProperties()); } return null; } catch (Exception e) { throw CoordinatorException.fatals.decodingError(e.toString()); } } @Override public CoordinatorClassInfo getCoordinatorClassInfo() { return new CoordinatorClassInfo(TARGET_PROPERTY_ID, TARGET_PROPERTY, "targetInfoExt"); } /** * Method used to construct a property object from property string * * @param stateStr Property string * @return Property object decoded from string * @throws Exception */ public PropertyInfo constructPropertyObj(String stateStr) throws Exception { if (stateStr != null) { final String[] strings = stateStr.split(ENCODING_SEPARATOR); if (strings.length == 0) { return new PropertyInfo(); } return new PropertyInfo(PropertyInfoUtil.splitKeyValue(strings)); } return null; } }