/*
* 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.http.servlet.configeditor;
import com.novell.ldapchai.exception.ChaiUnavailableException;
import password.pwm.AppProperty;
import password.pwm.PwmApplicationMode;
import password.pwm.PwmConstants;
import password.pwm.bean.SmsItemBean;
import password.pwm.bean.UserIdentity;
import password.pwm.config.Configuration;
import password.pwm.config.PwmSetting;
import password.pwm.config.PwmSettingCategory;
import password.pwm.config.PwmSettingSyntax;
import password.pwm.config.PwmSettingTemplate;
import password.pwm.config.PwmSettingTemplateSet;
import password.pwm.config.SettingUIFunction;
import password.pwm.config.StoredValue;
import password.pwm.config.stored.ConfigurationProperty;
import password.pwm.config.stored.StoredConfigurationImpl;
import password.pwm.config.stored.ValueMetaData;
import password.pwm.config.value.ActionValue;
import password.pwm.config.value.FileValue;
import password.pwm.config.value.PrivateKeyValue;
import password.pwm.config.value.ValueFactory;
import password.pwm.config.value.X509CertificateValue;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmException;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.health.ConfigurationChecker;
import password.pwm.health.DatabaseStatusChecker;
import password.pwm.health.HealthRecord;
import password.pwm.health.HealthStatus;
import password.pwm.health.HealthTopic;
import password.pwm.health.LDAPStatusChecker;
import password.pwm.http.HttpMethod;
import password.pwm.http.JspUrl;
import password.pwm.http.PwmHttpRequestWrapper;
import password.pwm.http.PwmRequest;
import password.pwm.http.PwmSession;
import password.pwm.http.bean.ConfigManagerBean;
import password.pwm.http.servlet.AbstractPwmServlet;
import password.pwm.http.servlet.configmanager.ConfigManagerServlet;
import password.pwm.i18n.Message;
import password.pwm.i18n.PwmLocaleBundle;
import password.pwm.ldap.LdapBrowser;
import password.pwm.util.PasswordData;
import password.pwm.util.Validator;
import password.pwm.util.java.JavaHelper;
import password.pwm.util.java.JsonUtil;
import password.pwm.util.java.StringUtil;
import password.pwm.util.java.TimeDuration;
import password.pwm.util.logging.PwmLogger;
import password.pwm.util.macro.MacroMachine;
import password.pwm.util.queue.SmsQueueManager;
import password.pwm.util.secure.HttpsServerCertificateManager;
import password.pwm.ws.server.RestResultBean;
import password.pwm.ws.server.rest.bean.HealthData;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
@WebServlet(
name = "ConfigEditorServlet",
urlPatterns = {
PwmConstants.URL_PREFIX_PRIVATE + "/config/editor",
PwmConstants.URL_PREFIX_PRIVATE + "/config/editor/*",
PwmConstants.URL_PREFIX_PRIVATE + "/config/configeditor",
PwmConstants.URL_PREFIX_PRIVATE + "/config/configeditor/*",
PwmConstants.URL_PREFIX_PRIVATE + "/config/ConfigEditor",
PwmConstants.URL_PREFIX_PRIVATE + "/config/ConfigEditor/*",
}
)
public class ConfigEditorServlet extends AbstractPwmServlet {
// ------------------------------ FIELDS ------------------------------
private static final PwmLogger LOGGER = PwmLogger.forClass(ConfigEditorServlet.class);
public enum ConfigEditorAction implements AbstractPwmServlet.ProcessAction {
readSetting(HttpMethod.POST),
writeSetting(HttpMethod.POST),
resetSetting(HttpMethod.POST),
ldapHealthCheck(HttpMethod.POST),
databaseHealthCheck(HttpMethod.POST),
smsHealthCheck(HttpMethod.POST),
finishEditing(HttpMethod.POST),
executeSettingFunction(HttpMethod.POST),
setConfigurationPassword(HttpMethod.POST),
readChangeLog(HttpMethod.POST),
search(HttpMethod.POST),
cancelEditing(HttpMethod.POST),
uploadFile(HttpMethod.POST),
setOption(HttpMethod.POST),
menuTreeData(HttpMethod.POST),
settingData(HttpMethod.GET),
testMacro(HttpMethod.POST),
browseLdap(HttpMethod.POST),
copyProfile(HttpMethod.POST),
;
private final HttpMethod method;
ConfigEditorAction(final HttpMethod method) {
this.method = method;
}
public Collection<HttpMethod> permittedMethods() {
return Collections.singletonList(method);
}
}
protected ConfigEditorAction readProcessAction(final PwmRequest request)
throws PwmUnrecoverableException {
try {
return ConfigEditorAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
} catch (IllegalArgumentException e) {
return null;
}
}
protected void processAction(final PwmRequest pwmRequest)
throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException
{
final ConfigManagerBean configManagerBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, ConfigManagerBean.class);
if (configManagerBean.getStoredConfiguration() == null) {
final StoredConfigurationImpl loadedConfig = ConfigManagerServlet.readCurrentConfiguration(pwmRequest);
configManagerBean.setConfiguration(loadedConfig);
}
final ConfigEditorAction action = readProcessAction(pwmRequest);
if (action != null) {
Validator.validatePwmFormID(pwmRequest);
switch (action) {
case readSetting:
restReadSetting(pwmRequest, configManagerBean);
return;
case writeSetting:
restWriteSetting(pwmRequest, configManagerBean);
return;
case resetSetting:
restResetSetting(pwmRequest, configManagerBean);
return;
case ldapHealthCheck:
restLdapHealthCheck(pwmRequest, configManagerBean);
return;
case databaseHealthCheck:
restDatabaseHealthCheck(pwmRequest, configManagerBean);
return;
case smsHealthCheck:
restSmsHealthCheck(pwmRequest, configManagerBean);
return;
case finishEditing:
restFinishEditing(pwmRequest, configManagerBean);
return;
case executeSettingFunction:
restExecuteSettingFunction(pwmRequest, configManagerBean);
return;
case setConfigurationPassword:
restSetConfigurationPassword(pwmRequest, configManagerBean);
return;
case readChangeLog:
restReadChangeLog(pwmRequest, configManagerBean);
return;
case search:
restSearchSettings(pwmRequest, configManagerBean);
return;
case cancelEditing:
restCancelEditing(pwmRequest, configManagerBean);
return;
case uploadFile:
doUploadFile(pwmRequest, configManagerBean);
return;
case setOption:
setOptions(pwmRequest, configManagerBean);
return;
case menuTreeData:
restMenuTreeData(pwmRequest, configManagerBean);
return;
case settingData:
restConfigSettingData(pwmRequest, configManagerBean);
return;
case testMacro:
restTestMacro(pwmRequest);
return;
case browseLdap:
restBrowseLdap(pwmRequest, configManagerBean);
return;
case copyProfile:
restCopyProfile(pwmRequest, configManagerBean);
return;
default:
JavaHelper.unhandledSwitchStatement(action);
}
}
if (!pwmRequest.getPwmResponse().isCommitted()) {
pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_EDITOR);
}
}
private void restExecuteSettingFunction(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException {
final String bodyString = pwmRequest.readRequestBodyAsString();
final Map<String, String> requestMap = JsonUtil.deserializeStringMap(bodyString);
final PwmSetting pwmSetting = PwmSetting.forKey(requestMap.get("setting"));
final String functionName = requestMap.get("function");
final String profileID = pwmSetting.getCategory().hasProfiles() ? pwmRequest.readParameterAsString("profile") : null;
final String extraData = requestMap.get("extraData");
try {
final Class implementingClass = Class.forName(functionName);
final SettingUIFunction function = (SettingUIFunction) implementingClass.newInstance();
final Serializable result = function.provideFunction(pwmRequest, configManagerBean.getStoredConfiguration(), pwmSetting, profileID, extraData);
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setSuccessMessage(Message.Success_Unknown.getLocalizedMessage(pwmRequest.getLocale(),pwmRequest.getConfig()));
restResultBean.setData(result);
pwmRequest.outputJsonResult(restResultBean);
} catch (Exception e) {
final RestResultBean restResultBean;
if (e instanceof PwmException) {
restResultBean = RestResultBean.fromError(((PwmException) e).getErrorInformation(), pwmRequest, true);
} else {
restResultBean = new RestResultBean();
restResultBean.setError(true);
restResultBean.setErrorDetail(e.getMessage());
restResultBean.setErrorMessage("error performing user search: " + e.getMessage());
}
pwmRequest.outputJsonResult(restResultBean);
}
}
private void restReadSetting(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException
{
final StoredConfigurationImpl storedConfig = configManagerBean.getStoredConfiguration();
final String key = pwmRequest.readParameterAsString("key");
final Object returnValue;
final LinkedHashMap<String, Object> returnMap = new LinkedHashMap<>();
final PwmSetting theSetting = PwmSetting.forKey(key);
if (key.startsWith("localeBundle")) {
final StringTokenizer st = new StringTokenizer(key, "-");
st.nextToken();
final PwmLocaleBundle bundleName = PwmLocaleBundle.valueOf(st.nextToken());
final String keyName = st.nextToken();
final Map<String, String> bundleMap = storedConfig.readLocaleBundleMap(bundleName.getTheClass().getName(), keyName);
if (bundleMap == null || bundleMap.isEmpty()) {
final Map<String, String> defaultValueMap = new LinkedHashMap<>();
final String defaultLocaleValue = ResourceBundle.getBundle(bundleName.getTheClass().getName(), PwmConstants.DEFAULT_LOCALE).getString(keyName);
for (final Locale locale : pwmRequest.getConfig().getKnownLocales()) {
final ResourceBundle localeBundle = ResourceBundle.getBundle(bundleName.getTheClass().getName(), locale);
if (locale.toString().equalsIgnoreCase(PwmConstants.DEFAULT_LOCALE.toString())) {
defaultValueMap.put("", defaultLocaleValue);
} else {
final String valueStr = localeBundle.getString(keyName);
if (!defaultLocaleValue.equals(valueStr)) {
final String localeStr = locale.toString();
defaultValueMap.put(localeStr, localeBundle.getString(keyName));
}
}
}
returnValue = defaultValueMap;
returnMap.put("isDefault", true);
} else {
returnValue = bundleMap;
returnMap.put("isDefault", false);
}
returnMap.put("key", key);
} else if (theSetting == null) {
final String errorStr = "readSettingAsString request for unknown key: " + key;
LOGGER.warn(errorStr);
pwmRequest.outputJsonResult(RestResultBean.fromError(new ErrorInformation(PwmError.ERROR_UNKNOWN, errorStr)));
return;
} else {
final String profile = theSetting.getCategory().hasProfiles() ? pwmRequest.readParameterAsString("profile") : null;
switch (theSetting.getSyntax()) {
case PASSWORD:
returnValue = Collections.singletonMap("isDefault", storedConfig.isDefaultValue(theSetting, profile));
break;
case X509CERT:
returnValue = ((X509CertificateValue) storedConfig.readSetting(theSetting, profile)).toInfoMap(true);
break;
case PRIVATE_KEY:
returnValue = ((PrivateKeyValue) storedConfig.readSetting(theSetting, profile)).toInfoMap(true);
break;
case ACTION:
returnValue = ((ActionValue)storedConfig.readSetting(theSetting, profile)).toInfoMap();
break;
case FILE:
returnValue = ((FileValue) storedConfig.readSetting(theSetting, profile)).toInfoMap();
break;
default:
returnValue = storedConfig.readSetting(theSetting, profile).toNativeObject();
}
returnMap.put("isDefault", storedConfig.isDefaultValue(theSetting, profile));
if (theSetting.getSyntax() == PwmSettingSyntax.SELECT) {
returnMap.put("options", theSetting.getOptions());
}
{
final ValueMetaData settingMetaData = storedConfig.readSettingMetadata(theSetting, profile);
if (settingMetaData != null) {
if (settingMetaData.getModifyDate() != null) {
returnMap.put("modifyTime", settingMetaData.getModifyDate());
}
if (settingMetaData.getUserIdentity() != null) {
returnMap.put("modifyUser", settingMetaData.getUserIdentity());
}
}
}
returnMap.put("key", key);
returnMap.put("category", theSetting.getCategory().toString());
returnMap.put("syntax", theSetting.getSyntax().toString());
}
returnMap.put("value", returnValue);
pwmRequest.outputJsonResult(new RestResultBean(returnMap));
}
private void restWriteSetting(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException {
final StoredConfigurationImpl storedConfig = configManagerBean.getStoredConfiguration();
final String key = pwmRequest.readParameterAsString("key");
final String bodyString = pwmRequest.readRequestBodyAsString();
final PwmSetting setting = PwmSetting.forKey(key);
final LinkedHashMap<String, Object> returnMap = new LinkedHashMap<>();
final UserIdentity loggedInUser = pwmRequest.getPwmSession().isAuthenticated()
? pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity()
: null;
if (key.startsWith("localeBundle")) {
final StringTokenizer st = new StringTokenizer(key, "-");
st.nextToken();
final PwmLocaleBundle bundleName = PwmLocaleBundle.valueOf(st.nextToken());
final String keyName = st.nextToken();
final Map<String, String> valueMap = JsonUtil.deserializeStringMap(bodyString);
final Map<String, String> outputMap = new LinkedHashMap<>(valueMap);
storedConfig.writeLocaleBundleMap(bundleName.getTheClass().getName(), keyName, outputMap);
returnMap.put("isDefault", outputMap.isEmpty());
returnMap.put("key", key);
} else {
final String profileID = setting.getCategory().hasProfiles() ? pwmRequest.readParameterAsString("profile") : null;
try {
final StoredValue storedValue = ValueFactory.fromJson(setting, bodyString);
final List<String> errorMsgs = storedValue.validateValue(setting);
if (errorMsgs != null && !errorMsgs.isEmpty()) {
returnMap.put("errorMessage", setting.getLabel(pwmRequest.getLocale()) + ": " + errorMsgs.get(0));
}
storedConfig.writeSetting(setting, profileID, storedValue, loggedInUser);
} catch (Exception e) {
final String errorMsg = "error writing default value for setting " + setting.toString() + ", error: " + e.getMessage();
LOGGER.error(errorMsg, e);
throw new IllegalStateException(errorMsg, e);
}
returnMap.put("key", key);
returnMap.put("category", setting.getCategory().toString());
returnMap.put("syntax", setting.getSyntax().toString());
returnMap.put("isDefault", storedConfig.isDefaultValue(setting, profileID));
}
pwmRequest.outputJsonResult(new RestResultBean(returnMap));
}
private void restResetSetting(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException {
final StoredConfigurationImpl storedConfig = configManagerBean.getStoredConfiguration();
final UserIdentity loggedInUser = pwmRequest.getUserInfoIfLoggedIn();
final String key = pwmRequest.readParameterAsString("key");
final PwmSetting setting = PwmSetting.forKey(key);
if (key.startsWith("localeBundle")) {
final StringTokenizer st = new StringTokenizer(key, "-");
st.nextToken();
final PwmLocaleBundle bundleName = PwmLocaleBundle.valueOf(st.nextToken());
final String keyName = st.nextToken();
storedConfig.resetLocaleBundleMap(bundleName.getTheClass().getName(), keyName);
} else {
final String profileID = setting.getCategory().hasProfiles() ? pwmRequest.readParameterAsString("profile") : null;
storedConfig.resetSetting(setting, profileID, loggedInUser);
}
pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
}
private void restSetConfigurationPassword(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, ServletException, PwmUnrecoverableException {
try {
final Map<String, String> postData = pwmRequest.readBodyAsJsonStringMap();
final String password = postData.get("password");
configManagerBean.getStoredConfiguration().setPassword(password);
configManagerBean.setPasswordVerified(true);
LOGGER.debug(pwmRequest, "config password updated");
final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
pwmRequest.outputJsonResult(restResultBean);
} catch (PwmOperationalException e) {
final RestResultBean restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmRequest);
pwmRequest.outputJsonResult(restResultBean);
}
}
private void restFinishEditing(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)
throws IOException, ServletException, PwmUnrecoverableException {
final PwmSession pwmSession = pwmRequest.getPwmSession();
RestResultBean restResultBean = new RestResultBean();
final HashMap<String, String> resultData = new HashMap<>();
restResultBean.setData(resultData);
final List<String> validationErrors = configManagerBean.getStoredConfiguration().validateValues();
if (!validationErrors.isEmpty()) {
final String errorString = validationErrors.get(0);
final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, errorString, new String[]{errorString});
restResultBean = RestResultBean.fromError(errorInfo, pwmRequest);
LOGGER.error(pwmSession, "save configuration aborted, error: " + errorString);
} else {
try {
ConfigManagerServlet.saveConfiguration(pwmRequest, configManagerBean.getStoredConfiguration());
configManagerBean.setConfiguration(null);
restResultBean.setError(false);
configManagerBean.setConfiguration(null);
LOGGER.debug(pwmSession, "save configuration operation completed");
} catch (PwmUnrecoverableException e) {
final ErrorInformation errorInfo = e.getErrorInformation();
restResultBean = RestResultBean.fromError(errorInfo, pwmRequest);
LOGGER.warn(pwmSession, "unable to save configuration: " + e.getMessage());
}
}
pwmRequest.outputJsonResult(restResultBean);
}
private void restCancelEditing(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, ServletException, PwmUnrecoverableException {
configManagerBean.setConfiguration(null);
pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
}
private void setOptions(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException {
{
final String updateDescriptionTextCmd = pwmRequest.readParameterAsString("updateNotesText");
if (updateDescriptionTextCmd != null && "true".equalsIgnoreCase(updateDescriptionTextCmd)) {
try {
final String bodyString = pwmRequest.readRequestBodyAsString();
final String value = JsonUtil.deserialize(bodyString, String.class);
configManagerBean.getStoredConfiguration().writeConfigProperty(ConfigurationProperty.NOTES,
value);
LOGGER.trace("updated notesText");
} catch (Exception e) {
LOGGER.error("error updating notesText: " + e.getMessage());
}
}
{
final String requestedTemplate = pwmRequest.readParameterAsString("template");
if (requestedTemplate != null && requestedTemplate.length() > 0) {
try {
final PwmSettingTemplate template = PwmSettingTemplate.valueOf(requestedTemplate);
configManagerBean.getStoredConfiguration().writeConfigProperty(
ConfigurationProperty.LDAP_TEMPLATE, template.toString());
LOGGER.trace("setting template to: " + requestedTemplate);
} catch (IllegalArgumentException e) {
configManagerBean.getStoredConfiguration().writeConfigProperty(
ConfigurationProperty.LDAP_TEMPLATE, PwmSettingTemplate.DEFAULT.toString());
LOGGER.error("unknown template set request: " + requestedTemplate);
}
}
}
}
}
void restReadChangeLog(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException {
final Locale locale = pwmRequest.getLocale();
final HashMap<String, Object> returnObj = new HashMap<>();
returnObj.put("html", configManagerBean.getStoredConfiguration().changeLogAsDebugString(locale, true));
returnObj.put("modified", configManagerBean.getStoredConfiguration().isModified());
try {
final ConfigurationChecker configurationChecker = new ConfigurationChecker();
final Configuration config = new Configuration(configManagerBean.getStoredConfiguration());
final List<HealthRecord> healthRecords = configurationChecker.doHealthCheck(
config,
pwmRequest.getLocale()
);
final HealthData healthData = new HealthData();
healthData.setOverall("CONFIG");
healthData.setRecords(password.pwm.ws.server.rest.bean.HealthRecord.fromHealthRecords(healthRecords, locale, config));
returnObj.put("health", healthData);
} catch (Exception e) {
LOGGER.error(pwmRequest, "error generating health records: " + e.getMessage());
}
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setData(returnObj);
pwmRequest.outputJsonResult(restResultBean);
}
void restSearchSettings(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException
{
final Instant startTime = Instant.now();
final String bodyData = pwmRequest.readRequestBodyAsString();
final Map<String, String> valueMap = JsonUtil.deserializeStringMap(bodyData);
final Locale locale = pwmRequest.getLocale();
final RestResultBean restResultBean = new RestResultBean();
final String searchTerm = valueMap.get("search");
final StoredConfigurationImpl storedConfiguration = configManagerBean.getStoredConfiguration();
if (searchTerm != null && !searchTerm.isEmpty()) {
final ArrayList<StoredConfigurationImpl.ConfigRecordID> searchResults = new ArrayList<>(configManagerBean.getStoredConfiguration().search(searchTerm, locale));
final ConcurrentHashMap<String, Map<String, SearchResultItem>> returnData = new ConcurrentHashMap<>();
searchResults
.parallelStream()
.filter(recordID -> recordID.getRecordType() == StoredConfigurationImpl.ConfigRecordID.RecordType.SETTING)
.forEach(recordID -> {
final PwmSetting setting = (PwmSetting) recordID.getRecordID();
final SearchResultItem item = new SearchResultItem(
setting.getCategory().toString(),
storedConfiguration.readSetting(setting, recordID.getProfileID()).toDebugString(locale),
setting.getCategory().toMenuLocationDebug(recordID.getProfileID(), locale),
storedConfiguration.isDefaultValue(setting, recordID.getProfileID()),
recordID.getProfileID()
);
final String returnCategory = item.getNavigation();
if (!returnData.containsKey(returnCategory)) {
returnData.put(returnCategory, new ConcurrentHashMap<>());
}
returnData.get(returnCategory).put(setting.getKey(), item);
});
final TreeMap<String, Map<String, SearchResultItem>> outputMap = new TreeMap<>();
for (final String key : returnData.keySet()) {
outputMap.put(key, new TreeMap<>(returnData.get(key)));
}
restResultBean.setData(returnData);
LOGGER.trace(pwmRequest, "finished search operation with " + returnData.size() + " results in " + TimeDuration.fromCurrent(startTime).asCompactString());
} else {
restResultBean.setData(new ArrayList<StoredConfigurationImpl.ConfigRecordID>());
}
pwmRequest.outputJsonResult(restResultBean);
}
private void restLdapHealthCheck(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException
{
final Instant startTime = Instant.now();
LOGGER.debug(pwmRequest, "beginning restLdapHealthCheck");
final String profileID = pwmRequest.readParameterAsString("profile");
final Configuration config = new Configuration(configManagerBean.getStoredConfiguration());
final HealthData healthData = LDAPStatusChecker.healthForNewConfiguration(pwmRequest.getPwmApplication(), config, pwmRequest.getLocale(), profileID, true, true);
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setData(healthData);
pwmRequest.outputJsonResult(restResultBean);
LOGGER.debug(pwmRequest, "completed restLdapHealthCheck in " + TimeDuration.fromCurrent(startTime).asCompactString());
}
private void restDatabaseHealthCheck(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException {
final Instant startTime = Instant.now();
LOGGER.debug(pwmRequest, "beginning restDatabaseHealthCheck");
final Configuration config = new Configuration(configManagerBean.getStoredConfiguration());
final List<HealthRecord> healthRecords = DatabaseStatusChecker.checkNewDatabaseStatus(pwmRequest.getPwmApplication(), config);
final HealthData healthData = HealthRecord.asHealthDataBean(config, pwmRequest.getLocale(), healthRecords);
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setData(healthData);
pwmRequest.outputJsonResult(restResultBean);
LOGGER.debug(pwmRequest, "completed restDatabaseHealthCheck in " + TimeDuration.fromCurrent(startTime).asCompactString());
}
private void restSmsHealthCheck(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException {
final Instant startTime = Instant.now();
LOGGER.debug(pwmRequest, "beginning restSmsHealthCheck");
final List<HealthRecord> returnRecords = new ArrayList<>();
final Configuration config = new Configuration(configManagerBean.getStoredConfiguration());
if (!SmsQueueManager.smsIsConfigured(config)) {
returnRecords.add(new HealthRecord(HealthStatus.INFO, HealthTopic.SMS, "SMS not configured"));
} else {
final Map<String, String> testParams = pwmRequest.readBodyAsJsonStringMap();
final SmsItemBean testSmsItem = new SmsItemBean(testParams.get("to"), testParams.get("message"));
try {
final String responseBody = SmsQueueManager.sendDirectMessage(config, testSmsItem);
returnRecords.add(new HealthRecord(HealthStatus.INFO, HealthTopic.SMS, "message sent"));
returnRecords.add(new HealthRecord(HealthStatus.INFO, HealthTopic.SMS, "response body: \n" + StringUtil.escapeHtml(responseBody)));
} catch (PwmException e) {
returnRecords.add(new HealthRecord(HealthStatus.WARN, HealthTopic.SMS, "unable to send message: " + e.getMessage()));
}
}
final RestResultBean restResultBean = new RestResultBean();
final HealthData healthData = HealthRecord.asHealthDataBean(config, pwmRequest.getLocale(), returnRecords);
restResultBean.setData(healthData);
pwmRequest.outputJsonResult(restResultBean);
LOGGER.debug(pwmRequest, "completed restSmsHealthCheck in " + TimeDuration.fromCurrent(startTime).asCompactString());
}
private void doUploadFile(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws PwmUnrecoverableException, IOException, ServletException
{
final String key = pwmRequest.readParameterAsString("key");
final PwmSetting setting = PwmSetting.forKey(key);
final int maxFileSize = Integer.parseInt(pwmRequest.getConfig().readAppProperty(AppProperty.CONFIG_MAX_JDBC_JAR_SIZE));
if (setting == PwmSetting.HTTPS_CERT) {
try {
final PasswordData passwordData = pwmRequest.readParameterAsPassword("password");
final String alias = pwmRequest.readParameterAsString("alias");
final HttpsServerCertificateManager.KeyStoreFormat keyStoreFormat;
try {
keyStoreFormat = HttpsServerCertificateManager.KeyStoreFormat.valueOf(pwmRequest.readParameterAsString("format"));
} catch (IllegalArgumentException e) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "unknown format type: " + e.getMessage(), new String[]{"format"}));
}
final Map<String, PwmRequest.FileUploadItem> fileUploads = pwmRequest.readFileUploads(maxFileSize, 1);
final ByteArrayInputStream fileIs = new ByteArrayInputStream(fileUploads.get(PwmConstants.PARAM_FILE_UPLOAD).getContent());
HttpsServerCertificateManager.importKey(
configManagerBean.getStoredConfiguration(),
keyStoreFormat,
fileIs,
passwordData,
alias
);
pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
return;
} catch (PwmException e) {
LOGGER.error(pwmRequest, "error during https certificate upload: " + e.getMessage());
pwmRequest.respondWithError(e.getErrorInformation(),false);
return;
}
}
final FileValue fileValue = readFileUploadToSettingValue(pwmRequest, maxFileSize);
if (fileValue != null) {
final UserIdentity userIdentity = pwmRequest.isAuthenticated()
? pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity()
: null;
configManagerBean.getStoredConfiguration().writeSetting(setting, fileValue, userIdentity);
pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
}
}
public static FileValue readFileUploadToSettingValue(
final PwmRequest pwmRequest,
final int maxFileSize
)
throws PwmUnrecoverableException, IOException, ServletException
{
final Map<String, PwmRequest.FileUploadItem> fileUploads;
try {
fileUploads = pwmRequest.readFileUploads(maxFileSize, 1);
} catch (PwmException e) {
pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
LOGGER.error(pwmRequest, "error during file upload: " + e.getErrorInformation().toDebugStr());
return null;
} catch (Throwable e) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, "error during file upload: " + e.getMessage());
pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
LOGGER.error(pwmRequest, errorInformation);
return null;
}
if (fileUploads.containsKey(PwmConstants.PARAM_FILE_UPLOAD)) {
final PwmRequest.FileUploadItem uploadItem = fileUploads.get(PwmConstants.PARAM_FILE_UPLOAD);
final Map<FileValue.FileInformation, FileValue.FileContent> newFileValueMap = new LinkedHashMap<>();
newFileValueMap.put(new FileValue.FileInformation(uploadItem.getName(), uploadItem.getType()), new FileValue.FileContent(uploadItem.getContent()));
return new FileValue(newFileValueMap);
}
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, "no file found in upload");
pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
LOGGER.error(pwmRequest, "error during file upload: " + errorInformation.toDebugStr());
return null;
}
private void restMenuTreeData(
final PwmRequest pwmRequest,
final ConfigManagerBean configManagerBean
)
throws IOException, PwmUnrecoverableException
{
final Instant startTime = Instant.now();
final ArrayList<NavTreeItem> navigationData = new ArrayList<>();
final Map<String,Object> inputParameters = pwmRequest.readBodyAsJsonMap(false);
final boolean modifiedSettingsOnly = (boolean)inputParameters.get("modifiedSettingsOnly");
final double level = (double)inputParameters.get("level");
final String filterText = (String)inputParameters.get("text");
{ // root node
final NavTreeItem categoryInfo = new NavTreeItem();
categoryInfo.setId("ROOT");
categoryInfo.setName("ROOT");
navigationData.add(categoryInfo);
}
{
final StoredConfigurationImpl storedConfiguration = configManagerBean.getStoredConfiguration();
final List<PwmSettingCategory> categories = NavTreeHelper.filteredCategories(
pwmRequest.getPwmApplication(),
storedConfiguration,
pwmRequest.getLocale(),
modifiedSettingsOnly,
level,
filterText
);
navigationData.addAll(NavTreeHelper.makeSettingNavItems(categories, storedConfiguration, pwmRequest.getLocale()));
}
boolean includeDisplayText = true;
if (level >= 1) {
for (final PwmLocaleBundle localeBundle : PwmLocaleBundle.values()) {
if (!localeBundle.isAdminOnly()) {
final Set<String> modifiedKeys = new TreeSet<>();
if (modifiedSettingsOnly) {
modifiedKeys.addAll(NavTreeHelper.determineModifiedKeysSettings(localeBundle, pwmRequest.getConfig(), configManagerBean.getStoredConfiguration()));
}
if (!modifiedSettingsOnly || !modifiedKeys.isEmpty()) {
final NavTreeItem categoryInfo = new NavTreeItem();
categoryInfo.setId(localeBundle.toString());
categoryInfo.setName(localeBundle.getTheClass().getSimpleName());
categoryInfo.setParent("DISPLAY_TEXT");
categoryInfo.setType(NavTreeHelper.NavItemType.displayText);
categoryInfo.setKeys(new TreeSet<>(modifiedSettingsOnly ? modifiedKeys : localeBundle.getKeys()));
navigationData.add(categoryInfo);
includeDisplayText = true;
}
}
}
}
if (includeDisplayText) {
final NavTreeItem categoryInfo = new NavTreeItem();
categoryInfo.setId("DISPLAY_TEXT");
categoryInfo.setName("Display Text");
categoryInfo.setType(NavTreeHelper.NavItemType.navigation);
categoryInfo.setParent("ROOT");
navigationData.add(categoryInfo);
}
NavTreeHelper.moveNavItemToTopOfList(PwmSettingCategory.NOTES.toString(), navigationData);
NavTreeHelper.moveNavItemToTopOfList(PwmSettingCategory.TEMPLATES.toString(), navigationData);
LOGGER.trace(pwmRequest,"completed navigation tree data request in " + TimeDuration.fromCurrent(startTime).asCompactString());
pwmRequest.outputJsonResult(new RestResultBean(navigationData));
}
private void restConfigSettingData(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)
throws IOException, PwmUnrecoverableException {
final PwmSettingTemplateSet template = configManagerBean.getStoredConfiguration().getTemplateSet();
final LinkedHashMap<String, Object> returnMap = new LinkedHashMap<>();
final Locale locale = pwmRequest.getLocale();
{
final LinkedHashMap<String, Object> settingMap = new LinkedHashMap<>();
for (final PwmSetting setting : PwmSetting.values()) {
settingMap.put(setting.getKey(), SettingInfo.forSetting(setting, template, locale));
}
returnMap.put("settings", settingMap);
}
{
final LinkedHashMap<String, Object> categoryMap = new LinkedHashMap<>();
for (final PwmSettingCategory category : PwmSettingCategory.values()) {
categoryMap.put(category.getKey(), CategoryInfo.forCategory(category, locale));
}
returnMap.put("categories", categoryMap);
}
{
final LinkedHashMap<String, Object> labelMap = new LinkedHashMap<>();
for (final PwmLocaleBundle localeBundle : PwmLocaleBundle.values()) {
final LocaleInfo localeInfo = new LocaleInfo();
localeInfo.description = localeBundle.getTheClass().getSimpleName();
localeInfo.key = localeBundle.toString();
localeInfo.adminOnly = localeBundle.isAdminOnly();
labelMap.put(localeBundle.getTheClass().getSimpleName(), localeInfo);
}
returnMap.put("locales", labelMap);
}
{
final LinkedHashMap<String, Object> varMap = new LinkedHashMap<>();
varMap.put("ldapProfileIds", configManagerBean.getStoredConfiguration().readSetting(PwmSetting.LDAP_PROFILE_LIST).toNativeObject());
varMap.put("currentTemplate",configManagerBean.getStoredConfiguration().getTemplateSet());
if (pwmRequest.getPwmApplication().getApplicationMode() == PwmApplicationMode.CONFIGURATION && !PwmConstants.TRIAL_MODE) {
if (!configManagerBean.isConfigUnlockedWarningShown()) {
varMap.put("configUnlocked",true);
configManagerBean.setConfigUnlockedWarningShown(true);
}
}
varMap.put("configurationNotes", configManagerBean.getStoredConfiguration().readConfigProperty(ConfigurationProperty.NOTES));
returnMap.put("var", varMap);
}
final RestResultBean restResultBean = new RestResultBean();
restResultBean.setData(returnMap);
pwmRequest.outputJsonResult(restResultBean);
}
private void restTestMacro(final PwmRequest pwmRequest) throws IOException, ServletException {
try {
final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
if (inputMap == null || !inputMap.containsKey("input")) {
pwmRequest.outputJsonResult(new RestResultBean("missing input"));
return;
}
final MacroMachine macroMachine;
if (pwmRequest.isAuthenticated()) {
macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
} else {
macroMachine = MacroMachine.forNonUserSpecific(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
}
final String input = inputMap.get("input");
final String output = macroMachine.expandMacros(input);
pwmRequest.outputJsonResult(new RestResultBean(output));
} catch (PwmUnrecoverableException e) {
LOGGER.error(pwmRequest, e.getErrorInformation());
pwmRequest.respondWithError(e.getErrorInformation());
}
}
private void restBrowseLdap(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)
throws IOException, ServletException, PwmUnrecoverableException
{
final Instant startTime = Instant.now();
final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
final String profile = inputMap.get("profile");
final String dn = inputMap.containsKey("dn") ? inputMap.get("dn") : "";
final LdapBrowser ldapBrowser = new LdapBrowser(configManagerBean.getStoredConfiguration());
final LdapBrowser.LdapBrowseResult result = ldapBrowser.doBrowse(profile, dn);
ldapBrowser.close();
LOGGER.trace(pwmRequest, "performed ldapBrowse operation in "
+ TimeDuration.fromCurrent(startTime).asCompactString()
+ ", result=" + JsonUtil.serialize(result));
pwmRequest.outputJsonResult(new RestResultBean(result));
}
private void restCopyProfile(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)
throws IOException, ServletException, PwmUnrecoverableException
{
final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
final String settingKey = inputMap.get("setting");
final PwmSetting setting = PwmSetting.forKey(settingKey);
PwmSettingCategory category = null;
for (final PwmSettingCategory loopCategory : PwmSettingCategory.values()) {
if (loopCategory.hasProfiles()) {
if (loopCategory.getProfileSetting() == setting) {
category = loopCategory;
}
}
}
final String sourceID = inputMap.get("sourceID");
final String destinationID = inputMap.get("destinationID");
try {
configManagerBean.getStoredConfiguration().copyProfileID(category, sourceID, destinationID, pwmRequest.getUserInfoIfLoggedIn());
pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest,Message.Success_Unknown));
} catch (PwmUnrecoverableException e) {
pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
}
}
}