Ver Fonte

issue 663 - fix misc configuguide bugs and add additional setting warnings

Jason Rivard há 3 anos atrás
pai
commit
e4b8886acd
22 ficheiros alterados com 366 adições e 222 exclusões
  1. 1 0
      server/src/main/java/password/pwm/health/HealthMessage.java
  2. 1 0
      server/src/main/java/password/pwm/http/PwmResponse.java
  3. 2 0
      server/src/main/java/password/pwm/http/filter/ApplicationModeFilter.java
  4. 15 0
      server/src/main/java/password/pwm/http/filter/AuthorizationFilter.java
  5. 0 16
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java
  6. 4 4
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServletUtils.java
  7. 43 0
      server/src/main/java/password/pwm/http/servlet/configeditor/ReadSettingResponse.java
  8. 48 111
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  9. 163 20
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideUtils.java
  10. 8 3
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java
  11. 5 1
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchService.java
  12. 1 0
      server/src/main/resources/password/pwm/i18n/ConfigGuide.properties
  13. 1 0
      server/src/main/resources/password/pwm/i18n/Health.properties
  14. 1 4
      webapp/src/main/webapp/WEB-INF/jsp/configguide-ldap_context.jsp
  15. 17 22
      webapp/src/main/webapp/WEB-INF/jsp/configguide-storage.jsp
  16. 3 1
      webapp/src/main/webapp/WEB-INF/jsp/configmanager-localdb.jsp
  17. 3 1
      webapp/src/main/webapp/WEB-INF/jsp/fragment/configguide-header.jsp
  18. 2 1
      webapp/src/main/webapp/public/resources/js/configeditor-settings-stringarray.js
  19. 26 23
      webapp/src/main/webapp/public/resources/js/configguide.js
  20. 7 1
      webapp/src/main/webapp/public/resources/js/main.js
  21. 5 14
      webapp/src/main/webapp/public/resources/js/uilibrary.js
  22. 10 0
      webapp/src/main/webapp/public/resources/style.css

+ 1 - 0
server/src/main/java/password/pwm/health/HealthMessage.java

@@ -35,6 +35,7 @@ public enum HealthMessage
     LDAP_AD_Unsecure( HealthStatus.WARN, HealthTopic.LDAP ),
     LDAP_AD_Unsecure( HealthStatus.WARN, HealthTopic.LDAP ),
     LDAP_AD_StaticIP( HealthStatus.WARN, HealthTopic.LDAP ),
     LDAP_AD_StaticIP( HealthStatus.WARN, HealthTopic.LDAP ),
     LDAP_AdminUserOk( HealthStatus.GOOD, HealthTopic.LDAP ),
     LDAP_AdminUserOk( HealthStatus.GOOD, HealthTopic.LDAP ),
+    LDAP_AdminNotInContext( HealthStatus.WARN, HealthTopic.LDAP ),
     LDAP_ProxyTestSameUser( HealthStatus.WARN, HealthTopic.Configuration ),
     LDAP_ProxyTestSameUser( HealthStatus.WARN, HealthTopic.Configuration ),
     LDAP_ProxyUserOk( HealthStatus.GOOD, HealthTopic.LDAP ),
     LDAP_ProxyUserOk( HealthStatus.GOOD, HealthTopic.LDAP ),
     LDAP_ProxyUserPwExpired( HealthStatus.WARN, HealthTopic.LDAP ),
     LDAP_ProxyUserPwExpired( HealthStatus.WARN, HealthTopic.LDAP ),

+ 1 - 0
server/src/main/java/password/pwm/http/PwmResponse.java

@@ -238,6 +238,7 @@ public class PwmResponse extends PwmHttpResponseWrapper
         final String outputString = restResultBean.toJson( pwmRequest.isPrettyPrintJsonParameterTrue() );
         final String outputString = restResultBean.toJson( pwmRequest.isPrettyPrintJsonParameterTrue() );
         resp.setContentType( HttpContentType.json.getHeaderValueWithEncoding() );
         resp.setContentType( HttpContentType.json.getHeaderValueWithEncoding() );
         resp.getWriter().print( outputString );
         resp.getWriter().print( outputString );
+        resp.getWriter().close();
     }
     }
 
 
 
 

+ 2 - 0
server/src/main/java/password/pwm/http/filter/ApplicationModeFilter.java

@@ -141,6 +141,8 @@ public class ApplicationModeFilter extends AbstractPwmFilter
         if ( !PwmConstants.TRIAL_MODE )
         if ( !PwmConstants.TRIAL_MODE )
         {
         {
             final Set<PwmServletDefinition> permittedServlets = Set.of(
             final Set<PwmServletDefinition> permittedServlets = Set.of(
+                    PwmServletDefinition.ConfigEditor,
+                    PwmServletDefinition.ConfigManager_Login,
                     PwmServletDefinition.ConfigManager,
                     PwmServletDefinition.ConfigManager,
                     PwmServletDefinition.ConfigGuide,
                     PwmServletDefinition.ConfigGuide,
                     PwmServletDefinition.PublicCommand,
                     PwmServletDefinition.PublicCommand,

+ 15 - 0
server/src/main/java/password/pwm/http/filter/AuthorizationFilter.java

@@ -31,6 +31,7 @@ import password.pwm.util.logging.PwmLogger;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import java.io.IOException;
 import java.io.IOException;
+import java.util.Optional;
 
 
 /**
 /**
  * Authorization servlet filter.  Manages PWM authorization levels.  Primarily,
  * Authorization servlet filter.  Manages PWM authorization levels.  Primarily,
@@ -56,6 +57,20 @@ public class AuthorizationFilter extends AbstractPwmFilter
     )
     )
             throws IOException, ServletException, PwmUnrecoverableException
             throws IOException, ServletException, PwmUnrecoverableException
     {
     {
+        if ( mode == PwmApplicationMode.CONFIGURATION )
+        {
+            final Optional<PwmServletDefinition> pwmServletDefinition = pwmRequest.getURL().forServletDefinition();
+            if ( pwmServletDefinition.isPresent() )
+            {
+                if ( pwmServletDefinition.get().getFlags().contains( PwmServletDefinition.Flag.RequiresConfigAuth )
+                        || pwmServletDefinition.get() == PwmServletDefinition.ConfigManager_Login )
+                {
+                    chain.doFilter();
+                    return;
+                }
+            }
+        }
+
         // if the user is not authenticated as a PWM Admin, redirect to error page.
         // if the user is not authenticated as a PWM Admin, redirect to error page.
         boolean hasPermission = false;
         boolean hasPermission = false;
         try
         try

+ 0 - 16
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -20,8 +20,6 @@
 
 
 package password.pwm.http.servlet.configeditor;
 package password.pwm.http.servlet.configeditor;
 
 
-import lombok.Builder;
-import lombok.Value;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
@@ -309,20 +307,6 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
 
 
-    @Value
-    @Builder
-    static class ReadSettingResponse implements Serializable
-    {
-        private final boolean isDefault;
-        private final String key;
-        private final String category;
-        private final Instant modifyTime;
-        private final UserIdentity modifyUser;
-        private final String syntax;
-        private final Object value;
-        private final Map<String, String> options;
-    }
-
     @ActionHandler( action = "writeSetting" )
     @ActionHandler( action = "writeSetting" )
     public ProcessStatus restWriteSetting(
     public ProcessStatus restWriteSetting(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest

+ 4 - 4
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServletUtils.java

@@ -163,7 +163,7 @@ public class ConfigEditorServletUtils
         return Collections.emptyMap();
         return Collections.emptyMap();
     }
     }
 
 
-    static ConfigEditorServlet.ReadSettingResponse handleLocaleBundleReadSetting(
+    static ReadSettingResponse handleLocaleBundleReadSetting(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final StoredConfiguration storedConfig,
             final StoredConfiguration storedConfig,
             final StoredConfigKey key
             final StoredConfigKey key
@@ -172,7 +172,7 @@ public class ConfigEditorServletUtils
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForLocaleBundle();
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForLocaleBundle();
-        final ConfigEditorServlet.ReadSettingResponse.ReadSettingResponseBuilder builder = ConfigEditorServlet.ReadSettingResponse.builder();
+        final ReadSettingResponse.ReadSettingResponseBuilder builder = ReadSettingResponse.builder();
         final PwmLocaleBundle pwmLocaleBundle = key.toLocaleBundle();
         final PwmLocaleBundle pwmLocaleBundle = key.toLocaleBundle();
         final String keyName = key.getProfileID();
         final String keyName = key.getProfileID();
         final Map<String, String> bundleMap = storedConfig.readLocaleBundleMap( pwmLocaleBundle, keyName, domainID );
         final Map<String, String> bundleMap = storedConfig.readLocaleBundleMap( pwmLocaleBundle, keyName, domainID );
@@ -209,14 +209,14 @@ public class ConfigEditorServletUtils
         return builder.build();
         return builder.build();
     }
     }
 
 
-    static ConfigEditorServlet.ReadSettingResponse handleReadSetting(
+    static ReadSettingResponse handleReadSetting(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final StoredConfiguration storedConfig,
             final StoredConfiguration storedConfig,
             final StoredConfigKey key
             final StoredConfigKey key
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        final ConfigEditorServlet.ReadSettingResponse.ReadSettingResponseBuilder builder = ConfigEditorServlet.ReadSettingResponse.builder();
+        final ReadSettingResponse.ReadSettingResponseBuilder builder = ReadSettingResponse.builder();
 
 
         final PwmSetting pwmSetting = key.toPwmSetting();
         final PwmSetting pwmSetting = key.toPwmSetting();
         final boolean isDefault = StoredConfigurationUtil.isDefaultValue( storedConfig, key );
         final boolean isDefault = StoredConfigurationUtil.isDefaultValue( storedConfig, key );

+ 43 - 0
server/src/main/java/password/pwm/http/servlet/configeditor/ReadSettingResponse.java

@@ -0,0 +1,43 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2021 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.http.servlet.configeditor;
+
+import lombok.Builder;
+import lombok.Value;
+import password.pwm.bean.UserIdentity;
+
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.Map;
+
+@Value
+@Builder
+public class ReadSettingResponse implements Serializable
+{
+    private final boolean isDefault;
+    private final String key;
+    private final String category;
+    private final Instant modifyTime;
+    private final UserIdentity modifyUser;
+    private final String syntax;
+    private final Object value;
+    private final Map<String, String> options;
+}

+ 48 - 111
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java

@@ -60,15 +60,19 @@ import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.configeditor.ConfigEditorServletUtils;
 import password.pwm.http.servlet.configeditor.ConfigEditorServletUtils;
+import password.pwm.http.servlet.configeditor.DomainManageMode;
+import password.pwm.http.servlet.configeditor.ReadSettingResponse;
 import password.pwm.http.servlet.configeditor.data.NavTreeSettings;
 import password.pwm.http.servlet.configeditor.data.NavTreeSettings;
 import password.pwm.http.servlet.configeditor.data.SettingData;
 import password.pwm.http.servlet.configeditor.data.SettingData;
 import password.pwm.http.servlet.configeditor.data.SettingDataMaker;
 import password.pwm.http.servlet.configeditor.data.SettingDataMaker;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
 import password.pwm.ldap.LdapBrowser;
 import password.pwm.ldap.LdapBrowser;
 import password.pwm.ldap.schema.SchemaOperationResult;
 import password.pwm.ldap.schema.SchemaOperationResult;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.MiscUtil;
 import password.pwm.util.java.MiscUtil;
-import password.pwm.util.json.JsonFactory;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
+import password.pwm.util.json.JsonFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
@@ -84,8 +88,6 @@ import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Optional;
@@ -104,6 +106,8 @@ public class ConfigGuideServlet extends ControlledPwmServlet
     private static final PwmLogger LOGGER = PwmLogger.getLogger( ConfigGuideServlet.class.getName() );
     private static final PwmLogger LOGGER = PwmLogger.getLogger( ConfigGuideServlet.class.getName() );
 
 
     private static final String LDAP_PROFILE_KEY = PwmConstants.PROFILE_ID_DEFAULT;
     private static final String LDAP_PROFILE_KEY = PwmConstants.PROFILE_ID_DEFAULT;
+    public static final String PARAM_STEP = "step";
+    public static final String PARAM_KEY = "key";
 
 
     public enum ConfigGuideAction implements AbstractPwmServlet.ProcessAction
     public enum ConfigGuideAction implements AbstractPwmServlet.ProcessAction
     {
     {
@@ -118,8 +122,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet
         skipGuide( HttpMethod.POST ),
         skipGuide( HttpMethod.POST ),
         readSetting( HttpMethod.POST ),
         readSetting( HttpMethod.POST ),
         writeSetting( HttpMethod.POST ),
         writeSetting( HttpMethod.POST ),
-        settingData( HttpMethod.GET ),;
-
+        settingData( HttpMethod.POST ),;
 
 
         private final HttpMethod method;
         private final HttpMethod method;
 
 
@@ -406,84 +409,17 @@ public class ConfigGuideServlet extends ControlledPwmServlet
     {
     {
         final ConfigGuideBean configGuideBean = getBean( pwmRequest );
         final ConfigGuideBean configGuideBean = getBean( pwmRequest );
 
 
-        final String requestedStep = pwmRequest.readParameterAsString( "step" );
-        GuideStep step = GuideStep.START;
-        if ( requestedStep != null && requestedStep.length() > 0 )
-        {
-            try
-            {
-                step = GuideStep.valueOf( requestedStep );
-            }
-            catch ( final IllegalArgumentException e )
-            {
-                final String errorMsg = "unknown goto step request: " + requestedStep;
-                LOGGER.error( pwmRequest, () -> errorMsg );
-            }
-        }
+        final String requestedStep = pwmRequest.readParameterAsString( PARAM_STEP );
 
 
-        if ( step == GuideStep.START )
-        {
-            configGuideBean.getFormData().clear();
-            configGuideBean.getFormData().putAll( ConfigGuideForm.defaultForm() );
-        }
-        else if ( step == GuideStep.NEXT )
-        {
-            step = configGuideBean.getStep().next();
-            while ( step != GuideStep.FINISH && !step.visible( configGuideBean ) )
-            {
-                step = step.next();
-            }
-        }
-        else if ( step == GuideStep.PREVIOUS )
-        {
-            step = configGuideBean.getStep().previous();
-            while ( step != GuideStep.START && !step.visible( configGuideBean ) )
-            {
-                step = step.previous();
-            }
-        }
+        final GuideStep inputStep = StringUtil.isEmpty( requestedStep )
+                ? GuideStep.START
+                : JavaHelper.readEnumFromString( GuideStep.class, requestedStep )
+                                .orElseThrow( () -> PwmUnrecoverableException.newException(
+                                        PwmError.ERROR_INTERNAL, "unknown step value" ) );
 
 
-        if ( step == GuideStep.FINISH )
-        {
-            final ContextManager contextManager = ContextManager.getContextManager( pwmRequest );
-            try
-            {
-                ConfigGuideUtils.writeConfig( contextManager, configGuideBean );
-                pwmRequest.getPwmSession().getSessionStateBean().setTheme( null );
-            }
-            catch ( final PwmException e )
-            {
-                final RestResultBean restResultBean = RestResultBean.fromError( e.getErrorInformation(), pwmRequest );
-                pwmRequest.outputJsonResult( restResultBean );
-                return ProcessStatus.Halt;
-            }
-            catch ( final Exception e )
-            {
-                LOGGER.error( pwmRequest, () -> "error during save: " + e.getMessage(), e );
-                final RestResultBean restResultBean = RestResultBean.fromError( new ErrorInformation(
-                        PwmError.ERROR_INTERNAL,
-                        "error during save: " + e.getMessage()
-                ), pwmRequest );
-                pwmRequest.outputJsonResult( restResultBean );
-                return ProcessStatus.Halt;
-            }
-            final HashMap<String, String> resultData = new HashMap<>();
-            resultData.put( "serverRestart", "true" );
-            pwmRequest.outputJsonResult( RestResultBean.withData( resultData, Map.class ) );
-            pwmRequest.invalidateSession();
-        }
-        else
-        {
-            configGuideBean.setStep( step );
-            pwmRequest.outputJsonResult( RestResultBean.forSuccessMessage( pwmRequest, Message.Success_Unknown ) );
-
-            {
-                final GuideStep finalStep = step;
-                LOGGER.trace( pwmRequest, () -> "setting current step to: " + finalStep );
-            }
-        }
+        final GuideStep nextEffectiveStep = ConfigGuideUtils.figureNextEffectiveStep( configGuideBean, inputStep );
 
 
-        return ProcessStatus.Continue;
+        return ConfigGuideUtils.executeNextStep( pwmRequest, configGuideBean, nextEffectiveStep );
     }
     }
 
 
     @ActionHandler( action = "extendSchema" )
     @ActionHandler( action = "extendSchema" )
@@ -558,26 +494,24 @@ public class ConfigGuideServlet extends ControlledPwmServlet
     @ActionHandler( action = "readSetting" )
     @ActionHandler( action = "readSetting" )
     public ProcessStatus restReadSetting( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException
     public ProcessStatus restReadSetting( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException
     {
     {
-        final String profileID = "default";
         final ConfigGuideBean configGuideBean = getBean( pwmRequest );
         final ConfigGuideBean configGuideBean = getBean( pwmRequest );
         final StoredConfiguration storedConfiguration = ConfigGuideForm.generateStoredConfig( configGuideBean );
         final StoredConfiguration storedConfiguration = ConfigGuideForm.generateStoredConfig( configGuideBean );
 
 
-        final String settingKey = pwmRequest.readParameterAsString( "key" );
-        final LinkedHashMap<String, Object> returnMap = new LinkedHashMap<>();
+        final String settingKey = pwmRequest.readParameterAsString( PARAM_KEY );
         final PwmSetting pwmSetting = PwmSetting.forKey( settingKey )
         final PwmSetting pwmSetting = PwmSetting.forKey( settingKey )
                 .orElseThrow( () -> new IllegalStateException( "invalid setting parameter value" ) );
                 .orElseThrow( () -> new IllegalStateException( "invalid setting parameter value" ) );
 
 
-        final StoredConfigKey key = StoredConfigKey.forSetting( pwmSetting, profileID, DomainID.DOMAIN_ID_DEFAULT );
+        final StoredConfigKey key = StoredConfigKey.forSetting( pwmSetting, ConfigGuideForm.LDAP_PROFILE_NAME, DomainID.DOMAIN_ID_DEFAULT );
 
 
-        final Object returnValue;
-        returnValue = StoredConfigurationUtil.getValueOrDefault( storedConfiguration, key ).toNativeObject();
-        returnMap.put( "isDefault", StoredConfigurationUtil.isDefaultValue( storedConfiguration, key ) );
-        returnMap.put( "key", settingKey );
-        returnMap.put( "category", pwmSetting.getCategory().toString() );
-        returnMap.put( "syntax", pwmSetting.getSyntax().toString() );
+        final ReadSettingResponse readSettingResponse = ReadSettingResponse.builder()
+                .isDefault( StoredConfigurationUtil.isDefaultValue( storedConfiguration, key ) )
+                .key( settingKey )
+                .category( pwmSetting.getCategory().toString() )
+                .syntax( pwmSetting.getSyntax().toString() )
+                .value( StoredConfigurationUtil.getValueOrDefault( storedConfiguration, key ).toNativeObject() )
+                .build();
 
 
-        returnMap.put( "value", returnValue );
-        pwmRequest.outputJsonResult( RestResultBean.withData( returnMap, Map.class ) );
+        pwmRequest.outputJsonResult( RestResultBean.withData( readSettingResponse, ReadSettingResponse.class ) );
 
 
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
@@ -586,26 +520,23 @@ public class ConfigGuideServlet extends ControlledPwmServlet
     public ProcessStatus restWriteSetting( final PwmRequest pwmRequest )
     public ProcessStatus restWriteSetting( final PwmRequest pwmRequest )
             throws PwmUnrecoverableException, IOException
             throws PwmUnrecoverableException, IOException
     {
     {
-        final String profileID = "default";
-        final String settingKey = pwmRequest.readParameterAsString( "key" );
         final String bodyString = pwmRequest.readRequestBodyAsString();
         final String bodyString = pwmRequest.readRequestBodyAsString();
-        final PwmSetting pwmSetting = PwmSetting.forKey( settingKey )
+        final PwmSetting pwmSetting = PwmSetting.forKey( pwmRequest.readParameterAsString( PARAM_KEY ) )
                 .orElseThrow( () -> new IllegalStateException( "invalid setting parameter value" ) );
                 .orElseThrow( () -> new IllegalStateException( "invalid setting parameter value" ) );
 
 
         final ConfigGuideBean configGuideBean = getBean( pwmRequest );
         final ConfigGuideBean configGuideBean = getBean( pwmRequest );
         final StoredConfiguration storedConfiguration = ConfigGuideForm.generateStoredConfig( configGuideBean );
         final StoredConfiguration storedConfiguration = ConfigGuideForm.generateStoredConfig( configGuideBean );
 
 
-        final StoredConfigKey key = StoredConfigKey.forSetting( pwmSetting, profileID, DomainID.DOMAIN_ID_DEFAULT );
-
-        final LinkedHashMap<String, Object> returnMap = new LinkedHashMap<>();
+        final StoredConfigKey key = StoredConfigKey.forSetting( pwmSetting, ConfigGuideForm.LDAP_PROFILE_NAME, DomainID.DOMAIN_ID_DEFAULT );
 
 
         try
         try
         {
         {
             final StoredValue storedValue = ValueFactory.fromJson( pwmSetting, bodyString );
             final StoredValue storedValue = ValueFactory.fromJson( pwmSetting, bodyString );
-            final List<String> errorMsgs = storedValue.validateValue( pwmSetting );
-            if ( errorMsgs != null && !errorMsgs.isEmpty() )
+            final List<String> errorMessages = storedValue.validateValue( pwmSetting );
+            if ( errorMessages != null && !errorMessages.isEmpty() )
             {
             {
-                returnMap.put( "errorMessage", pwmSetting.getLabel( pwmRequest.getLocale() ) + ": " + errorMsgs.get( 0 ) );
+                final String msg = pwmSetting.getLabel( pwmRequest.getLocale() ) + ": " + errorMessages.get( 0 );
+                throw new PwmUnrecoverableException( new ErrorInformation( PwmError.CONFIG_FORMAT_ERROR, msg ) );
             }
             }
 
 
             if ( pwmSetting == PwmSetting.CHALLENGE_RANDOM_CHALLENGES )
             if ( pwmSetting == PwmSetting.CHALLENGE_RANDOM_CHALLENGES )
@@ -615,17 +546,23 @@ public class ConfigGuideServlet extends ControlledPwmServlet
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
+            if ( e instanceof PwmUnrecoverableException )
+            {
+                throw ( PwmUnrecoverableException ) e;
+            }
+
             final String errorMsg = "error writing default value for setting " + pwmSetting + ", error: " + e.getMessage();
             final String errorMsg = "error writing default value for setting " + pwmSetting + ", error: " + e.getMessage();
-            LOGGER.error( () -> errorMsg, e );
-            throw new IllegalStateException( errorMsg, e );
+            throw new PwmUnrecoverableException( new ErrorInformation( PwmError.CONFIG_FORMAT_ERROR, errorMsg ) );
         }
         }
-        returnMap.put( "key", settingKey );
-        returnMap.put( "category", pwmSetting.getCategory().toString() );
-        returnMap.put( "syntax", pwmSetting.getSyntax().toString() );
-        returnMap.put( "isDefault", StoredConfigurationUtil.isDefaultValue( storedConfiguration, key ) );
-        pwmRequest.outputJsonResult( RestResultBean.withData( returnMap, Map.class ) );
 
 
+        final ReadSettingResponse readSettingResponse = ReadSettingResponse.builder()
+                .key( pwmSetting.getKey() )
+                .category( pwmSetting.getCategory().toString() )
+                .syntax( pwmSetting.getSyntax().toString() )
+                .isDefault( StoredConfigurationUtil.isDefaultValue( storedConfiguration, key ) )
+                .build();
 
 
+        pwmRequest.outputJsonResult( RestResultBean.withData( readSettingResponse, ReadSettingResponse.class ) );
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
 
 
@@ -637,14 +574,14 @@ public class ConfigGuideServlet extends ControlledPwmServlet
         final StoredConfiguration storedConfiguration = ConfigGuideForm.generateStoredConfig( configGuideBean );
         final StoredConfiguration storedConfiguration = ConfigGuideForm.generateStoredConfig( configGuideBean );
 
 
         final SettingData settingData = SettingDataMaker.generateSettingData(
         final SettingData settingData = SettingDataMaker.generateSettingData(
-                ConfigGuideForm.DOMAIN_ID,
+                DomainID.systemId(),
                 storedConfiguration,
                 storedConfiguration,
                 pwmRequest.getLabel(),
                 pwmRequest.getLabel(),
                 pwmRequest.getLocale(),
                 pwmRequest.getLocale(),
-                NavTreeSettings.forBasic()
+                NavTreeSettings.builder().domainManageMode( DomainManageMode.single ).build()
         );
         );
 
 
-        final RestResultBean restResultBean = RestResultBean.withData( settingData, SettingData.class );
+        final RestResultBean<SettingData> restResultBean = RestResultBean.withData( settingData, SettingData.class );
         pwmRequest.outputJsonResult( restResultBean );
         pwmRequest.outputJsonResult( restResultBean );
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }

+ 163 - 20
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideUtils.java

@@ -20,6 +20,10 @@
 
 
 package password.pwm.http.servlet.configguide;
 package password.pwm.http.servlet.configguide;
 
 
+import com.novell.ldapchai.ChaiEntry;
+import com.novell.ldapchai.ChaiEntryFactory;
+import com.novell.ldapchai.exception.ChaiOperationException;
+import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiConfiguration;
 import com.novell.ldapchai.provider.ChaiConfiguration;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiSetting;
 import com.novell.ldapchai.provider.ChaiSetting;
@@ -28,6 +32,7 @@ import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.PwmDomain;
 import password.pwm.PwmDomain;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
@@ -46,14 +51,17 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthMessage;
 import password.pwm.health.HealthMessage;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthRecord;
 import password.pwm.http.ContextManager;
 import password.pwm.http.ContextManager;
+import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.http.servlet.configeditor.function.UserMatchViewerFunction;
 import password.pwm.http.servlet.configeditor.function.UserMatchViewerFunction;
+import password.pwm.i18n.ConfigGuide;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
 import password.pwm.ldap.LdapPermissionCalculator;
 import password.pwm.ldap.LdapPermissionCalculator;
 import password.pwm.ldap.schema.SchemaManager;
 import password.pwm.ldap.schema.SchemaManager;
 import password.pwm.ldap.schema.SchemaOperationResult;
 import password.pwm.ldap.schema.SchemaOperationResult;
+import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.Percent;
 import password.pwm.util.java.Percent;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -70,7 +78,7 @@ import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.SocketAddress;
 import java.util.ArrayList;
 import java.util.ArrayList;
-import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Optional;
@@ -146,10 +154,10 @@ public class ConfigGuideUtils
         try
         try
         {
         {
             final ChaiConfiguration chaiConfiguration = ChaiConfiguration.builder(
             final ChaiConfiguration chaiConfiguration = ChaiConfiguration.builder(
-                    ldapUrl,
-                    form.get( ConfigGuideFormField.PARAM_LDAP_PROXY_DN ),
-                    form.get( ConfigGuideFormField.PARAM_LDAP_PROXY_PW )
-            )
+                            ldapUrl,
+                            form.get( ConfigGuideFormField.PARAM_LDAP_PROXY_DN ),
+                            form.get( ConfigGuideFormField.PARAM_LDAP_PROXY_PW )
+                    )
                     .setSetting( ChaiSetting.PROMISCUOUS_SSL, "true" )
                     .setSetting( ChaiSetting.PROMISCUOUS_SSL, "true" )
                     .build();
                     .build();
 
 
@@ -235,7 +243,7 @@ public class ConfigGuideUtils
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.CONFIG_UPLOAD_FAILURE, errorMsg, new String[]
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.CONFIG_UPLOAD_FAILURE, errorMsg, new String[]
                     {
                     {
                             errorMsg,
                             errorMsg,
-                            }
+                    }
             );
             );
             pwmRequest.respondWithError( errorInformation, true );
             pwmRequest.respondWithError( errorInformation, true );
         }
         }
@@ -286,26 +294,17 @@ public class ConfigGuideUtils
             final Map<ConfigGuideFormField, String> form = configGuideBean.getFormData();
             final Map<ConfigGuideFormField, String> form = configGuideBean.getFormData();
             final PwmApplication tempApplication = PwmApplication.createPwmApplication(
             final PwmApplication tempApplication = PwmApplication.createPwmApplication(
                     pwmRequest.getPwmApplication().getPwmEnvironment().makeRuntimeInstance( new AppConfig( storedConfiguration ) ) );
                     pwmRequest.getPwmApplication().getPwmEnvironment().makeRuntimeInstance( new AppConfig( storedConfiguration ) ) );
-            final PwmDomain pwmDomain = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID );
 
 
             final String adminDN = form.get( ConfigGuideFormField.PARAM_LDAP_ADMIN_USER );
             final String adminDN = form.get( ConfigGuideFormField.PARAM_LDAP_ADMIN_USER );
             final UserIdentity adminIdentity = UserIdentity.create( adminDN, ConfigGuideForm.LDAP_PROFILE_NAME, ConfigGuideForm.DOMAIN_ID );
             final UserIdentity adminIdentity = UserIdentity.create( adminDN, ConfigGuideForm.LDAP_PROFILE_NAME, ConfigGuideForm.DOMAIN_ID );
 
 
-            final UserMatchViewerFunction userMatchViewerFunction = new UserMatchViewerFunction();
-            final Collection<UserIdentity> results = userMatchViewerFunction.discoverMatchingUsers(
-                    pwmRequest.getLabel(),
-                    pwmDomain,
-                    1,
-                    storedConfiguration,
-                    StoredConfigKey.forSetting( PwmSetting.QUERY_MATCH_PWM_ADMIN, null, ConfigGuideForm.DOMAIN_ID ) );
+            checkAdminInContext( tempApplication, pwmRequest.getLabel(), configGuideBean, adminIdentity )
+                    .ifPresent( records::add );
 
 
-            if ( !results.isEmpty() )
+            if ( records.isEmpty() )
             {
             {
-                final UserIdentity foundIdentity = results.iterator().next();
-                if ( foundIdentity.canonicalEquals( pwmRequest.getLabel(), adminIdentity, tempApplication ) )
-                {
-                    records.add( HealthRecord.forMessage( ConfigGuideForm.DOMAIN_ID, HealthMessage.LDAP_AdminUserOk ) );
-                }
+                checkAdminUserExists( tempApplication, pwmRequest.getLabel(), adminIdentity )
+                        .ifPresent( records::add );
             }
             }
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
@@ -328,4 +327,148 @@ public class ConfigGuideUtils
 
 
         return records;
         return records;
     }
     }
+
+    private static Optional<HealthRecord> checkAdminUserExists(
+            final PwmApplication tempApplication,
+            final SessionLabel sessionLabel,
+            final UserIdentity adminIdentity
+    )
+            throws PwmUnrecoverableException, PwmOperationalException
+    {
+        final PwmDomain pwmDomain = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID );
+
+        final UserMatchViewerFunction userMatchViewerFunction = new UserMatchViewerFunction();
+        final List<UserIdentity> results = userMatchViewerFunction.discoverMatchingUsers(
+                sessionLabel,
+                pwmDomain,
+                1,
+                tempApplication.getConfig().getStoredConfiguration(),
+                StoredConfigKey.forSetting( PwmSetting.QUERY_MATCH_PWM_ADMIN, null, ConfigGuideForm.DOMAIN_ID ) );
+
+        if ( !results.isEmpty() )
+        {
+            final UserIdentity foundIdentity = results.get( 0 );
+            if ( foundIdentity.canonicalEquals( sessionLabel, adminIdentity, tempApplication ) )
+            {
+                return Optional.of( HealthRecord.forMessage( ConfigGuideForm.DOMAIN_ID, HealthMessage.LDAP_AdminUserOk ) );
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    private static Optional<HealthRecord> checkAdminInContext(
+            final PwmApplication tempApplication,
+            final SessionLabel sessionLabel,
+            final ConfigGuideBean configGuideBean,
+            final UserIdentity adminIdentity
+    )
+            throws PwmUnrecoverableException, ChaiUnavailableException, ChaiOperationException
+    {
+        final Map<ConfigGuideFormField, String> form = configGuideBean.getFormData();
+
+        final ChaiProvider chaiProvider = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID ).getProxyChaiProvider( sessionLabel, ConfigGuideForm.LDAP_PROFILE_NAME );
+        final ChaiEntry contextEntry = ChaiEntryFactory.newChaiFactory( chaiProvider ).newChaiEntry( form.get( ConfigGuideFormField.PARAM_LDAP_CONTEXT ) );
+        final String canonicalContextDN = contextEntry.readCanonicalDN();
+        final UserIdentity canonicalAdmin = adminIdentity.canonicalized( sessionLabel, tempApplication );
+        if ( !canonicalAdmin.getUserDN().endsWith( canonicalContextDN ) )
+        {
+            final String contextTitle = LocaleHelper.getLocalizedMessage(
+                    PwmConstants.DEFAULT_LOCALE,
+                    "ldap_context_title",
+                    tempApplication.getConfig(),
+                    ConfigGuide.class );
+
+            return Optional.of( HealthRecord.forMessage(
+                    ConfigGuideForm.DOMAIN_ID,
+                    HealthMessage.LDAP_AdminNotInContext,
+                    contextTitle,
+                    canonicalContextDN
+            ) );
+        }
+
+        return Optional.empty();
+    }
+
+
+    static GuideStep figureNextEffectiveStep(
+            final ConfigGuideBean configGuideBean,
+            final GuideStep inputStep
+    )
+    {
+        GuideStep step = inputStep;
+        if ( step == GuideStep.START )
+        {
+            configGuideBean.getFormData().clear();
+            configGuideBean.getFormData().putAll( ConfigGuideForm.defaultForm() );
+        }
+        else if ( step == GuideStep.NEXT )
+        {
+            step = configGuideBean.getStep().next();
+            while ( step != GuideStep.FINISH && !step.visible( configGuideBean ) )
+            {
+                step = step.next();
+            }
+        }
+        else if ( step == GuideStep.PREVIOUS )
+        {
+            step = configGuideBean.getStep().previous();
+            while ( step != GuideStep.START && !step.visible( configGuideBean ) )
+            {
+                step = step.previous();
+            }
+        }
+
+        return step;
+    }
+
+    static ProcessStatus executeNextStep(
+            final PwmRequest pwmRequest,
+            final ConfigGuideBean configGuideBean,
+            final GuideStep step
+    )
+            throws IOException, PwmUnrecoverableException
+    {
+        if ( step == GuideStep.FINISH )
+        {
+            final ContextManager contextManager = ContextManager.getContextManager( pwmRequest );
+            try
+            {
+                ConfigGuideUtils.writeConfig( contextManager, configGuideBean );
+                pwmRequest.getPwmSession().getSessionStateBean().setTheme( null );
+            }
+            catch ( final PwmException e )
+            {
+                final RestResultBean restResultBean = RestResultBean.fromError( e.getErrorInformation(), pwmRequest );
+                pwmRequest.outputJsonResult( restResultBean );
+                return ProcessStatus.Halt;
+            }
+            catch ( final Exception e )
+            {
+                LOGGER.error( pwmRequest, () -> "error during save: " + e.getMessage(), e );
+                final RestResultBean restResultBean = RestResultBean.fromError( new ErrorInformation(
+                        PwmError.ERROR_INTERNAL,
+                        "error during save: " + e.getMessage()
+                ), pwmRequest );
+                pwmRequest.outputJsonResult( restResultBean );
+                return ProcessStatus.Halt;
+            }
+            final HashMap<String, String> resultData = new HashMap<>();
+            resultData.put( "serverRestart", "true" );
+            pwmRequest.outputJsonResult( RestResultBean.withData( resultData, Map.class ) );
+            pwmRequest.invalidateSession();
+        }
+        else
+        {
+            configGuideBean.setStep( step );
+            pwmRequest.outputJsonResult( RestResultBean.forSuccessMessage( pwmRequest, Message.Success_Unknown ) );
+
+            {
+                final GuideStep finalStep = step;
+                LOGGER.trace( pwmRequest, () -> "setting current step to: " + finalStep );
+            }
+        }
+
+        return ProcessStatus.Continue;
+    }
 }
 }

+ 8 - 3
server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java

@@ -196,12 +196,17 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
 
 
     private static void updateLoginHistory( final PwmRequest pwmRequest, final UserIdentity userIdentity, final boolean successful )
     private static void updateLoginHistory( final PwmRequest pwmRequest, final UserIdentity userIdentity, final boolean successful )
     {
     {
+        if ( userIdentity == null )
+        {
+            return;
+        }
+
         final ConfigLoginHistory configLoginHistory = readConfigLoginHistory( pwmRequest );
         final ConfigLoginHistory configLoginHistory = readConfigLoginHistory( pwmRequest );
         final ConfigLoginEvent event = new ConfigLoginEvent(
         final ConfigLoginEvent event = new ConfigLoginEvent(
-                userIdentity == null ? "n/a" : userIdentity.toDisplayString(),
+                userIdentity.toDisplayString(),
                 Instant.now(),
                 Instant.now(),
-                pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress()
-        );
+                pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress() );
+
         final int maxEvents = Integer.parseInt( pwmRequest.getPwmDomain().getConfig().readAppProperty( AppProperty.CONFIG_HISTORY_MAX_ITEMS ) );
         final int maxEvents = Integer.parseInt( pwmRequest.getPwmDomain().getConfig().readAppProperty( AppProperty.CONFIG_HISTORY_MAX_ITEMS ) );
         configLoginHistory.addEvent( event, maxEvents, successful );
         configLoginHistory.addEvent( event, maxEvents, successful );
         pwmRequest.getPwmApplication().writeAppAttribute( AppAttribute.CONFIG_LOGIN_HISTORY, configLoginHistory );
         pwmRequest.getPwmApplication().writeAppAttribute( AppAttribute.CONFIG_LOGIN_HISTORY, configLoginHistory );

+ 5 - 1
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchService.java

@@ -50,7 +50,11 @@ public class PeopleSearchService extends AbstractPwmService implements PwmServic
     @Override
     @Override
     public void shutdownImpl()
     public void shutdownImpl()
     {
     {
-        threadPoolExecutor.shutdown();
+        if ( threadPoolExecutor != null )
+        {
+            threadPoolExecutor.shutdown();
+            threadPoolExecutor = null;
+        }
     }
     }
 
 
     @Override
     @Override

+ 1 - 0
server/src/main/resources/password/pwm/i18n/ConfigGuide.properties

@@ -24,6 +24,7 @@ ldap_admin_title=LDAP Proxy Credentials
 ldap_admin_title_proxy_dn=Proxy DN
 ldap_admin_title_proxy_dn=Proxy DN
 ldap_admin_title_proxy_pw=Proxy Password
 ldap_admin_title_proxy_pw=Proxy Password
 ldap_cert_description=The following are the LDAP server certificates read from the server at <code>%1%</code>. Please verify these certificates match your LDAP server.
 ldap_cert_description=The following are the LDAP server certificates read from the server at <code>%1%</code>. Please verify these certificates match your LDAP server.
+ldap_context_title=LDAP Login Root Context
 ldap_context_description=Please enter the top level container of your LDAP directory that contains users. This sets the top level LDAP container where an LDAP sub-tree search is performed to find user entries. If you need to enter multiple containers, you can add them after this guide completes.  Authentication to @PwmAppName@ is permitted only for users that are contained within the configured context values.
 ldap_context_description=Please enter the top level container of your LDAP directory that contains users. This sets the top level LDAP container where an LDAP sub-tree search is performed to find user entries. If you need to enter multiple containers, you can add them after this guide completes.  Authentication to @PwmAppName@ is permitted only for users that are contained within the configured context values.
 ldap_context_admin_title=Administrator User
 ldap_context_admin_title=Administrator User
 ldap_context_admin_description=<p>A user in your LDAP directory will be used to control administrative access to @PwmAppName@.</p><p>Please enter the LDAP distinguished name (DN) of the user to use to control administrative access.</p><p>As with all users, <b>administrative users must be contained within the previously configured LDAP Login Root Context ("%1%")</b>.</p><p>After the configuration guide is complete, you can assign additional users as administrators.</p>
 ldap_context_admin_description=<p>A user in your LDAP directory will be used to control administrative access to @PwmAppName@.</p><p>Please enter the LDAP distinguished name (DN) of the user to use to control administrative access.</p><p>As with all users, <b>administrative users must be contained within the previously configured LDAP Login Root Context ("%1%")</b>.</p><p>After the configuration guide is complete, you can assign additional users as administrators.</p>

+ 1 - 0
server/src/main/resources/password/pwm/i18n/Health.properties

@@ -23,6 +23,7 @@ HealthMessage_NoData=Health data is not currently available.  Please check again
 HealthMessage_LDAP_OK=All configured LDAP servers are reachable
 HealthMessage_LDAP_OK=All configured LDAP servers are reachable
 HealthMessage_LDAP_No_Connection=Unable to connect to LDAP server %1%, error: %2%
 HealthMessage_LDAP_No_Connection=Unable to connect to LDAP server %1%, error: %2%
 HealthMessage_LDAP_AdminUserOk=The admin user has been validated.
 HealthMessage_LDAP_AdminUserOk=The admin user has been validated.
+HealthMessage_LDAP_AdminNotInContext=The admin user must be a child entry of the %1% setting value "%2%".
 HealthMessage_LDAP_ProxyTestSameUser=%1% setting is the same value as the %2% setting
 HealthMessage_LDAP_ProxyTestSameUser=%1% setting is the same value as the %2% setting
 HealthMessage_LDAP_ProxyUserOk=The proxy user for the %1 profile is okay.
 HealthMessage_LDAP_ProxyUserOk=The proxy user for the %1 profile is okay.
 HealthMessage_LDAP_ProxyUserPwExpired=Proxy user %1% password will expire within %2%.  The proxy user password should never expire. 
 HealthMessage_LDAP_ProxyUserPwExpired=Proxy user %1% password will expire within %2%.  The proxy user password should never expire. 

+ 1 - 4
webapp/src/main/webapp/WEB-INF/jsp/configguide-ldap_context.jsp

@@ -44,7 +44,7 @@
             <%@ include file="/WEB-INF/jsp/fragment/message.jsp" %>
             <%@ include file="/WEB-INF/jsp/fragment/message.jsp" %>
             <br/>
             <br/>
             <div class="setting_outline">
             <div class="setting_outline">
-                <div class="setting_title">LDAP Login Root Context</div>
+                <div class="setting_title"><pwm:display key="ldap_context_title" bundle="ConfigGuide"/></div>
                 <div class="setting_body">
                 <div class="setting_body">
                     <p><pwm:display key="ldap_context_description" bundle="ConfigGuide"/></p>
                     <p><pwm:display key="ldap_context_description" bundle="ConfigGuide"/></p>
                     <br/>
                     <br/>
@@ -88,9 +88,6 @@
 
 
         PWM_GLOBAL['startupFunctions'].push(function(){
         PWM_GLOBAL['startupFunctions'].push(function(){
             PWM_VAR['originalHealthBody'] = PWM_MAIN.getObject('healthBody').innerHTML;
             PWM_VAR['originalHealthBody'] = PWM_MAIN.getObject('healthBody').innerHTML;
-            require(["dojo/parser","dijit/TitlePane","dijit/form/Form","dijit/form/ValidationTextBox","dijit/form/NumberSpinner","dijit/form/CheckBox"],function(dojoParser){
-                dojoParser.parse();
-            });
             checkIfNextEnabled();
             checkIfNextEnabled();
 
 
             PWM_MAIN.addEventHandler('button_next','click',function(){PWM_GUIDE.gotoStep('NEXT')});
             PWM_MAIN.addEventHandler('button_next','click',function(){PWM_GUIDE.gotoStep('NEXT')});

+ 17 - 22
webapp/src/main/webapp/WEB-INF/jsp/configguide-storage.jsp

@@ -44,15 +44,15 @@
             <pwm:display key="Display_ConfigGuideSelectStorage" bundle="Config"/>
             <pwm:display key="Display_ConfigGuideSelectStorage" bundle="Config"/>
             <br/>
             <br/>
             <select id="<%=ConfigGuideFormField.PARAM_TEMPLATE_STORAGE%>" name="<%=ConfigGuideFormField.PARAM_TEMPLATE_STORAGE%>">
             <select id="<%=ConfigGuideFormField.PARAM_TEMPLATE_STORAGE%>" name="<%=ConfigGuideFormField.PARAM_TEMPLATE_STORAGE%>">
-            <% if (selectedTemplate == null || selectedTemplate.isEmpty()) { %>
-            <option value="NOTSELECTED" selected disabled> -- Please select a template -- </option>
-            <% } %>
-            <% for (final String loopTemplate : PwmSetting.TEMPLATE_STORAGE.getOptions().keySet()) { %>
-            <% final boolean selected = loopTemplate.equals(selectedTemplate); %>
-            <option value="<%=loopTemplate%>"<% if (selected) { %> selected="selected"<% } %>>
-                <%=PwmSetting.TEMPLATE_STORAGE.getOptions().get(loopTemplate)%>
-            </option>
-            <% } %>
+                <% if (selectedTemplate == null || selectedTemplate.isEmpty()) { %>
+                <option value="NOTSELECTED" selected disabled> -- Please select a template -- </option>
+                <% } %>
+                <% for (final String loopTemplate : PwmSetting.TEMPLATE_STORAGE.getOptions().keySet()) { %>
+                <% final boolean selected = loopTemplate.equals(selectedTemplate); %>
+                <option value="<%=loopTemplate%>"<% if (selected) { %> selected="selected"<% } %>>
+                    <%=PwmSetting.TEMPLATE_STORAGE.getOptions().get(loopTemplate)%>
+                </option>
+                <% } %>
             </select>
             </select>
 
 
             <br/>
             <br/>
@@ -60,25 +60,22 @@
         </form>
         </form>
         <p>
         <p>
             <b>LDAP</b> <p> Storing user data in LDAP is ideal if your LDAP directory is extensible and can accommodate the storage.  You will need to extend
             <b>LDAP</b> <p> Storing user data in LDAP is ideal if your LDAP directory is extensible and can accommodate the storage.  You will need to extend
-            the LDAP server's schema or adjust the configuration to use pre-existing defined attributes.  You will also need to adjust the access control lists (ACLs) or rights
-            in the LDAP directory to accommodate the challenge/response storage and other data.  See the documentation for more information.</p>
+        the LDAP server's schema or adjust the configuration to use pre-existing defined attributes.  You will also need to adjust the access control lists (ACLs) or rights
+        in the LDAP directory to accommodate the challenge/response storage and other data.  See the documentation for more information.</p>
         </p>
         </p>
         <p>
         <p>
             <b>Remote Database</b> <p> If modifying the LDAP's server schema and rights is not desired or possible, you can use a database to store user data.
             <b>Remote Database</b> <p> If modifying the LDAP's server schema and rights is not desired or possible, you can use a database to store user data.
-            Your database vendor will supply you with the appropriate JDBC driver file and configuration instructions.</p>
+        Your database vendor will supply you with the appropriate JDBC driver file and configuration instructions.</p>
         </p>
         </p>
         <p>
         <p>
             <b>LocalDB (Testing only)</b> <p> This server has it's own embedded local database (LocalDB) that is capable of storing user challenge/responses.  This option should never be used in a production
             <b>LocalDB (Testing only)</b> <p> This server has it's own embedded local database (LocalDB) that is capable of storing user challenge/responses.  This option should never be used in a production
-            environment and is provided only for testing purposes.  User data including challenge/response answers stored in the LocalDB are server specific.</p>
+        environment and is provided only for testing purposes.  User data including challenge/response answers stored in the LocalDB are server specific.</p>
         </p>
         </p>
         <%@ include file="fragment/configguide-buttonbar.jsp" %>
         <%@ include file="fragment/configguide-buttonbar.jsp" %>
     </div>
     </div>
     <div class="push"></div>
     <div class="push"></div>
 </div>
 </div>
 <pwm:script>
 <pwm:script>
-    <script type="text/javascript">
-
-    </script>
     <script type="text/javascript">
     <script type="text/javascript">
         function formHandler() {
         function formHandler() {
             PWM_GUIDE.updateForm();
             PWM_GUIDE.updateForm();
@@ -98,13 +95,11 @@
         }
         }
 
 
         PWM_GLOBAL['startupFunctions'].push(function(){
         PWM_GLOBAL['startupFunctions'].push(function(){
-            PWM_GLOBAL['startupFunctions'].push(function(){
-                PWM_MAIN.addEventHandler('button_next','click',function(){ PWM_GUIDE.gotoStep('NEXT')});
-                PWM_MAIN.addEventHandler('button_previous','click',function(){PWM_GUIDE.gotoStep('PREVIOUS')});
+            PWM_MAIN.addEventHandler('button_next','click',function(){ PWM_GUIDE.gotoStep('NEXT')});
+            PWM_MAIN.addEventHandler('button_previous','click',function(){PWM_GUIDE.gotoStep('PREVIOUS')});
 
 
-                PWM_MAIN.addEventHandler('<%=ConfigGuideFormField.PARAM_TEMPLATE_STORAGE%>','change',function(){
-                    formHandler();
-                });
+            PWM_MAIN.addEventHandler('<%=ConfigGuideFormField.PARAM_TEMPLATE_STORAGE%>','change',function(){
+                formHandler();
             });
             });
             updateNextButton();
             updateNextButton();
         });
         });

+ 3 - 1
webapp/src/main/webapp/WEB-INF/jsp/configmanager-localdb.jsp

@@ -66,7 +66,9 @@
                         <li>Data Caches</li>
                         <li>Data Caches</li>
                         <li>Email and SMS Queues</li>
                         <li>Email and SMS Queues</li>
                     </ul>
                     </ul>
-                    The LocalDB can be downloaded or uploaded.  This is generally only useful when upgrading or migrating a server, and the data is being backed up or restored.
+                    <p>The LocalDB can be downloaded or uploaded.
+                        This is generally only useful when upgrading or migrating a server, and the data is being backed up or restored.</p>
+                    <p>User-generated data is not stored in the LocalDB unless specifically configured to do so.</p>
                 </td>
                 </td>
             </tr>
             </tr>
         </table>
         </table>

+ 3 - 1
webapp/src/main/webapp/WEB-INF/jsp/fragment/configguide-header.jsp

@@ -48,4 +48,6 @@
 <pwm:script-ref url="/public/resources/js/configeditor.js"/>
 <pwm:script-ref url="/public/resources/js/configeditor.js"/>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
 <pwm:script-ref url="/public/resources/js/uilibrary.js"/>
 <pwm:script-ref url="/public/resources/js/uilibrary.js"/>
-<progress style="opacity: 0.5; width:96%; height:7px; padding: 0; margin: 0 1%;" value="<%=pctComplete%>" max="100"></progress>
+<div class="headerProgressBar">
+    <progress value="<%=pctComplete%>" max="100">100%</progress>
+</div>

+ 2 - 1
webapp/src/main/webapp/public/resources/js/configeditor-settings-stringarray.js

@@ -141,7 +141,8 @@ StringArrayValueHandler.drawRow = function(settingKey, iteration, value, itemCou
         rowHtml += '</td>';
         rowHtml += '</td>';
     }
     }
 
 
-    const showDeleteButtons = (itemCount > 1 || (!settingInfo['required'])) && (settingProperties['Minimum'] && itemCount > settingProperties['Minimum'])
+    const minValuesRequired = settingProperties['Minimum'] ? settingProperties['Minimum'] : settingInfo['required'] ? 1 : 0;
+    const showDeleteButtons = itemCount > minValuesRequired;
     const deleteButtonID = 'button-' + settingKey + '-' + iteration + '-delete';
     const deleteButtonID = 'button-' + settingKey + '-' + iteration + '-delete';
     if (showDeleteButtons) {
     if (showDeleteButtons) {
         rowHtml += '<td class="noborder nopadding" style="width:10px" title="Delete">';
         rowHtml += '<td class="noborder nopadding" style="width:10px" title="Delete">';

+ 26 - 23
webapp/src/main/webapp/public/resources/js/configguide.js

@@ -26,7 +26,7 @@ var PWM_GLOBAL = PWM_GLOBAL || {};
 
 
 PWM_GUIDE.selectTemplate = function(template) {
 PWM_GUIDE.selectTemplate = function(template) {
     PWM_MAIN.showWaitDialog({title:'Loading...',loadFunction:function() {
     PWM_MAIN.showWaitDialog({title:'Loading...',loadFunction:function() {
-            var url = PWM_MAIN.addParamToUrl(window.location.href,'processAction','selectTemplate');
+            let url = PWM_MAIN.addParamToUrl(window.location.href, 'processAction', 'selectTemplate');
             url = PWM_MAIN.addParamToUrl(url, 'template', template);
             url = PWM_MAIN.addParamToUrl(url, 'template', template);
             PWM_MAIN.showDialog(url,function(result){
             PWM_MAIN.showDialog(url,function(result){
                 if (!result['error']) {
                 if (!result['error']) {
@@ -41,11 +41,11 @@ PWM_GUIDE.selectTemplate = function(template) {
 };
 };
 
 
 PWM_GUIDE.updateForm = function() {
 PWM_GUIDE.updateForm = function() {
-    var formJson = PWM_MAIN.JSLibrary.formToValueMap('configForm');
-    var url = PWM_MAIN.addParamToUrl(window.location.href,'processAction','updateForm');
-    var loadFunction = function() {
+    const formJson = PWM_MAIN.JSLibrary.formToValueMap('configForm');
+    const url = PWM_MAIN.addParamToUrl(window.location.href, 'processAction', 'updateForm');
+    const loadFunction = function () {
         PWM_MAIN.log("sent form params to server: " + formJson);
         PWM_MAIN.log("sent form params to server: " + formJson);
-    }
+    };
     PWM_MAIN.ajaxRequest(url,loadFunction,{content:formJson});
     PWM_MAIN.ajaxRequest(url,loadFunction,{content:formJson});
 };
 };
 
 
@@ -53,9 +53,9 @@ PWM_GUIDE.gotoStep = function(step) {
     PWM_MAIN.showWaitDialog({loadFunction:function(){
     PWM_MAIN.showWaitDialog({loadFunction:function(){
             //preload in case of server restart
             //preload in case of server restart
             PWM_MAIN.preloadAll(function(){
             PWM_MAIN.preloadAll(function(){
-                var url = PWM_MAIN.addParamToUrl(window.location.href,'processAction','gotoStep');
+                let url = PWM_MAIN.addParamToUrl(window.location.href, 'processAction', 'gotoStep');
                 url = PWM_MAIN.addParamToUrl(url, 'step', step);
                 url = PWM_MAIN.addParamToUrl(url, 'step', step);
-                var loadFunction = function(result) {
+                const loadFunction = function (result) {
                     if (result['error']) {
                     if (result['error']) {
                         PWM_MAIN.showErrorDialog(result);
                         PWM_MAIN.showErrorDialog(result);
                         return;
                         return;
@@ -73,9 +73,9 @@ PWM_GUIDE.gotoStep = function(step) {
 };
 };
 
 
 PWM_GUIDE.setUseConfiguredCerts = function(value) {
 PWM_GUIDE.setUseConfiguredCerts = function(value) {
-    var url = PWM_MAIN.addParamToUrl(window.location.href,'processAction','useConfiguredCerts');
+    let url = PWM_MAIN.addParamToUrl(window.location.href, 'processAction', 'useConfiguredCerts');
     url = PWM_MAIN.addParamToUrl(url, 'value', value);
     url = PWM_MAIN.addParamToUrl(url, 'value', value);
-    var loadFunction = function(result) {
+    const loadFunction = function (result) {
         if (result['error']) {
         if (result['error']) {
             PWM_MAIN.showError(result['errorDetail']);
             PWM_MAIN.showError(result['errorDetail']);
         }
         }
@@ -86,15 +86,17 @@ PWM_GUIDE.setUseConfiguredCerts = function(value) {
 PWM_GUIDE.extendSchema = function() {
 PWM_GUIDE.extendSchema = function() {
     PWM_MAIN.showConfirmDialog({text:"Are you sure you want to extend the LDAP schema?",okAction:function(){
     PWM_MAIN.showConfirmDialog({text:"Are you sure you want to extend the LDAP schema?",okAction:function(){
             PWM_MAIN.showWaitDialog({loadFunction:function() {
             PWM_MAIN.showWaitDialog({loadFunction:function() {
-                    var url = PWM_MAIN.addParamToUrl(window.location.href,'processAction','extendSchema');
-                    var loadFunction = function(result) {
+                    const url = PWM_MAIN.addParamToUrl(window.location.href, 'processAction', 'extendSchema');
+                    const loadFunction = function (result) {
                         if (result['error']) {
                         if (result['error']) {
                             PWM_MAIN.showError(result['errorDetail']);
                             PWM_MAIN.showError(result['errorDetail']);
                         } else {
                         } else {
-                            var output = '<pre>' + result['data'] + '</pre>';
-                            PWM_MAIN.showDialog({title:"Results",text:output,okAction:function(){
+                            const output = '<pre>' + result['data'] + '</pre>';
+                            PWM_MAIN.showDialog({
+                                title: "Results", text: output, okAction: function () {
                                     window.location.reload();
                                     window.location.reload();
-                                }});
+                                }
+                            });
                         }
                         }
                     };
                     };
                     PWM_MAIN.ajaxRequest(url,loadFunction);
                     PWM_MAIN.ajaxRequest(url,loadFunction);
@@ -105,24 +107,25 @@ PWM_GUIDE.extendSchema = function() {
 PWM_GUIDE.skipGuide = function() {
 PWM_GUIDE.skipGuide = function() {
     PWM_MAIN.preloadAll(function(){
     PWM_MAIN.preloadAll(function(){
         PWM_MAIN.showConfirmDialog({text:PWM_CONFIG.showString('Confirm_SkipGuide'),okAction:function() {
         PWM_MAIN.showConfirmDialog({text:PWM_CONFIG.showString('Confirm_SkipGuide'),okAction:function() {
-
-                var skipGuideFunction = function(password) {
-                    var contents = {};
+                const skipGuideFunction = function (password) {
+                    const contents = {};
                     contents['password'] = password;
                     contents['password'] = password;
-                    var url = PWM_MAIN.addParamToUrl(window.location.href,'processAction','skipGuide');
-                    var loadFunction = function(result) {
+                    const url = PWM_MAIN.addParamToUrl(window.location.href, 'processAction', 'skipGuide');
+                    const loadFunction = function (result) {
                         if (result['error']) {
                         if (result['error']) {
                             PWM_MAIN.showError(result['errorDetail']);
                             PWM_MAIN.showError(result['errorDetail']);
                         } else {
                         } else {
-                            PWM_MAIN.showWaitDialog({loadFunction:function(){
+                            PWM_MAIN.showWaitDialog({
+                                loadFunction: function () {
                                     PWM_CONFIG.waitForRestart();
                                     PWM_CONFIG.waitForRestart();
-                                }});
+                                }
+                            });
                         }
                         }
                     };
                     };
-                    PWM_MAIN.ajaxRequest(url,loadFunction,{content:contents});
+                    PWM_MAIN.ajaxRequest(url, loadFunction, {content: contents});
                 };
                 };
 
 
-                var text = 'Set Configuration Password';
+                const text = 'Set Configuration Password';
                 UILibrary.passwordDialogPopup({minimumLength:8, title:text, writeFunction:skipGuideFunction});
                 UILibrary.passwordDialogPopup({minimumLength:8, title:text, writeFunction:skipGuideFunction});
             }});
             }});
     });
     });

+ 7 - 1
webapp/src/main/webapp/public/resources/js/main.js

@@ -718,14 +718,20 @@ PWM_MAIN.showWaitDialog = function(options) {
 
 
     options = options || {};
     options = options || {};
     options['title'] = options['title'] || '';
     options['title'] = options['title'] || '';
+    var progressBar = options['progressBar'];
 
 
     var waitOverlayDiv = document.createElement('div');
     var waitOverlayDiv = document.createElement('div');
     waitOverlayDiv.setAttribute('id','wait-overlay');
     waitOverlayDiv.setAttribute('id','wait-overlay');
     document.body.appendChild(waitOverlayDiv);
     document.body.appendChild(waitOverlayDiv);
 
 
+    var htmlContent = '<span>' + options['title'] + '</span>';
+    htmlContent += progressBar
+        ? '<progress value="-1" id="wait-progress"/>'
+        : '<div id="wait-overlay-inner"></div>';
+
     var waitOverlayMessage = document.createElement('div');
     var waitOverlayMessage = document.createElement('div');
     waitOverlayMessage.setAttribute('id','wait-overlay-message');
     waitOverlayMessage.setAttribute('id','wait-overlay-message');
-    waitOverlayMessage.innerHTML = '<span>' + options['title'] + '</span><div id="wait-overlay-inner"></div>';
+    waitOverlayMessage.innerHTML = htmlContent;
     document.body.appendChild(waitOverlayMessage);
     document.body.appendChild(waitOverlayMessage);
 
 
     if ('loadFunction' in options) {
     if ('loadFunction' in options) {

+ 5 - 14
webapp/src/main/webapp/public/resources/js/uilibrary.js

@@ -493,19 +493,10 @@ UILibrary.uploadFileDialog = function(options) {
         if (data.lengthComputable) {
         if (data.lengthComputable) {
             var decimal = data.loaded / data.total;
             var decimal = data.loaded / data.total;
             console.log('upload progress: ' + decimal);
             console.log('upload progress: ' + decimal);
-            require(["dijit/registry"],function(registry){
-                var progressBar = registry.byId('progressBar');
-                if (progressBar) {
-                    progressBar.set("maximum", 100);
-                    progressBar.set("indeterminate", false);
-                    progressBar.set("value", decimal * 100);
-                }
-                var html5Bar = PWM_MAIN.getObject("wait");
-                if (html5Bar) {
-                    html5Bar.setAttribute("max", 100);
-                    html5Bar.setAttribute("value", decimal * 100);
-                }
-            });
+            var waitProgressObject = PWM_MAIN.getObject('wait-progress');
+            if (waitProgressObject) {
+                waitProgressObject.setAttribute('value', decimal.toString());
+            }
         } else {
         } else {
             console.log('progressFunction: no data');
             console.log('progressFunction: no data');
             return;
             return;
@@ -554,7 +545,7 @@ UILibrary.uploadFileDialog = function(options) {
         xhr.send(fd);
         xhr.send(fd);
         PWM_GLOBAL['inhibitHealthUpdate'] = true;
         PWM_GLOBAL['inhibitHealthUpdate'] = true;
         PWM_MAIN.IdleTimeoutHandler.cancelCountDownTimer();
         PWM_MAIN.IdleTimeoutHandler.cancelCountDownTimer();
-        PWM_MAIN.showWaitDialog({title:PWM_MAIN.showString('Display_Uploading')});
+        PWM_MAIN.showWaitDialog({title:PWM_MAIN.showString('Display_Uploading'),progressBar:true});
     };
     };
 
 
     completeFunction = 'completeFunction' in options ? options['completeFunction'] : completeFunction;
     completeFunction = 'completeFunction' in options ? options['completeFunction'] : completeFunction;

+ 10 - 0
webapp/src/main/webapp/public/resources/style.css

@@ -1023,6 +1023,16 @@ input[type=search] {
     padding-bottom: 10px;
     padding-bottom: 10px;
 }
 }
 
 
+.headerProgressBar {
+    width: 600px;
+    padding: 5px;
+    margin-top: 20px;
+    position: relative;
+    margin-left: auto;
+    margin-right: auto;
+    clear: both;
+}
+
 progress:not([value]) {
 progress:not([value]) {
     width: 100%;
     width: 100%;
     height: 20px;
     height: 20px;