private SystemSettings removePrivateSettings(SystemSettings settings) { SystemSettings cleansed = new SystemSettings(settings); for (SystemSetting s : SystemSetting.values()) { if (!s.isPublic()) { cleansed.remove(s); } } return cleansed; }
/** * Call this to transform a system setting to a more appropriate value. Importantly, this * (de)obfuscates the password fields as they go from and to the DB. We use the @{link * PicketBoxObfuscator} so that people are able encode their passwords in the system settings * export files using the "rhq-encode-password.sh" script. */ private String transformSystemConfigurationPropertyFromDb( SystemSetting prop, String value, boolean unobfuscate) { // to support Oracle (whose booleans may be 1 or 0) transform the boolean settings properly switch (prop) { case LDAP_BASED_JAAS_PROVIDER: if (RHQConstants.JDBCJAASProvider.equals(value)) { return Boolean.toString(false); } else if (RHQConstants.LDAPJAASProvider.equals(value)) { return Boolean.toString(true); } else { return value == null ? "" : value; } case USE_SSL_FOR_LDAP: if (RHQConstants.LDAP_PROTOCOL_SECURED.equals(value)) { return Boolean.toString(true); } else { return Boolean.toString(false); } default: switch (prop.getType()) { case BOOLEAN: if ("0".equals(value)) { return Boolean.FALSE.toString(); } else if ("1".equals(value)) { return Boolean.TRUE.toString(); } else { return value == null ? Boolean.FALSE.toString() : value; } case PASSWORD: if (unobfuscate && value != null && value.trim().length() > 0) { return PicketBoxObfuscator.decode(value); } else { return value == null ? "" : value; } default: if (value == null) { switch (prop.getType()) { case DOUBLE: case FLOAT: case INTEGER: case LONG: value = "0"; break; default: value = ""; } } return value; } } }
/** * Call this to transform a system setting to a more appropriate value. Importantly, this * (de)obfuscates the password fields as they go from and to the DB. We use the @{link * PicketBoxObfuscator} so that people are able encode their passwords in the system settings * export files using the "rhq-encode-password.sh" script. */ private String transformSystemConfigurationPropertyToDb( SystemSetting prop, String newValue, String oldValue) { // the 0,1 -> true false scenario is no problem here, because the values are stored // as string anyway (so no conversion is done). I assume the above is a historical // code that could be safely eliminated. switch (prop) { case LDAP_BASED_JAAS_PROVIDER: if (Boolean.parseBoolean(newValue)) { return RHQConstants.LDAPJAASProvider; } else { return RHQConstants.JDBCJAASProvider; } case USE_SSL_FOR_LDAP: if (Boolean.parseBoolean(newValue)) { return RHQConstants.LDAP_PROTOCOL_SECURED; } else { return RHQConstants.LDAP_PROTOCOL_UNSECURED; } default: if (prop.getType() == PropertySimpleType.PASSWORD && newValue != null) { if (PropertySimple.MASKED_VALUE.equals(newValue)) { return oldValue; } else { return PicketBoxObfuscator.encode(newValue); } } else { return newValue; } } }
private String unmask(SystemSetting prop, String newValue, String currentValue) { if (prop.getType() == PropertySimpleType.PASSWORD && PropertySimple.MASKED_VALUE.equals(newValue)) { newValue = currentValue; } return newValue; }
private void transformToSystemSettingsFormat(Map<String, String> map) { for (Map.Entry<String, String> e : map.entrySet()) { SystemSetting prop = SystemSetting.getByInternalName(e.getKey()); if (prop != null) { // this is a legacy method that supplies values in the DB-specific format. // we therefore have to transform the values as if they came from the database. String value = transformSystemConfigurationPropertyFromDb(prop, e.getValue(), false); e.setValue(value); } } }
public void testPasswordFieldsObfuscation() { SystemSettings masked = systemManager.getSystemSettings(overlord); SystemSettings unmasked = systemManager.getUnmaskedSystemSettings(true); SystemSettings obfuscated = systemManager.getObfuscatedSystemSettings(true); for (SystemSetting setting : SystemSetting.values()) { if (setting.getType() == PropertySimpleType.PASSWORD) { if (masked.containsKey(setting) && masked.get(setting) != null) { assertEquals( "Unexpected unmasked value", PropertySimple.MASKED_VALUE, masked.get(setting)); assertEquals( "Unmasked and obfuscated values don't correspond", obfuscated.get(setting), PicketBoxObfuscator.encode(unmasked.get(setting))); } } } systemManager.deobfuscate(obfuscated); assertEquals(unmasked, obfuscated); }
private void checkFormats(SystemSettings settings, Properties config) { assert settings.size() == config.size() : "The old and new style system settings differ in size"; for (String name : config.stringPropertyNames()) { SystemSetting setting = SystemSetting.getByInternalName(name); String oldStyleValue = config.getProperty(name); String newStyleValue = settings.get(setting); assert setting != null : "Could not find a system setting called '" + name + "'."; switch (setting) { case USE_SSL_FOR_LDAP: if (RHQConstants.LDAP_PROTOCOL_SECURED.equals(oldStyleValue)) { assert Boolean.valueOf(newStyleValue) : "Secured LDAP protocol should be represented by a 'true' in new style settings."; } else if (RHQConstants.LDAP_PROTOCOL_UNSECURED.equals(oldStyleValue)) { assert !Boolean.valueOf(newStyleValue) : "Unsecured LDAP protocol should be represented by a 'false' in the new style settings."; } else { assert false : "Unknown value for system setting '" + setting + "': [" + oldStyleValue + "]."; } break; case LDAP_BASED_JAAS_PROVIDER: if (RHQConstants.LDAPJAASProvider.equals(oldStyleValue)) { assert Boolean.valueOf(newStyleValue) : "LDAP JAAS provider should be represented by a 'true' in new style settings."; } else if (RHQConstants.JDBCJAASProvider.equals(oldStyleValue)) { assert !Boolean.valueOf(newStyleValue) : "JDBC JAAS provider should be represented by a 'false' in the new style settings."; } else { assert false : "Unknown value for system setting '" + setting + "': [" + oldStyleValue + "]."; } break; default: assert oldStyleValue != null && newStyleValue != null && oldStyleValue.equals(newStyleValue) : "Old and new style values unexpectedly differ for system setting '" + setting + "': old=[" + oldStyleValue + "], new=[" + newStyleValue + "]."; } } }
private void fillCache(Collection<SystemConfiguration> configs) { SystemSettings settings = new SystemSettings(); for (SystemConfiguration config : configs) { SystemSetting prop = SystemSetting.getByInternalName(config.getPropertyKey()); if (prop == null) { LOG.warn( "The database contains unknown system configuration setting [" + config.getPropertyKey() + "]."); continue; } if (config.getPropertyValue() == null) { // for some reason, the configuration is not found in the DB, so fallback to the persisted // default. // if there isn't even a persisted default, just use an empty string. String defaultValue = config.getDefaultPropertyValue(); defaultValue = transformSystemConfigurationPropertyFromDb(prop, defaultValue, true); settings.put(prop, defaultValue); } else { String value = config.getPropertyValue(); value = transformSystemConfigurationPropertyFromDb(prop, value, true); settings.put(prop, value); } } settings.setDriftPlugins(getDriftServerPlugins()); synchronized (this) { // only update the caches if the settings were actually changed if (cachedSystemSettings == null || !safeEquals( cachedSystemSettings.get(SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME), settings.get(SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME))) { cachedSystemSettings = settings; cachedObfuscatedSystemSettings = new SystemSettings(settings); for (Map.Entry<SystemSetting, String> entry : cachedObfuscatedSystemSettings.entrySet()) { String value = entry.getValue(); if (value != null && entry.getKey().getType() == PropertySimpleType.PASSWORD) { entry.setValue(PicketBoxObfuscator.encode(value)); } } } } }
@Override public void setAnySystemSettings( SystemSettings settings, boolean skipValidation, boolean ignoreReadOnly) { // first, we need to get the current settings so we'll know if we need to persist or merge the // new ones @SuppressWarnings("unchecked") List<SystemConfiguration> configs = entityManager.createNamedQuery(SystemConfiguration.QUERY_FIND_ALL).getResultList(); Map<String, SystemConfiguration> existingConfigMap = new HashMap<String, SystemConfiguration>(); for (SystemConfiguration config : configs) { existingConfigMap.put(config.getPropertyKey(), config); } boolean changed = false; SystemConfiguration lastUpdateTime = existingConfigMap.get(SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.getInternalName()); // verify each new setting and persist them to the database // note that if a new setting is the same as the old one, we do nothing - leave the old entity // as is for (Map.Entry<SystemSetting, String> e : settings.entrySet()) { SystemSetting prop = e.getKey(); String value = e.getValue(); if (!skipValidation) { verifyNewSystemConfigurationProperty(prop, value, settings); } SystemConfiguration existingConfig = existingConfigMap.get(prop.getInternalName()); if (e.getKey() == SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME) { // we don't let the user persist their own last system config update time // in any manner lastUpdateTime = existingConfig; } else if (existingConfig == null) { value = transformSystemConfigurationPropertyToDb(prop, value, null); existingConfig = new SystemConfiguration(prop.getInternalName(), value); entityManager.persist(existingConfig); changed = true; existingConfigMap.put(existingConfig.getPropertyKey(), existingConfig); } else { // make sure we compare the new value with a database-agnostic value // it is important to compare in the database-agnostic format instead // of database specific because the conversion isn't reflective. // Some legacy code somewhere is (or just used to) store booleans as "0"s and "1"s // even though they are stored as strings and thus don't suffer from Oracle's // lack of support for boolean data type. If we encounter such values, we convert // them to "false"/"true" and store that value to database. This is a one way operation // and therefore we need to compare the database-agnostic (i.e. "true"/"false") and // not database specific. // // More importantly though, we store the password fields obfuscated, so we need to compare // apples with // apples here. String existingValue = transformSystemConfigurationPropertyFromDb( prop, existingConfig.getPropertyValue(), true); // we need to unmask the new value so that we can compare for changes. only after that can // we transform // it into the DB format. value = unmask(prop, value, existingValue); // also for oracle, treat null and empty string as the same. if ((isEmpty(existingValue) && !isEmpty(value)) || (null != existingValue && !existingValue.equals(value))) { // SystemSetting#isReadOnly should be a superset of the "fReadOnly" field in the database // but let's just be super paranoid here... if ((prop.isReadOnly() || (existingConfig.getFreadOnly() != null && existingConfig.getFreadOnly().booleanValue())) && !(isStorageSetting(prop) || ignoreReadOnly)) { throw new IllegalArgumentException( "The setting [" + prop.getInternalName() + "] is read-only - you cannot change its current value! Current value is [" + existingConfig.getPropertyValue() + "] while the new value was [" + value + "]."); } // transform to the database-specific format value = transformSystemConfigurationPropertyToDb(prop, value, existingValue); existingConfig.setPropertyValue(value); entityManager.merge(existingConfig); changed = true; } } } if (changed) { if (lastUpdateTime == null) { lastUpdateTime = new SystemConfiguration( SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.getInternalName(), Long.toString(System.currentTimeMillis())); lastUpdateTime.setFreadOnly(SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.isReadOnly()); entityManager.persist(lastUpdateTime); } else { lastUpdateTime.setPropertyValue(Long.toString(System.currentTimeMillis())); entityManager.merge(lastUpdateTime); } existingConfigMap.put( SystemSetting.LAST_SYSTEM_CONFIG_UPDATE_TIME.getInternalName(), lastUpdateTime); fillCache(existingConfigMap.values()); } }