浏览代码

changes to support non-static ldapchai APIs

Jason Rivard 7 年之前
父节点
当前提交
f942c69029
共有 38 个文件被更改,包括 285 次插入143 次删除
  1. 1 1
      server/pom.xml
  2. 1 2
      server/src/main/java/password/pwm/PwmApplication.java
  3. 9 5
      server/src/main/java/password/pwm/config/function/UserMatchViewerFunction.java
  4. 2 2
      server/src/main/java/password/pwm/config/profile/LdapProfile.java
  5. 3 2
      server/src/main/java/password/pwm/config/profile/NewUserProfile.java
  6. 20 16
      server/src/main/java/password/pwm/health/LDAPStatusChecker.java
  7. 4 4
      server/src/main/java/password/pwm/http/SessionManager.java
  8. 3 2
      server/src/main/java/password/pwm/http/servlet/ActivateUserServlet.java
  9. 1 2
      server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java
  10. 1 2
      server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java
  11. 4 1
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java
  12. 6 3
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  13. 7 3
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideUtils.java
  14. 1 0
      server/src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java
  15. 1 1
      server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskDetailInfoBean.java
  16. 5 7
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java
  17. 13 6
      server/src/main/java/password/pwm/ldap/LdapBrowser.java
  18. 48 19
      server/src/main/java/password/pwm/ldap/LdapConnectionService.java
  19. 10 9
      server/src/main/java/password/pwm/ldap/LdapDebugDataGenerator.java
  20. 45 8
      server/src/main/java/password/pwm/ldap/LdapOperationsHelper.java
  21. 1 2
      server/src/main/java/password/pwm/ldap/UserInfoReader.java
  22. 3 3
      server/src/main/java/password/pwm/ldap/auth/LDAPAuthenticationRequest.java
  23. 1 0
      server/src/main/java/password/pwm/ldap/auth/SessionAuthenticator.java
  24. 2 3
      server/src/main/java/password/pwm/ldap/schema/EdirSchemaExtender.java
  25. 3 4
      server/src/main/java/password/pwm/ldap/search/UserSearchEngine.java
  26. 1 0
      server/src/main/java/password/pwm/svc/PwmServiceEnum.java
  27. 0 22
      server/src/main/java/password/pwm/svc/sessiontrack/SessionTrackService.java
  28. 2 0
      server/src/main/java/password/pwm/svc/telemetry/TelemetryService.java
  29. 1 1
      server/src/main/java/password/pwm/util/cli/commands/ImportResponsesCommand.java
  30. 2 1
      server/src/main/java/password/pwm/util/cli/commands/LdapSchemaExtendCommand.java
  31. 56 0
      server/src/main/java/password/pwm/util/localdb/LocalDBService.java
  32. 1 0
      server/src/main/java/password/pwm/util/localdb/Xodus_LocalDB.java
  33. 1 1
      server/src/main/java/password/pwm/util/logging/PwmLogEvent.java
  34. 2 4
      server/src/main/java/password/pwm/util/operations/PasswordUtility.java
  35. 3 3
      server/src/main/java/password/pwm/util/operations/cr/NMASCrOperator.java
  36. 1 2
      server/src/main/java/password/pwm/ws/server/RestServlet.java
  37. 18 1
      server/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp
  38. 2 1
      server/src/main/webapp/WEB-INF/jsp/configguide-ldap_schema.jsp

+ 1 - 1
server/pom.xml

@@ -876,4 +876,4 @@
             </snapshots>
         </pluginRepository>
     </pluginRepositories>
-</project>
+</project>

+ 1 - 2
server/src/main/java/password/pwm/PwmApplication.java

@@ -22,7 +22,6 @@
 
 package password.pwm;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
@@ -445,7 +444,7 @@ public class PwmApplication {
     {
         try {
             final ChaiProvider proxiedProvider = getProxyChaiProvider(userIdentity.getLdapProfileID());
-            return ChaiFactory.createChaiUser(userIdentity.getUserDN(), proxiedProvider);
+            return proxiedProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
         } catch (ChaiUnavailableException e) {
             throw PwmUnrecoverableException.fromChaiException(e);
         }

+ 9 - 5
server/src/main/java/password/pwm/config/function/UserMatchViewerFunction.java

@@ -23,7 +23,7 @@
 package password.pwm.config.function;
 
 import com.novell.ldapchai.ChaiEntry;
-import com.novell.ldapchai.ChaiFactory;
+import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
@@ -127,13 +127,17 @@ public class UserMatchViewerFunction implements SettingUIFunction {
             ChaiEntry chaiEntry = null;
             try {
                 final ChaiProvider proxiedProvider = pwmApplication.getProxyChaiProvider(loopID);
-                chaiEntry = ChaiFactory.createChaiEntry(baseDN, proxiedProvider);
+                chaiEntry = proxiedProvider.getEntryFactory().newChaiEntry(baseDN);
             } catch (Exception e) {
                 LOGGER.error("error while testing entry DN for profile '" + profileID + "', error:" + profileID);
             }
-            if (chaiEntry != null && !chaiEntry.isValid()) {
-                final String errorMsg = "entry DN '" + baseDN + "' is not valid for profile " + loopID;
-                throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_LDAP_DATA_ERROR, errorMsg));
+            try {
+                if (chaiEntry != null && !chaiEntry.exists()) {
+                    final String errorMsg = "entry DN '" + baseDN + "' is not valid for profile " + loopID;
+                    throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_LDAP_DATA_ERROR, errorMsg));
+                }
+            } catch (ChaiUnavailableException e) {
+                throw PwmUnrecoverableException.fromChaiException(e);
             }
         }
     }

+ 2 - 2
server/src/main/java/password/pwm/config/profile/LdapProfile.java

@@ -23,7 +23,7 @@
 package password.pwm.config.profile;
 
 import com.novell.ldapchai.ChaiEntry;
-import com.novell.ldapchai.ChaiFactory;
+
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
@@ -146,7 +146,7 @@ public class LdapProfile extends AbstractProfile implements Profile {
         if (canonicalValue == null) {
             try {
                 final ChaiProvider chaiProvider = this.getProxyChaiProvider(pwmApplication);
-                final ChaiEntry chaiEntry = ChaiFactory.createChaiEntry(dnValue, chaiProvider);
+                final ChaiEntry chaiEntry = chaiProvider.getEntryFactory().newChaiEntry(dnValue);
                 canonicalValue = chaiEntry.readCanonicalDN();
 
                 if (enableCanonicalCache) {

+ 3 - 2
server/src/main/java/password/pwm/config/profile/NewUserProfile.java

@@ -22,9 +22,9 @@
 
 package password.pwm.config.profile;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
@@ -115,7 +115,8 @@ public class NewUserProfile extends AbstractProfile {
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_INVALID_CONFIG,"user ldap dn in setting " + PwmSetting.NEWUSER_PASSWORD_POLICY_USER.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE) + " can not be resolved"));
             } else {
                 try {
-                    final ChaiUser chaiUser = ChaiFactory.createChaiUser(lookupDN, pwmApplication.getProxyChaiProvider(defaultLdapProfile.getIdentifier()));
+                    final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(defaultLdapProfile.getIdentifier());
+                    final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser(lookupDN);
                     final UserIdentity userIdentity = new UserIdentity(lookupDN, defaultLdapProfile.getIdentifier());
                     thePolicy = PasswordUtility.readPasswordPolicyForUser(pwmApplication, null, userIdentity, chaiUser, userLocale);
                 } catch (ChaiUnavailableException e) {

+ 20 - 16
server/src/main/java/password/pwm/health/LDAPStatusChecker.java

@@ -23,7 +23,6 @@
 package password.pwm.health;
 
 import com.novell.ldapchai.ChaiEntry;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiError;
 import com.novell.ldapchai.exception.ChaiErrors;
@@ -31,7 +30,6 @@ import com.novell.ldapchai.exception.ChaiException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiConfiguration;
 import com.novell.ldapchai.provider.ChaiProvider;
-import com.novell.ldapchai.provider.ChaiProviderFactory;
 import com.novell.ldapchai.provider.ChaiSetting;
 import com.novell.ldapchai.provider.DirectoryVendor;
 import com.novell.ldapchai.util.ChaiUtility;
@@ -101,7 +99,7 @@ public class LDAPStatusChecker implements HealthChecker {
                     checkBasicLdapConnectivity(pwmApplication, config, entry.getValue(), true));
 
             if (profileRecords.isEmpty()) {
-                profileRecords.addAll(checkLdapServerUrls(config, ldapProfiles.get(profileID)));
+                profileRecords.addAll(checkLdapServerUrls(pwmApplication, config, ldapProfiles.get(profileID)));
             }
 
             if (profileRecords.isEmpty()) {
@@ -192,6 +190,7 @@ public class LDAPStatusChecker implements HealthChecker {
             try {
 
                 chaiProvider = LdapOperationsHelper.createChaiProvider(
+                        pwmApplication,
                         SessionLabel.HEALTH_SESSION_LABEL,
                         ldapProfile,
                         config,
@@ -199,7 +198,7 @@ public class LDAPStatusChecker implements HealthChecker {
                         proxyUserPW
                 );
 
-                theUser = ChaiFactory.createChaiUser(testUserDN, chaiProvider);
+                theUser = chaiProvider.getEntryFactory().newChaiUser(testUserDN);
 
             } catch (ChaiUnavailableException e) {
                 returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_TestUserUnavailable,
@@ -353,7 +352,11 @@ public class LDAPStatusChecker implements HealthChecker {
     }
 
 
-    public List<HealthRecord> checkLdapServerUrls(final Configuration config, final LdapProfile ldapProfile)
+    public List<HealthRecord> checkLdapServerUrls(
+            final PwmApplication pwmApplication,
+            final Configuration config,
+            final LdapProfile ldapProfile
+    )
     {
         final List<HealthRecord> returnRecords = new ArrayList<>();
         final List<String> serverURLs = ldapProfile.readSettingAsStringArray(PwmSetting.LDAP_SERVER_URLS);
@@ -362,6 +365,7 @@ public class LDAPStatusChecker implements HealthChecker {
             ChaiProvider chaiProvider = null;
             try {
                 chaiProvider = LdapOperationsHelper.createChaiProvider(
+                        pwmApplication,
                         SessionLabel.HEALTH_SESSION_LABEL,
                         config,
                         ldapProfile,
@@ -369,8 +373,8 @@ public class LDAPStatusChecker implements HealthChecker {
                         proxyDN,
                         ldapProfile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD)
                 );
-                final ChaiUser proxyUser = ChaiFactory.createChaiUser(proxyDN, chaiProvider);
-                proxyUser.isValid();
+                final ChaiUser proxyUser = chaiProvider.getEntryFactory().newChaiUser(proxyDN);
+                proxyUser.exists();
             } catch (Exception e) {
                 final String errorString = "error connecting to ldap server '" + loopURL + "': " + e.getMessage();
                 returnRecords.add(new HealthRecord(
@@ -406,9 +410,9 @@ public class LDAPStatusChecker implements HealthChecker {
                 if (proxyPW == null) {
                     return Collections.singletonList(new HealthRecord(HealthStatus.WARN,HealthTopic.LDAP,"Missing Proxy User Password"));
                 }
-                chaiProvider = LdapOperationsHelper.createChaiProvider(SessionLabel.HEALTH_SESSION_LABEL,ldapProfile,config,proxyDN,proxyPW);
-                final ChaiEntry adminEntry = ChaiFactory.createChaiEntry(proxyDN,chaiProvider);
-                adminEntry.isValid();
+                chaiProvider = LdapOperationsHelper.createChaiProvider(pwmApplication, SessionLabel.HEALTH_SESSION_LABEL,ldapProfile,config,proxyDN,proxyPW);
+                final ChaiEntry adminEntry = chaiProvider.getEntryFactory().newChaiEntry(proxyDN);
+                adminEntry.exists();
                 directoryVendor = chaiProvider.getDirectoryVendor();
             } catch (ChaiException e) {
                 final ChaiError chaiError = ChaiErrors.getErrorForMessage(e.getMessage());
@@ -447,7 +451,7 @@ public class LDAPStatusChecker implements HealthChecker {
             if (testContextlessRoot) {
                 for (final String loopContext : ldapProfile.readSettingAsStringArray(PwmSetting.LDAP_CONTEXTLESS_ROOT)) {
                     try {
-                        final ChaiEntry contextEntry = ChaiFactory.createChaiEntry(loopContext,chaiProvider);
+                        final ChaiEntry contextEntry = chaiProvider.getEntryFactory().newChaiEntry(loopContext);
                         final Set<String> objectClasses = contextEntry.readObjectClass();
 
                         if (objectClasses == null || objectClasses.isEmpty()) {
@@ -549,7 +553,7 @@ public class LDAPStatusChecker implements HealthChecker {
                 );
                 final Collection<ChaiConfiguration> replicaConfigs = ChaiUtility.splitConfigurationPerReplica(profileChaiConfiguration, Collections.<ChaiSetting,String>emptyMap());
                 for (final ChaiConfiguration chaiConfiguration : replicaConfigs) {
-                    final ChaiProvider loopProvider = ChaiProviderFactory.createProvider(chaiConfiguration);
+                    final ChaiProvider loopProvider = pwmApplication.getLdapConnectionService().getChaiProviderFactory().newProvider(chaiConfiguration);
                     replicaVendorMap.put(chaiConfiguration.getSetting(ChaiSetting.BIND_URLS), loopProvider.getDirectoryVendor());
                 }
             }
@@ -614,7 +618,7 @@ public class LDAPStatusChecker implements HealthChecker {
                 );
                 final Collection<ChaiConfiguration> replicaConfigs = ChaiUtility.splitConfigurationPerReplica(profileChaiConfiguration, Collections.<ChaiSetting,String>emptyMap());
                 for (final ChaiConfiguration chaiConfiguration : replicaConfigs) {
-                    final ChaiProvider loopProvider = ChaiProviderFactory.createProvider(chaiConfiguration);
+                    final ChaiProvider loopProvider = pwmApplication.getLdapConnectionService().getChaiProviderFactory().newProvider(chaiConfiguration);
                     final ChaiEntry rootDSE = ChaiUtility.getRootDSE(loopProvider);
                     final Set<String> controls = rootDSE.readMultiStringAttribute("supportedControl");
                     final boolean asnSupported = controls.contains(PwmConstants.LDAP_AD_PASSWORD_POLICY_CONTROL_ASN);
@@ -766,8 +770,8 @@ public class LDAPStatusChecker implements HealthChecker {
         final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(ldapProfileID);
         try {
             if (!isExampleDN(dnValue)) {
-                final ChaiEntry baseDNEntry = ChaiFactory.createChaiEntry(dnValue, chaiProvider);
-                if (!baseDNEntry.isValid()) {
+                final ChaiEntry baseDNEntry = chaiProvider.getEntryFactory().newChaiEntry(dnValue);
+                if (!baseDNEntry.exists()) {
                     return "DN '" + dnValue + "' is invalid";
                 } else {
                     final String canonicalDN = baseDNEntry.readCanonicalDN();
@@ -820,7 +824,7 @@ public class LDAPStatusChecker implements HealthChecker {
         profileRecords.addAll(ldapStatusChecker.checkBasicLdapConnectivity(tempApplication, config, ldapProfile,
                 testContextless));
         if (fullTest) {
-            profileRecords.addAll(ldapStatusChecker.checkLdapServerUrls(config, ldapProfile));
+            profileRecords.addAll(ldapStatusChecker.checkLdapServerUrls(pwmApplication, config, ldapProfile));
         }
 
         if (profileRecords.isEmpty()) {

+ 4 - 4
server/src/main/java/password/pwm/http/SessionManager.java

@@ -22,7 +22,6 @@
 
 package password.pwm.http;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
@@ -88,6 +87,7 @@ public class SessionManager {
 
         try {
             this.chaiProvider = LdapOperationsHelper.createChaiProvider(
+                    pwmApplication,
                     pwmSession.getLabel(),
                     userIdentity.getLdapProfile(pwmApplication.getConfig()),
                     pwmApplication.getConfig(),
@@ -95,7 +95,7 @@ public class SessionManager {
                     userPassword
             );
             final String userDN = userIdentity.getUserDN();
-            ChaiFactory.createChaiEntry(userDN,chaiProvider).isValid();
+            chaiProvider.getEntryFactory().newChaiEntry(userDN).exists();
         } catch (ChaiUnavailableException e) {
             final ErrorInformation errorInformation = new ErrorInformation(
                     PwmError.ERROR_DIRECTORY_UNAVAILABLE,
@@ -130,7 +130,7 @@ public class SessionManager {
             throw new IllegalStateException("user not logged in");
         }
 
-        return ChaiFactory.createChaiUser(userDN.getUserDN(), this.getChaiProvider());
+        return this.getChaiProvider().getEntryFactory().newChaiUser(userDN.getUserDN());
     }
 
     public boolean hasActiveLdapConnection() {
@@ -149,7 +149,7 @@ public class SessionManager {
                 throw new PwmUnrecoverableException(PwmError.ERROR_NO_LDAP_CONNECTION);
             }
             final ChaiProvider provider = this.getChaiProvider();
-            return ChaiFactory.createChaiUser(userIdentity.getUserDN(), provider);
+            return provider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
         } catch (ChaiUnavailableException e) {
             throw PwmUnrecoverableException.fromChaiException(e);
         }

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/ActivateUserServlet.java

@@ -22,11 +22,11 @@
 
 package password.pwm.http.servlet;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ImpossiblePasswordPolicyException;
+import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
@@ -476,7 +476,8 @@ public class ActivateUserServlet extends AbstractPwmServlet {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final String searchFilter = figureLdapSearchFilter(pwmRequest);
-        final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID()));
+        final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
+        final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
 
         for (final Map.Entry<FormConfiguration, String> entry : formValues.entrySet()) {
             final FormConfiguration formItem = entry.getKey();

+ 1 - 2
server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java

@@ -22,7 +22,6 @@
 
 package password.pwm.http.servlet;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
@@ -428,7 +427,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
             provider.createEntry(guestUserDN, createObjectClasses, createAttributes);
             LOGGER.info(pwmSession, "created user object: " + guestUserDN);
 
-            final ChaiUser theUser = ChaiFactory.createChaiUser(guestUserDN, provider);
+            final ChaiUser theUser = provider.getEntryFactory().newChaiUser(guestUserDN);
             final UserIdentity userIdentity = new UserIdentity(guestUserDN, pwmSession.getUserInfo().getUserIdentity().getLdapProfileID());
 
             // write the expiration date:

+ 1 - 2
server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java

@@ -151,8 +151,7 @@ public class AppDashboardData implements Serializable {
     }
 
     private static int ldapConnectionCount(final PwmApplication pwmApplication) {
-        return pwmApplication.getSessionTrackService().ldapConnectionCount()
-                + pwmApplication.getLdapConnectionService().connectionCount();
+        return pwmApplication.getLdapConnectionService().connectionCount();
     }
 
     private static List<DisplayElement> makeAboutData(

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

@@ -876,7 +876,10 @@ public class ConfigEditorServlet extends ControlledPwmServlet {
         final String profile = inputMap.get("profile");
         final String dn = inputMap.containsKey("dn") ? inputMap.get("dn") : "";
 
-        final LdapBrowser ldapBrowser = new LdapBrowser(configManagerBean.getStoredConfiguration());
+        final LdapBrowser ldapBrowser = new LdapBrowser(
+                pwmRequest.getPwmApplication().getLdapConnectionService().getChaiProviderFactory(),
+                configManagerBean.getStoredConfiguration()
+        );
 
         LdapBrowser.LdapBrowseResult result;
         try {

+ 6 - 3
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java

@@ -330,7 +330,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet {
     private ProcessStatus restBrowseLdap(
             final PwmRequest pwmRequest
     )
-            throws IOException, ServletException, PwmUnrecoverableException
+            throws IOException, PwmUnrecoverableException
     {
         final ConfigGuideBean configGuideBean = getBean(pwmRequest);
 
@@ -345,7 +345,10 @@ public class ConfigGuideServlet extends ControlledPwmServlet {
         final String profile = inputMap.get("profile");
         final String dn = inputMap.getOrDefault("dn", "");
 
-        final LdapBrowser ldapBrowser = new LdapBrowser(storedConfiguration);
+        final LdapBrowser ldapBrowser = new LdapBrowser(
+                pwmRequest.getPwmApplication().getLdapConnectionService().getChaiProviderFactory(),
+                storedConfiguration
+        );
         final LdapBrowser.LdapBrowseResult result = ldapBrowser.doBrowse(profile, dn);
         ldapBrowser.close();
 
@@ -445,7 +448,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet {
         final ConfigGuideBean configGuideBean = getBean(pwmRequest);
 
         try {
-            final SchemaOperationResult schemaOperationResult = ConfigGuideUtils.extendSchema(configGuideBean, true);
+            final SchemaOperationResult schemaOperationResult = ConfigGuideUtils.extendSchema(pwmRequest.getPwmApplication(), configGuideBean, true);
             pwmRequest.outputJsonResult(RestResultBean.withData(schemaOperationResult.getOperationLog()));
         } catch (Exception e) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN,e.getMessage());

+ 7 - 3
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideUtils.java

@@ -2,7 +2,6 @@ package password.pwm.http.servlet.configguide;
 
 import com.novell.ldapchai.provider.ChaiConfiguration;
 import com.novell.ldapchai.provider.ChaiProvider;
-import com.novell.ldapchai.provider.ChaiProviderFactory;
 import com.novell.ldapchai.provider.ChaiSetting;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 import password.pwm.PwmApplication;
@@ -84,7 +83,11 @@ public class ConfigGuideUtils {
         }
     }
 
-    public static SchemaOperationResult extendSchema(final ConfigGuideBean configGuideBean, final boolean doSchemaExtension) {
+    public static SchemaOperationResult extendSchema(
+            final PwmApplication pwmApplication,
+            final ConfigGuideBean configGuideBean,
+            final boolean doSchemaExtension
+    ) {
         final Map<ConfigGuideFormField,String> form = configGuideBean.getFormData();
         final boolean ldapServerSecure = "true".equalsIgnoreCase(form.get(ConfigGuideFormField.PARAM_LDAP_SECURE));
         final String ldapUrl = "ldap" + (ldapServerSecure ? "s" : "") + "://" + form.get(ConfigGuideFormField.PARAM_LDAP_HOST) + ":" + form.get(ConfigGuideFormField.PARAM_LDAP_PORT);
@@ -96,7 +99,8 @@ public class ConfigGuideUtils {
             )
                     .setSetting(ChaiSetting.PROMISCUOUS_SSL,"true")
                     .build();
-            final ChaiProvider chaiProvider = ChaiProviderFactory.createProvider(chaiConfiguration);
+
+            final ChaiProvider chaiProvider = pwmApplication.getLdapConnectionService().getChaiProviderFactory().newProvider(chaiConfiguration);
             if (doSchemaExtension) {
                 return SchemaManager.extendSchema(chaiProvider);
             } else {

+ 1 - 0
server/src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java

@@ -361,6 +361,7 @@ public class DebugItemGenerator {
         @Override
         public void outputItem(final PwmApplication pwmApplication, final PwmRequest pwmRequest, final OutputStream outputStream) throws Exception {
             final List<LdapDebugDataGenerator.LdapDebugInfo> ldapDebugInfos = LdapDebugDataGenerator.makeLdapDebugInfos(
+                    pwmApplication,
                     pwmRequest.getSessionLabel(),
                     pwmApplication.getConfig(),
                     pwmRequest.getLocale()

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskDetailInfoBean.java

@@ -101,7 +101,7 @@ public class HelpdeskDetailInfoBean implements Serializable {
         final Locale actorLocale = pwmRequest.getLocale();
         final ChaiUser theUser = HelpdeskServlet.getChaiUser(pwmRequest, helpdeskProfile, userIdentity);
 
-        if (!theUser.isValid()) {
+        if (!theUser.exists()) {
             return null;
         }
 

+ 5 - 7
server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java

@@ -22,13 +22,11 @@
 
 package password.pwm.http.servlet.newuser;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiConfiguration;
 import com.novell.ldapchai.provider.ChaiProvider;
-import com.novell.ldapchai.provider.ChaiProviderFactory;
 import com.novell.ldapchai.provider.ChaiSetting;
 import com.novell.ldapchai.provider.DirectoryVendor;
 import password.pwm.AppProperty;
@@ -38,13 +36,13 @@ import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.TokenVerificationProgress;
 import password.pwm.bean.UserIdentity;
-import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.TokenStorageMethod;
 import password.pwm.config.profile.LdapProfile;
 import password.pwm.config.profile.NewUserProfile;
 import password.pwm.config.profile.PwmPasswordPolicy;
+import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmDataValidationException;
 import password.pwm.error.PwmError;
@@ -169,7 +167,7 @@ class NewUserUtils {
             throw new PwmOperationalException(errorInformation);
         }
 
-        final ChaiUser theUser = ChaiFactory.createChaiUser(newUserDN, chaiProvider);
+        final ChaiUser theUser = chaiProvider.getEntryFactory().newChaiUser(newUserDN);
 
         final boolean useTempPw;
         {
@@ -190,7 +188,7 @@ class NewUserUtils {
                         .build();
                 temporaryPassword = RandomPasswordGenerator.createRandomPassword(pwmSession.getLabel(), randomGeneratorConfig, pwmApplication);
             }
-            final ChaiUser proxiedUser = ChaiFactory.createChaiUser(newUserDN, chaiProvider);
+            final ChaiUser proxiedUser = chaiProvider.getEntryFactory().newChaiUser(newUserDN);
             try { //set password as admin
                 proxiedUser.setPassword(temporaryPassword.getStringValue());
                 NewUserUtils.LOGGER.debug(pwmSession, "set temporary password for new user entry: " + newUserDN);
@@ -222,8 +220,8 @@ class NewUserUtils {
                         .setSetting(ChaiSetting.BIND_DN, newUserDN)
                         .setSetting(ChaiSetting.BIND_PASSWORD, temporaryPassword.getStringValue())
                         .build();
-                final ChaiProvider bindAsProvider = ChaiProviderFactory.createProvider(chaiConfiguration);
-                final ChaiUser bindAsUser = ChaiFactory.createChaiUser(newUserDN, bindAsProvider);
+                final ChaiProvider bindAsProvider = pwmApplication.getLdapConnectionService().getChaiProviderFactory().newProvider(chaiConfiguration);
+                final ChaiUser bindAsUser = bindAsProvider.getEntryFactory().newChaiUser(newUserDN);
                 bindAsUser.changePassword(temporaryPassword.getStringValue(), userPassword.getStringValue());
                 NewUserUtils.LOGGER.debug(pwmSession, "changed to user requested password for new user entry: " + newUserDN);
                 bindAsProvider.close();

+ 13 - 6
server/src/main/java/password/pwm/ldap/LdapBrowser.java

@@ -23,10 +23,10 @@
 package password.pwm.ldap;
 
 import com.novell.ldapchai.ChaiEntry;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.ChaiProviderFactory;
 import com.novell.ldapchai.provider.DirectoryVendor;
 import com.novell.ldapchai.provider.SearchScope;
 import com.novell.ldapchai.util.ChaiUtility;
@@ -52,11 +52,18 @@ import java.util.TreeMap;
 
 public class LdapBrowser {
     private static final PwmLogger LOGGER = PwmLogger.forClass(LdapBrowser.class);
-    final StoredConfigurationImpl storedConfiguration;
+    private final StoredConfigurationImpl storedConfiguration;
 
-    private Map<String,ChaiProvider> providerCache = new HashMap<>();
+    private final ChaiProviderFactory chaiProviderFactory ;
+    private final Map<String,ChaiProvider> providerCache = new HashMap<>();
 
-    public LdapBrowser(final StoredConfigurationImpl storedConfiguration) throws PwmUnrecoverableException {
+    public LdapBrowser(
+            final ChaiProviderFactory chaiProviderFactory,
+            final StoredConfigurationImpl storedConfiguration
+            )
+            throws PwmUnrecoverableException
+    {
+        this.chaiProviderFactory = chaiProviderFactory;
         this.storedConfiguration = storedConfiguration;
     }
 
@@ -108,7 +115,7 @@ public class LdapBrowser {
         if (adRootDNList(profileID).contains(dn)) {
             result.setParentDN("");
         } else if (dn != null && !dn.isEmpty()) {
-            final ChaiEntry dnEntry = ChaiFactory.createChaiEntry(dn, getChaiProvider(profileID));
+            final ChaiEntry dnEntry = getChaiProvider(profileID).getEntryFactory().newChaiEntry(dn);
             final ChaiEntry parentEntry = dnEntry.getParentEntry();
             if (parentEntry == null) {
                 result.setParentDN("");
@@ -124,7 +131,7 @@ public class LdapBrowser {
         if (!providerCache.containsKey(profile)) {
             final Configuration configuration = new Configuration(storedConfiguration);
             final LdapProfile ldapProfile = LdapProfile.makeFromStoredConfiguration(storedConfiguration, profile);
-            final ChaiProvider chaiProvider = LdapOperationsHelper.openProxyChaiProvider(null,ldapProfile,configuration,null);
+            final ChaiProvider chaiProvider = LdapOperationsHelper.openProxyChaiProvider(chaiProviderFactory, null,ldapProfile,configuration,null);
             providerCache.put(profile,chaiProvider);
         }
         return providerCache.get(profile);

+ 48 - 19
server/src/main/java/password/pwm/ldap/LdapConnectionService.java

@@ -40,10 +40,9 @@ import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 
 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.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -57,7 +56,7 @@ public class LdapConnectionService implements PwmService {
     private STATUS status = STATUS.NEW;
     private AtomicLoopIntIncrementer slotIncrementer;
     private final ThreadLocal<Map<LdapProfile,ChaiProvider>> threadLocalProvider = new ThreadLocal<>();
-    private final ChaiProviderFactory chaiProviderFactory = ChaiProviderFactory.newProviderFactory();
+    private ChaiProviderFactory chaiProviderFactory;
 
     public STATUS status()
     {
@@ -69,6 +68,8 @@ public class LdapConnectionService implements PwmService {
     {
         this.pwmApplication = pwmApplication;
 
+        chaiProviderFactory = ChaiProviderFactory.newProviderFactory();
+
         // read the lastLoginTime
         this.lastLdapErrors.putAll(readLastLdapFailure(pwmApplication));
 
@@ -87,9 +88,9 @@ public class LdapConnectionService implements PwmService {
     {
         status = STATUS.CLOSED;
         LOGGER.trace("closing ldap proxy connections");
-        for (final ChaiProvider existingProvider : getAllProviders()) {
+        if (chaiProviderFactory != null) {
             try {
-                existingProvider.close();
+                chaiProviderFactory.close();
             } catch (Exception e) {
                 LOGGER.error("error closing ldap proxy connection: " + e.getMessage(), e);
             }
@@ -104,7 +105,13 @@ public class LdapConnectionService implements PwmService {
 
     public ServiceInfoBean serviceInfo()
     {
-        return new ServiceInfoBean(Collections.singletonList(DataStorageMethod.LDAP));
+        final Map<String,String> debugProperties = new LinkedHashMap<>();
+        debugProperties.putAll(chaiProviderFactory.getGlobalStatistics());
+        debugProperties.putAll(connectionDebugInfo());
+        return new ServiceInfoBean(
+                Collections.singletonList(DataStorageMethod.LDAP),
+                Collections.unmodifiableMap(debugProperties)
+        );
     }
 
 
@@ -153,6 +160,7 @@ public class LdapConnectionService implements PwmService {
 
         try {
             final ChaiProvider newProvider = LdapOperationsHelper.openProxyChaiProvider(
+                    pwmApplication,
                     null,
                     ldapProfile,
                     pwmApplication.getConfig(),
@@ -232,25 +240,46 @@ public class LdapConnectionService implements PwmService {
         return perProfile;
     }
 
-    private Collection<ChaiProvider> getAllProviders() {
-        final List<ChaiProvider> returnList = new ArrayList<>();
-        for (final Map<Integer,ChaiProvider> loopProfileMap : proxyChaiProviders.values()) {
-            for (final ChaiProvider chaiProvider : loopProfileMap.values()) {
-                if (chaiProvider != null) {
-                    returnList.add(chaiProvider);
+    public int connectionCount() {
+        int count = 0;
+        if (chaiProviderFactory != null) {
+            for (final ChaiProvider chaiProvider : chaiProviderFactory.activeProviders()) {
+                if (chaiProvider.isConnected()) {
+                    count++;
                 }
             }
         }
-        return Collections.unmodifiableList(returnList);
+        return count;
     }
 
-    public int connectionCount() {
-        int count = 0;
-        for (final ChaiProvider chaiProvider : getAllProviders()) {
-            if (chaiProvider.isConnected()) {
-                count++;
+    public ChaiProviderFactory getChaiProviderFactory() {
+        return chaiProviderFactory;
+    }
+
+    private enum DebugKey {
+        ALLOCATED_CONNECTIONS,
+        ACTIVE_CONNECTIONS,
+        IDLE_CONNECTIONS,
+    }
+
+    private Map<String,String> connectionDebugInfo() {
+        int allocatedConnections = 0;
+        int activeConnections = 0;
+        int idleConnections = 0;
+        if (chaiProviderFactory != null) {
+            for (final ChaiProvider chaiProvider : chaiProviderFactory.activeProviders()) {
+                allocatedConnections++;
+                if (chaiProvider.isConnected()) {
+                    activeConnections++;
+                } else {
+                    idleConnections++;
+                }
             }
         }
-        return count;
+        final Map<String,String> debugInfo = new HashMap<>();
+        debugInfo.put(DebugKey.ALLOCATED_CONNECTIONS.name(), String.valueOf(allocatedConnections));
+        debugInfo.put(DebugKey.ACTIVE_CONNECTIONS.name(), String.valueOf(activeConnections));
+        debugInfo.put(DebugKey.IDLE_CONNECTIONS.name(), String.valueOf(idleConnections));
+        return Collections.unmodifiableMap(debugInfo);
     }
 }

+ 10 - 9
server/src/main/java/password/pwm/ldap/LdapDebugDataGenerator.java

@@ -23,14 +23,13 @@
 package password.pwm.ldap;
 
 import com.novell.ldapchai.ChaiEntry;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiConfiguration;
 import com.novell.ldapchai.provider.ChaiProvider;
-import com.novell.ldapchai.provider.ChaiProviderFactory;
 import com.novell.ldapchai.provider.ChaiSetting;
 import com.novell.ldapchai.util.ChaiUtility;
+import password.pwm.PwmApplication;
 import password.pwm.bean.SessionLabel;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
@@ -50,6 +49,7 @@ public class LdapDebugDataGenerator {
     private static final PwmLogger LOGGER = PwmLogger.forClass(LdapDebugDataGenerator.class);
 
     public static List<LdapDebugInfo> makeLdapDebugInfos(
+            final PwmApplication pwmApplication,
             final SessionLabel sessionLabel,
             final Configuration configuration,
             final Locale locale
@@ -63,6 +63,7 @@ public class LdapDebugDataGenerator {
             ldapDebugInfo.setDisplayName(ldapProfile.getDisplayName(locale));
             try {
                 final ChaiProvider chaiProvider = LdapOperationsHelper.createChaiProvider(
+                        pwmApplication,
                         null,
                         ldapProfile,
                         configuration,
@@ -74,7 +75,7 @@ public class LdapDebugDataGenerator {
                 for (final ChaiConfiguration chaiConfiguration : chaiConfigurations) {
                     final LdapDebugServerInfo ldapDebugServerInfo = new LdapDebugServerInfo();
                     ldapDebugServerInfo.setLdapServerlUrl(chaiConfiguration.getSetting(ChaiSetting.BIND_URLS));
-                    final ChaiProvider loopProvider = ChaiProviderFactory.createProvider(chaiConfiguration);
+                    final ChaiProvider loopProvider = chaiProvider.getProviderFactory().newProvider(chaiConfiguration);
 
                     {
                         final ChaiEntry rootDSEentry = ChaiUtility.getRootDSE(loopProvider);
@@ -86,8 +87,8 @@ public class LdapDebugDataGenerator {
                         final String proxyUserDN = ldapProfile.readSettingAsString(PwmSetting.LDAP_PROXY_USER_DN);
                         if (proxyUserDN != null) {
                             ldapDebugServerInfo.setProxyDN(proxyUserDN);
-                            final ChaiEntry proxyUserEntry = ChaiFactory.createChaiEntry(proxyUserDN, chaiProvider);
-                            if (proxyUserEntry.isValid()) {
+                            final ChaiEntry proxyUserEntry = chaiProvider.getEntryFactory().newChaiEntry(proxyUserDN);
+                            if (proxyUserEntry.exists()) {
                                 final Map<String, List<String>> proxyUserData = LdapOperationsHelper.readAllEntryAttributeValues(proxyUserEntry);
                                 ldapDebugServerInfo.setProxyUserAttributes(proxyUserData);
                             }
@@ -99,8 +100,8 @@ public class LdapDebugDataGenerator {
                         final String testUserDN = ldapProfile.readSettingAsString(PwmSetting.LDAP_TEST_USER_DN);
                         if (testUserDN != null) {
                             ldapDebugServerInfo.setTestUserDN(testUserDN);
-                            final ChaiEntry testUserEntry = ChaiFactory.createChaiEntry(testUserDN, chaiProvider);
-                            if (testUserEntry.isValid()) {
+                            final ChaiEntry testUserEntry = chaiProvider.getEntryFactory().newChaiEntry(testUserDN);
+                            if (testUserEntry.exists()) {
                                 final Map<String, List<String>> testUserdata = LdapOperationsHelper.readAllEntryAttributeValues(testUserEntry);
                                 ldapDebugServerInfo.setTestUserAttributes(testUserdata);
                             }
@@ -122,8 +123,8 @@ public class LdapDebugDataGenerator {
     private Map<String,List<String>> readUserAttributeData(final ChaiProvider chaiProvider, final String userDN)
             throws ChaiUnavailableException, ChaiOperationException
     {
-        final ChaiEntry testUserEntry = ChaiFactory.createChaiEntry(userDN, chaiProvider);
-        if (testUserEntry.isValid()) {
+        final ChaiEntry testUserEntry = chaiProvider.getEntryFactory().newChaiEntry(userDN);
+        if (testUserEntry.exists()) {
             final Map<String,List<String>> returnData = new LinkedHashMap<>();
             final Map<String, List<String>> testUserdata = LdapOperationsHelper.readAllEntryAttributeValues(testUserEntry);
             testUserdata.put("dn",Collections.singletonList(userDN));

+ 45 - 8
server/src/main/java/password/pwm/ldap/LdapOperationsHelper.java

@@ -24,7 +24,6 @@ package password.pwm.ldap;
 
 import com.novell.ldapchai.ChaiConstant;
 import com.novell.ldapchai.ChaiEntry;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.cr.Answer;
 import com.novell.ldapchai.exception.ChaiOperationException;
@@ -91,7 +90,7 @@ public class LdapOperationsHelper {
             return;
         }
         final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
-        final ChaiUser theUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+        final ChaiUser theUser = chaiProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
         addUserObjectClass(sessionLabel, theUser, newObjClasses);
     }
 
@@ -125,6 +124,25 @@ public class LdapOperationsHelper {
     }
 
     public static ChaiProvider openProxyChaiProvider(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final LdapProfile ldapProfile,
+            final Configuration config,
+            final StatisticsManager statisticsManager
+    )
+            throws PwmUnrecoverableException
+    {
+        return openProxyChaiProvider(
+                pwmApplication.getLdapConnectionService().getChaiProviderFactory(),
+                sessionLabel,
+                ldapProfile,
+                config,
+                statisticsManager
+        );
+    }
+
+    static ChaiProvider openProxyChaiProvider(
+            final ChaiProviderFactory chaiProviderFactory,
             final SessionLabel sessionLabel,
             final LdapProfile ldapProfile,
             final Configuration config,
@@ -138,7 +156,7 @@ public class LdapOperationsHelper {
         final PasswordData proxyPW = ldapProfile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD);
 
         try {
-            return createChaiProvider(sessionLabel, ldapProfile, config, proxyDN, proxyPW);
+            return createChaiProvider(chaiProviderFactory, sessionLabel, ldapProfile, config, proxyDN, proxyPW);
         } catch (ChaiUnavailableException e) {
             if (statisticsManager != null) {
                 statisticsManager.incrementValue(Statistic.LDAP_UNAVAILABLE_COUNT);
@@ -218,7 +236,6 @@ public class LdapOperationsHelper {
      * <p/>
      * Any ldap operation exceptions are not reported (but logged).
      *
-     * @param pwmSession       for looking up session info
      * @param theUser          User to write to
      * @param formValues       A map with {@link FormConfiguration} keys and String values.
      * @throws ChaiUnavailableException if the directory is unavailable
@@ -449,8 +466,28 @@ public class LdapOperationsHelper {
         }
     }
 
+    public static ChaiProvider createChaiProvider(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final LdapProfile ldapProfile,
+            final Configuration config,
+            final String userDN,
+            final PasswordData userPassword
+    )
+            throws ChaiUnavailableException, PwmUnrecoverableException
+    {
+        return createChaiProvider(
+                pwmApplication.getLdapConnectionService().getChaiProviderFactory(),
+                sessionLabel,
+                ldapProfile,
+                config,
+                userDN,
+                userPassword
+        );
+    }
 
     public static ChaiProvider createChaiProvider(
+            final ChaiProviderFactory chaiProviderFactory,
             final SessionLabel sessionLabel,
             final LdapProfile ldapProfile,
             final Configuration config,
@@ -462,10 +499,11 @@ public class LdapOperationsHelper {
         final List<String> ldapURLs = ldapProfile.readSettingAsStringArray(PwmSetting.LDAP_SERVER_URLS);
         final ChaiConfiguration chaiConfig = createChaiConfiguration(config, ldapProfile, ldapURLs, userDN, userPassword);
         LOGGER.trace(sessionLabel,"creating new ldap connection using config: " + chaiConfig.toString());
-        return ChaiProviderFactory.createProvider(chaiConfig);
+        return chaiProviderFactory.newProvider(chaiConfig);
     }
 
     public static ChaiProvider createChaiProvider(
+            final PwmApplication pwmApplication,
             final SessionLabel sessionLabel,
             final Configuration config,
             final LdapProfile ldapProfile,
@@ -477,7 +515,7 @@ public class LdapOperationsHelper {
     {
         final ChaiConfiguration chaiConfig = createChaiConfiguration( config, ldapProfile, ldapURLs, userDN, userPassword);
         LOGGER.trace(sessionLabel,"creating new ldap connection using config: " + chaiConfig.toString());
-        return ChaiProviderFactory.createProvider(chaiConfig);
+        return pwmApplication.getLdapConnectionService().getChaiProviderFactory().newProvider(chaiConfig);
     }
 
     public static ChaiConfiguration createChaiConfiguration(
@@ -551,7 +589,6 @@ public class LdapOperationsHelper {
         if (idleTimeoutMs > 0) {
             configBuilder.setSetting(ChaiSetting.WATCHDOG_ENABLE, "true");
             configBuilder.setSetting(ChaiSetting.WATCHDOG_IDLE_TIMEOUT, idleTimeoutMsString);
-            configBuilder.setSetting(ChaiSetting.WATCHDOG_CHECK_FREQUENCY, Long.toString(5 * 1000));
         } else {
             configBuilder.setSetting(ChaiSetting.WATCHDOG_ENABLE, "false");
         }
@@ -719,7 +756,7 @@ public class LdapOperationsHelper {
         }
 
         final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
-        final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+        final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
 
         // use chai (nmas) to retrieve user password
         if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.EDIRECTORY_READ_USER_PWD)) {

+ 1 - 2
server/src/main/java/password/pwm/ldap/UserInfoReader.java

@@ -22,7 +22,6 @@
 
 package password.pwm.ldap;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiException;
 import com.novell.ldapchai.exception.ChaiOperationException;
@@ -108,7 +107,7 @@ public class UserInfoReader implements UserInfo {
         this.sessionLabel = sessionLabel;
 
         final ChaiProvider cachingProvider = CachingProxyWrapper.create(ChaiProvider.class, chaiProvider);
-        this.chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), cachingProvider);
+        this.chaiUser = cachingProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
     }
 
     static UserInfo create(

+ 3 - 3
server/src/main/java/password/pwm/ldap/auth/LDAPAuthenticationRequest.java

@@ -23,7 +23,6 @@
 package password.pwm.ldap.auth;
 
 import com.novell.ldapchai.ChaiConstant;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiError;
 import com.novell.ldapchai.exception.ChaiException;
@@ -318,6 +317,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
         try {
             //read a provider using the user's DN and password.
             userProvider = LdapOperationsHelper.createChaiProvider(
+                    pwmApplication,
                     sessionLabel,
                     userIdentity.getLdapProfile(pwmApplication.getConfig()),
                     pwmApplication.getConfig(),
@@ -372,7 +372,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
         final boolean configAlwaysUseProxy = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);
 
         final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
-        final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+        final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
 
         // try setting a random password on the account to authenticate.
         if (!configAlwaysUseProxy && requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE) {
@@ -523,7 +523,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
         final LdapProfile profile = pwmApplication.getConfig().getLdapProfiles().get(userIdentity.getLdapProfileID());
         final String proxyDN = profile.readSettingAsString(PwmSetting.LDAP_PROXY_USER_DN);
         final PasswordData proxyPassword = profile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD);
-        return LdapOperationsHelper.createChaiProvider(sessionLabel, profile, pwmApplication.getConfig(), proxyDN, proxyPassword);
+        return LdapOperationsHelper.createChaiProvider(pwmApplication, sessionLabel, profile, pwmApplication.getConfig(), proxyDN, proxyPassword);
     }
 
     private void log(final PwmLogLevel level, final CharSequence message) {

+ 1 - 0
server/src/main/java/password/pwm/ldap/auth/SessionAuthenticator.java

@@ -248,6 +248,7 @@ public class SessionAuthenticator {
             //read a provider using the user's DN and password.
 
             provider = LdapOperationsHelper.createChaiProvider(
+                    pwmApplication,
                     sessionLabel,
                     userIdentity.getLdapProfile(pwmApplication.getConfig()),
                     pwmApplication.getConfig(),

+ 2 - 3
server/src/main/java/password/pwm/ldap/schema/EdirSchemaExtender.java

@@ -24,7 +24,6 @@ package password.pwm.ldap.schema;
 
 import com.novell.ldap.client.SchemaParser;
 import com.novell.ldapchai.ChaiEntry;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
@@ -52,11 +51,11 @@ public class EdirSchemaExtender implements SchemaExtender {
     private ChaiEntry schemaEntry;
 
     private final StringBuilder activityLog = new StringBuilder();
-    private final Map<String,SchemaDefinition.State> stateMap = new HashMap();
+    private final Map<String,SchemaDefinition.State> stateMap = new HashMap<>();
 
     public void init(final ChaiProvider chaiProvider) throws PwmUnrecoverableException {
         try {
-            schemaEntry = ChaiFactory.createChaiEntry(LDAP_SCHEMA_DN, chaiProvider);
+            schemaEntry = chaiProvider.getEntryFactory().newChaiEntry(LDAP_SCHEMA_DN);
         } catch (ChaiUnavailableException e) {
             throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, e.getMessage()));
         }

+ 3 - 4
server/src/main/java/password/pwm/ldap/search/UserSearchEngine.java

@@ -22,7 +22,6 @@
 
 package password.pwm.ldap.search;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
@@ -149,7 +148,7 @@ public class UserSearchEngine implements PwmService {
             if (inputIdentity != null) {
                 try {
                     final ChaiUser theUser = pwmApplication.getProxiedChaiUser(inputIdentity);
-                    if (theUser.isValid()) {
+                    if (theUser.exists()) {
                         final String canonicalDN;
                         canonicalDN = theUser.readCanonicalDN();
                         return new UserIdentity(canonicalDN, inputIdentity.getLdapProfileID());
@@ -525,8 +524,8 @@ public class UserSearchEngine implements PwmService {
         final Collection<LdapProfile> ldapProfiles = pwmApplication.getConfig().getLdapProfiles().values();
         for (final LdapProfile ldapProfile : ldapProfiles) {
             final ChaiProvider provider = pwmApplication.getProxyChaiProvider(ldapProfile.getIdentifier());
-            final ChaiUser user = ChaiFactory.createChaiUser(userDN, provider);
-            if (user.isValid()) {
+            final ChaiUser user = provider.getEntryFactory().newChaiUser(userDN);
+            if (user.exists()) {
                 try {
                     return new UserIdentity(user.readCanonicalDN(), ldapProfile.getIdentifier());
                 } catch (ChaiOperationException e) {

+ 1 - 0
server/src/main/java/password/pwm/svc/PwmServiceEnum.java

@@ -29,6 +29,7 @@ import java.util.Collections;
 import java.util.List;
 
 public enum PwmServiceEnum {
+    LocalDBService(         password.pwm.util.localdb.LocalDBService.class,         Flag.StartDuringRuntimeInstance),
     SecureService(          password.pwm.util.secure.SecureService.class,           Flag.StartDuringRuntimeInstance),
     LdapConnectionService(  password.pwm.ldap.LdapConnectionService.class,          Flag.StartDuringRuntimeInstance),
     DatabaseService(        password.pwm.util.db.DatabaseService.class,             Flag.StartDuringRuntimeInstance),

+ 0 - 22
server/src/main/java/password/pwm/svc/sessiontrack/SessionTrackService.java

@@ -130,28 +130,6 @@ public class SessionTrackService implements PwmService {
         return Collections.emptyMap();
     }
 
-    public int ldapConnectionCount() {
-        int counter = 0;
-        try {
-            for (final String identifer : pwmApplication.getConfig().getLdapProfiles().keySet()) {
-                if (pwmApplication.getProxyChaiProvider(identifer).isConnected()) {
-                    counter++;
-                }
-            }
-
-            for (final PwmSession loopSession : currentValidSessionSet()) {
-                if (loopSession != null) {
-                    if (loopSession.getSessionManager().hasActiveLdapConnection()) {
-                        counter++;
-                    }
-                }
-            }
-        } catch (Exception e) {
-            LOGGER.error("unexpected error counting ldap connections: " + e.getMessage());
-        }
-        return counter;
-    }
-
     private Set<PwmSession> currentValidSessionSet() {
         final Set<PwmSession> returnSet = new HashSet<>();
         for (final PwmSession pwmSession : copyOfSessionSet()) {

+ 2 - 0
server/src/main/java/password/pwm/svc/telemetry/TelemetryService.java

@@ -140,6 +140,8 @@ public class TelemetryService implements PwmService {
         executorService = JavaHelper.makeSingleThreadExecutorService(pwmApplication, TelemetryService.class);
 
         scheduleNextJob();
+
+        status = STATUS.OPEN;
     }
 
     private void initSender() throws PwmUnrecoverableException

+ 1 - 1
server/src/main/java/password/pwm/util/cli/commands/ImportResponsesCommand.java

@@ -63,7 +63,7 @@ public class ImportResponsesCommand extends AbstractCliCommand {
 
                 final UserIdentity userIdentity = UserIdentity.fromDelimitedKey(inputData.username);
                 final ChaiUser user = pwmApplication.getProxiedChaiUser(userIdentity);
-                if (user.isValid()) {
+                if (user.exists()) {
                     out("writing responses to user '" + user.getEntryDN() + "'");
                     try {
                         final ChallengeProfile challengeProfile = pwmApplication.getCrService().readUserChallengeProfile(

+ 2 - 1
server/src/main/java/password/pwm/util/cli/commands/LdapSchemaExtendCommand.java

@@ -51,7 +51,8 @@ public class LdapSchemaExtendCommand extends AbstractCliCommand {
             console.writer().flush();
             bindPW = new String(console.readPassword());
         }
-        final ChaiProvider chaiProvider = ChaiProviderFactory.createProvider(ldapUrl, bindDN, bindPW);
+        final ChaiProviderFactory chaiProviderFactory = cliEnvironment.getPwmApplication().getLdapConnectionService().getChaiProviderFactory();
+        final ChaiProvider chaiProvider = chaiProviderFactory.newProvider(ldapUrl, bindDN, bindPW);
         final SchemaOperationResult operationResult = SchemaManager.extendSchema(chaiProvider);
         final boolean checkOk = operationResult.isSuccess();
         if (checkOk) {

+ 56 - 0
server/src/main/java/password/pwm/util/localdb/LocalDBService.java

@@ -0,0 +1,56 @@
+package password.pwm.util.localdb;
+
+import password.pwm.PwmApplication;
+import password.pwm.config.option.DataStorageMethod;
+import password.pwm.error.PwmException;
+import password.pwm.health.HealthRecord;
+import password.pwm.svc.PwmService;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class LocalDBService implements PwmService {
+    private PwmApplication pwmApplication;
+
+    @Override
+    public STATUS status() {
+        if ( pwmApplication != null
+                && pwmApplication.getLocalDB() != null
+                && pwmApplication.getLocalDB().status() == LocalDB.Status.OPEN)
+        {
+            return STATUS.OPEN;
+        }
+
+        return STATUS.CLOSED;
+    }
+
+    @Override
+    public void init(final PwmApplication pwmApplication) throws PwmException {
+        this.pwmApplication = pwmApplication;
+    }
+
+    @Override
+    public void close() {
+        //no-op
+    }
+
+    @Override
+    public List<HealthRecord> healthCheck() {
+        return null;
+    }
+
+    @Override
+    public ServiceInfoBean serviceInfo() {
+        final Map<String,String> returnInfo = new LinkedHashMap<>();
+        if (status() == STATUS.OPEN) {
+            final Map<String,Serializable> localDbInfo = pwmApplication.getLocalDB().debugInfo();
+            for (final Map.Entry<String,Serializable> entry : localDbInfo.entrySet()) {
+                returnInfo.put( entry.getKey(), String.valueOf(entry.getValue()) );
+            }
+        }
+        return new ServiceInfoBean(Collections.singleton(DataStorageMethod.LOCALDB), Collections.unmodifiableMap(returnInfo));
+    }
+}

+ 1 - 0
server/src/main/java/password/pwm/util/localdb/Xodus_LocalDB.java

@@ -159,6 +159,7 @@ public class Xodus_LocalDB implements LocalDBProvider {
         final EnvironmentConfig environmentConfig = new EnvironmentConfig();
         environmentConfig.setEnvCloseForcedly(true);
         environmentConfig.setMemoryUsage(50 * 1024 * 1024);
+        environmentConfig.setEnvGatherStatistics(true);
 
         for (final Map.Entry<String,String> entry : initParameters.entrySet()) {
             final String key = entry.getKey();

+ 1 - 1
server/src/main/java/password/pwm/util/logging/PwmLogEvent.java

@@ -39,7 +39,7 @@ import java.time.Instant;
 @EqualsAndHashCode
 public class PwmLogEvent implements Serializable, Comparable {
 
-    private static final int MAX_MESSAGE_LENGTH = 100_000;
+    private static final int MAX_MESSAGE_LENGTH = 50_000;
 
     private final PwmLogLevel level;
 

+ 2 - 4
server/src/main/java/password/pwm/util/operations/PasswordUtility.java

@@ -22,7 +22,6 @@
 
 package password.pwm.util.operations;
 
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiPasswordPolicy;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiException;
@@ -32,7 +31,6 @@ import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.impl.oracleds.entry.OracleDSEntries;
 import com.novell.ldapchai.provider.ChaiConfiguration;
 import com.novell.ldapchai.provider.ChaiProvider;
-import com.novell.ldapchai.provider.ChaiProviderFactory;
 import com.novell.ldapchai.provider.ChaiSetting;
 import com.novell.ldapchai.provider.DirectoryVendor;
 import com.novell.ldapchai.util.ChaiUtility;
@@ -359,7 +357,7 @@ public class PasswordUtility {
         final String bindDN;
 
         try {
-            final ChaiUser theUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+            final ChaiUser theUser = chaiProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
             bindDN = chaiProvider.getChaiConfiguration().getSetting(ChaiSetting.BIND_DN);
             bindIsSelf = userIdentity.canonicalEquals(new UserIdentity(bindDN, userIdentity.getLdapProfileID()), pwmApplication);
 
@@ -563,7 +561,7 @@ public class PasswordUtility {
             final String loopReplicaUrl = loopConfiguration.getSetting(ChaiSetting.BIND_DN);
             ChaiProvider loopProvider = null;
             try {
-                loopProvider = ChaiProviderFactory.createProvider(loopConfiguration);
+                loopProvider = pwmApplication.getLdapConnectionService().getChaiProviderFactory().newProvider(loopConfiguration);
                 final Instant lastModifiedDate = determinePwdLastModified(pwmApplication, sessionLabel, userIdentity);
                 returnValue.put(loopReplicaUrl, lastModifiedDate);
             } catch (ChaiUnavailableException e) {

+ 3 - 3
server/src/main/java/password/pwm/util/operations/cr/NMASCrOperator.java

@@ -24,7 +24,6 @@ package password.pwm.util.operations.cr;
 
 import com.novell.ldap.LDAPConnection;
 import com.novell.ldap.LDAPException;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.cr.ChaiChallenge;
 import com.novell.ldapchai.cr.ChaiChallengeSet;
@@ -412,8 +411,9 @@ public class NMASCrOperator implements CrOperator {
         }
 
         private LDAPConnection makeLdapConnection() throws Exception {
-            final ChaiProvider chaiProvider = ChaiProviderFactory.createProvider(chaiConfiguration);
-            final ChaiUser theUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+            final ChaiProviderFactory chaiProviderFactory = pwmApplication.getLdapConnectionService().getChaiProviderFactory();
+            final ChaiProvider chaiProvider = chaiProviderFactory.newProvider(chaiConfiguration);
+            final ChaiUser theUser = chaiProvider.getEntryFactory().newChaiUser(userIdentity.getUserDN());
             try {
                 if (theUser.isPasswordLocked()) {
                     LOGGER.trace("user " + theUser.getEntryDN() + " appears to be intruder locked, aborting nmas ResponseSet loading" );

+ 1 - 2
server/src/main/java/password/pwm/ws/server/RestServlet.java

@@ -1,7 +1,6 @@
 package password.pwm.ws.server;
 
 import com.google.gson.stream.MalformedJsonException;
-import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
@@ -306,7 +305,7 @@ public abstract class RestServlet extends HttpServlet{
 
         public ChaiUser getChaiUser() throws PwmUnrecoverableException {
             try {
-                return ChaiFactory.createChaiUser(userIdentity.getUserDN(), getChaiProvider());
+                return getChaiProvider().getEntryFactory().newChaiUser(userIdentity.getUserDN());
             } catch (ChaiUnavailableException e) {
                 throw PwmUnrecoverableException.fromChaiException(e);
             }

+ 18 - 1
server/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp

@@ -238,9 +238,13 @@
                         </td>
                     </tr>
                     <% for (final AppDashboardData.ServiceData loopService : appDashboardData.getServices()) { %>
-                    <tr>
+                    <tr id="serviceName-<%=loopService.getName()%>">
                         <td>
                             <%= loopService.getName() %>
+                            <% if (!JavaHelper.isEmpty(loopService.getDebugData())) { %>
+                            &nbsp;
+                            <div class="btn-icon pwm-icon pwm-icon-list-alt"></div>
+                            <% } %>
                         </td>
                         <td>
                             <%= loopService.getStatus() %>
@@ -430,6 +434,19 @@
                     }})
                 });
             });
+            <% for (final AppDashboardData.ServiceData loopService : appDashboardData.getServices()) { %>
+            <% if (!JavaHelper.isEmpty(loopService.getDebugData())) { %>
+            PWM_MAIN.addEventHandler('serviceName-<%=loopService.getName()%>','click',function(){
+                var tableText = '<table>';
+                <% for (final Map.Entry<String,String> entry : loopService.getDebugData().entrySet()) { %>
+                tableText += '<tr><td><%=StringUtil.escapeJS(entry.getKey())%></td>'
+                + '<td><%=StringUtil.escapeJS(entry.getValue())%></td></tr>';
+                <% } %>
+                tableText += '</table>';
+                PWM_MAIN.showDialog({title:'Debug Properties',text:tableText});
+            });
+            <% } %>
+            <% } %>
         });
     </script>
 </pwm:script>

+ 2 - 1
server/src/main/webapp/WEB-INF/jsp/configguide-ldap_schema.jsp

@@ -41,7 +41,8 @@
     boolean existingSchemaGood = false;
     String schemaActivityLog = "";
     try {
-        final SchemaOperationResult schemaManager = ConfigGuideUtils.extendSchema(configGuideBean,false);
+        final PwmApplication pwmApplication = JspUtility.getPwmRequest(pageContext).getPwmApplication();
+        final SchemaOperationResult schemaManager = ConfigGuideUtils.extendSchema(pwmApplication, configGuideBean, false);
         existingSchemaGood = schemaManager.isSuccess();
         schemaActivityLog = schemaManager.getOperationLog();
     } catch (Exception e) {