/* * 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.configmanager; import com.novell.ldapchai.exception.ChaiUnavailableException; import org.apache.commons.csv.CSVPrinter; import org.apache.commons.io.IOUtils; import password.pwm.AppProperty; import password.pwm.Permission; import password.pwm.PwmApplication; import password.pwm.PwmConstants; import password.pwm.config.stored.ConfigurationProperty; import password.pwm.config.stored.ConfigurationReader; import password.pwm.config.stored.StoredConfigurationImpl; import password.pwm.error.ErrorInformation; import password.pwm.error.PwmError; import password.pwm.error.PwmException; import password.pwm.error.PwmUnrecoverableException; import password.pwm.http.ContextManager; import password.pwm.http.HttpHeader; import password.pwm.http.HttpMethod; import password.pwm.http.JspUrl; import password.pwm.http.PwmRequest; import password.pwm.http.PwmRequestAttribute; import password.pwm.http.PwmResponse; import password.pwm.http.PwmSession; import password.pwm.http.bean.ConfigManagerBean; import password.pwm.http.servlet.AbstractPwmServlet; import password.pwm.http.servlet.PwmServletDefinition; import password.pwm.http.servlet.configguide.ConfigGuideServlet; import password.pwm.i18n.Admin; import password.pwm.i18n.Config; import password.pwm.i18n.Display; import password.pwm.svc.PwmService; import password.pwm.svc.event.AuditEvent; import password.pwm.svc.event.AuditRecord; import password.pwm.svc.event.AuditRecordFactory; import password.pwm.util.LDAPPermissionCalculator; import password.pwm.util.LocaleHelper; import password.pwm.util.java.JavaHelper; import password.pwm.util.logging.PwmLogger; import password.pwm.ws.server.RestResultBean; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import java.io.IOException; import java.io.OutputStream; import java.time.Instant; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.zip.ZipOutputStream; @WebServlet( name = "ConfigManagerServlet", urlPatterns = { PwmConstants.URL_PREFIX_PRIVATE + "/config/manager", PwmConstants.URL_PREFIX_PRIVATE + "/config/ConfigManager" } ) public class ConfigManagerServlet extends AbstractPwmServlet { private static final PwmLogger LOGGER = PwmLogger.forClass(ConfigManagerServlet.class); public enum ConfigManagerAction implements ProcessAction { lockConfiguration(HttpMethod.POST), startEditing(HttpMethod.POST), downloadConfig(HttpMethod.GET), generateSupportZip(HttpMethod.GET), uploadConfig(HttpMethod.POST), summary(HttpMethod.GET), permissions(HttpMethod.GET), viewLog(HttpMethod.GET), downloadPermissionCsv(HttpMethod.GET), ; private final HttpMethod method; ConfigManagerAction(final HttpMethod method) { this.method = method; } public Collection<HttpMethod> permittedMethods() { return Collections.singletonList(method); } } protected ConfigManagerAction readProcessAction(final PwmRequest request) throws PwmUnrecoverableException { try { return ConfigManagerAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST)); } catch (IllegalArgumentException e) { return null; } } protected void processAction(final PwmRequest pwmRequest) throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException { final ConfigManagerAction processAction = readProcessAction(pwmRequest); if (processAction != null) { switch (processAction) { case lockConfiguration: restLockConfiguration(pwmRequest); break; case startEditing: doStartEditing(pwmRequest); break; case downloadConfig: doDownloadConfig(pwmRequest); break; case generateSupportZip: doGenerateSupportZip(pwmRequest); break; case uploadConfig: ConfigGuideServlet.restUploadConfig(pwmRequest); return; case summary: showSummary(pwmRequest); return; case permissions: showPermissions(pwmRequest); return; case downloadPermissionCsv: downloadPermissionReportCsv(pwmRequest); return; default: JavaHelper.unhandledSwitchStatement(processAction); } return; } initRequestAttributes(pwmRequest); pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_MODE_CONFIGURATION); } void initRequestAttributes(final PwmRequest pwmRequest) throws PwmUnrecoverableException { final ConfigurationReader configurationReader = pwmRequest.getContextManager().getConfigReader(); pwmRequest.setAttribute(PwmRequestAttribute.PageTitle,LocaleHelper.getLocalizedMessage(Config.Title_ConfigManager, pwmRequest)); pwmRequest.setAttribute(PwmRequestAttribute.ApplicationPath, pwmRequest.getPwmApplication().getPwmEnvironment().getApplicationPath().getAbsolutePath()); pwmRequest.setAttribute(PwmRequestAttribute.ConfigFilename, configurationReader.getConfigFile().getAbsolutePath()); { final Instant lastModifyTime = configurationReader.getStoredConfiguration().modifyTime(); final String output = lastModifyTime == null ? LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable,pwmRequest) : JavaHelper.toIsoDate(lastModifyTime); pwmRequest.setAttribute(PwmRequestAttribute.ConfigLastModified, output); } pwmRequest.setAttribute(PwmRequestAttribute.ConfigHasPassword, LocaleHelper.booleanString(configurationReader.getStoredConfiguration().hasPassword(), pwmRequest.getLocale(), pwmRequest.getConfig())); } private void doStartEditing(final PwmRequest pwmRequest) throws IOException, PwmUnrecoverableException, ServletException { forwardToEditor(pwmRequest); } private void restLockConfiguration(final PwmRequest pwmRequest) throws IOException, ServletException, PwmUnrecoverableException, ChaiUnavailableException { final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final PwmSession pwmSession = pwmRequest.getPwmSession(); if (PwmConstants.TRIAL_MODE) { final String msg = LocaleHelper.getLocalizedMessage(Admin.Notice_TrialRestrictConfig, pwmRequest); final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_TRIAL_VIOLATION, msg); final RestResultBean restResultBean = RestResultBean.fromError(errorInfo, pwmRequest); LOGGER.debug(pwmSession, errorInfo); pwmRequest.outputJsonResult(restResultBean); return; } if (!pwmSession.isAuthenticated()) { final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_AUTHENTICATION_REQUIRED,"You must be authenticated before restricting the configuration"); final RestResultBean restResultBean = RestResultBean.fromError(errorInfo, pwmRequest); LOGGER.debug(pwmSession, errorInfo); pwmRequest.outputJsonResult(restResultBean); return; } if (!pwmSession.getSessionManager().checkPermission(pwmApplication, Permission.PWMADMIN)) { final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"You must be authenticated with admin privileges before restricting the configuration"); final RestResultBean restResultBean = RestResultBean.fromError(errorInfo, pwmRequest); LOGGER.debug(pwmSession, errorInfo); pwmRequest.outputJsonResult(restResultBean); return; } try { final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest); if (!storedConfiguration.hasPassword()) { final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"Please set a configuration password before restricting the configuration"}); final RestResultBean restResultBean = RestResultBean.fromError(errorInfo, pwmRequest); LOGGER.debug(pwmSession, errorInfo); pwmRequest.outputJsonResult(restResultBean); return; } storedConfiguration.writeConfigProperty(ConfigurationProperty.CONFIG_IS_EDITABLE, "false"); saveConfiguration(pwmRequest, storedConfiguration); final ConfigManagerBean configManagerBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, ConfigManagerBean.class); configManagerBean.setConfiguration(null); } catch (PwmException e) { final ErrorInformation errorInfo = e.getErrorInformation(); final RestResultBean restResultBean = RestResultBean.fromError(errorInfo, pwmRequest); LOGGER.debug(pwmSession, errorInfo.toDebugStr()); pwmRequest.outputJsonResult(restResultBean); return; } catch (Exception e) { final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_UNKNOWN,e.getMessage()); final RestResultBean restResultBean = RestResultBean.fromError(errorInfo, pwmRequest); LOGGER.debug(pwmSession, errorInfo.toDebugStr()); pwmRequest.outputJsonResult(restResultBean); return; } final HashMap<String,String> resultData = new HashMap<>(); LOGGER.info(pwmSession, "Configuration Locked"); pwmRequest.outputJsonResult(new RestResultBean(resultData)); } public static void saveConfiguration( final PwmRequest pwmRequest, final StoredConfigurationImpl storedConfiguration ) throws PwmUnrecoverableException { { final List<String> errorStrings = storedConfiguration.validateValues(); if (errorStrings != null && !errorStrings.isEmpty()) { final String errorString = errorStrings.get(0); throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorString})); } } try { final ContextManager contextManager = ContextManager.getContextManager(pwmRequest.getHttpServletRequest().getSession().getServletContext()); contextManager.getConfigReader().saveConfiguration( storedConfiguration, contextManager.getPwmApplication(), pwmRequest.getSessionLabel() ); final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); if (pwmApplication.getAuditManager() != null && pwmApplication.getAuditManager().status() == PwmService.STATUS.OPEN) { final String modifyMessage = "Configuration Changes: " + storedConfiguration.changeLogAsDebugString(PwmConstants.DEFAULT_LOCALE, false); final AuditRecord auditRecord = new AuditRecordFactory(pwmApplication).createUserAuditRecord( AuditEvent.MODIFY_CONFIGURATION, pwmRequest.getUserInfoIfLoggedIn(), pwmRequest.getSessionLabel(), modifyMessage ); pwmApplication.getAuditManager().submit(auditRecord); } contextManager.requestPwmApplicationRestart(); } catch (Exception e) { final String errorString = "error saving file: " + e.getMessage(); LOGGER.error(pwmRequest, errorString); throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorString})); } } static void forwardToEditor(final PwmRequest pwmRequest) throws IOException, ServletException, PwmUnrecoverableException { pwmRequest.sendRedirect(PwmServletDefinition.ConfigEditor); } private void doDownloadConfig(final PwmRequest pwmRequest) throws IOException, ServletException, PwmUnrecoverableException { final PwmSession pwmSession = pwmRequest.getPwmSession(); final PwmResponse resp = pwmRequest.getPwmResponse(); try { final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest); final OutputStream responseWriter = resp.getOutputStream(); resp.setHeader(HttpHeader.ContentDisposition, "attachment;filename=" + PwmConstants.DEFAULT_CONFIG_FILE_FILENAME); resp.setContentType(PwmConstants.ContentTypeValue.xml); storedConfiguration.toXml(responseWriter); responseWriter.close(); } catch (Exception e) { LOGGER.error(pwmSession, "unable to download configuration: " + e.getMessage()); } } private void doGenerateSupportZip(final PwmRequest pwmRequest) throws IOException, ServletException { final PwmResponse resp = pwmRequest.getPwmResponse(); resp.setHeader(HttpHeader.ContentDisposition, "attachment;filename=" + PwmConstants.PWM_APP_NAME + "-Support.zip"); resp.setContentType(PwmConstants.ContentTypeValue.zip); final String pathPrefix = PwmConstants.PWM_APP_NAME + "-Support" + "/"; ZipOutputStream zipOutput = null; try { zipOutput = new ZipOutputStream(resp.getOutputStream(), PwmConstants.DEFAULT_CHARSET); DebugItemGenerator.outputZipDebugFile(pwmRequest, zipOutput, pathPrefix); } catch (Exception e) { LOGGER.error(pwmRequest, "error during zip debug building: " + e.getMessage()); } finally { if (zipOutput != null) { try { zipOutput.close(); } catch (Exception e) { LOGGER.error(pwmRequest, "error during zip debug closing: " + e.getMessage()); } } } } public static StoredConfigurationImpl readCurrentConfiguration(final PwmRequest pwmRequest) throws PwmUnrecoverableException { final ContextManager contextManager = ContextManager.getContextManager(pwmRequest.getHttpServletRequest().getSession()); final ConfigurationReader runningConfigReader = contextManager.getConfigReader(); final StoredConfigurationImpl runningConfig = runningConfigReader.getStoredConfiguration(); return StoredConfigurationImpl.copy(runningConfig); } private void showSummary(final PwmRequest pwmRequest) throws IOException, ServletException, PwmUnrecoverableException { final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest); final LinkedHashMap<String,Object> outputMap = new LinkedHashMap<>(storedConfiguration.toOutputMap(pwmRequest.getLocale())); pwmRequest.setAttribute(PwmRequestAttribute.ConfigurationSummaryOutput,outputMap); pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_EDITOR_SUMMARY); } private void showPermissions(final PwmRequest pwmRequest) throws IOException, ServletException, PwmUnrecoverableException { final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest); final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(storedConfiguration); pwmRequest.setAttribute(PwmRequestAttribute.LdapPermissionItems,ldapPermissionCalculator); pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_PERMISSIONS); } private void downloadPermissionReportCsv( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException { pwmRequest.getPwmResponse().markAsDownload( PwmConstants.ContentTypeValue.csv, pwmRequest.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV) ); final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(pwmRequest.getPwmResponse().getOutputStream()); try { final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest); final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(storedConfiguration); for (final LDAPPermissionCalculator.PermissionRecord permissionRecord : ldapPermissionCalculator.getPermissionRecords()) { final String settingTxt = permissionRecord.getPwmSetting() == null ? LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable, pwmRequest) : permissionRecord.getPwmSetting().toMenuLocationDebug(permissionRecord.getProfile(), pwmRequest.getLocale()); csvPrinter.printRecord( permissionRecord.getActor().getLabel(pwmRequest.getLocale(), pwmRequest.getConfig()), permissionRecord.getAttribute(), permissionRecord.getAccess().toString(), settingTxt ); } } catch (Exception e) { final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN,e.getMessage()); LOGGER.error(pwmRequest, errorInformation); pwmRequest.respondWithError(errorInformation); } finally { IOUtils.closeQuietly(csvPrinter); } } }