|
@@ -1,1521 +1,1562 @@
|
|
|
-/*
|
|
|
- * Password Management Servlets (PWM)
|
|
|
- * http://code.google.com/p/pwm/
|
|
|
- *
|
|
|
- * Copyright (c) 2006-2009 Novell, Inc.
|
|
|
- * Copyright (c) 2009-2015 The PWM Project
|
|
|
- *
|
|
|
- * 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; either version 2 of the License, or
|
|
|
- * (at your option) any later version.
|
|
|
- *
|
|
|
- * 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, write to the Free Software
|
|
|
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
- */
|
|
|
-
|
|
|
-package password.pwm.config;
|
|
|
-
|
|
|
-import org.jdom2.*;
|
|
|
-import org.jdom2.xpath.XPathExpression;
|
|
|
-import org.jdom2.xpath.XPathFactory;
|
|
|
-import password.pwm.AppProperty;
|
|
|
-import password.pwm.PwmConstants;
|
|
|
-import password.pwm.bean.UserIdentity;
|
|
|
-import password.pwm.config.option.ADPolicyComplexity;
|
|
|
-import password.pwm.config.value.PasswordValue;
|
|
|
-import password.pwm.config.value.StringArrayValue;
|
|
|
-import password.pwm.config.value.StringValue;
|
|
|
-import password.pwm.config.value.ValueFactory;
|
|
|
-import password.pwm.error.*;
|
|
|
-import password.pwm.i18n.Config;
|
|
|
-import password.pwm.i18n.LocaleHelper;
|
|
|
-import password.pwm.i18n.PwmLocaleBundle;
|
|
|
-import password.pwm.util.*;
|
|
|
-import password.pwm.util.logging.PwmLogger;
|
|
|
-import password.pwm.util.secure.PwmRandom;
|
|
|
-import password.pwm.util.secure.PwmSecurityKey;
|
|
|
-import password.pwm.util.secure.SecureEngine;
|
|
|
-
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.InputStream;
|
|
|
-import java.io.OutputStream;
|
|
|
-import java.io.Serializable;
|
|
|
-import java.text.ParseException;
|
|
|
-import java.util.*;
|
|
|
-import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
|
-
|
|
|
-/**
|
|
|
- * @author Jason D. Rivard
|
|
|
- */
|
|
|
-public class StoredConfiguration implements Serializable {
|
|
|
-// ------------------------------ FIELDS ------------------------------
|
|
|
-
|
|
|
- public enum ConfigProperty {
|
|
|
- PROPERTY_KEY_CONFIG_IS_EDITABLE("configIsEditable"),
|
|
|
- PROPERTY_KEY_CONFIG_EPOCH("configEpoch"),
|
|
|
- PROPERTY_KEY_TEMPLATE("configTemplate"),
|
|
|
- PROPERTY_KEY_NOTES("notes"),
|
|
|
- PROPERTY_KEY_PASSWORD_HASH("configPasswordHash"),
|
|
|
- PROPERTY_KEY_SAVE_CONFIG_ON_START("saveConfigOnStart"),
|
|
|
- ;
|
|
|
-
|
|
|
- private final String key;
|
|
|
-
|
|
|
- private ConfigProperty(String key)
|
|
|
- {
|
|
|
- this.key = key;
|
|
|
- }
|
|
|
-
|
|
|
- public String getKey()
|
|
|
- {
|
|
|
- return key;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public static class SettingMetaData implements Serializable {
|
|
|
- private Date modifyDate;
|
|
|
- private UserIdentity userIdentity;
|
|
|
-
|
|
|
- public Date getModifyDate()
|
|
|
- {
|
|
|
- return modifyDate;
|
|
|
- }
|
|
|
-
|
|
|
- public UserIdentity getUserIdentity()
|
|
|
- {
|
|
|
- return userIdentity;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static final PwmLogger LOGGER = PwmLogger.forClass(StoredConfiguration.class);
|
|
|
- private static final String XML_FORMAT_VERSION = "4";
|
|
|
-
|
|
|
- public static final String XML_ELEMENT_ROOT = "PwmConfiguration";
|
|
|
- public static final String XML_ELEMENT_PROPERTIES = "properties";
|
|
|
- public static final String XML_ELEMENT_PROPERTY = "property";
|
|
|
- public static final String XML_ELEMENT_SETTINGS = "settings";
|
|
|
- public static final String XML_ELEMENT_SETTING = "setting";
|
|
|
- public static final String XML_ELEMENT_DEFAULT = "default";
|
|
|
-
|
|
|
- public static final String XML_ATTRIBUTE_TYPE = "type";
|
|
|
- public static final String XML_ATTRIBUTE_KEY = "key";
|
|
|
- public static final String XML_ATTRIBUTE_SYNTAX = "syntax";
|
|
|
- public static final String XML_ATTRIBUTE_PROFILE = "profile";
|
|
|
- public static final String XML_ATTRIBUTE_VALUE_APP = "app";
|
|
|
- public static final String XML_ATTRIBUTE_VALUE_CONFIG = "config";
|
|
|
- public static final String XML_ATTRIBUTE_CREATE_TIME = "createTime";
|
|
|
- public static final String XML_ATTRIBUTE_MODIFY_TIME = "modifyTime";
|
|
|
- public static final String XML_ATTRIBUTE_MODIFY_USER = "modifyUser";
|
|
|
- public static final String XML_ATTRIBUTE_SYNTAX_VERSION = "syntaxVersion";
|
|
|
-
|
|
|
- private Document document = new Document(new Element(XML_ELEMENT_ROOT));
|
|
|
- private ChangeLog changeLog = new ChangeLog();
|
|
|
-
|
|
|
- private boolean locked = false;
|
|
|
- private boolean setting_writeLabels = true;
|
|
|
- private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
|
|
|
-
|
|
|
-// -------------------------- STATIC METHODS --------------------------
|
|
|
-
|
|
|
- public static StoredConfiguration newStoredConfiguration() throws PwmUnrecoverableException {
|
|
|
- return new StoredConfiguration();
|
|
|
- }
|
|
|
-
|
|
|
- public static StoredConfiguration copy(final StoredConfiguration input) throws PwmUnrecoverableException {
|
|
|
- final StoredConfiguration copy = new StoredConfiguration();
|
|
|
- copy.document = input.document.clone();
|
|
|
- return copy;
|
|
|
- }
|
|
|
-
|
|
|
- public static StoredConfiguration fromXml(final InputStream xmlData)
|
|
|
- throws PwmUnrecoverableException
|
|
|
- {
|
|
|
- final Date startTime = new Date();
|
|
|
- //validateXmlSchema(xmlData);
|
|
|
-
|
|
|
- final Document inputDocument = XmlUtil.parseXml(xmlData);
|
|
|
- final StoredConfiguration newConfiguration = StoredConfiguration.newStoredConfiguration();
|
|
|
-
|
|
|
- try {
|
|
|
- newConfiguration.document = inputDocument;
|
|
|
- newConfiguration.createTime(); // verify create time;
|
|
|
- ConfigurationCleaner.cleanup(newConfiguration);
|
|
|
- } catch (Exception e) {
|
|
|
- final String errorMsg = "error reading configuration file format, error=" + e.getMessage();
|
|
|
- final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorMsg});
|
|
|
- throw new PwmUnrecoverableException(errorInfo);
|
|
|
- }
|
|
|
-
|
|
|
- checkIfXmlRequiresUpdate(newConfiguration);
|
|
|
- final TimeDuration totalDuration = TimeDuration.fromCurrent(startTime);
|
|
|
- LOGGER.debug("successfully loaded configuration (" + totalDuration.asCompactString() + ")");
|
|
|
- return newConfiguration;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Loop through all settings to see if setting value has flag {@link StoredValue#requiresStoredUpdate()} set to true.
|
|
|
- * If so, then call {@link #writeSetting(PwmSetting, StoredValue, password.pwm.bean.UserIdentity)} or {@link #writeSetting(PwmSetting, String, StoredValue, password.pwm.bean.UserIdentity)}
|
|
|
- * for that value so that the xml dom can be updated.
|
|
|
- * @param storedConfiguration stored configuration to check
|
|
|
- */
|
|
|
- private static void checkIfXmlRequiresUpdate(final StoredConfiguration storedConfiguration) throws PwmUnrecoverableException {
|
|
|
- for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
- if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
- final StoredValue value = storedConfiguration.readSetting(setting);
|
|
|
- if (value.requiresStoredUpdate()) {
|
|
|
- storedConfiguration.writeSetting(setting, value, null);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
- if (category.hasProfiles()) {
|
|
|
- for (final String profileID : storedConfiguration.profilesForSetting(category.getProfileSetting())) {
|
|
|
- for (final PwmSetting profileSetting : category.getSettings()) {
|
|
|
- final StoredValue value = storedConfiguration.readSetting(profileSetting, profileID);
|
|
|
- if (value.requiresStoredUpdate()) {
|
|
|
- storedConfiguration.writeSetting(profileSetting, profileID, value, null);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void resetAllPasswordValues(final String comment) {
|
|
|
- for (final Iterator<SettingValueRecord> settingValueRecordIterator = new StoredValueIterator(false); settingValueRecordIterator.hasNext();) {
|
|
|
- final SettingValueRecord settingValueRecord = settingValueRecordIterator.next();
|
|
|
- if (settingValueRecord.getSetting().getSyntax() == PwmSettingSyntax.PASSWORD) {
|
|
|
- this.resetSetting(settingValueRecord.getSetting(),settingValueRecord.getProfile(),null);
|
|
|
- if (comment != null && !comment.isEmpty()) {
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForSetting(settingValueRecord.getSetting(), settingValueRecord.getProfile());
|
|
|
- final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
- if (settingElement != null) {
|
|
|
- settingElement.addContent(new Comment(comment));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public StoredConfiguration() throws PwmUnrecoverableException {
|
|
|
- ConfigurationCleaner.cleanup(this);
|
|
|
- final String createTime = PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date());
|
|
|
- document.getRootElement().setAttribute(XML_ATTRIBUTE_CREATE_TIME,createTime);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public String readConfigProperty(final ConfigProperty propertyName) {
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForConfigProperty(propertyName);
|
|
|
- final Element propertyElement = (Element)xp.evaluateFirst(document);
|
|
|
- return propertyElement == null ? null : propertyElement.getText();
|
|
|
- }
|
|
|
-
|
|
|
- public void writeConfigProperty(
|
|
|
- final ConfigProperty propertyName,
|
|
|
- final String value
|
|
|
- ) {
|
|
|
- domModifyLock.writeLock().lock();
|
|
|
- try {
|
|
|
-
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForConfigProperty(propertyName);
|
|
|
- final List<Element> propertyElements = xp.evaluate(document);
|
|
|
- for (final Element propertyElement : propertyElements) {
|
|
|
- propertyElement.detach();
|
|
|
- }
|
|
|
-
|
|
|
- final Element propertyElement = new Element(XML_ELEMENT_PROPERTY);
|
|
|
- propertyElement.setAttribute(new Attribute(XML_ATTRIBUTE_KEY,propertyName.getKey()));
|
|
|
- propertyElement.setContent(new Text(value));
|
|
|
-
|
|
|
- if (null == XPathBuilder.xpathForConfigProperties().evaluateFirst(document)) {
|
|
|
- Element configProperties = new Element(XML_ELEMENT_PROPERTIES);
|
|
|
- configProperties.setAttribute(new Attribute(XML_ATTRIBUTE_TYPE,XML_ATTRIBUTE_VALUE_CONFIG));
|
|
|
- document.getRootElement().addContent(configProperties);
|
|
|
- }
|
|
|
-
|
|
|
- final XPathExpression xp2 = XPathBuilder.xpathForConfigProperties();
|
|
|
- final Element propertiesElement = (Element)xp2.evaluateFirst(document);
|
|
|
- propertyElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
- propertiesElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
- propertiesElement.addContent(propertyElement);
|
|
|
- } finally {
|
|
|
- domModifyLock.writeLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void lock() {
|
|
|
- locked = true;
|
|
|
- }
|
|
|
-
|
|
|
- public Map<String,String> readLocaleBundleMap(final String bundleName, final String keyName) {
|
|
|
- domModifyLock.readLock().lock();
|
|
|
- try {
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForLocaleBundleSetting(bundleName, keyName);
|
|
|
- final Element localeBundleElement = (Element)xp.evaluateFirst(document);
|
|
|
- if (localeBundleElement != null) {
|
|
|
- final Map<String,String> bundleMap = new LinkedHashMap<>();
|
|
|
- for (final Element valueElement : localeBundleElement.getChildren("value")) {
|
|
|
- final String localeStrValue = valueElement.getAttributeValue("locale");
|
|
|
- bundleMap.put(localeStrValue == null ? "" : localeStrValue, valueElement.getText());
|
|
|
- }
|
|
|
- if (!bundleMap.isEmpty()) {
|
|
|
- return bundleMap;
|
|
|
- }
|
|
|
- }
|
|
|
- } finally {
|
|
|
- domModifyLock.readLock().unlock();
|
|
|
- }
|
|
|
- return Collections.emptyMap();
|
|
|
- }
|
|
|
-
|
|
|
- public Map<String,Object> toOutputMap(final Locale locale) {
|
|
|
- final List<Map<String,String>> settingData = new ArrayList<>();
|
|
|
- for (final StoredConfiguration.SettingValueRecord settingValueRecord : this.modifiedSettings()) {
|
|
|
- final Map<String,String> recordMap = new HashMap<>();
|
|
|
- recordMap.put("label", settingValueRecord.getSetting().getLabel(locale));
|
|
|
- if (settingValueRecord.getProfile() != null) {
|
|
|
- recordMap.put("profile", settingValueRecord.getProfile());
|
|
|
- }
|
|
|
- if (settingValueRecord.getStoredValue() != null) {
|
|
|
- recordMap.put("value", settingValueRecord.getStoredValue().toDebugString(locale));
|
|
|
- }
|
|
|
- final SettingMetaData settingMetaData = readSettingMetadata(settingValueRecord.getSetting(), settingValueRecord.getProfile());
|
|
|
- if (settingMetaData != null) {
|
|
|
- if (settingMetaData.getModifyDate() != null) {
|
|
|
- recordMap.put("modifyTime", PwmConstants.DEFAULT_DATETIME_FORMAT.format(settingMetaData.getModifyDate()));
|
|
|
- }
|
|
|
- if (settingMetaData.getUserIdentity() != null) {
|
|
|
- recordMap.put("modifyUser",settingMetaData.getUserIdentity().toDisplayString());
|
|
|
- }
|
|
|
- }
|
|
|
- settingData.add(recordMap);
|
|
|
- }
|
|
|
-
|
|
|
- final HashMap<String,Object> outputObj = new HashMap<>();
|
|
|
- outputObj.put("settings",settingData);
|
|
|
- outputObj.put("template", this.getTemplate().toString());
|
|
|
-
|
|
|
- return Collections.unmodifiableMap(outputObj);
|
|
|
- }
|
|
|
-
|
|
|
- public void resetLocaleBundleMap(final String bundleName, final String keyName) {
|
|
|
- preModifyActions();
|
|
|
- domModifyLock.writeLock().lock();
|
|
|
- try {
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForLocaleBundleSetting(bundleName, keyName);
|
|
|
- final List<Element> oldBundleElements = xp.evaluate(document);
|
|
|
- if (oldBundleElements != null) {
|
|
|
- for (final Element element : oldBundleElements) {
|
|
|
- element.detach();
|
|
|
- }
|
|
|
- }
|
|
|
- } finally {
|
|
|
- domModifyLock.writeLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void resetSetting(final PwmSetting setting, final UserIdentity userIdentity) {
|
|
|
- resetSetting(setting, null, userIdentity);
|
|
|
- }
|
|
|
-
|
|
|
- public void resetSetting(final PwmSetting setting, final String profileID, final UserIdentity userIdentity) {
|
|
|
- changeLog.updateChangeLog(setting, profileID, defaultValue(setting, this.getTemplate()));
|
|
|
- domModifyLock.writeLock().lock();
|
|
|
- preModifyActions();
|
|
|
- try {
|
|
|
- final Element settingElement = createOrGetSettingElement(document, setting, profileID);
|
|
|
- settingElement.removeContent();
|
|
|
- settingElement.addContent(new Element(XML_ELEMENT_DEFAULT));
|
|
|
- updateMetaData(settingElement, userIdentity);
|
|
|
- } finally {
|
|
|
- domModifyLock.writeLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isDefaultValue(final PwmSetting setting) {
|
|
|
- return isDefaultValue(setting, null);
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isDefaultValue(final PwmSetting setting, final String profileID) {
|
|
|
- domModifyLock.readLock().lock();
|
|
|
- try {
|
|
|
- final StoredValue currentValue = readSetting(setting, profileID);
|
|
|
- if (setting.getSyntax() == PwmSettingSyntax.PASSWORD) {
|
|
|
- return currentValue == null || currentValue.toNativeObject() == null;
|
|
|
- }
|
|
|
- final StoredValue defaultValue = defaultValue(setting, this.getTemplate());
|
|
|
- final String currentJsonValue = JsonUtil.serialize((Serializable)currentValue.toNativeObject());
|
|
|
- final String defaultJsonValue = JsonUtil.serialize((Serializable)defaultValue.toNativeObject());
|
|
|
- return defaultJsonValue.equalsIgnoreCase(currentJsonValue);
|
|
|
- } finally {
|
|
|
- domModifyLock.readLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static StoredValue defaultValue(final PwmSetting pwmSetting, final PwmSettingTemplate template)
|
|
|
- {
|
|
|
- try {
|
|
|
- return pwmSetting.getDefaultValue(template);
|
|
|
- } catch (PwmException e) {
|
|
|
- final String errorMsg = "error reading default value for setting " + pwmSetting.toString() + ", error: " + e.getErrorInformation().toDebugStr();
|
|
|
- LOGGER.error(errorMsg,e);
|
|
|
- throw new IllegalStateException(errorMsg);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public PwmSettingTemplate getTemplate() {
|
|
|
- final String propertyValue = readConfigProperty(ConfigProperty.PROPERTY_KEY_TEMPLATE);
|
|
|
- try {
|
|
|
- return PwmSettingTemplate.valueOf(propertyValue);
|
|
|
- } catch (IllegalArgumentException e) {
|
|
|
- return PwmSettingTemplate.DEFAULT;
|
|
|
- } catch (NullPointerException e) {
|
|
|
- return PwmSettingTemplate.DEFAULT;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void setTemplate(PwmSettingTemplate template) {
|
|
|
- writeConfigProperty(ConfigProperty.PROPERTY_KEY_TEMPLATE, template.toString());
|
|
|
- }
|
|
|
-
|
|
|
- public String toString(final PwmSetting setting, final String profileID ) {
|
|
|
- final StoredValue storedValue = readSetting(setting, profileID);
|
|
|
- return setting.getKey() + "=" + storedValue.toDebugString(null);
|
|
|
- }
|
|
|
-
|
|
|
- public Map<String,String> getModifiedSettingDebugValues(final Locale locale, final boolean prettyPrint) {
|
|
|
- final Map<String,String> returnObj = new LinkedHashMap<>();
|
|
|
- for (SettingValueRecord record : this.modifiedSettings()) {
|
|
|
- final String label = record.getSetting().toMenuLocationDebug(record.getProfile(),locale);
|
|
|
- final String value = record.getStoredValue().toDebugString(locale);
|
|
|
- returnObj.put(label,value);
|
|
|
- }
|
|
|
- return returnObj;
|
|
|
- }
|
|
|
-
|
|
|
- List<SettingValueRecord> modifiedSettings() {
|
|
|
- final List<SettingValueRecord> returnObj = new ArrayList<>();
|
|
|
- domModifyLock.readLock().lock();
|
|
|
- try {
|
|
|
- for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
- if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
- if (!isDefaultValue(setting,null)) {
|
|
|
- final StoredValue value = readSetting(setting);
|
|
|
- if (value != null) {
|
|
|
- returnObj.add(new SettingValueRecord(setting, null, value));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
- if (category.hasProfiles()) {
|
|
|
- for (final String profileID : this.profilesForSetting(category.getProfileSetting())) {
|
|
|
- for (final PwmSetting profileSetting : category.getSettings()) {
|
|
|
- if (!isDefaultValue(profileSetting, profileID)) {
|
|
|
- final StoredValue value = readSetting(profileSetting, profileID);
|
|
|
- if (value != null) {
|
|
|
- returnObj.add(new SettingValueRecord(profileSetting, profileID, value));
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return returnObj;
|
|
|
- } finally {
|
|
|
- domModifyLock.readLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public Serializable toJsonDebugObject() {
|
|
|
- domModifyLock.readLock().lock();
|
|
|
- try {
|
|
|
- final TreeMap<String,Object> outputObject = new TreeMap<>();
|
|
|
-
|
|
|
- for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
- if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
- if (!isDefaultValue(setting,null)) {
|
|
|
- final StoredValue value = readSetting(setting);
|
|
|
- outputObject.put(setting.getKey(), value.toDebugJsonObject(null));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
- if (category.hasProfiles()) {
|
|
|
- final TreeMap<String,Object> profiles = new TreeMap<>();
|
|
|
- for (final String profileID : this.profilesForSetting(category.getProfileSetting())) {
|
|
|
- final TreeMap<String,Object> profileObject = new TreeMap<>();
|
|
|
- for (final PwmSetting profileSetting : category.getSettings()) {
|
|
|
- if (!isDefaultValue(profileSetting, profileID)) {
|
|
|
- final StoredValue value = readSetting(profileSetting, profileID);
|
|
|
- profileObject.put(profileSetting.getKey(), value.toDebugJsonObject(null));
|
|
|
- }
|
|
|
- }
|
|
|
- profiles.put(profileID,profileObject);
|
|
|
- }
|
|
|
- outputObject.put(category.getProfileSetting().getKey(),profiles);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return outputObject;
|
|
|
- } finally {
|
|
|
- domModifyLock.readLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void toXml(final OutputStream outputStream)
|
|
|
- throws IOException, PwmUnrecoverableException
|
|
|
- {
|
|
|
- ConfigurationCleaner.updateMandatoryElements(document);
|
|
|
- XmlUtil.outputDocument(document, outputStream);
|
|
|
- }
|
|
|
-
|
|
|
- public List<String> profilesForSetting(final PwmSetting pwmSetting) {
|
|
|
- if (!pwmSetting.getCategory().hasProfiles() && pwmSetting.getSyntax() != PwmSettingSyntax.PROFILE) {
|
|
|
- throw new IllegalArgumentException("cannot build profile list for non-profile setting " + pwmSetting.toString());
|
|
|
- }
|
|
|
-
|
|
|
- final PwmSetting profileSetting;
|
|
|
- if (pwmSetting.getSyntax() == PwmSettingSyntax.PROFILE) {
|
|
|
- profileSetting = pwmSetting;
|
|
|
- } else {
|
|
|
- profileSetting = pwmSetting.getCategory().getProfileSetting();
|
|
|
- }
|
|
|
-
|
|
|
- final List<String> settingValues = (List<String>)readSetting(profileSetting).toNativeObject();
|
|
|
- final LinkedList<String> profiles = new LinkedList<>();
|
|
|
- profiles.addAll(settingValues);
|
|
|
- for (Iterator<String> iterator = profiles.iterator(); iterator.hasNext();) {
|
|
|
- final String profile = iterator.next();
|
|
|
- if (profile == null || profile.length() < 1) {
|
|
|
- iterator.remove();
|
|
|
- }
|
|
|
- }
|
|
|
- return Collections.unmodifiableList(profiles);
|
|
|
- }
|
|
|
-
|
|
|
- public List<String> validateValues() {
|
|
|
- final long startTime = System.currentTimeMillis();
|
|
|
- final List<String> errorStrings = new ArrayList<>();
|
|
|
-
|
|
|
- for (final PwmSetting loopSetting : PwmSetting.values()) {
|
|
|
-
|
|
|
- if (loopSetting.getCategory().hasProfiles()) {
|
|
|
- for (final String profile : profilesForSetting(loopSetting)) {
|
|
|
- final String errorAppend = profile;
|
|
|
- final StoredValue loopValue = readSetting(loopSetting,profile);
|
|
|
-
|
|
|
- try {
|
|
|
- final List<String> errors = loopValue.validateValue(loopSetting);
|
|
|
- for (final String loopError : errors) {
|
|
|
- errorStrings.add(loopSetting.toMenuLocationDebug(profile,PwmConstants.DEFAULT_LOCALE) + errorAppend + " " + loopError);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- LOGGER.error("unexpected error during validate value for " + loopSetting.toMenuLocationDebug(profile,PwmConstants.DEFAULT_LOCALE) + errorAppend + ", error: " + e.getMessage(),e);
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- final StoredValue loopValue = readSetting(loopSetting);
|
|
|
-
|
|
|
- try {
|
|
|
- final List<String> errors = loopValue.validateValue(loopSetting);
|
|
|
- for (final String loopError : errors) {
|
|
|
- errorStrings.add(loopSetting.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE) + loopError);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- LOGGER.error("unexpected error during validate value for " + loopSetting.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE) + ", error: " + e.getMessage(),e);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- LOGGER.trace("StoredConfiguration validator completed in " + TimeDuration.fromCurrent(startTime).asCompactString());
|
|
|
- return errorStrings;
|
|
|
- }
|
|
|
-
|
|
|
- public SettingMetaData readSettingMetadata(final PwmSetting setting, final String profileID) {
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForSetting(setting, profileID);
|
|
|
- final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
-
|
|
|
- if (settingElement == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- final SettingMetaData metaData = new SettingMetaData();
|
|
|
- try {
|
|
|
- if (settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_TIME) != null) {
|
|
|
- metaData.modifyDate = PwmConstants.DEFAULT_DATETIME_FORMAT.parse(
|
|
|
- settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_TIME));
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- LOGGER.error("can't read modifyDate for setting " + setting.getKey() + ", profile " + profileID + ", error: " + e.getMessage());
|
|
|
- }
|
|
|
- try {
|
|
|
- if (settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_USER) != null) {
|
|
|
- metaData.userIdentity = UserIdentity.fromDelimitedKey(
|
|
|
- settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_USER));
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- LOGGER.error("can't read userIdentity for setting " + setting.getKey() + ", profile " + profileID + ", error: " + e.getMessage());
|
|
|
- }
|
|
|
- return metaData;
|
|
|
- }
|
|
|
-
|
|
|
- public List<ConfigRecordID> search(final String searchTerm, final Locale locale) {
|
|
|
- if (searchTerm == null) {
|
|
|
- return Collections.emptyList();
|
|
|
- }
|
|
|
-
|
|
|
- final LinkedHashSet<ConfigRecordID> returnSet = new LinkedHashSet<>();
|
|
|
- boolean firstIter = true;
|
|
|
- for (final String searchWord : StringUtil.whitespaceSplit(searchTerm)) { // split on whitespace
|
|
|
- final LinkedHashSet<ConfigRecordID> loopResults = new LinkedHashSet<>();
|
|
|
- for (final PwmSetting loopSetting : PwmSetting.values()) {
|
|
|
- if (loopSetting.getCategory().hasProfiles()) {
|
|
|
- for (final String profile : profilesForSetting(loopSetting)) {
|
|
|
- final StoredValue loopValue = readSetting(loopSetting, profile);
|
|
|
- if (matchSetting(loopSetting,loopValue,searchWord,locale)) {
|
|
|
- loopResults.add(new ConfigRecordID(ConfigRecordID.RecordType.SETTING,loopSetting,profile));
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- final StoredValue loopValue = readSetting(loopSetting);
|
|
|
- if (matchSetting(loopSetting,loopValue,searchWord,locale)) {
|
|
|
- loopResults.add(new ConfigRecordID(ConfigRecordID.RecordType.SETTING,loopSetting,null));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (firstIter) {
|
|
|
- returnSet.addAll(loopResults);
|
|
|
- } else {
|
|
|
- returnSet.retainAll(loopResults);
|
|
|
- }
|
|
|
- firstIter = false;
|
|
|
- }
|
|
|
-
|
|
|
- return new ArrayList<>(returnSet);
|
|
|
- }
|
|
|
-
|
|
|
- public boolean matchSetting(final PwmSetting setting, final StoredValue value, final String searchTerm, final Locale locale) {
|
|
|
- if (setting.isHidden() || setting.getCategory().isHidden()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (searchTerm == null || searchTerm.isEmpty()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- final String lowerSearchTerm = searchTerm.toLowerCase();
|
|
|
-
|
|
|
- {
|
|
|
- final String key = setting.getKey();
|
|
|
- if (key.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- {
|
|
|
- final String label = setting.getLabel(locale);
|
|
|
- if (label.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- {
|
|
|
- final String descr = setting.getDescription(locale);
|
|
|
- if (descr.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- {
|
|
|
- final String menuLocationString = setting.toMenuLocationDebug(null,locale);
|
|
|
- if (menuLocationString.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (setting.isConfidential()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- {
|
|
|
- final String valueDebug = value.toDebugString(locale);
|
|
|
- if (valueDebug != null && valueDebug.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- if (PwmSettingSyntax.SELECT == setting.getSyntax()
|
|
|
- || PwmSettingSyntax.OPTIONLIST == setting.getSyntax()
|
|
|
- || PwmSettingSyntax.VERIFICATION_METHOD == setting.getSyntax()
|
|
|
- ) {
|
|
|
- for (final String key : setting.getOptions().keySet()) {
|
|
|
- if (key.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- final String optionValue = setting.getOptions().get(key);
|
|
|
- if (optionValue != null && optionValue.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public StoredValue readSetting(final PwmSetting setting) {
|
|
|
- return readSetting(setting,null);
|
|
|
- }
|
|
|
-
|
|
|
- public StoredValue readSetting(final PwmSetting setting, final String profileID) {
|
|
|
- if (profileID == null && setting.getCategory().hasProfiles()) {
|
|
|
- IllegalArgumentException e = new IllegalArgumentException("reading of setting " + setting.getKey() + " requires a non-null profileID");
|
|
|
- LOGGER.error("error",e);
|
|
|
- throw e;
|
|
|
- }
|
|
|
- if (profileID != null && !setting.getCategory().hasProfiles()) {
|
|
|
- throw new IllegalStateException("cannot read setting key " + setting.getKey() + " with non-null profileID");
|
|
|
- }
|
|
|
- domModifyLock.readLock().lock();
|
|
|
- try {
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForSetting(setting, profileID);
|
|
|
- final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
-
|
|
|
- if (settingElement == null) {
|
|
|
- return defaultValue(setting, getTemplate());
|
|
|
- }
|
|
|
-
|
|
|
- if (settingElement.getChild(XML_ELEMENT_DEFAULT) != null) {
|
|
|
- return defaultValue(setting, getTemplate());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- return ValueFactory.fromXmlValues(setting, settingElement, getKey());
|
|
|
- } catch (PwmException e) {
|
|
|
- final String errorMsg = "unexpected error reading setting '" + setting.getKey() + "' profile '" + profileID + "', error: " + e.getMessage();
|
|
|
- throw new IllegalStateException(errorMsg);
|
|
|
- }
|
|
|
- } finally {
|
|
|
- domModifyLock.readLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void writeLocaleBundleMap(final String bundleName, final String keyName, final Map<String,String> localeMap) {
|
|
|
- ResourceBundle theBundle = null;
|
|
|
- for (final PwmLocaleBundle bundle : PwmLocaleBundle.values()) {
|
|
|
- if (bundle.getTheClass().getName().equals(bundleName)) {
|
|
|
- theBundle = ResourceBundle.getBundle(bundleName);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (theBundle == null) {
|
|
|
- LOGGER.info("ignoring unknown locale bundle for bundle=" + bundleName + ", key=" + keyName);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (theBundle.getString(keyName) == null) {
|
|
|
- LOGGER.info("ignoring unknown key for bundle=" + bundleName + ", key=" + keyName);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- resetLocaleBundleMap(bundleName, keyName);
|
|
|
- if (localeMap == null || localeMap.isEmpty()) {
|
|
|
- LOGGER.info("cleared locale bundle map for bundle=" + bundleName + ", key=" + keyName);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- preModifyActions();
|
|
|
- changeLog.updateChangeLog(bundleName, keyName, localeMap);
|
|
|
- try {
|
|
|
- domModifyLock.writeLock().lock();
|
|
|
- final Element localeBundleElement = new Element("localeBundle");
|
|
|
- localeBundleElement.setAttribute("bundle",bundleName);
|
|
|
- localeBundleElement.setAttribute("key",keyName);
|
|
|
- for (final String locale : localeMap.keySet()) {
|
|
|
- final Element valueElement = new Element("value");
|
|
|
- if (locale != null && locale.length() > 0) {
|
|
|
- valueElement.setAttribute("locale",locale);
|
|
|
- }
|
|
|
- valueElement.setContent(new CDATA(localeMap.get(locale)));
|
|
|
- localeBundleElement.addContent(valueElement);
|
|
|
- }
|
|
|
- localeBundleElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
- document.getRootElement().addContent(localeBundleElement);
|
|
|
- } finally {
|
|
|
- domModifyLock.writeLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void copyProfileID(final PwmSettingCategory category, final String sourceID, final String destinationID, final UserIdentity userIdentity)
|
|
|
- throws PwmUnrecoverableException
|
|
|
- {
|
|
|
- if (!category.hasProfiles()) {
|
|
|
- throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category " + category + ", category does not have profiles");
|
|
|
- }
|
|
|
- final List<String> existingProfiles = this.profilesForSetting(category.getProfileSetting());
|
|
|
- if (!existingProfiles.contains(sourceID)) {
|
|
|
- throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category, source profileID '" + sourceID + "' does not exist");
|
|
|
- }
|
|
|
- if (existingProfiles.contains(destinationID)) {
|
|
|
- throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category, destination profileID '" + destinationID+ "' already exists");
|
|
|
- }
|
|
|
- for (final PwmSetting pwmSetting : category.getSettings()) {
|
|
|
- if (!isDefaultValue(pwmSetting, sourceID)) {
|
|
|
- final StoredValue value = readSetting(pwmSetting, sourceID);
|
|
|
- writeSetting(pwmSetting, destinationID, value, userIdentity);
|
|
|
- }
|
|
|
- }
|
|
|
- final List<String> newProfileIDList = new ArrayList<>();
|
|
|
- newProfileIDList.addAll(existingProfiles);
|
|
|
- newProfileIDList.add(destinationID);
|
|
|
- writeSetting(category.getProfileSetting(), new StringArrayValue(newProfileIDList), userIdentity);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public void writeSetting(
|
|
|
- final PwmSetting setting,
|
|
|
- final StoredValue value,
|
|
|
- final UserIdentity userIdentity
|
|
|
- ) throws PwmUnrecoverableException {
|
|
|
- writeSetting(setting, null, value, userIdentity);
|
|
|
- }
|
|
|
-
|
|
|
- public void writeSetting(
|
|
|
- final PwmSetting setting,
|
|
|
- final String profileID,
|
|
|
- final StoredValue value,
|
|
|
- final UserIdentity userIdentity
|
|
|
- ) throws PwmUnrecoverableException {
|
|
|
- if (profileID == null && setting.getCategory().hasProfiles()) {
|
|
|
- throw new IllegalArgumentException("reading of setting " + setting.getKey() + " requires a non-null profileID");
|
|
|
- }
|
|
|
- if (profileID != null && !setting.getCategory().hasProfiles()) {
|
|
|
- throw new IllegalArgumentException("cannot specify profile for non-profile setting");
|
|
|
- }
|
|
|
-
|
|
|
- preModifyActions();
|
|
|
- changeLog.updateChangeLog(setting, profileID, value);
|
|
|
- domModifyLock.writeLock().lock();
|
|
|
- try {
|
|
|
- final Element settingElement = createOrGetSettingElement(document, setting, profileID);
|
|
|
- settingElement.removeContent();
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString());
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_SYNTAX_VERSION, Integer.toString(value.currentSyntaxVersion()));
|
|
|
-
|
|
|
- if (setting_writeLabels) {
|
|
|
- final Element labelElement = new Element("label");
|
|
|
- labelElement.addContent(setting.getLabel(PwmConstants.DEFAULT_LOCALE));
|
|
|
- settingElement.addContent(labelElement);
|
|
|
- }
|
|
|
-
|
|
|
- if (setting.getSyntax() == PwmSettingSyntax.PASSWORD) {
|
|
|
- final List<Element> valueElements = ((PasswordValue)value).toXmlValues("value", getKey());
|
|
|
- settingElement.addContent(new Comment("Note: This value is encrypted and can not be edited directly."));
|
|
|
- settingElement.addContent(new Comment("Please use the Configuration Manager GUI to modify this value."));
|
|
|
- settingElement.addContent(valueElements);
|
|
|
- } else {
|
|
|
- settingElement.addContent(value.toXmlValues("value"));
|
|
|
- }
|
|
|
-
|
|
|
- updateMetaData(settingElement, userIdentity);
|
|
|
- } finally {
|
|
|
- domModifyLock.writeLock().unlock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public String settingChecksum()
|
|
|
- throws PwmUnrecoverableException
|
|
|
- {
|
|
|
- final Date startTime = new Date();
|
|
|
-
|
|
|
- final List<SettingValueRecord> modifiedSettings = modifiedSettings();
|
|
|
- final StringBuilder sb = new StringBuilder();
|
|
|
- sb.append("PwmSettingsChecksum");
|
|
|
- for (SettingValueRecord settingValueRecord : modifiedSettings) {
|
|
|
- final StoredValue storedValue = settingValueRecord.getStoredValue();
|
|
|
- sb.append(storedValue.valueHash());
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- final String result = SecureEngine.hash(sb.toString(), PwmConstants.SETTING_CHECKSUM_HASH_METHOD);
|
|
|
- LOGGER.trace("computed setting checksum in " + TimeDuration.fromCurrent(startTime).asCompactString());
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void preModifyActions() {
|
|
|
- if (locked) {
|
|
|
- throw new UnsupportedOperationException("StoredConfiguration is locked and cannot be modified");
|
|
|
- }
|
|
|
- document.getRootElement().setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
- }
|
|
|
-
|
|
|
-// -------------------------- INNER CLASSES --------------------------
|
|
|
-
|
|
|
- public void setPassword(final String password)
|
|
|
- throws PwmOperationalException
|
|
|
- {
|
|
|
- if (password == null || password.isEmpty()) {
|
|
|
- throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"can not set blank password"}));
|
|
|
- }
|
|
|
- final String trimmedPassword = password.trim();
|
|
|
- if (trimmedPassword.length() < 1) {
|
|
|
- throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"can not set blank password"}));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- final String salt = BCrypt.gensalt();
|
|
|
- final String passwordHash = BCrypt.hashpw(password,salt);
|
|
|
- this.writeConfigProperty(ConfigProperty.PROPERTY_KEY_PASSWORD_HASH, passwordHash);
|
|
|
- }
|
|
|
-
|
|
|
- public boolean verifyPassword(final String password) {
|
|
|
- if (!hasPassword()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- final String passwordHash = this.readConfigProperty(ConfigProperty.PROPERTY_KEY_PASSWORD_HASH);
|
|
|
- return BCrypt.checkpw(password,passwordHash);
|
|
|
- }
|
|
|
-
|
|
|
- public boolean hasPassword() {
|
|
|
- final String passwordHash = this.readConfigProperty(ConfigProperty.PROPERTY_KEY_PASSWORD_HASH);
|
|
|
- return passwordHash != null && passwordHash.length() > 0;
|
|
|
- }
|
|
|
-
|
|
|
- private static abstract class XPathBuilder {
|
|
|
- private static XPathExpression xpathForLocaleBundleSetting(final String bundleName, final String keyName) {
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- final String xpathString;
|
|
|
- xpathString = "//localeBundle[@bundle=\"" + bundleName + "\"][@key=\"" + keyName + "\"]";
|
|
|
- return xpfac.compile(xpathString);
|
|
|
- }
|
|
|
-
|
|
|
- private static XPathExpression xpathForSetting(final PwmSetting setting, final String profileID) {
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- final String xpathString;
|
|
|
- if (profileID == null || profileID.length() < 1) {
|
|
|
- xpathString = "//setting[@key=\"" + setting.getKey() + "\"][(not (@profile)) or @profile=\"\"]";
|
|
|
- } else {
|
|
|
- xpathString = "//setting[@key=\"" + setting.getKey() + "\"][@profile=\"" + profileID + "\"]";
|
|
|
- }
|
|
|
-
|
|
|
- return xpfac.compile(xpathString);
|
|
|
- }
|
|
|
-
|
|
|
- private static XPathExpression xpathForAppProperty(final AppProperty appProperty) {
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- final String xpathString;
|
|
|
- xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]/"
|
|
|
- + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + appProperty.getKey() + "\"]";
|
|
|
- return xpfac.compile(xpathString);
|
|
|
- }
|
|
|
-
|
|
|
- private static XPathExpression xpathForAppProperties() {
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- final String xpathString;
|
|
|
- xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]";
|
|
|
- return xpfac.compile(xpathString);
|
|
|
- }
|
|
|
-
|
|
|
- private static XPathExpression xpathForConfigProperty(final ConfigProperty configProperty) {
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- final String xpathString;
|
|
|
- xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]/"
|
|
|
- + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + configProperty.getKey() + "\"]";
|
|
|
- return xpfac.compile(xpathString);
|
|
|
- }
|
|
|
-
|
|
|
- private static XPathExpression xpathForConfigProperties() {
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- final String xpathString;
|
|
|
- xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]";
|
|
|
- return xpfac.compile(xpathString);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private static class ConfigurationCleaner {
|
|
|
- private static void cleanup(final StoredConfiguration configuration) throws PwmUnrecoverableException {
|
|
|
- updateProperitiesWithoutType(configuration);
|
|
|
- updateMandatoryElements(configuration.document);
|
|
|
- profilizeNonProfiledSettings(configuration);
|
|
|
- stripOrphanedProfileSettings(configuration);
|
|
|
- migrateAppProperties(configuration);
|
|
|
- updateDeprecatedSettings(configuration);
|
|
|
- }
|
|
|
-
|
|
|
- private static void updateMandatoryElements(final Document document) {
|
|
|
- final Element rootElement = document.getRootElement();
|
|
|
-
|
|
|
- {
|
|
|
- final XPathExpression commentXPath = XPathFactory.instance().compile("//comment()[1]");
|
|
|
- final Comment existingComment = (Comment)commentXPath.evaluateFirst(rootElement);
|
|
|
- if (existingComment != null) {
|
|
|
- existingComment.detach();
|
|
|
- }
|
|
|
- final Comment comment = new Comment(generateCommentText());
|
|
|
- rootElement.addContent(0,comment);
|
|
|
- }
|
|
|
-
|
|
|
- rootElement.setAttribute("pwmVersion", PwmConstants.BUILD_VERSION);
|
|
|
- rootElement.setAttribute("pwmBuild", PwmConstants.BUILD_NUMBER);
|
|
|
- rootElement.setAttribute("pwmBuildType", PwmConstants.BUILD_TYPE);
|
|
|
- rootElement.setAttribute("xmlVersion", XML_FORMAT_VERSION);
|
|
|
-
|
|
|
- { // migrate old properties
|
|
|
-
|
|
|
- // read correct (new) //properties[@type="config"]
|
|
|
- final XPathExpression configPropertiesXpath = XPathFactory.instance().compile(
|
|
|
- "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]");
|
|
|
- final Element configPropertiesElement = (Element)configPropertiesXpath.evaluateFirst(rootElement);
|
|
|
-
|
|
|
- // read list of old //properties[not (@type)]/property
|
|
|
- final XPathExpression nonAttributedProperty = XPathFactory.instance().compile(
|
|
|
- "//" + XML_ELEMENT_PROPERTIES + "[not (@" + XML_ATTRIBUTE_TYPE + ")]/" + XML_ELEMENT_PROPERTY);
|
|
|
- final List<Element> nonAttributedProperties = nonAttributedProperty.evaluate(rootElement);
|
|
|
-
|
|
|
- if (configPropertiesElement != null && nonAttributedProperties != null) {
|
|
|
- for (Element element : nonAttributedProperties) {
|
|
|
- element.detach();
|
|
|
- configPropertiesElement.addContent(element);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // remove old //properties[not (@type] element
|
|
|
- final XPathExpression oldPropertiesXpath = XPathFactory.instance().compile(
|
|
|
- "//" + XML_ELEMENT_PROPERTIES + "[not (@" + XML_ATTRIBUTE_TYPE + ")]");
|
|
|
- final List<Element> oldPropertiesElements = oldPropertiesXpath.evaluate(rootElement);
|
|
|
- if (oldPropertiesElements != null) {
|
|
|
- for (Element element : oldPropertiesElements) {
|
|
|
- element.detach();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static String generateCommentText() {
|
|
|
- final StringBuilder commentText = new StringBuilder();
|
|
|
- commentText.append("\t\t").append(" ").append("\n");
|
|
|
- commentText.append("\t\t").append("This configuration file has been auto-generated by the ").append(PwmConstants.PWM_APP_NAME).append(" password self service application.").append("\n");
|
|
|
- commentText.append("\t\t").append("").append("\n");
|
|
|
- commentText.append("\t\t").append("WARNING: This configuration file contains sensitive security information, please handle with care!").append("\n");
|
|
|
- commentText.append("\t\t").append("").append("\n");
|
|
|
- commentText.append("\t\t").append("WARNING: If a server is currently running using this configuration file, it will be restarted").append("\n");
|
|
|
- commentText.append("\t\t").append(" and the configuration updated immediately when it is modified.").append("\n");
|
|
|
- commentText.append("\t\t").append("").append("\n");
|
|
|
- commentText.append("\t\t").append("NOTICE: This file is encoded as UTF-8. Do not save or edit this file with an editor that does not").append("\n");
|
|
|
- commentText.append("\t\t").append(" support UTF-8 encoding.").append("\n");
|
|
|
- commentText.append("\t\t").append("").append("\n");
|
|
|
- commentText.append("\t\t").append("If unable to edit using the application ConfigurationEditor web UI, the following options are available.").append("\n");
|
|
|
- commentText.append("\t\t").append(" or 1. Edit this file directly by hand.").append("\n");
|
|
|
- commentText.append("\t\t").append(" or 2. Unlock the configuration by setting the property 'configIsEditable' to 'true' in this file. This will ").append("\n");
|
|
|
- commentText.append("\t\t").append(" allow access to the ConfigurationEditor web UI without having to authenticate to an LDAP server first.").append("\n");
|
|
|
- commentText.append("\t\t").append(" or 3. Unlock the configuration by using the the command line utility. ").append("\n");
|
|
|
- commentText.append("\t\t").append("").append("\n");
|
|
|
- return commentText.toString();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private static void profilizeNonProfiledSettings(final StoredConfiguration storedConfiguration) throws PwmUnrecoverableException {
|
|
|
- final String NEW_PROFILE_NAME = "default";
|
|
|
- final Document document = storedConfiguration.document;
|
|
|
- for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
- if (setting.getCategory().hasProfiles()) {
|
|
|
-
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForSetting(setting, null);
|
|
|
- final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
- if (settingElement != null) {
|
|
|
- LOGGER.info("moving setting " + setting.getKey() + " without profile attribute to profile \"" + NEW_PROFILE_NAME + "\".");
|
|
|
- // change setting to "default" profile.
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_PROFILE, NEW_PROFILE_NAME);
|
|
|
-
|
|
|
- final PwmSetting profileSetting = setting.getCategory().getProfileSetting();
|
|
|
- final List<String> profileStringDefinitions = new ArrayList<>();
|
|
|
- {
|
|
|
- final StringArrayValue profileDefinitions = (StringArrayValue) storedConfiguration.readSetting(profileSetting);
|
|
|
- if (profileDefinitions != null) {
|
|
|
- if (profileDefinitions.toNativeObject() != null) {
|
|
|
- profileStringDefinitions.addAll(profileDefinitions.toNativeObject());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!profileStringDefinitions.contains(NEW_PROFILE_NAME)) {
|
|
|
- profileStringDefinitions.add(NEW_PROFILE_NAME);
|
|
|
- storedConfiguration.writeSetting(profileSetting,new StringArrayValue(profileStringDefinitions),null);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void updateProperitiesWithoutType(final StoredConfiguration storedConfiguration) {
|
|
|
- final Document document = storedConfiguration.document;
|
|
|
- final String xpathString = "//properties[not(@type)]";
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- final XPathExpression xp = xpfac.compile(xpathString);
|
|
|
- final List<Element> propertiesElements = (List<Element>)xp.evaluate(document);
|
|
|
- for (final Element propertiesElement : propertiesElements) {
|
|
|
- propertiesElement.setAttribute(XML_ATTRIBUTE_TYPE,XML_ATTRIBUTE_VALUE_CONFIG);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void stripOrphanedProfileSettings(final StoredConfiguration storedConfiguration) {
|
|
|
- final Document document = storedConfiguration.document;
|
|
|
- final XPathFactory xpfac = XPathFactory.instance();
|
|
|
- for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
- if (setting.getCategory().hasProfiles()) {
|
|
|
- final List<String> validProfiles = storedConfiguration.profilesForSetting(setting);
|
|
|
- final String xpathString = "//setting[@key=\"" + setting.getKey() + "\"]";
|
|
|
- final XPathExpression xp = xpfac.compile(xpathString);
|
|
|
- final List<Element> settingElements = (List<Element>)xp.evaluate(document);
|
|
|
- for (final Element settingElement : settingElements) {
|
|
|
- final String profileID = settingElement.getAttributeValue(XML_ATTRIBUTE_PROFILE);
|
|
|
- if (profileID != null) {
|
|
|
- if (!validProfiles.contains(profileID)) {
|
|
|
- LOGGER.info("removing setting " + setting.getKey() + " with profile \"" + profileID + "\", profile is not a valid profile");
|
|
|
- settingElement.detach();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void migrateAppProperties(final StoredConfiguration storedConfiguration) throws PwmUnrecoverableException {
|
|
|
- final Document document = storedConfiguration.document;
|
|
|
- final XPathExpression xPathExpression = XPathBuilder.xpathForAppProperties();
|
|
|
- final List<Element> appPropertiesElements = (List<Element>)xPathExpression.evaluate(document);
|
|
|
- for (final Element element : appPropertiesElements) {
|
|
|
- final List<Element> properties = element.getChildren();
|
|
|
- for (final Element property : properties) {
|
|
|
- final String key = property.getAttributeValue("key");
|
|
|
- final String value = property.getText();
|
|
|
- if (key != null && !key.isEmpty() && value != null && !value.isEmpty()) {
|
|
|
- LOGGER.info("migrating app-property config element '" + key + "' to setting " + PwmSetting.APP_PROPERTY_OVERRIDES.getKey());
|
|
|
- final String newValue = key + "=" + value;
|
|
|
- List<String> existingValues = (List<String>)storedConfiguration.readSetting(PwmSetting.APP_PROPERTY_OVERRIDES).toNativeObject();
|
|
|
- if (existingValues == null) {
|
|
|
- existingValues = new ArrayList<>();
|
|
|
- }
|
|
|
- existingValues = new ArrayList<>(existingValues);
|
|
|
- existingValues.add(newValue);
|
|
|
- storedConfiguration.writeSetting(PwmSetting.APP_PROPERTY_OVERRIDES,new StringArrayValue(existingValues),null);
|
|
|
- }
|
|
|
- }
|
|
|
- element.detach();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void updateDeprecatedSettings(final StoredConfiguration storedConfiguration) throws PwmUnrecoverableException {
|
|
|
- final UserIdentity actor = new UserIdentity("UpgradeProcessor", null);
|
|
|
- for (final String profileID : storedConfiguration.profilesForSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY)) {
|
|
|
- if (!storedConfiguration.isDefaultValue(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY, profileID)) {
|
|
|
- boolean ad2003Enabled = (boolean) storedConfiguration.readSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY,profileID).toNativeObject();
|
|
|
- final StoredValue value;
|
|
|
- if (ad2003Enabled) {
|
|
|
- value = new StringValue(ADPolicyComplexity.AD2003.toString());
|
|
|
- } else {
|
|
|
- value = new StringValue(ADPolicyComplexity.NONE.toString());
|
|
|
- }
|
|
|
- LOGGER.warn("converting deprecated non-default setting " + PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY.getKey() + "/" + profileID
|
|
|
- + " to replacement setting " + PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY_LEVEL + ", value=" + value.toNativeObject().toString());
|
|
|
- storedConfiguration.writeSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY_LEVEL, profileID, value, actor);
|
|
|
- storedConfiguration.resetSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY, profileID, actor);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- {
|
|
|
- if (!storedConfiguration.isDefaultValue(PwmSetting.CHALLENGE_REQUIRE_RESPONSES)) {
|
|
|
- final StoredValue configValue = storedConfiguration.readSetting(PwmSetting.RECOVERY_VERIFICATION_METHODS, "default");
|
|
|
- final VerificationMethodValue.VerificationMethodSettings existingSettings = (VerificationMethodValue.VerificationMethodSettings)configValue.toNativeObject();
|
|
|
- final Map<RecoveryVerificationMethod,VerificationMethodValue.VerificationMethodSetting> newMethods = new HashMap<>();
|
|
|
- newMethods.putAll(existingSettings.getMethodSettings());
|
|
|
- VerificationMethodValue.VerificationMethodSetting setting = new VerificationMethodValue.VerificationMethodSetting(VerificationMethodValue.EnabledState.disabled);
|
|
|
- newMethods.put(RecoveryVerificationMethod.CHALLENGE_RESPONSES,setting);
|
|
|
- final VerificationMethodValue.VerificationMethodSettings newSettings = new VerificationMethodValue.VerificationMethodSettings(
|
|
|
- newMethods,
|
|
|
- existingSettings.getMinOptionalRequired()
|
|
|
- );
|
|
|
- storedConfiguration.writeSetting(PwmSetting.RECOVERY_VERIFICATION_METHODS, "default", new VerificationMethodValue(newSettings), actor);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- {
|
|
|
- if (!storedConfiguration.isDefaultValue(PwmSetting.FORGOTTEN_PASSWORD_REQUIRE_OTP)) {
|
|
|
- final StoredValue configValue = storedConfiguration.readSetting(PwmSetting.RECOVERY_VERIFICATION_METHODS, "default");
|
|
|
- final VerificationMethodValue.VerificationMethodSettings existingSettings = (VerificationMethodValue.VerificationMethodSettings)configValue.toNativeObject();
|
|
|
- final Map<RecoveryVerificationMethod,VerificationMethodValue.VerificationMethodSetting> newMethods = new HashMap<>();
|
|
|
- newMethods.putAll(existingSettings.getMethodSettings());
|
|
|
- VerificationMethodValue.VerificationMethodSetting setting = new VerificationMethodValue.VerificationMethodSetting(VerificationMethodValue.EnabledState.required);
|
|
|
- newMethods.put(RecoveryVerificationMethod.CHALLENGE_RESPONSES,setting);
|
|
|
- final VerificationMethodValue.VerificationMethodSettings newSettings = new VerificationMethodValue.VerificationMethodSettings(
|
|
|
- newMethods,
|
|
|
- existingSettings.getMinOptionalRequired()
|
|
|
- );
|
|
|
- storedConfiguration.writeSetting(PwmSetting.FORGOTTEN_PASSWORD_REQUIRE_OTP, "default", new VerificationMethodValue(newSettings), actor);
|
|
|
- }
|
|
|
- }
|
|
|
- */
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public static class ConfigRecordID implements Serializable {
|
|
|
- private RecordType recordType;
|
|
|
- private Object recordID;
|
|
|
- private String profileID;
|
|
|
-
|
|
|
- public enum RecordType {
|
|
|
- SETTING,
|
|
|
- LOCALE_BUNDLE,
|
|
|
- }
|
|
|
-
|
|
|
- public ConfigRecordID(
|
|
|
- RecordType recordType,
|
|
|
- Object recordID,
|
|
|
- String profileID
|
|
|
- )
|
|
|
- {
|
|
|
- this.recordType = recordType;
|
|
|
- this.recordID = recordID;
|
|
|
- this.profileID = profileID;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean equals(Object o)
|
|
|
- {
|
|
|
- if (this == o) return true;
|
|
|
- if (o == null || getClass() != o.getClass()) return false;
|
|
|
-
|
|
|
- ConfigRecordID that = (ConfigRecordID) o;
|
|
|
-
|
|
|
- if (profileID != null ? !profileID.equals(that.profileID) : that.profileID != null) return false;
|
|
|
- if (recordID != null ? !recordID.equals(that.recordID) : that.recordID != null) return false;
|
|
|
- if (recordType != that.recordType) return false;
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int hashCode()
|
|
|
- {
|
|
|
- int result = recordType != null ? recordType.hashCode() : 0;
|
|
|
- result = 31 * result + (recordID != null ? recordID.hashCode() : 0);
|
|
|
- result = 31 * result + (profileID != null ? profileID.hashCode() : 0);
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- public RecordType getRecordType()
|
|
|
- {
|
|
|
- return recordType;
|
|
|
- }
|
|
|
-
|
|
|
- public Object getRecordID()
|
|
|
- {
|
|
|
- return recordID;
|
|
|
- }
|
|
|
-
|
|
|
- public String getProfileID()
|
|
|
- {
|
|
|
- return profileID;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public String changeLogAsDebugString(final Locale locale, final boolean asHtml) {
|
|
|
- return changeLog.changeLogAsDebugString(locale, asHtml);
|
|
|
- }
|
|
|
-
|
|
|
- private PwmSecurityKey cachedKey = null;
|
|
|
- public PwmSecurityKey getKey() throws PwmUnrecoverableException {
|
|
|
- if (cachedKey == null) {
|
|
|
- cachedKey = new PwmSecurityKey(createTime() + StoredConfiguration.class.getSimpleName());
|
|
|
- }
|
|
|
- return cachedKey;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isModified() {
|
|
|
- return changeLog.isModified();
|
|
|
- }
|
|
|
-
|
|
|
- private class ChangeLog implements Serializable {
|
|
|
- /* values contain the _original_ toJson version of the value. */
|
|
|
- private Map<ConfigRecordID,String> changeLog = new LinkedHashMap<>();
|
|
|
-
|
|
|
- public boolean isModified() {
|
|
|
- return !changeLog.isEmpty();
|
|
|
- }
|
|
|
-
|
|
|
- public String changeLogAsDebugString(final Locale locale, boolean asHtml) {
|
|
|
- final Map<String,String> outputMap = new TreeMap<>();
|
|
|
- final String SEPARATOR = LocaleHelper.getLocalizedMessage(locale, Config.Display_SettingNavigationSeparator, null);
|
|
|
-
|
|
|
- for (final ConfigRecordID configRecordID : changeLog.keySet()) {
|
|
|
- switch (configRecordID.recordType) {
|
|
|
- case SETTING: {
|
|
|
- final StoredValue currentValue = readSetting((PwmSetting) configRecordID.recordID, configRecordID.profileID);
|
|
|
- final PwmSetting pwmSetting = (PwmSetting) configRecordID.recordID;
|
|
|
- final String keyName = pwmSetting.toMenuLocationDebug(configRecordID.getProfileID(), locale);
|
|
|
- final String debugValue = currentValue.toDebugString(locale);
|
|
|
- outputMap.put(keyName,debugValue);
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case LOCALE_BUNDLE: {
|
|
|
- final String key = (String) configRecordID.recordID;
|
|
|
- final String bundleName = key.split("!")[0];
|
|
|
- final String keys = key.split("!")[1];
|
|
|
- final Map<String,String> currentValue = readLocaleBundleMap(bundleName,keys);
|
|
|
- final String debugValue = JsonUtil.serializeMap(currentValue, JsonUtil.Flag.PrettyPrint);
|
|
|
- outputMap.put("LocaleBundle" + SEPARATOR + bundleName + " " + keys,debugValue);
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- final StringBuilder output = new StringBuilder();
|
|
|
- if (outputMap.isEmpty()) {
|
|
|
- output.append("No setting changes.");
|
|
|
- } else {
|
|
|
- for (final String keyName : outputMap.keySet()) {
|
|
|
- final String value = outputMap.get(keyName);
|
|
|
- if (asHtml) {
|
|
|
- output.append("<div class=\"changeLogKey\">");
|
|
|
- output.append(keyName);
|
|
|
- output.append("</div><div class=\"changeLogValue\">");
|
|
|
- output.append(StringUtil.escapeHtml(value));
|
|
|
- output.append("</div>");
|
|
|
- } else {
|
|
|
- output.append(keyName);
|
|
|
- output.append("\n");
|
|
|
- output.append(" Value: ");
|
|
|
- output.append(value);
|
|
|
- output.append("\n");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return output.toString();
|
|
|
- }
|
|
|
-
|
|
|
- public void updateChangeLog(final String bundleName, final String keyName, final Map<String,String> localeValueMap) {
|
|
|
- final String key = bundleName + "!" + keyName;
|
|
|
- final Map<String,String> currentValue = readLocaleBundleMap(bundleName, keyName);
|
|
|
- final String currentJsonValue = JsonUtil.serializeMap(currentValue);
|
|
|
- final String newJsonValue = JsonUtil.serializeMap(localeValueMap);
|
|
|
- final ConfigRecordID configRecordID = new ConfigRecordID(ConfigRecordID.RecordType.LOCALE_BUNDLE, key, null);
|
|
|
- updateChangeLog(configRecordID,currentJsonValue,newJsonValue);
|
|
|
- }
|
|
|
-
|
|
|
- public void updateChangeLog(final PwmSetting setting, final String profileID, final StoredValue newValue) {
|
|
|
- final StoredValue currentValue = readSetting(setting, profileID);
|
|
|
- final String currentJsonValue = JsonUtil.serialize(currentValue);
|
|
|
- final String newJsonValue = JsonUtil.serialize(newValue);
|
|
|
- final ConfigRecordID configRecordID = new ConfigRecordID(ConfigRecordID.RecordType.SETTING, setting, profileID);
|
|
|
- updateChangeLog(configRecordID,currentJsonValue,newJsonValue);
|
|
|
- }
|
|
|
-
|
|
|
- public void updateChangeLog(final ConfigRecordID configRecordID, final String currentValueString, final String newValueString) {
|
|
|
- if (changeLog.containsKey(configRecordID)) {
|
|
|
- final String currentRecord = changeLog.get(configRecordID);
|
|
|
-
|
|
|
- if (currentRecord == null && newValueString == null) {
|
|
|
- changeLog.remove(configRecordID);
|
|
|
- } else if (currentRecord != null && currentRecord.equals(newValueString)) {
|
|
|
- changeLog.remove(configRecordID);
|
|
|
- }
|
|
|
- } else {
|
|
|
- changeLog.put(configRecordID,currentValueString);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public static void validateXmlSchema(final String xmlDocument)
|
|
|
- throws PwmUnrecoverableException
|
|
|
- {
|
|
|
- return;
|
|
|
- /*
|
|
|
- try {
|
|
|
- final InputStream xsdInputStream = PwmSetting.class.getClassLoader().getResourceAsStream("password/pwm/config/StoredConfiguration.xsd");
|
|
|
- final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
|
|
- final Schema schema = factory.newSchema(new StreamSource(xsdInputStream));
|
|
|
- Validator validator = schema.newValidator();
|
|
|
- validator.validate(new StreamSource(new StringReader(xmlDocument)));
|
|
|
- } catch (Exception e) {
|
|
|
- final String errorMsg = "error while validating setting file schema definition: " + e.getMessage();
|
|
|
- throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg));
|
|
|
- }
|
|
|
- */
|
|
|
- }
|
|
|
-
|
|
|
- private static void updateMetaData(final Element settingElement, final UserIdentity userIdentity) {
|
|
|
- final Element settingsElement = settingElement.getDocument().getRootElement().getChild(XML_ELEMENT_SETTINGS);
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
- settingsElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
- settingElement.removeAttribute(XML_ATTRIBUTE_MODIFY_USER);
|
|
|
- settingsElement.removeAttribute(XML_ATTRIBUTE_MODIFY_USER);
|
|
|
- if (userIdentity != null) {
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_MODIFY_USER, userIdentity.toDelimitedKey());
|
|
|
- settingsElement.setAttribute(XML_ATTRIBUTE_MODIFY_USER, userIdentity.toDelimitedKey());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static Element createOrGetSettingElement(
|
|
|
- final Document document,
|
|
|
- final PwmSetting setting,
|
|
|
- final String profileID
|
|
|
- ) {
|
|
|
- final XPathExpression xp = XPathBuilder.xpathForSetting(setting, profileID);
|
|
|
- final Element existingSettingElement = (Element)xp.evaluateFirst(document);
|
|
|
- if (existingSettingElement != null) {
|
|
|
- return existingSettingElement;
|
|
|
- }
|
|
|
-
|
|
|
- final Element settingElement = new Element(XML_ELEMENT_SETTING);
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_KEY, setting.getKey());
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString());
|
|
|
- if (profileID != null && profileID.length() > 0) {
|
|
|
- settingElement.setAttribute(XML_ATTRIBUTE_PROFILE, profileID);
|
|
|
- }
|
|
|
-
|
|
|
- Element settingsElement = document.getRootElement().getChild(XML_ELEMENT_SETTINGS);
|
|
|
- if (settingsElement == null) {
|
|
|
- settingsElement = new Element(XML_ELEMENT_SETTINGS);
|
|
|
- document.getRootElement().addContent(settingsElement);
|
|
|
- }
|
|
|
- settingsElement.addContent(settingElement);
|
|
|
-
|
|
|
- return settingElement;
|
|
|
- }
|
|
|
-
|
|
|
- static class SettingValueRecord implements Serializable {
|
|
|
- private PwmSetting setting;
|
|
|
- private String profile;
|
|
|
- private StoredValue storedValue;
|
|
|
-
|
|
|
- public SettingValueRecord(
|
|
|
- PwmSetting setting,
|
|
|
- String profile,
|
|
|
- StoredValue storedValue
|
|
|
- )
|
|
|
- {
|
|
|
- this.setting = setting;
|
|
|
- this.profile = profile;
|
|
|
- this.storedValue = storedValue;
|
|
|
- }
|
|
|
-
|
|
|
- public PwmSetting getSetting()
|
|
|
- {
|
|
|
- return setting;
|
|
|
- }
|
|
|
-
|
|
|
- public String getProfile()
|
|
|
- {
|
|
|
- return profile;
|
|
|
- }
|
|
|
-
|
|
|
- public StoredValue getStoredValue()
|
|
|
- {
|
|
|
- return storedValue;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- class StoredValueIterator implements Iterator<StoredConfiguration.SettingValueRecord> {
|
|
|
-
|
|
|
- private Queue<SettingValueRecord> settingQueue = new LinkedList<>();
|
|
|
-
|
|
|
- public StoredValueIterator(boolean includeDefaults) {
|
|
|
- for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
- if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
- if (includeDefaults || !isDefaultValue(setting)) {
|
|
|
- SettingValueRecord settingValueRecord = new SettingValueRecord(setting, null, null);
|
|
|
- settingQueue.add(settingValueRecord);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
- if (category.hasProfiles()) {
|
|
|
- for (final String profileID : profilesForSetting(category.getProfileSetting())) {
|
|
|
- for (final PwmSetting setting : category.getSettings()) {
|
|
|
- if (includeDefaults || !isDefaultValue(setting,profileID)) {
|
|
|
- SettingValueRecord settingValueRecord = new SettingValueRecord(setting, profileID, null);
|
|
|
- settingQueue.add(settingValueRecord);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean hasNext()
|
|
|
- {
|
|
|
- return !settingQueue.isEmpty();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public SettingValueRecord next()
|
|
|
- {
|
|
|
- StoredConfiguration.SettingValueRecord settingValueRecord = settingQueue.poll();
|
|
|
- return new SettingValueRecord(
|
|
|
- settingValueRecord.getSetting(),
|
|
|
- settingValueRecord.getProfile(),
|
|
|
- readSetting(settingValueRecord.getSetting(),settingValueRecord.getProfile())
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void remove()
|
|
|
- {
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private String createTime() {
|
|
|
- final Element rootElement = document.getRootElement();
|
|
|
- final String createTimeString = rootElement.getAttributeValue(XML_ATTRIBUTE_CREATE_TIME);
|
|
|
- if (createTimeString == null || createTimeString.isEmpty()) {
|
|
|
- throw new IllegalStateException("missing createTime timestamp");
|
|
|
- }
|
|
|
- return createTimeString;
|
|
|
- }
|
|
|
-
|
|
|
- public Date modifyTime() {
|
|
|
- final Element rootElement = document.getRootElement();
|
|
|
- final String modifyTimeString = rootElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_TIME);
|
|
|
- if (modifyTimeString != null) {
|
|
|
- try {
|
|
|
- return PwmConstants.DEFAULT_DATETIME_FORMAT.parse(modifyTimeString);
|
|
|
- } catch (ParseException e) {
|
|
|
- LOGGER.error("error parsing root last modified timestamp: " + e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- public void initNewRandomSecurityKey()
|
|
|
- throws PwmUnrecoverableException
|
|
|
- {
|
|
|
- if (!isDefaultValue(PwmSetting.PWM_SECURITY_KEY)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- writeSetting(
|
|
|
- PwmSetting.PWM_SECURITY_KEY,
|
|
|
- new PasswordValue(new PasswordData(PwmRandom.getInstance().alphaNumericString(1024))),
|
|
|
- null
|
|
|
- );
|
|
|
-
|
|
|
- LOGGER.debug("initialized new random security key");
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
+/*
|
|
|
+ * Password Management Servlets (PWM)
|
|
|
+ * http://code.google.com/p/pwm/
|
|
|
+ *
|
|
|
+ * Copyright (c) 2006-2009 Novell, Inc.
|
|
|
+ * Copyright (c) 2009-2015 The PWM Project
|
|
|
+ *
|
|
|
+ * 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; either version 2 of the License, or
|
|
|
+ * (at your option) any later version.
|
|
|
+ *
|
|
|
+ * 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, write to the Free Software
|
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
+ */
|
|
|
+
|
|
|
+package password.pwm.config.stored;
|
|
|
+
|
|
|
+import org.jdom2.*;
|
|
|
+import org.jdom2.xpath.XPathExpression;
|
|
|
+import org.jdom2.xpath.XPathFactory;
|
|
|
+import password.pwm.AppProperty;
|
|
|
+import password.pwm.PwmConstants;
|
|
|
+import password.pwm.bean.UserIdentity;
|
|
|
+import password.pwm.config.*;
|
|
|
+import password.pwm.config.option.ADPolicyComplexity;
|
|
|
+import password.pwm.config.value.*;
|
|
|
+import password.pwm.error.*;
|
|
|
+import password.pwm.i18n.Config;
|
|
|
+import password.pwm.i18n.LocaleHelper;
|
|
|
+import password.pwm.i18n.PwmLocaleBundle;
|
|
|
+import password.pwm.util.*;
|
|
|
+import password.pwm.util.logging.PwmLogger;
|
|
|
+import password.pwm.util.secure.PwmRandom;
|
|
|
+import password.pwm.util.secure.PwmSecurityKey;
|
|
|
+import password.pwm.util.secure.SecureEngine;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.OutputStream;
|
|
|
+import java.io.Serializable;
|
|
|
+import java.text.ParseException;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author Jason D. Rivard
|
|
|
+ */
|
|
|
+public class StoredConfigurationImpl implements Serializable, StoredConfiguration {
|
|
|
+// ------------------------------ FIELDS ------------------------------
|
|
|
+
|
|
|
+ public enum ConfigProperty {
|
|
|
+ PROPERTY_KEY_CONFIG_IS_EDITABLE("configIsEditable"),
|
|
|
+ PROPERTY_KEY_CONFIG_EPOCH("configEpoch"),
|
|
|
+ PROPERTY_KEY_TEMPLATE("configTemplate"),
|
|
|
+ PROPERTY_KEY_NOTES("notes"),
|
|
|
+ PROPERTY_KEY_PASSWORD_HASH("configPasswordHash"),
|
|
|
+ PROPERTY_KEY_SAVE_CONFIG_ON_START("saveConfigOnStart"),
|
|
|
+ ;
|
|
|
+
|
|
|
+ private final String key;
|
|
|
+
|
|
|
+ private ConfigProperty(String key)
|
|
|
+ {
|
|
|
+ this.key = key;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getKey()
|
|
|
+ {
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class SettingMetaData implements Serializable {
|
|
|
+ private Date modifyDate;
|
|
|
+ private UserIdentity userIdentity;
|
|
|
+
|
|
|
+ public Date getModifyDate()
|
|
|
+ {
|
|
|
+ return modifyDate;
|
|
|
+ }
|
|
|
+
|
|
|
+ public UserIdentity getUserIdentity()
|
|
|
+ {
|
|
|
+ return userIdentity;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final PwmLogger LOGGER = PwmLogger.forClass(StoredConfigurationImpl.class);
|
|
|
+ private static final String XML_FORMAT_VERSION = "4";
|
|
|
+
|
|
|
+ public static final String XML_ELEMENT_ROOT = "PwmConfiguration";
|
|
|
+ public static final String XML_ELEMENT_PROPERTIES = "properties";
|
|
|
+ public static final String XML_ELEMENT_PROPERTY = "property";
|
|
|
+ public static final String XML_ELEMENT_SETTINGS = "settings";
|
|
|
+ public static final String XML_ELEMENT_SETTING = "setting";
|
|
|
+ public static final String XML_ELEMENT_DEFAULT = "default";
|
|
|
+
|
|
|
+ public static final String XML_ATTRIBUTE_TYPE = "type";
|
|
|
+ public static final String XML_ATTRIBUTE_KEY = "key";
|
|
|
+ public static final String XML_ATTRIBUTE_SYNTAX = "syntax";
|
|
|
+ public static final String XML_ATTRIBUTE_PROFILE = "profile";
|
|
|
+ public static final String XML_ATTRIBUTE_VALUE_APP = "app";
|
|
|
+ public static final String XML_ATTRIBUTE_VALUE_CONFIG = "config";
|
|
|
+ public static final String XML_ATTRIBUTE_CREATE_TIME = "createTime";
|
|
|
+ public static final String XML_ATTRIBUTE_MODIFY_TIME = "modifyTime";
|
|
|
+ public static final String XML_ATTRIBUTE_MODIFY_USER = "modifyUser";
|
|
|
+ public static final String XML_ATTRIBUTE_SYNTAX_VERSION = "syntaxVersion";
|
|
|
+
|
|
|
+ private Document document = new Document(new Element(XML_ELEMENT_ROOT));
|
|
|
+ private ChangeLog changeLog = new ChangeLog();
|
|
|
+
|
|
|
+ private boolean locked = false;
|
|
|
+ private boolean setting_writeLabels = true;
|
|
|
+ private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
|
|
|
+
|
|
|
+// -------------------------- STATIC METHODS --------------------------
|
|
|
+
|
|
|
+ public static StoredConfigurationImpl newStoredConfiguration() throws PwmUnrecoverableException {
|
|
|
+ return new StoredConfigurationImpl();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static StoredConfigurationImpl copy(final StoredConfigurationImpl input) throws PwmUnrecoverableException {
|
|
|
+ final StoredConfigurationImpl copy = new StoredConfigurationImpl();
|
|
|
+ copy.document = input.document.clone();
|
|
|
+ return copy;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static StoredConfigurationImpl fromXml(final InputStream xmlData)
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ final Date startTime = new Date();
|
|
|
+ //validateXmlSchema(xmlData);
|
|
|
+
|
|
|
+ final Document inputDocument = XmlUtil.parseXml(xmlData);
|
|
|
+ final StoredConfigurationImpl newConfiguration = StoredConfigurationImpl.newStoredConfiguration();
|
|
|
+
|
|
|
+ try {
|
|
|
+ newConfiguration.document = inputDocument;
|
|
|
+ newConfiguration.createTime(); // verify create time;
|
|
|
+ ConfigurationCleaner.cleanup(newConfiguration);
|
|
|
+ } catch (Exception e) {
|
|
|
+ final String errorMsg = "error reading configuration file format, error=" + e.getMessage();
|
|
|
+ final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorMsg});
|
|
|
+ throw new PwmUnrecoverableException(errorInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ checkIfXmlRequiresUpdate(newConfiguration);
|
|
|
+ final TimeDuration totalDuration = TimeDuration.fromCurrent(startTime);
|
|
|
+ LOGGER.debug("successfully loaded configuration (" + totalDuration.asCompactString() + ")");
|
|
|
+ return newConfiguration;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Loop through all settings to see if setting value has flag {@link StoredValue#requiresStoredUpdate()} set to true.
|
|
|
+ * If so, then call {@link #writeSetting(PwmSetting, StoredValue, password.pwm.bean.UserIdentity)} or {@link #writeSetting(PwmSetting, String, StoredValue, password.pwm.bean.UserIdentity)}
|
|
|
+ * for that value so that the xml dom can be updated.
|
|
|
+ * @param storedConfiguration stored configuration to check
|
|
|
+ */
|
|
|
+ private static void checkIfXmlRequiresUpdate(final StoredConfigurationImpl storedConfiguration) throws PwmUnrecoverableException {
|
|
|
+ for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
+ if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
+ final StoredValue value = storedConfiguration.readSetting(setting);
|
|
|
+ if (value.requiresStoredUpdate()) {
|
|
|
+ storedConfiguration.writeSetting(setting, value, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
+ if (category.hasProfiles()) {
|
|
|
+ for (final String profileID : storedConfiguration.profilesForSetting(category.getProfileSetting())) {
|
|
|
+ for (final PwmSetting profileSetting : category.getSettings()) {
|
|
|
+ final StoredValue value = storedConfiguration.readSetting(profileSetting, profileID);
|
|
|
+ if (value.requiresStoredUpdate()) {
|
|
|
+ storedConfiguration.writeSetting(profileSetting, profileID, value, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void resetAllPasswordValues(final String comment) {
|
|
|
+ for (final Iterator<SettingValueRecord> settingValueRecordIterator = new StoredValueIterator(false); settingValueRecordIterator.hasNext();) {
|
|
|
+ final SettingValueRecord settingValueRecord = settingValueRecordIterator.next();
|
|
|
+ if (settingValueRecord.getSetting().getSyntax() == PwmSettingSyntax.PASSWORD) {
|
|
|
+ this.resetSetting(settingValueRecord.getSetting(),settingValueRecord.getProfile(),null);
|
|
|
+ if (comment != null && !comment.isEmpty()) {
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForSetting(settingValueRecord.getSetting(), settingValueRecord.getProfile());
|
|
|
+ final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
+ if (settingElement != null) {
|
|
|
+ settingElement.addContent(new Comment(comment));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public StoredConfigurationImpl() throws PwmUnrecoverableException {
|
|
|
+ ConfigurationCleaner.cleanup(this);
|
|
|
+ final String createTime = PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date());
|
|
|
+ document.getRootElement().setAttribute(XML_ATTRIBUTE_CREATE_TIME,createTime);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public String readConfigProperty(final ConfigProperty propertyName) {
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForConfigProperty(propertyName);
|
|
|
+ final Element propertyElement = (Element)xp.evaluateFirst(document);
|
|
|
+ return propertyElement == null ? null : propertyElement.getText();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void writeConfigProperty(
|
|
|
+ final ConfigProperty propertyName,
|
|
|
+ final String value
|
|
|
+ ) {
|
|
|
+ domModifyLock.writeLock().lock();
|
|
|
+ try {
|
|
|
+
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForConfigProperty(propertyName);
|
|
|
+ final List<Element> propertyElements = xp.evaluate(document);
|
|
|
+ for (final Element propertyElement : propertyElements) {
|
|
|
+ propertyElement.detach();
|
|
|
+ }
|
|
|
+
|
|
|
+ final Element propertyElement = new Element(XML_ELEMENT_PROPERTY);
|
|
|
+ propertyElement.setAttribute(new Attribute(XML_ATTRIBUTE_KEY,propertyName.getKey()));
|
|
|
+ propertyElement.setContent(new Text(value));
|
|
|
+
|
|
|
+ if (null == XPathBuilder.xpathForConfigProperties().evaluateFirst(document)) {
|
|
|
+ Element configProperties = new Element(XML_ELEMENT_PROPERTIES);
|
|
|
+ configProperties.setAttribute(new Attribute(XML_ATTRIBUTE_TYPE,XML_ATTRIBUTE_VALUE_CONFIG));
|
|
|
+ document.getRootElement().addContent(configProperties);
|
|
|
+ }
|
|
|
+
|
|
|
+ final XPathExpression xp2 = XPathBuilder.xpathForConfigProperties();
|
|
|
+ final Element propertiesElement = (Element)xp2.evaluateFirst(document);
|
|
|
+ propertyElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
+ propertiesElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
+ propertiesElement.addContent(propertyElement);
|
|
|
+ } finally {
|
|
|
+ domModifyLock.writeLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void lock() {
|
|
|
+ locked = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Map<String,String> readLocaleBundleMap(final String bundleName, final String keyName) {
|
|
|
+ domModifyLock.readLock().lock();
|
|
|
+ try {
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForLocaleBundleSetting(bundleName, keyName);
|
|
|
+ final Element localeBundleElement = (Element)xp.evaluateFirst(document);
|
|
|
+ if (localeBundleElement != null) {
|
|
|
+ final Map<String,String> bundleMap = new LinkedHashMap<>();
|
|
|
+ for (final Element valueElement : localeBundleElement.getChildren("value")) {
|
|
|
+ final String localeStrValue = valueElement.getAttributeValue("locale");
|
|
|
+ bundleMap.put(localeStrValue == null ? "" : localeStrValue, valueElement.getText());
|
|
|
+ }
|
|
|
+ if (!bundleMap.isEmpty()) {
|
|
|
+ return bundleMap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ domModifyLock.readLock().unlock();
|
|
|
+ }
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Map<String,Object> toOutputMap(final Locale locale) {
|
|
|
+ final List<Map<String,String>> settingData = new ArrayList<>();
|
|
|
+ for (final StoredConfigurationImpl.SettingValueRecord settingValueRecord : this.modifiedSettings()) {
|
|
|
+ final Map<String,String> recordMap = new HashMap<>();
|
|
|
+ recordMap.put("label", settingValueRecord.getSetting().getLabel(locale));
|
|
|
+ if (settingValueRecord.getProfile() != null) {
|
|
|
+ recordMap.put("profile", settingValueRecord.getProfile());
|
|
|
+ }
|
|
|
+ if (settingValueRecord.getStoredValue() != null) {
|
|
|
+ recordMap.put("value", settingValueRecord.getStoredValue().toDebugString(locale));
|
|
|
+ }
|
|
|
+ final SettingMetaData settingMetaData = readSettingMetadata(settingValueRecord.getSetting(), settingValueRecord.getProfile());
|
|
|
+ if (settingMetaData != null) {
|
|
|
+ if (settingMetaData.getModifyDate() != null) {
|
|
|
+ recordMap.put("modifyTime", PwmConstants.DEFAULT_DATETIME_FORMAT.format(settingMetaData.getModifyDate()));
|
|
|
+ }
|
|
|
+ if (settingMetaData.getUserIdentity() != null) {
|
|
|
+ recordMap.put("modifyUser",settingMetaData.getUserIdentity().toDisplayString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ settingData.add(recordMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ final HashMap<String,Object> outputObj = new HashMap<>();
|
|
|
+ outputObj.put("settings", settingData);
|
|
|
+ outputObj.put("template", this.getTemplate().toString());
|
|
|
+
|
|
|
+ return Collections.unmodifiableMap(outputObj);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void resetLocaleBundleMap(final String bundleName, final String keyName) {
|
|
|
+ preModifyActions();
|
|
|
+ domModifyLock.writeLock().lock();
|
|
|
+ try {
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForLocaleBundleSetting(bundleName, keyName);
|
|
|
+ final List<Element> oldBundleElements = xp.evaluate(document);
|
|
|
+ if (oldBundleElements != null) {
|
|
|
+ for (final Element element : oldBundleElements) {
|
|
|
+ element.detach();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ domModifyLock.writeLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void resetSetting(final PwmSetting setting, final String profileID, final UserIdentity userIdentity) {
|
|
|
+ changeLog.updateChangeLog(setting, profileID, defaultValue(setting, this.getTemplate()));
|
|
|
+ domModifyLock.writeLock().lock();
|
|
|
+ preModifyActions();
|
|
|
+ try {
|
|
|
+ final Element settingElement = createOrGetSettingElement(document, setting, profileID);
|
|
|
+ settingElement.removeContent();
|
|
|
+ settingElement.addContent(new Element(XML_ELEMENT_DEFAULT));
|
|
|
+ updateMetaData(settingElement, userIdentity);
|
|
|
+ } finally {
|
|
|
+ domModifyLock.writeLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean isDefaultValue(final PwmSetting setting) {
|
|
|
+ return isDefaultValue(setting, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean isDefaultValue(final PwmSetting setting, final String profileID) {
|
|
|
+ domModifyLock.readLock().lock();
|
|
|
+ try {
|
|
|
+ final StoredValue currentValue = readSetting(setting, profileID);
|
|
|
+ if (setting.getSyntax() == PwmSettingSyntax.PASSWORD) {
|
|
|
+ return currentValue == null || currentValue.toNativeObject() == null;
|
|
|
+ }
|
|
|
+ final StoredValue defaultValue = defaultValue(setting, this.getTemplate());
|
|
|
+ final String currentJsonValue = JsonUtil.serialize((Serializable)currentValue.toNativeObject());
|
|
|
+ final String defaultJsonValue = JsonUtil.serialize((Serializable)defaultValue.toNativeObject());
|
|
|
+ return defaultJsonValue.equalsIgnoreCase(currentJsonValue);
|
|
|
+ } finally {
|
|
|
+ domModifyLock.readLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static StoredValue defaultValue(final PwmSetting pwmSetting, final PwmSettingTemplate template)
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ return pwmSetting.getDefaultValue(template);
|
|
|
+ } catch (PwmException e) {
|
|
|
+ final String errorMsg = "error reading default value for setting " + pwmSetting.toString() + ", error: " + e.getErrorInformation().toDebugStr();
|
|
|
+ LOGGER.error(errorMsg,e);
|
|
|
+ throw new IllegalStateException(errorMsg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public PwmSettingTemplate getTemplate() {
|
|
|
+ final String propertyValue = readConfigProperty(ConfigProperty.PROPERTY_KEY_TEMPLATE);
|
|
|
+ try {
|
|
|
+ return PwmSettingTemplate.valueOf(propertyValue);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ return PwmSettingTemplate.DEFAULT;
|
|
|
+ } catch (NullPointerException e) {
|
|
|
+ return PwmSettingTemplate.DEFAULT;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setTemplate(PwmSettingTemplate template) {
|
|
|
+ writeConfigProperty(ConfigProperty.PROPERTY_KEY_TEMPLATE, template.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ public String toString(final PwmSetting setting, final String profileID ) {
|
|
|
+ final StoredValue storedValue = readSetting(setting, profileID);
|
|
|
+ return setting.getKey() + "=" + storedValue.toDebugString(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Map<String,String> getModifiedSettingDebugValues(final Locale locale, final boolean prettyPrint) {
|
|
|
+ final Map<String,String> returnObj = new LinkedHashMap<>();
|
|
|
+ for (SettingValueRecord record : this.modifiedSettings()) {
|
|
|
+ final String label = record.getSetting().toMenuLocationDebug(record.getProfile(),locale);
|
|
|
+ final String value = record.getStoredValue().toDebugString(locale);
|
|
|
+ returnObj.put(label,value);
|
|
|
+ }
|
|
|
+ return returnObj;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<SettingValueRecord> modifiedSettings() {
|
|
|
+ final List<SettingValueRecord> returnObj = new ArrayList<>();
|
|
|
+ domModifyLock.readLock().lock();
|
|
|
+ try {
|
|
|
+ for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
+ if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
+ if (!isDefaultValue(setting,null)) {
|
|
|
+ final StoredValue value = readSetting(setting);
|
|
|
+ if (value != null) {
|
|
|
+ returnObj.add(new SettingValueRecord(setting, null, value));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
+ if (category.hasProfiles()) {
|
|
|
+ for (final String profileID : this.profilesForSetting(category.getProfileSetting())) {
|
|
|
+ for (final PwmSetting profileSetting : category.getSettings()) {
|
|
|
+ if (!isDefaultValue(profileSetting, profileID)) {
|
|
|
+ final StoredValue value = readSetting(profileSetting, profileID);
|
|
|
+ if (value != null) {
|
|
|
+ returnObj.add(new SettingValueRecord(profileSetting, profileID, value));
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return returnObj;
|
|
|
+ } finally {
|
|
|
+ domModifyLock.readLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public Serializable toJsonDebugObject() {
|
|
|
+ domModifyLock.readLock().lock();
|
|
|
+ try {
|
|
|
+ final TreeMap<String,Object> outputObject = new TreeMap<>();
|
|
|
+
|
|
|
+ for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
+ if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
+ if (!isDefaultValue(setting,null)) {
|
|
|
+ final StoredValue value = readSetting(setting);
|
|
|
+ outputObject.put(setting.getKey(), value.toDebugJsonObject(null));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
+ if (category.hasProfiles()) {
|
|
|
+ final TreeMap<String,Object> profiles = new TreeMap<>();
|
|
|
+ for (final String profileID : this.profilesForSetting(category.getProfileSetting())) {
|
|
|
+ final TreeMap<String,Object> profileObject = new TreeMap<>();
|
|
|
+ for (final PwmSetting profileSetting : category.getSettings()) {
|
|
|
+ if (!isDefaultValue(profileSetting, profileID)) {
|
|
|
+ final StoredValue value = readSetting(profileSetting, profileID);
|
|
|
+ profileObject.put(profileSetting.getKey(), value.toDebugJsonObject(null));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ profiles.put(profileID,profileObject);
|
|
|
+ }
|
|
|
+ outputObject.put(category.getProfileSetting().getKey(),profiles);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return outputObject;
|
|
|
+ } finally {
|
|
|
+ domModifyLock.readLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void toXml(final OutputStream outputStream)
|
|
|
+ throws IOException, PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ ConfigurationCleaner.updateMandatoryElements(document);
|
|
|
+ XmlUtil.outputDocument(document, outputStream);
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<String> profilesForSetting(final PwmSetting pwmSetting) {
|
|
|
+ if (!pwmSetting.getCategory().hasProfiles() && pwmSetting.getSyntax() != PwmSettingSyntax.PROFILE) {
|
|
|
+ throw new IllegalArgumentException("cannot build profile list for non-profile setting " + pwmSetting.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ final PwmSetting profileSetting;
|
|
|
+ if (pwmSetting.getSyntax() == PwmSettingSyntax.PROFILE) {
|
|
|
+ profileSetting = pwmSetting;
|
|
|
+ } else {
|
|
|
+ profileSetting = pwmSetting.getCategory().getProfileSetting();
|
|
|
+ }
|
|
|
+
|
|
|
+ final List<String> settingValues = (List<String>)readSetting(profileSetting).toNativeObject();
|
|
|
+ final LinkedList<String> profiles = new LinkedList<>();
|
|
|
+ profiles.addAll(settingValues);
|
|
|
+ for (Iterator<String> iterator = profiles.iterator(); iterator.hasNext();) {
|
|
|
+ final String profile = iterator.next();
|
|
|
+ if (profile == null || profile.length() < 1) {
|
|
|
+ iterator.remove();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Collections.unmodifiableList(profiles);
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<String> validateValues() {
|
|
|
+ final long startTime = System.currentTimeMillis();
|
|
|
+ final List<String> errorStrings = new ArrayList<>();
|
|
|
+
|
|
|
+ for (final PwmSetting loopSetting : PwmSetting.values()) {
|
|
|
+
|
|
|
+ if (loopSetting.getCategory().hasProfiles()) {
|
|
|
+ for (final String profile : profilesForSetting(loopSetting)) {
|
|
|
+ final String errorAppend = profile;
|
|
|
+ final StoredValue loopValue = readSetting(loopSetting,profile);
|
|
|
+
|
|
|
+ try {
|
|
|
+ final List<String> errors = loopValue.validateValue(loopSetting);
|
|
|
+ for (final String loopError : errors) {
|
|
|
+ errorStrings.add(loopSetting.toMenuLocationDebug(profile,PwmConstants.DEFAULT_LOCALE) + errorAppend + " " + loopError);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOGGER.error("unexpected error during validate value for " + loopSetting.toMenuLocationDebug(profile,PwmConstants.DEFAULT_LOCALE) + errorAppend + ", error: " + e.getMessage(),e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ final StoredValue loopValue = readSetting(loopSetting);
|
|
|
+
|
|
|
+ try {
|
|
|
+ final List<String> errors = loopValue.validateValue(loopSetting);
|
|
|
+ for (final String loopError : errors) {
|
|
|
+ errorStrings.add(loopSetting.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE) + loopError);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOGGER.error("unexpected error during validate value for " + loopSetting.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE) + ", error: " + e.getMessage(),e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LOGGER.trace("StoredConfiguration validator completed in " + TimeDuration.fromCurrent(startTime).asCompactString());
|
|
|
+ return errorStrings;
|
|
|
+ }
|
|
|
+
|
|
|
+ public SettingMetaData readSettingMetadata(final PwmSetting setting, final String profileID) {
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForSetting(setting, profileID);
|
|
|
+ final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
+
|
|
|
+ if (settingElement == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ final SettingMetaData metaData = new SettingMetaData();
|
|
|
+ try {
|
|
|
+ if (settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_TIME) != null) {
|
|
|
+ metaData.modifyDate = PwmConstants.DEFAULT_DATETIME_FORMAT.parse(
|
|
|
+ settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_TIME));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOGGER.error("can't read modifyDate for setting " + setting.getKey() + ", profile " + profileID + ", error: " + e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ if (settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_USER) != null) {
|
|
|
+ metaData.userIdentity = UserIdentity.fromDelimitedKey(
|
|
|
+ settingElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_USER));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOGGER.error("can't read userIdentity for setting " + setting.getKey() + ", profile " + profileID + ", error: " + e.getMessage());
|
|
|
+ }
|
|
|
+ return metaData;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<ConfigRecordID> search(final String searchTerm, final Locale locale) {
|
|
|
+ if (searchTerm == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ final LinkedHashSet<ConfigRecordID> returnSet = new LinkedHashSet<>();
|
|
|
+ boolean firstIter = true;
|
|
|
+ for (final String searchWord : StringUtil.whitespaceSplit(searchTerm)) { // split on whitespace
|
|
|
+ final LinkedHashSet<ConfigRecordID> loopResults = new LinkedHashSet<>();
|
|
|
+ for (final PwmSetting loopSetting : PwmSetting.values()) {
|
|
|
+ if (loopSetting.getCategory().hasProfiles()) {
|
|
|
+ for (final String profile : profilesForSetting(loopSetting)) {
|
|
|
+ final StoredValue loopValue = readSetting(loopSetting, profile);
|
|
|
+ if (matchSetting(loopSetting,loopValue,searchWord,locale)) {
|
|
|
+ loopResults.add(new ConfigRecordID(ConfigRecordID.RecordType.SETTING,loopSetting,profile));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ final StoredValue loopValue = readSetting(loopSetting);
|
|
|
+ if (matchSetting(loopSetting,loopValue,searchWord,locale)) {
|
|
|
+ loopResults.add(new ConfigRecordID(ConfigRecordID.RecordType.SETTING,loopSetting,null));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (firstIter) {
|
|
|
+ returnSet.addAll(loopResults);
|
|
|
+ } else {
|
|
|
+ returnSet.retainAll(loopResults);
|
|
|
+ }
|
|
|
+ firstIter = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return new ArrayList<>(returnSet);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean matchSetting(final PwmSetting setting, final StoredValue value, final String searchTerm, final Locale locale) {
|
|
|
+ if (setting.isHidden() || setting.getCategory().isHidden()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (searchTerm == null || searchTerm.isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ final String lowerSearchTerm = searchTerm.toLowerCase();
|
|
|
+
|
|
|
+ {
|
|
|
+ final String key = setting.getKey();
|
|
|
+ if (key.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ {
|
|
|
+ final String label = setting.getLabel(locale);
|
|
|
+ if (label.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ {
|
|
|
+ final String descr = setting.getDescription(locale);
|
|
|
+ if (descr.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ {
|
|
|
+ final String menuLocationString = setting.toMenuLocationDebug(null,locale);
|
|
|
+ if (menuLocationString.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (setting.isConfidential()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ {
|
|
|
+ final String valueDebug = value.toDebugString(locale);
|
|
|
+ if (valueDebug != null && valueDebug.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (PwmSettingSyntax.SELECT == setting.getSyntax()
|
|
|
+ || PwmSettingSyntax.OPTIONLIST == setting.getSyntax()
|
|
|
+ || PwmSettingSyntax.VERIFICATION_METHOD == setting.getSyntax()
|
|
|
+ ) {
|
|
|
+ for (final String key : setting.getOptions().keySet()) {
|
|
|
+ if (key.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ final String optionValue = setting.getOptions().get(key);
|
|
|
+ if (optionValue != null && optionValue.toLowerCase().contains(lowerSearchTerm)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public StoredValue readSetting(final PwmSetting setting) {
|
|
|
+ return readSetting(setting,null);
|
|
|
+ }
|
|
|
+
|
|
|
+ public StoredValue readSetting(final PwmSetting setting, final String profileID) {
|
|
|
+ if (profileID == null && setting.getCategory().hasProfiles()) {
|
|
|
+ IllegalArgumentException e = new IllegalArgumentException("reading of setting " + setting.getKey() + " requires a non-null profileID");
|
|
|
+ LOGGER.error("error", e);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ if (profileID != null && !setting.getCategory().hasProfiles()) {
|
|
|
+ throw new IllegalStateException("cannot read setting key " + setting.getKey() + " with non-null profileID");
|
|
|
+ }
|
|
|
+ domModifyLock.readLock().lock();
|
|
|
+ try {
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForSetting(setting, profileID);
|
|
|
+ final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
+
|
|
|
+ if (settingElement == null) {
|
|
|
+ return defaultValue(setting, getTemplate());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (settingElement.getChild(XML_ELEMENT_DEFAULT) != null) {
|
|
|
+ return defaultValue(setting, getTemplate());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ return ValueFactory.fromXmlValues(setting, settingElement, getKey());
|
|
|
+ } catch (PwmException e) {
|
|
|
+ final String errorMsg = "unexpected error reading setting '" + setting.getKey() + "' profile '" + profileID + "', error: " + e.getMessage();
|
|
|
+ throw new IllegalStateException(errorMsg);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ domModifyLock.readLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void writeLocaleBundleMap(final String bundleName, final String keyName, final Map<String,String> localeMap) {
|
|
|
+ ResourceBundle theBundle = null;
|
|
|
+ for (final PwmLocaleBundle bundle : PwmLocaleBundle.values()) {
|
|
|
+ if (bundle.getTheClass().getName().equals(bundleName)) {
|
|
|
+ theBundle = ResourceBundle.getBundle(bundleName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (theBundle == null) {
|
|
|
+ LOGGER.info("ignoring unknown locale bundle for bundle=" + bundleName + ", key=" + keyName);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (theBundle.getString(keyName) == null) {
|
|
|
+ LOGGER.info("ignoring unknown key for bundle=" + bundleName + ", key=" + keyName);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ resetLocaleBundleMap(bundleName, keyName);
|
|
|
+ if (localeMap == null || localeMap.isEmpty()) {
|
|
|
+ LOGGER.info("cleared locale bundle map for bundle=" + bundleName + ", key=" + keyName);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ preModifyActions();
|
|
|
+ changeLog.updateChangeLog(bundleName, keyName, localeMap);
|
|
|
+ try {
|
|
|
+ domModifyLock.writeLock().lock();
|
|
|
+ final Element localeBundleElement = new Element("localeBundle");
|
|
|
+ localeBundleElement.setAttribute("bundle",bundleName);
|
|
|
+ localeBundleElement.setAttribute("key",keyName);
|
|
|
+ for (final String locale : localeMap.keySet()) {
|
|
|
+ final Element valueElement = new Element("value");
|
|
|
+ if (locale != null && locale.length() > 0) {
|
|
|
+ valueElement.setAttribute("locale",locale);
|
|
|
+ }
|
|
|
+ valueElement.setContent(new CDATA(localeMap.get(locale)));
|
|
|
+ localeBundleElement.addContent(valueElement);
|
|
|
+ }
|
|
|
+ localeBundleElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
+ document.getRootElement().addContent(localeBundleElement);
|
|
|
+ } finally {
|
|
|
+ domModifyLock.writeLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void renameProfileID(final PwmSettingCategory category, final String sourceID, final String destinationID, final UserIdentity userIdentity)
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ copyProfileID(category, sourceID, destinationID, userIdentity);
|
|
|
+ if (category == PwmSettingCategory.LDAP_PROFILE) {
|
|
|
+ for (final SettingValueRecord settingValueRecord : this.modifiedSettings()) {
|
|
|
+ if (settingValueRecord.getSetting().getSyntax() == PwmSettingSyntax.USER_PERMISSION) {
|
|
|
+ UserPermissionValue value = (UserPermissionValue)settingValueRecord.getStoredValue();
|
|
|
+ final List<UserPermission> newValues = new ArrayList<>();
|
|
|
+ boolean modified = false;
|
|
|
+
|
|
|
+ for (final UserPermission userPermission : value.toNativeObject()) {
|
|
|
+ if (userPermission != null && userPermission.getLdapProfileID() != null) {
|
|
|
+ if (userPermission.getLdapProfileID().equals(sourceID)) {
|
|
|
+ final UserPermission newPermission = new UserPermission(
|
|
|
+ userPermission.getType(),
|
|
|
+ destinationID,
|
|
|
+ userPermission.getLdapQuery(),
|
|
|
+ userPermission.getLdapBase());
|
|
|
+ modified = true;
|
|
|
+ newValues.add(newPermission);
|
|
|
+ } else {
|
|
|
+ newValues.add(userPermission);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (modified) {
|
|
|
+ UserPermissionValue newUserPermissionValue = new UserPermissionValue(newValues);
|
|
|
+ this.writeSetting(settingValueRecord.getSetting(),settingValueRecord.getProfile(),newUserPermissionValue,userIdentity);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ final List<String> existingProfiles = this.profilesForSetting(category.getProfileSetting());
|
|
|
+ final List<String> newProfileIDList = new ArrayList<>();
|
|
|
+ newProfileIDList.addAll(existingProfiles);
|
|
|
+ newProfileIDList.remove(sourceID);
|
|
|
+ writeSetting(category.getProfileSetting(), new StringArrayValue(newProfileIDList), userIdentity);
|
|
|
+ }
|
|
|
+
|
|
|
+ ConfigurationCleaner.stripOrphanedProfileSettings(this);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void copyProfileID(final PwmSettingCategory category, final String sourceID, final String destinationID, final UserIdentity userIdentity)
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ if (!category.hasProfiles()) {
|
|
|
+ throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category " + category + ", category does not have profiles");
|
|
|
+ }
|
|
|
+ final List<String> existingProfiles = this.profilesForSetting(category.getProfileSetting());
|
|
|
+ if (!existingProfiles.contains(sourceID)) {
|
|
|
+ throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category, source profileID '" + sourceID + "' does not exist");
|
|
|
+ }
|
|
|
+ if (existingProfiles.contains(destinationID)) {
|
|
|
+ throw PwmUnrecoverableException.newException(PwmError.ERROR_INVALID_CONFIG, "can not copy profile ID for category, destination profileID '" + destinationID+ "' already exists");
|
|
|
+ }
|
|
|
+ for (final PwmSetting pwmSetting : category.getSettings()) {
|
|
|
+ if (!isDefaultValue(pwmSetting, sourceID)) {
|
|
|
+ final StoredValue value = readSetting(pwmSetting, sourceID);
|
|
|
+ writeSetting(pwmSetting, destinationID, value, userIdentity);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ final List<String> newProfileIDList = new ArrayList<>();
|
|
|
+ newProfileIDList.addAll(existingProfiles);
|
|
|
+ newProfileIDList.add(destinationID);
|
|
|
+ writeSetting(category.getProfileSetting(), new StringArrayValue(newProfileIDList), userIdentity);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void writeSetting(
|
|
|
+ final PwmSetting setting,
|
|
|
+ final StoredValue value,
|
|
|
+ final UserIdentity userIdentity
|
|
|
+ ) throws PwmUnrecoverableException {
|
|
|
+ writeSetting(setting, null, value, userIdentity);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void writeSetting(
|
|
|
+ final PwmSetting setting,
|
|
|
+ final String profileID,
|
|
|
+ final StoredValue value,
|
|
|
+ final UserIdentity userIdentity
|
|
|
+ ) throws PwmUnrecoverableException {
|
|
|
+ if (profileID == null && setting.getCategory().hasProfiles()) {
|
|
|
+ throw new IllegalArgumentException("reading of setting " + setting.getKey() + " requires a non-null profileID");
|
|
|
+ }
|
|
|
+ if (profileID != null && !setting.getCategory().hasProfiles()) {
|
|
|
+ throw new IllegalArgumentException("cannot specify profile for non-profile setting");
|
|
|
+ }
|
|
|
+
|
|
|
+ preModifyActions();
|
|
|
+ changeLog.updateChangeLog(setting, profileID, value);
|
|
|
+ domModifyLock.writeLock().lock();
|
|
|
+ try {
|
|
|
+ final Element settingElement = createOrGetSettingElement(document, setting, profileID);
|
|
|
+ settingElement.removeContent();
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString());
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_SYNTAX_VERSION, Integer.toString(value.currentSyntaxVersion()));
|
|
|
+
|
|
|
+ if (setting_writeLabels) {
|
|
|
+ final Element labelElement = new Element("label");
|
|
|
+ labelElement.addContent(setting.getLabel(PwmConstants.DEFAULT_LOCALE));
|
|
|
+ settingElement.addContent(labelElement);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (setting.getSyntax() == PwmSettingSyntax.PASSWORD) {
|
|
|
+ final List<Element> valueElements = ((PasswordValue)value).toXmlValues("value", getKey());
|
|
|
+ settingElement.addContent(new Comment("Note: This value is encrypted and can not be edited directly."));
|
|
|
+ settingElement.addContent(new Comment("Please use the Configuration Manager GUI to modify this value."));
|
|
|
+ settingElement.addContent(valueElements);
|
|
|
+ } else {
|
|
|
+ settingElement.addContent(value.toXmlValues("value"));
|
|
|
+ }
|
|
|
+
|
|
|
+ updateMetaData(settingElement, userIdentity);
|
|
|
+ } finally {
|
|
|
+ domModifyLock.writeLock().unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public String settingChecksum()
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ final Date startTime = new Date();
|
|
|
+
|
|
|
+ final List<SettingValueRecord> modifiedSettings = modifiedSettings();
|
|
|
+ final StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("PwmSettingsChecksum");
|
|
|
+ for (SettingValueRecord settingValueRecord : modifiedSettings) {
|
|
|
+ final StoredValue storedValue = settingValueRecord.getStoredValue();
|
|
|
+ sb.append(storedValue.valueHash());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ final String result = SecureEngine.hash(sb.toString(), PwmConstants.SETTING_CHECKSUM_HASH_METHOD);
|
|
|
+ LOGGER.trace("computed setting checksum in " + TimeDuration.fromCurrent(startTime).asCompactString());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private void preModifyActions() {
|
|
|
+ if (locked) {
|
|
|
+ throw new UnsupportedOperationException("StoredConfiguration is locked and cannot be modified");
|
|
|
+ }
|
|
|
+ document.getRootElement().setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
+ }
|
|
|
+
|
|
|
+// -------------------------- INNER CLASSES --------------------------
|
|
|
+
|
|
|
+ public void setPassword(final String password)
|
|
|
+ throws PwmOperationalException
|
|
|
+ {
|
|
|
+ if (password == null || password.isEmpty()) {
|
|
|
+ throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"can not set blank password"}));
|
|
|
+ }
|
|
|
+ final String trimmedPassword = password.trim();
|
|
|
+ if (trimmedPassword.length() < 1) {
|
|
|
+ throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"can not set blank password"}));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ final String salt = BCrypt.gensalt();
|
|
|
+ final String passwordHash = BCrypt.hashpw(password,salt);
|
|
|
+ this.writeConfigProperty(ConfigProperty.PROPERTY_KEY_PASSWORD_HASH, passwordHash);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean verifyPassword(final String password) {
|
|
|
+ if (!hasPassword()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ final String passwordHash = this.readConfigProperty(ConfigProperty.PROPERTY_KEY_PASSWORD_HASH);
|
|
|
+ return BCrypt.checkpw(password,passwordHash);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean hasPassword() {
|
|
|
+ final String passwordHash = this.readConfigProperty(ConfigProperty.PROPERTY_KEY_PASSWORD_HASH);
|
|
|
+ return passwordHash != null && passwordHash.length() > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static abstract class XPathBuilder {
|
|
|
+ private static XPathExpression xpathForLocaleBundleSetting(final String bundleName, final String keyName) {
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ final String xpathString;
|
|
|
+ xpathString = "//localeBundle[@bundle=\"" + bundleName + "\"][@key=\"" + keyName + "\"]";
|
|
|
+ return xpfac.compile(xpathString);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XPathExpression xpathForSetting(final PwmSetting setting, final String profileID) {
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ final String xpathString;
|
|
|
+ if (profileID == null || profileID.length() < 1) {
|
|
|
+ xpathString = "//setting[@key=\"" + setting.getKey() + "\"][(not (@profile)) or @profile=\"\"]";
|
|
|
+ } else {
|
|
|
+ xpathString = "//setting[@key=\"" + setting.getKey() + "\"][@profile=\"" + profileID + "\"]";
|
|
|
+ }
|
|
|
+
|
|
|
+ return xpfac.compile(xpathString);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XPathExpression xpathForAppProperty(final AppProperty appProperty) {
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ final String xpathString;
|
|
|
+ xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]/"
|
|
|
+ + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + appProperty.getKey() + "\"]";
|
|
|
+ return xpfac.compile(xpathString);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XPathExpression xpathForAppProperties() {
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ final String xpathString;
|
|
|
+ xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]";
|
|
|
+ return xpfac.compile(xpathString);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XPathExpression xpathForConfigProperty(final ConfigProperty configProperty) {
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ final String xpathString;
|
|
|
+ xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]/"
|
|
|
+ + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + configProperty.getKey() + "\"]";
|
|
|
+ return xpfac.compile(xpathString);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XPathExpression xpathForConfigProperties() {
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ final String xpathString;
|
|
|
+ xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]";
|
|
|
+ return xpfac.compile(xpathString);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static class ConfigurationCleaner {
|
|
|
+ private static void cleanup(final StoredConfigurationImpl configuration) throws PwmUnrecoverableException {
|
|
|
+ updateProperitiesWithoutType(configuration);
|
|
|
+ updateMandatoryElements(configuration.document);
|
|
|
+ profilizeNonProfiledSettings(configuration);
|
|
|
+ stripOrphanedProfileSettings(configuration);
|
|
|
+ migrateAppProperties(configuration);
|
|
|
+ updateDeprecatedSettings(configuration);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void updateMandatoryElements(final Document document) {
|
|
|
+ final Element rootElement = document.getRootElement();
|
|
|
+
|
|
|
+ {
|
|
|
+ final XPathExpression commentXPath = XPathFactory.instance().compile("//comment()[1]");
|
|
|
+ final Comment existingComment = (Comment)commentXPath.evaluateFirst(rootElement);
|
|
|
+ if (existingComment != null) {
|
|
|
+ existingComment.detach();
|
|
|
+ }
|
|
|
+ final Comment comment = new Comment(generateCommentText());
|
|
|
+ rootElement.addContent(0,comment);
|
|
|
+ }
|
|
|
+
|
|
|
+ rootElement.setAttribute("pwmVersion", PwmConstants.BUILD_VERSION);
|
|
|
+ rootElement.setAttribute("pwmBuild", PwmConstants.BUILD_NUMBER);
|
|
|
+ rootElement.setAttribute("pwmBuildType", PwmConstants.BUILD_TYPE);
|
|
|
+ rootElement.setAttribute("xmlVersion", XML_FORMAT_VERSION);
|
|
|
+
|
|
|
+ { // migrate old properties
|
|
|
+
|
|
|
+ // read correct (new) //properties[@type="config"]
|
|
|
+ final XPathExpression configPropertiesXpath = XPathFactory.instance().compile(
|
|
|
+ "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]");
|
|
|
+ final Element configPropertiesElement = (Element)configPropertiesXpath.evaluateFirst(rootElement);
|
|
|
+
|
|
|
+ // read list of old //properties[not (@type)]/property
|
|
|
+ final XPathExpression nonAttributedProperty = XPathFactory.instance().compile(
|
|
|
+ "//" + XML_ELEMENT_PROPERTIES + "[not (@" + XML_ATTRIBUTE_TYPE + ")]/" + XML_ELEMENT_PROPERTY);
|
|
|
+ final List<Element> nonAttributedProperties = nonAttributedProperty.evaluate(rootElement);
|
|
|
+
|
|
|
+ if (configPropertiesElement != null && nonAttributedProperties != null) {
|
|
|
+ for (Element element : nonAttributedProperties) {
|
|
|
+ element.detach();
|
|
|
+ configPropertiesElement.addContent(element);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // remove old //properties[not (@type] element
|
|
|
+ final XPathExpression oldPropertiesXpath = XPathFactory.instance().compile(
|
|
|
+ "//" + XML_ELEMENT_PROPERTIES + "[not (@" + XML_ATTRIBUTE_TYPE + ")]");
|
|
|
+ final List<Element> oldPropertiesElements = oldPropertiesXpath.evaluate(rootElement);
|
|
|
+ if (oldPropertiesElements != null) {
|
|
|
+ for (Element element : oldPropertiesElements) {
|
|
|
+ element.detach();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String generateCommentText() {
|
|
|
+ final StringBuilder commentText = new StringBuilder();
|
|
|
+ commentText.append("\t\t").append(" ").append("\n");
|
|
|
+ commentText.append("\t\t").append("This configuration file has been auto-generated by the ").append(PwmConstants.PWM_APP_NAME).append(" password self service application.").append("\n");
|
|
|
+ commentText.append("\t\t").append("").append("\n");
|
|
|
+ commentText.append("\t\t").append("WARNING: This configuration file contains sensitive security information, please handle with care!").append("\n");
|
|
|
+ commentText.append("\t\t").append("").append("\n");
|
|
|
+ commentText.append("\t\t").append("WARNING: If a server is currently running using this configuration file, it will be restarted").append("\n");
|
|
|
+ commentText.append("\t\t").append(" and the configuration updated immediately when it is modified.").append("\n");
|
|
|
+ commentText.append("\t\t").append("").append("\n");
|
|
|
+ commentText.append("\t\t").append("NOTICE: This file is encoded as UTF-8. Do not save or edit this file with an editor that does not").append("\n");
|
|
|
+ commentText.append("\t\t").append(" support UTF-8 encoding.").append("\n");
|
|
|
+ commentText.append("\t\t").append("").append("\n");
|
|
|
+ commentText.append("\t\t").append("If unable to edit using the application ConfigurationEditor web UI, the following options are available.").append("\n");
|
|
|
+ commentText.append("\t\t").append(" or 1. Edit this file directly by hand.").append("\n");
|
|
|
+ commentText.append("\t\t").append(" or 2. Remove restrictions of the configuration by setting the property 'configIsEditable' to 'true' in this file. This will ").append("\n");
|
|
|
+ commentText.append("\t\t").append(" allow access to the ConfigurationEditor web UI without having to authenticate to an LDAP server first.").append("\n");
|
|
|
+ commentText.append("\t\t").append(" or 3. Remove restrictions of the configuration by using the the command line utility. ").append("\n");
|
|
|
+ commentText.append("\t\t").append("").append("\n");
|
|
|
+ return commentText.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static void profilizeNonProfiledSettings(final StoredConfigurationImpl storedConfiguration) throws PwmUnrecoverableException {
|
|
|
+ final String NEW_PROFILE_NAME = "default";
|
|
|
+ final Document document = storedConfiguration.document;
|
|
|
+ for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
+ if (setting.getCategory().hasProfiles()) {
|
|
|
+
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForSetting(setting, null);
|
|
|
+ final Element settingElement = (Element)xp.evaluateFirst(document);
|
|
|
+ if (settingElement != null) {
|
|
|
+ LOGGER.info("moving setting " + setting.getKey() + " without profile attribute to profile \"" + NEW_PROFILE_NAME + "\".");
|
|
|
+ // change setting to "default" profile.
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_PROFILE, NEW_PROFILE_NAME);
|
|
|
+
|
|
|
+ final PwmSetting profileSetting = setting.getCategory().getProfileSetting();
|
|
|
+ final List<String> profileStringDefinitions = new ArrayList<>();
|
|
|
+ {
|
|
|
+ final StringArrayValue profileDefinitions = (StringArrayValue) storedConfiguration.readSetting(profileSetting);
|
|
|
+ if (profileDefinitions != null) {
|
|
|
+ if (profileDefinitions.toNativeObject() != null) {
|
|
|
+ profileStringDefinitions.addAll(profileDefinitions.toNativeObject());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!profileStringDefinitions.contains(NEW_PROFILE_NAME)) {
|
|
|
+ profileStringDefinitions.add(NEW_PROFILE_NAME);
|
|
|
+ storedConfiguration.writeSetting(profileSetting,new StringArrayValue(profileStringDefinitions),null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void updateProperitiesWithoutType(final StoredConfigurationImpl storedConfiguration) {
|
|
|
+ final Document document = storedConfiguration.document;
|
|
|
+ final String xpathString = "//properties[not(@type)]";
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ final XPathExpression xp = xpfac.compile(xpathString);
|
|
|
+ final List<Element> propertiesElements = (List<Element>)xp.evaluate(document);
|
|
|
+ for (final Element propertiesElement : propertiesElements) {
|
|
|
+ propertiesElement.setAttribute(XML_ATTRIBUTE_TYPE,XML_ATTRIBUTE_VALUE_CONFIG);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void stripOrphanedProfileSettings(final StoredConfigurationImpl storedConfiguration) {
|
|
|
+ final Document document = storedConfiguration.document;
|
|
|
+ final XPathFactory xpfac = XPathFactory.instance();
|
|
|
+ for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
+ if (setting.getCategory().hasProfiles()) {
|
|
|
+ final List<String> validProfiles = storedConfiguration.profilesForSetting(setting);
|
|
|
+ final String xpathString = "//setting[@key=\"" + setting.getKey() + "\"]";
|
|
|
+ final XPathExpression xp = xpfac.compile(xpathString);
|
|
|
+ final List<Element> settingElements = (List<Element>)xp.evaluate(document);
|
|
|
+ for (final Element settingElement : settingElements) {
|
|
|
+ final String profileID = settingElement.getAttributeValue(XML_ATTRIBUTE_PROFILE);
|
|
|
+ if (profileID != null) {
|
|
|
+ if (!validProfiles.contains(profileID)) {
|
|
|
+ LOGGER.info("removing setting " + setting.getKey() + " with profile \"" + profileID + "\", profile is not a valid profile");
|
|
|
+ settingElement.detach();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void migrateAppProperties(final StoredConfigurationImpl storedConfiguration) throws PwmUnrecoverableException {
|
|
|
+ final Document document = storedConfiguration.document;
|
|
|
+ final XPathExpression xPathExpression = XPathBuilder.xpathForAppProperties();
|
|
|
+ final List<Element> appPropertiesElements = (List<Element>)xPathExpression.evaluate(document);
|
|
|
+ for (final Element element : appPropertiesElements) {
|
|
|
+ final List<Element> properties = element.getChildren();
|
|
|
+ for (final Element property : properties) {
|
|
|
+ final String key = property.getAttributeValue("key");
|
|
|
+ final String value = property.getText();
|
|
|
+ if (key != null && !key.isEmpty() && value != null && !value.isEmpty()) {
|
|
|
+ LOGGER.info("migrating app-property config element '" + key + "' to setting " + PwmSetting.APP_PROPERTY_OVERRIDES.getKey());
|
|
|
+ final String newValue = key + "=" + value;
|
|
|
+ List<String> existingValues = (List<String>)storedConfiguration.readSetting(PwmSetting.APP_PROPERTY_OVERRIDES).toNativeObject();
|
|
|
+ if (existingValues == null) {
|
|
|
+ existingValues = new ArrayList<>();
|
|
|
+ }
|
|
|
+ existingValues = new ArrayList<>(existingValues);
|
|
|
+ existingValues.add(newValue);
|
|
|
+ storedConfiguration.writeSetting(PwmSetting.APP_PROPERTY_OVERRIDES,new StringArrayValue(existingValues),null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ element.detach();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void updateDeprecatedSettings(final StoredConfigurationImpl storedConfiguration) throws PwmUnrecoverableException {
|
|
|
+ final UserIdentity actor = new UserIdentity("UpgradeProcessor", null);
|
|
|
+ for (final String profileID : storedConfiguration.profilesForSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY)) {
|
|
|
+ if (!storedConfiguration.isDefaultValue(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY, profileID)) {
|
|
|
+ boolean ad2003Enabled = (boolean) storedConfiguration.readSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY,profileID).toNativeObject();
|
|
|
+ final StoredValue value;
|
|
|
+ if (ad2003Enabled) {
|
|
|
+ value = new StringValue(ADPolicyComplexity.AD2003.toString());
|
|
|
+ } else {
|
|
|
+ value = new StringValue(ADPolicyComplexity.NONE.toString());
|
|
|
+ }
|
|
|
+ LOGGER.warn("converting deprecated non-default setting " + PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY.getKey() + "/" + profileID
|
|
|
+ + " to replacement setting " + PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY_LEVEL + ", value=" + value.toNativeObject().toString());
|
|
|
+ storedConfiguration.writeSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY_LEVEL, profileID, value, actor);
|
|
|
+ storedConfiguration.resetSetting(PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY, profileID, actor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ {
|
|
|
+ if (!storedConfiguration.isDefaultValue(PwmSetting.CHALLENGE_REQUIRE_RESPONSES)) {
|
|
|
+ final StoredValue configValue = storedConfiguration.readSetting(PwmSetting.RECOVERY_VERIFICATION_METHODS, "default");
|
|
|
+ final VerificationMethodValue.VerificationMethodSettings existingSettings = (VerificationMethodValue.VerificationMethodSettings)configValue.toNativeObject();
|
|
|
+ final Map<RecoveryVerificationMethod,VerificationMethodValue.VerificationMethodSetting> newMethods = new HashMap<>();
|
|
|
+ newMethods.putAll(existingSettings.getMethodSettings());
|
|
|
+ VerificationMethodValue.VerificationMethodSetting setting = new VerificationMethodValue.VerificationMethodSetting(VerificationMethodValue.EnabledState.disabled);
|
|
|
+ newMethods.put(RecoveryVerificationMethod.CHALLENGE_RESPONSES,setting);
|
|
|
+ final VerificationMethodValue.VerificationMethodSettings newSettings = new VerificationMethodValue.VerificationMethodSettings(
|
|
|
+ newMethods,
|
|
|
+ existingSettings.getMinOptionalRequired()
|
|
|
+ );
|
|
|
+ storedConfiguration.writeSetting(PwmSetting.RECOVERY_VERIFICATION_METHODS, "default", new VerificationMethodValue(newSettings), actor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ if (!storedConfiguration.isDefaultValue(PwmSetting.FORGOTTEN_PASSWORD_REQUIRE_OTP)) {
|
|
|
+ final StoredValue configValue = storedConfiguration.readSetting(PwmSetting.RECOVERY_VERIFICATION_METHODS, "default");
|
|
|
+ final VerificationMethodValue.VerificationMethodSettings existingSettings = (VerificationMethodValue.VerificationMethodSettings)configValue.toNativeObject();
|
|
|
+ final Map<RecoveryVerificationMethod,VerificationMethodValue.VerificationMethodSetting> newMethods = new HashMap<>();
|
|
|
+ newMethods.putAll(existingSettings.getMethodSettings());
|
|
|
+ VerificationMethodValue.VerificationMethodSetting setting = new VerificationMethodValue.VerificationMethodSetting(VerificationMethodValue.EnabledState.required);
|
|
|
+ newMethods.put(RecoveryVerificationMethod.CHALLENGE_RESPONSES,setting);
|
|
|
+ final VerificationMethodValue.VerificationMethodSettings newSettings = new VerificationMethodValue.VerificationMethodSettings(
|
|
|
+ newMethods,
|
|
|
+ existingSettings.getMinOptionalRequired()
|
|
|
+ );
|
|
|
+ storedConfiguration.writeSetting(PwmSetting.FORGOTTEN_PASSWORD_REQUIRE_OTP, "default", new VerificationMethodValue(newSettings), actor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static class ConfigRecordID implements Serializable {
|
|
|
+ private RecordType recordType;
|
|
|
+ private Object recordID;
|
|
|
+ private String profileID;
|
|
|
+
|
|
|
+ public enum RecordType {
|
|
|
+ SETTING,
|
|
|
+ LOCALE_BUNDLE,
|
|
|
+ }
|
|
|
+
|
|
|
+ public ConfigRecordID(
|
|
|
+ RecordType recordType,
|
|
|
+ Object recordID,
|
|
|
+ String profileID
|
|
|
+ )
|
|
|
+ {
|
|
|
+ this.recordType = recordType;
|
|
|
+ this.recordID = recordID;
|
|
|
+ this.profileID = profileID;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object o)
|
|
|
+ {
|
|
|
+ if (this == o) return true;
|
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
|
+
|
|
|
+ ConfigRecordID that = (ConfigRecordID) o;
|
|
|
+
|
|
|
+ if (profileID != null ? !profileID.equals(that.profileID) : that.profileID != null) return false;
|
|
|
+ if (recordID != null ? !recordID.equals(that.recordID) : that.recordID != null) return false;
|
|
|
+ if (recordType != that.recordType) return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode()
|
|
|
+ {
|
|
|
+ int result = recordType != null ? recordType.hashCode() : 0;
|
|
|
+ result = 31 * result + (recordID != null ? recordID.hashCode() : 0);
|
|
|
+ result = 31 * result + (profileID != null ? profileID.hashCode() : 0);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public RecordType getRecordType()
|
|
|
+ {
|
|
|
+ return recordType;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Object getRecordID()
|
|
|
+ {
|
|
|
+ return recordID;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getProfileID()
|
|
|
+ {
|
|
|
+ return profileID;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public String changeLogAsDebugString(final Locale locale, final boolean asHtml) {
|
|
|
+ return changeLog.changeLogAsDebugString(locale, asHtml);
|
|
|
+ }
|
|
|
+
|
|
|
+ private PwmSecurityKey cachedKey = null;
|
|
|
+ public PwmSecurityKey getKey() throws PwmUnrecoverableException {
|
|
|
+ if (cachedKey == null) {
|
|
|
+ cachedKey = new PwmSecurityKey(createTime() + "StoredConfiguration");
|
|
|
+ }
|
|
|
+ return cachedKey;
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean isModified() {
|
|
|
+ return changeLog.isModified();
|
|
|
+ }
|
|
|
+
|
|
|
+ private class ChangeLog implements Serializable {
|
|
|
+ /* values contain the _original_ toJson version of the value. */
|
|
|
+ private Map<ConfigRecordID,String> changeLog = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ public boolean isModified() {
|
|
|
+ return !changeLog.isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ public String changeLogAsDebugString(final Locale locale, boolean asHtml) {
|
|
|
+ final Map<String,String> outputMap = new TreeMap<>();
|
|
|
+ final String SEPARATOR = LocaleHelper.getLocalizedMessage(locale, Config.Display_SettingNavigationSeparator, null);
|
|
|
+
|
|
|
+ for (final ConfigRecordID configRecordID : changeLog.keySet()) {
|
|
|
+ switch (configRecordID.recordType) {
|
|
|
+ case SETTING: {
|
|
|
+ final StoredValue currentValue = readSetting((PwmSetting) configRecordID.recordID, configRecordID.profileID);
|
|
|
+ final PwmSetting pwmSetting = (PwmSetting) configRecordID.recordID;
|
|
|
+ final String keyName = pwmSetting.toMenuLocationDebug(configRecordID.getProfileID(), locale);
|
|
|
+ final String debugValue = currentValue.toDebugString(locale);
|
|
|
+ outputMap.put(keyName,debugValue);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case LOCALE_BUNDLE: {
|
|
|
+ final String key = (String) configRecordID.recordID;
|
|
|
+ final String bundleName = key.split("!")[0];
|
|
|
+ final String keys = key.split("!")[1];
|
|
|
+ final Map<String,String> currentValue = readLocaleBundleMap(bundleName,keys);
|
|
|
+ final String debugValue = JsonUtil.serializeMap(currentValue, JsonUtil.Flag.PrettyPrint);
|
|
|
+ outputMap.put("LocaleBundle" + SEPARATOR + bundleName + " " + keys,debugValue);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ final StringBuilder output = new StringBuilder();
|
|
|
+ if (outputMap.isEmpty()) {
|
|
|
+ output.append("No setting changes.");
|
|
|
+ } else {
|
|
|
+ for (final String keyName : outputMap.keySet()) {
|
|
|
+ final String value = outputMap.get(keyName);
|
|
|
+ if (asHtml) {
|
|
|
+ output.append("<div class=\"changeLogKey\">");
|
|
|
+ output.append(keyName);
|
|
|
+ output.append("</div><div class=\"changeLogValue\">");
|
|
|
+ output.append(StringUtil.escapeHtml(value));
|
|
|
+ output.append("</div>");
|
|
|
+ } else {
|
|
|
+ output.append(keyName);
|
|
|
+ output.append("\n");
|
|
|
+ output.append(" Value: ");
|
|
|
+ output.append(value);
|
|
|
+ output.append("\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return output.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void updateChangeLog(final String bundleName, final String keyName, final Map<String,String> localeValueMap) {
|
|
|
+ final String key = bundleName + "!" + keyName;
|
|
|
+ final Map<String,String> currentValue = readLocaleBundleMap(bundleName, keyName);
|
|
|
+ final String currentJsonValue = JsonUtil.serializeMap(currentValue);
|
|
|
+ final String newJsonValue = JsonUtil.serializeMap(localeValueMap);
|
|
|
+ final ConfigRecordID configRecordID = new ConfigRecordID(ConfigRecordID.RecordType.LOCALE_BUNDLE, key, null);
|
|
|
+ updateChangeLog(configRecordID,currentJsonValue,newJsonValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void updateChangeLog(final PwmSetting setting, final String profileID, final StoredValue newValue) {
|
|
|
+ final StoredValue currentValue = readSetting(setting, profileID);
|
|
|
+ final String currentJsonValue = JsonUtil.serialize(currentValue);
|
|
|
+ final String newJsonValue = JsonUtil.serialize(newValue);
|
|
|
+ final ConfigRecordID configRecordID = new ConfigRecordID(ConfigRecordID.RecordType.SETTING, setting, profileID);
|
|
|
+ updateChangeLog(configRecordID,currentJsonValue,newJsonValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void updateChangeLog(final ConfigRecordID configRecordID, final String currentValueString, final String newValueString) {
|
|
|
+ if (changeLog.containsKey(configRecordID)) {
|
|
|
+ final String currentRecord = changeLog.get(configRecordID);
|
|
|
+
|
|
|
+ if (currentRecord == null && newValueString == null) {
|
|
|
+ changeLog.remove(configRecordID);
|
|
|
+ } else if (currentRecord != null && currentRecord.equals(newValueString)) {
|
|
|
+ changeLog.remove(configRecordID);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ changeLog.put(configRecordID,currentValueString);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void validateXmlSchema(final String xmlDocument)
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ /*
|
|
|
+ try {
|
|
|
+ final InputStream xsdInputStream = PwmSetting.class.getClassLoader().getResourceAsStream("password/pwm/config/StoredConfiguration.xsd");
|
|
|
+ final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
|
|
+ final Schema schema = factory.newSchema(new StreamSource(xsdInputStream));
|
|
|
+ Validator validator = schema.newValidator();
|
|
|
+ validator.validate(new StreamSource(new StringReader(xmlDocument)));
|
|
|
+ } catch (Exception e) {
|
|
|
+ final String errorMsg = "error while validating setting file schema definition: " + e.getMessage();
|
|
|
+ throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg));
|
|
|
+ }
|
|
|
+ */
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void updateMetaData(final Element settingElement, final UserIdentity userIdentity) {
|
|
|
+ final Element settingsElement = settingElement.getDocument().getRootElement().getChild(XML_ELEMENT_SETTINGS);
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
+ settingsElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME,PwmConstants.DEFAULT_DATETIME_FORMAT.format(new Date()));
|
|
|
+ settingElement.removeAttribute(XML_ATTRIBUTE_MODIFY_USER);
|
|
|
+ settingsElement.removeAttribute(XML_ATTRIBUTE_MODIFY_USER);
|
|
|
+ if (userIdentity != null) {
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_MODIFY_USER, userIdentity.toDelimitedKey());
|
|
|
+ settingsElement.setAttribute(XML_ATTRIBUTE_MODIFY_USER, userIdentity.toDelimitedKey());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Element createOrGetSettingElement(
|
|
|
+ final Document document,
|
|
|
+ final PwmSetting setting,
|
|
|
+ final String profileID
|
|
|
+ ) {
|
|
|
+ final XPathExpression xp = XPathBuilder.xpathForSetting(setting, profileID);
|
|
|
+ final Element existingSettingElement = (Element)xp.evaluateFirst(document);
|
|
|
+ if (existingSettingElement != null) {
|
|
|
+ return existingSettingElement;
|
|
|
+ }
|
|
|
+
|
|
|
+ final Element settingElement = new Element(XML_ELEMENT_SETTING);
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_KEY, setting.getKey());
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString());
|
|
|
+ if (profileID != null && profileID.length() > 0) {
|
|
|
+ settingElement.setAttribute(XML_ATTRIBUTE_PROFILE, profileID);
|
|
|
+ }
|
|
|
+
|
|
|
+ Element settingsElement = document.getRootElement().getChild(XML_ELEMENT_SETTINGS);
|
|
|
+ if (settingsElement == null) {
|
|
|
+ settingsElement = new Element(XML_ELEMENT_SETTINGS);
|
|
|
+ document.getRootElement().addContent(settingsElement);
|
|
|
+ }
|
|
|
+ settingsElement.addContent(settingElement);
|
|
|
+
|
|
|
+ return settingElement;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class SettingValueRecord implements Serializable {
|
|
|
+ private PwmSetting setting;
|
|
|
+ private String profile;
|
|
|
+ private StoredValue storedValue;
|
|
|
+
|
|
|
+ public SettingValueRecord(
|
|
|
+ PwmSetting setting,
|
|
|
+ String profile,
|
|
|
+ StoredValue storedValue
|
|
|
+ )
|
|
|
+ {
|
|
|
+ this.setting = setting;
|
|
|
+ this.profile = profile;
|
|
|
+ this.storedValue = storedValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ public PwmSetting getSetting()
|
|
|
+ {
|
|
|
+ return setting;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getProfile()
|
|
|
+ {
|
|
|
+ return profile;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StoredValue getStoredValue()
|
|
|
+ {
|
|
|
+ return storedValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ class StoredValueIterator implements Iterator<StoredConfigurationImpl.SettingValueRecord> {
|
|
|
+
|
|
|
+ private Queue<SettingValueRecord> settingQueue = new LinkedList<>();
|
|
|
+
|
|
|
+ public StoredValueIterator(boolean includeDefaults) {
|
|
|
+ for (final PwmSetting setting : PwmSetting.values()) {
|
|
|
+ if (setting.getSyntax() != PwmSettingSyntax.PROFILE && !setting.getCategory().hasProfiles()) {
|
|
|
+ if (includeDefaults || !isDefaultValue(setting)) {
|
|
|
+ SettingValueRecord settingValueRecord = new SettingValueRecord(setting, null, null);
|
|
|
+ settingQueue.add(settingValueRecord);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (final PwmSettingCategory category : PwmSettingCategory.values()) {
|
|
|
+ if (category.hasProfiles()) {
|
|
|
+ for (final String profileID : profilesForSetting(category.getProfileSetting())) {
|
|
|
+ for (final PwmSetting setting : category.getSettings()) {
|
|
|
+ if (includeDefaults || !isDefaultValue(setting,profileID)) {
|
|
|
+ SettingValueRecord settingValueRecord = new SettingValueRecord(setting, profileID, null);
|
|
|
+ settingQueue.add(settingValueRecord);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean hasNext()
|
|
|
+ {
|
|
|
+ return !settingQueue.isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public SettingValueRecord next()
|
|
|
+ {
|
|
|
+ StoredConfigurationImpl.SettingValueRecord settingValueRecord = settingQueue.poll();
|
|
|
+ return new SettingValueRecord(
|
|
|
+ settingValueRecord.getSetting(),
|
|
|
+ settingValueRecord.getProfile(),
|
|
|
+ readSetting(settingValueRecord.getSetting(),settingValueRecord.getProfile())
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void remove()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String createTime() {
|
|
|
+ final Element rootElement = document.getRootElement();
|
|
|
+ final String createTimeString = rootElement.getAttributeValue(XML_ATTRIBUTE_CREATE_TIME);
|
|
|
+ if (createTimeString == null || createTimeString.isEmpty()) {
|
|
|
+ throw new IllegalStateException("missing createTime timestamp");
|
|
|
+ }
|
|
|
+ return createTimeString;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Date modifyTime() {
|
|
|
+ final Element rootElement = document.getRootElement();
|
|
|
+ final String modifyTimeString = rootElement.getAttributeValue(XML_ATTRIBUTE_MODIFY_TIME);
|
|
|
+ if (modifyTimeString != null) {
|
|
|
+ try {
|
|
|
+ return PwmConstants.DEFAULT_DATETIME_FORMAT.parse(modifyTimeString);
|
|
|
+ } catch (ParseException e) {
|
|
|
+ LOGGER.error("error parsing root last modified timestamp: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void initNewRandomSecurityKey()
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ if (!isDefaultValue(PwmSetting.PWM_SECURITY_KEY)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ writeSetting(
|
|
|
+ PwmSetting.PWM_SECURITY_KEY,
|
|
|
+ new PasswordValue(new PasswordData(PwmRandom.getInstance().alphaNumericString(1024))),
|
|
|
+ null
|
|
|
+ );
|
|
|
+
|
|
|
+ LOGGER.debug("initialized new random security key");
|
|
|
+ }
|
|
|
+
|
|
|
+}
|