/*
* 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.health;
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.config.PwmSetting;
import password.pwm.config.PwmSettingSyntax;
import password.pwm.config.option.DataStorageMethod;
import password.pwm.config.profile.LdapProfile;
import password.pwm.config.profile.NewUserProfile;
import password.pwm.config.profile.PwmPasswordPolicy;
import password.pwm.error.PwmException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.i18n.Config;
import password.pwm.util.LocaleHelper;
import password.pwm.util.PasswordData;
import password.pwm.util.logging.PwmLogger;
import password.pwm.util.operations.PasswordUtility;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public class ConfigurationChecker implements HealthChecker {
private static final PwmLogger LOGGER = PwmLogger.forClass(ConfigurationChecker.class);
public List<HealthRecord> doHealthCheck(final PwmApplication pwmApplication) {
if (pwmApplication.getConfig() == null) {
return Collections.emptyList();
}
final Configuration config = pwmApplication.getConfig();
final List<HealthRecord> records = new ArrayList<>();
if (pwmApplication.getApplicationMode() == PwmApplicationMode.CONFIGURATION) {
records.add(HealthRecord.forMessage(HealthMessage.Config_ConfigMode));
}
if (config.readSettingAsBoolean(PwmSetting.NEWUSER_ENABLE)) {
for (final NewUserProfile newUserProfile : config.getNewUserProfiles().values()) {
try {
newUserProfile.getNewUserPasswordPolicy(pwmApplication, PwmConstants.DEFAULT_LOCALE);
} catch (PwmUnrecoverableException e) {
records.add(new HealthRecord(HealthStatus.WARN,HealthTopic.Configuration,e.getMessage()));
}
}
}
records.addAll(doHealthCheck(config, PwmConstants.DEFAULT_LOCALE));
return records;
}
public List<HealthRecord> doHealthCheck(final Configuration config, final Locale locale)
{
final List<HealthRecord> records = new ArrayList<>();
if (config.readSettingAsBoolean(PwmSetting.HIDE_CONFIGURATION_HEALTH_WARNINGS)) {
return records;
}
records.addAll(allChecks(config, locale));
final String siteUrl = config.readSettingAsString(PwmSetting.PWM_SITE_URL);
final String SEPARATOR = LocaleHelper.getLocalizedMessage(locale, Config.Display_SettingNavigationSeparator, null);
try {
if (siteUrl == null || siteUrl.isEmpty() || siteUrl.equals(
PwmSetting.PWM_SITE_URL.getDefaultValue(config.getTemplate()).toNativeObject())) {
records.add(
HealthRecord.forMessage(HealthMessage.Config_NoSiteURL, PwmSetting.PWM_SITE_URL.toMenuLocationDebug(null,locale)));
}
} catch (PwmException e) {
LOGGER.error(SessionLabel.HEALTH_SESSION_LABEL,"error while inspecting site URL setting: " + e.getMessage());
}
if (config.readSettingAsBoolean(PwmSetting.LDAP_ENABLE_WIRE_TRACE)) {
records.add(
HealthRecord.forMessage(HealthMessage.Config_LDAPWireTrace,PwmSetting.LDAP_ENABLE_WIRE_TRACE.toMenuLocationDebug(null,locale)));
}
if (Boolean.parseBoolean(config.readAppProperty(AppProperty.LDAP_PROMISCUOUS_ENABLE))) {
final String appPropertyKey = "AppProperty" + SEPARATOR + AppProperty.LDAP_PROMISCUOUS_ENABLE.getKey();
records.add(HealthRecord.forMessage(HealthMessage.Config_PromiscuousLDAP, appPropertyKey));
}
if (config.readSettingAsBoolean(PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS)) {
records.add(HealthRecord.forMessage(HealthMessage.Config_ShowDetailedErrors,PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS.toMenuLocationDebug(null,locale)));
}
for (final LdapProfile ldapProfile : config.getLdapProfiles().values()) {
final String testUserDN = ldapProfile.readSettingAsString(PwmSetting.LDAP_TEST_USER_DN);
if (testUserDN == null || testUserDN.length() < 1) {
records.add(HealthRecord.forMessage(HealthMessage.Config_AddTestUser,PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(),locale)));
}
}
for (final LdapProfile ldapProfile : config.getLdapProfiles().values()) {
final List<String> ldapServerURLs = ldapProfile.readSettingAsStringArray(PwmSetting.LDAP_SERVER_URLS);
if (ldapServerURLs != null && !ldapServerURLs.isEmpty()) {
for (final String urlStringValue : ldapServerURLs) {
try {
final URI url = new URI(urlStringValue);
final boolean secure = "ldaps".equalsIgnoreCase(url.getScheme());
if (!secure) {
records.add(HealthRecord.forMessage(
HealthMessage.Config_LDAPUnsecure,
PwmSetting.LDAP_SERVER_URLS.toMenuLocationDebug(ldapProfile.getIdentifier(),locale)
));
}
} catch (URISyntaxException e) {
records.add(HealthRecord.forMessage(HealthMessage.Config_ParseError,
e.getMessage(),
PwmSetting.LDAP_SERVER_URLS.toMenuLocationDebug(ldapProfile.getIdentifier(), locale),
urlStringValue
));
}
}
}
}
{
for (final PwmSetting setting : PwmSetting.values()) {
if (setting.getSyntax() == PwmSettingSyntax.PASSWORD) {
if (!setting.getCategory().hasProfiles()) {
if (!config.isDefaultValue(setting)) {
try {
final PasswordData passwordValue = config.readSettingAsPassword(setting);
final int strength = PasswordUtility.judgePasswordStrength(
passwordValue.getStringValue());
if (strength < 50) {
records.add(HealthRecord.forMessage(HealthMessage.Config_WeakPassword,
setting.toMenuLocationDebug(null,locale), String.valueOf(strength)));
}
} catch (Exception e) {
LOGGER.error(SessionLabel.HEALTH_SESSION_LABEL,"error while inspecting setting "
+ setting.toMenuLocationDebug(null,locale) + ", error: " + e.getMessage());
}
}
}
}
}
for (final LdapProfile profile : config.getLdapProfiles().values()) {
final PwmSetting setting = PwmSetting.LDAP_PROXY_USER_PASSWORD;
try {
final PasswordData passwordValue = profile.readSettingAsPassword(setting);
final int strength = PasswordUtility.judgePasswordStrength(passwordValue == null ? null : passwordValue.getStringValue());
if (strength < 50) {
records.add(HealthRecord.forMessage(HealthMessage.Config_WeakPassword,
setting.toMenuLocationDebug(profile.getIdentifier(),locale),
String.valueOf(strength)));
}
} catch (PwmException e) {
LOGGER.error(SessionLabel.HEALTH_SESSION_LABEL,"error while inspecting setting " + setting.toMenuLocationDebug(profile.getIdentifier(),locale) + ", error: " + e.getMessage());
}
}
}
/*
if (config.readSettingAsBoolean(PwmSetting.FORGOTTEN_PASSWORD_ENABLE)) {
if (!config.readSettingAsBoolean(PwmSetting.CHALLENGE_REQUIRE_RESPONSES)) {
if (config.readSettingAsTokenSendMethod(PwmSetting.RECOVERY_TOKEN_SEND_METHOD) == MessageSendMethod.NONE) {
final Collection<FormConfiguration> formSettings = config.readSettingAsForm(PwmSetting.RECOVERY_ATTRIBUTE_FORM);
if (formSettings == null || formSettings.isEmpty()) {
records.add(HealthRecord.forMessage(HealthMessage.Config_NoRecoveryEnabled));
}
}
}
}
*/
return records;
}
private List<HealthRecord> allChecks(
final Configuration config,
final Locale locale
) {
final List<HealthRecord> records = new ArrayList<>();
for (final Class<? extends ConfigHealthCheck> clazz : ALL_CHECKS) {
final ConfigHealthCheck healthCheckClass;
try {
healthCheckClass = clazz.newInstance();
records.addAll(healthCheckClass.healthCheck(config, locale));
} catch (Exception e) {
LOGGER.error("unexpected error during health check operation for class " + clazz.toString() + ", error:" + e.getMessage(),e);
}
}
return records;
}
private static final List<Class<? extends ConfigHealthCheck>> ALL_CHECKS = Collections.unmodifiableList(Arrays.asList(
VerifyPasswordPolicyConfigs.class,
VerifyResponseLdapAttribute.class,
VerifyDbConfiguredIfNeeded.class
));
static class VerifyResponseLdapAttribute implements ConfigHealthCheck {
@Override
public List<HealthRecord> healthCheck(
final Configuration config,
final Locale locale
) {
final List<HealthRecord> records = new ArrayList<>();
final PwmSetting[] interestedSettings = new PwmSetting[]{PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE, PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE};
for (final PwmSetting loopSetting : interestedSettings) {
if (config.getResponseStorageLocations(loopSetting).contains(DataStorageMethod.LDAP)) {
for (final LdapProfile ldapProfile : config.getLdapProfiles().values()) {
final String responseAttr = ldapProfile.readSettingAsString(PwmSetting.CHALLENGE_USER_ATTRIBUTE);
final boolean hasResponseAttribute = responseAttr != null && !responseAttr.isEmpty();
if (!hasResponseAttribute) {
records.add(HealthRecord.forMessage(HealthMessage.Config_MissingLDAPResponseAttr,
loopSetting.toMenuLocationDebug(null, locale),
PwmSetting.CHALLENGE_USER_ATTRIBUTE.toMenuLocationDebug(ldapProfile.getIdentifier(), locale)
));
}
}
}
}
return records;
}
}
static class VerifyDbConfiguredIfNeeded implements ConfigHealthCheck {
@Override
public List<HealthRecord> healthCheck(final Configuration config, final Locale locale) {
final List<HealthRecord> records = new ArrayList<>();
if (!config.hasDbConfigured()) {
if (config.helper().shouldHaveDbConfigured()) {
records.add(HealthRecord.forMessage(HealthMessage.Config_MissingDB));
}
}
if (config.getResponseStorageLocations(PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE).contains(DataStorageMethod.LOCALDB)) {
records.add(HealthRecord.forMessage(
HealthMessage.Config_UsingLocalDBResponseStorage,
PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE.toMenuLocationDebug(null, locale)));
}
if (config.getOtpSecretStorageLocations(PwmSetting.OTP_SECRET_WRITE_PREFERENCE).contains(DataStorageMethod.LOCALDB)) {
records.add(HealthRecord.forMessage(
HealthMessage.Config_UsingLocalDBResponseStorage,
PwmSetting.OTP_SECRET_WRITE_PREFERENCE.toMenuLocationDebug(null,locale)));
}
return records;
}
}
static class VerifyPasswordPolicyConfigs implements ConfigHealthCheck {
@Override
public List<HealthRecord> healthCheck(final Configuration config, final Locale locale) {
final List<HealthRecord> records = new ArrayList<>();
for (final String profileID : config.getPasswordProfileIDs()) {
try {
final PwmPasswordPolicy pwmPasswordPolicy = config.getPasswordPolicy(profileID, locale);
records.addAll(pwmPasswordPolicy.health(locale));
} catch (Exception e) {
LOGGER.error("unexpected error during password policy health check: " + e.getMessage(),e);
}
}
return records;
}
}
interface ConfigHealthCheck {
List<HealthRecord> healthCheck(
Configuration configuration,
Locale locale);
}
}