password.pwm.config.stored.ConfigurationReader.java Source code

Java tutorial

Introduction

Here is the source code for password.pwm.config.stored.ConfigurationReader.java

Source

/*
 * Password Management Servlets (PWM)
 * http://www.pwm-project.org
 *
 * Copyright (c) 2006-2009 Novell, Inc.
 * Copyright (c) 2009-2017 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.apache.commons.io.FileUtils;
import password.pwm.AppProperty;
import password.pwm.PwmApplication;
import password.pwm.PwmApplicationMode;
import password.pwm.PwmConstants;
import password.pwm.bean.SessionLabel;
import password.pwm.config.Configuration;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.util.java.FileSystemUtility;
import password.pwm.util.java.JsonUtil;
import password.pwm.util.java.TimeDuration;
import password.pwm.util.logging.PwmLogger;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.Date;
import java.util.List;

/**
 * Read the PWM configuration.
 *
 * @author Jason D. Rivard
 */
public class ConfigurationReader {
    // ------------------------------ FIELDS ------------------------------

    private static final PwmLogger LOGGER = PwmLogger.getLogger(ConfigurationReader.class.getName());

    private final File configFile;
    private final String configFileChecksum;
    private Configuration configuration;
    private StoredConfigurationImpl storedConfiguration;
    private ErrorInformation configFileError;

    private Date configurationReadTime;

    private PwmApplicationMode configMode = PwmApplicationMode.NEW;

    private volatile boolean saveInProgress;

    public ConfigurationReader(final File configFile) throws PwmUnrecoverableException {
        this.configFile = configFile;

        this.configFileChecksum = readFileChecksum(configFile);
        try {
            this.storedConfiguration = readStoredConfig();
            this.configFileError = null;
        } catch (PwmUnrecoverableException e) {
            this.configFileError = e.getErrorInformation();
            LOGGER.warn("error reading configuration file: " + e.getMessage());
        }

        if (storedConfiguration == null) {
            this.storedConfiguration = StoredConfigurationImpl.newStoredConfiguration();
        }

        LOGGER.debug("configuration mode: " + configMode);
    }

    public PwmApplicationMode getConfigMode() {
        return configMode;
    }

    public StoredConfigurationImpl getStoredConfiguration() {
        return storedConfiguration;
    }

    public Configuration getConfiguration() throws PwmUnrecoverableException {
        if (configuration == null) {
            configuration = new Configuration(
                    this.storedConfiguration == null ? StoredConfigurationImpl.newStoredConfiguration()
                            : this.storedConfiguration);
            storedConfiguration.lock();
        }
        return configuration;
    }

    private StoredConfigurationImpl readStoredConfig() throws PwmUnrecoverableException {
        LOGGER.debug("loading configuration file: " + configFile);

        configurationReadTime = new Date();

        if (!configFile.exists()) {
            LOGGER.warn("configuration file '" + configFile.getAbsolutePath() + "' does not exist");
            return null;
        }

        final Instant startTime = Instant.now();
        final InputStream theFileData;
        try {
            final byte[] contents = FileUtils.readFileToByteArray(configFile);
            theFileData = new ByteArrayInputStream(contents);
        } catch (Exception e) {
            final String errorMsg = "unable to read configuration file: " + e.getMessage();
            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null,
                    new String[] { errorMsg });
            this.configMode = PwmApplicationMode.ERROR;
            throw new PwmUnrecoverableException(errorInformation);
        }

        final StoredConfigurationImpl storedConfiguration;
        try {
            storedConfiguration = StoredConfigurationImpl.fromXml(theFileData);
            //restoredConfiguration = (new NGStoredConfigurationFactory()).fromXml(theFileData);
        } catch (PwmUnrecoverableException e) {
            final String errorMsg = "unable to parse configuration file: " + e.getMessage();
            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null,
                    new String[] { errorMsg });
            this.configMode = PwmApplicationMode.ERROR;
            throw new PwmUnrecoverableException(errorInformation);
        }

        final List<String> validationErrorMsgs = storedConfiguration.validateValues();
        if (validationErrorMsgs != null && !validationErrorMsgs.isEmpty()) {
            final String errorMsg = "value error in config file, please investigate: " + validationErrorMsgs.get(0);
            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null,
                    new String[] { errorMsg });
            this.configMode = PwmApplicationMode.ERROR;
            throw new PwmUnrecoverableException(errorInformation);
        }

        final String configIsEditable = storedConfiguration
                .readConfigProperty(ConfigurationProperty.CONFIG_IS_EDITABLE);
        if (PwmConstants.TRIAL_MODE || (configIsEditable != null && "true".equalsIgnoreCase(configIsEditable))) {
            this.configMode = PwmApplicationMode.CONFIGURATION;
        } else {
            this.configMode = PwmApplicationMode.RUNNING;
        }

        LOGGER.debug(
                "configuration reading/parsing complete in " + TimeDuration.fromCurrent(startTime).asLongString());

        return storedConfiguration;
    }

    public void saveConfiguration(final StoredConfigurationImpl storedConfiguration,
            final PwmApplication pwmApplication, final SessionLabel sessionLabel)
            throws IOException, PwmUnrecoverableException, PwmOperationalException {
        File backupDirectory = null;
        int backupRotations = 0;
        if (pwmApplication != null) {
            final Configuration configuration = new Configuration(storedConfiguration);
            final String backupDirSetting = configuration.readAppProperty(AppProperty.BACKUP_LOCATION);
            if (backupDirSetting != null && backupDirSetting.length() > 0) {
                final File pwmPath = pwmApplication.getPwmEnvironment().getApplicationPath();
                backupDirectory = FileSystemUtility.figureFilepath(backupDirSetting, pwmPath);
            }
            backupRotations = Integer.parseInt(configuration.readAppProperty(AppProperty.BACKUP_CONFIG_COUNT));
        }

        { // increment the config epoch
            String epochStrValue = storedConfiguration.readConfigProperty(ConfigurationProperty.CONFIG_EPOCH);
            try {
                final BigInteger epochValue = epochStrValue == null || epochStrValue.length() < 0 ? BigInteger.ZERO
                        : new BigInteger(epochStrValue);
                epochStrValue = epochValue.add(BigInteger.ONE).toString();
            } catch (Exception e) {
                LOGGER.error(sessionLabel,
                        "error trying to parse previous config epoch property: " + e.getMessage());
                epochStrValue = "0";
            }
            storedConfiguration.writeConfigProperty(ConfigurationProperty.CONFIG_EPOCH, epochStrValue);
        }

        if (backupDirectory != null && !backupDirectory.exists()) {
            if (!backupDirectory.mkdirs()) {
                throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_UNKNOWN,
                        "unable to create backup directory structure '" + backupDirectory.toString() + "'"));
            }
        }

        try {
            final File tempWriteFile = new File(configFile.getAbsoluteFile() + ".new");
            LOGGER.info(sessionLabel, "beginning write to configuration file " + tempWriteFile);
            saveInProgress = true;

            storedConfiguration.toXml(new FileOutputStream(tempWriteFile, false));
            LOGGER.info("saved configuration " + JsonUtil.serialize(storedConfiguration.toJsonDebugObject()));
            if (pwmApplication != null) {
                final String actualChecksum = storedConfiguration.settingChecksum();
                pwmApplication.writeAppAttribute(PwmApplication.AppAttribute.CONFIG_HASH, actualChecksum);
            }

            LOGGER.trace(
                    "renaming file " + tempWriteFile.getAbsolutePath() + " to " + configFile.getAbsolutePath());
            try {
                Files.move(tempWriteFile.toPath(), configFile.toPath(), StandardCopyOption.REPLACE_EXISTING,
                        StandardCopyOption.ATOMIC_MOVE);
            } catch (Exception e) {
                final String errorMsg = "unable to rename temporary save file from "
                        + tempWriteFile.getAbsolutePath() + " to " + configFile.getAbsolutePath() + "; error: "
                        + e.getMessage();
                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg));
            }

            if (backupDirectory != null) {
                final String configFileName = configFile.getName();
                final String backupFilePath = backupDirectory.getAbsolutePath() + File.separatorChar
                        + configFileName + "-backup";
                final File backupFile = new File(backupFilePath);
                FileSystemUtility.rotateBackups(backupFile, backupRotations);
                storedConfiguration.toXml(new FileOutputStream(backupFile, false));
            }
        } finally {
            saveInProgress = false;
        }
    }

    public boolean modifiedSinceLoad() {
        final String currentChecksum = readFileChecksum(configFile);
        return !currentChecksum.equals(configFileChecksum);
    }

    private static String readFileChecksum(final File file) {
        if (!file.exists()) {
            return "";
        }

        return String.valueOf(file.lastModified() + String.valueOf(file.length()));
    }

    public Date getConfigurationReadTime() {
        return configurationReadTime;
    }

    public ErrorInformation getConfigFileError() {
        return configFileError;
    }

    public File getConfigFile() {
        return configFile;
    }

    public boolean isSaveInProgress() {
        return saveInProgress;
    }
}