ソースを参照

ldapchai 0.7 update

Jason Rivard 7 年 前
コミット
5cfd610883
20 ファイル変更138 行追加131 行削除
  1. 1 1
      server/pom.xml
  2. 2 2
      server/src/main/java/password/pwm/bean/TelemetryPublishBean.java
  3. 9 8
      server/src/main/java/password/pwm/health/LDAPStatusChecker.java
  4. 8 19
      server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java
  5. 7 2
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideUtils.java
  6. 2 3
      server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskDetailInfoBean.java
  7. 8 6
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java
  8. 6 4
      server/src/main/java/password/pwm/ldap/LdapBrowser.java
  9. 2 0
      server/src/main/java/password/pwm/ldap/LdapConnectionService.java
  10. 35 31
      server/src/main/java/password/pwm/ldap/LdapOperationsHelper.java
  11. 8 4
      server/src/main/java/password/pwm/ldap/LdapPermissionTester.java
  12. 1 2
      server/src/main/java/password/pwm/ldap/UserInfo.java
  13. 2 3
      server/src/main/java/password/pwm/ldap/UserInfoBean.java
  14. 5 11
      server/src/main/java/password/pwm/ldap/UserInfoReader.java
  15. 11 9
      server/src/main/java/password/pwm/ldap/auth/LDAPAuthenticationRequest.java
  16. 6 5
      server/src/main/java/password/pwm/ldap/schema/SchemaManager.java
  17. 2 2
      server/src/main/java/password/pwm/svc/telemetry/TelemetryService.java
  18. 2 2
      server/src/main/java/password/pwm/util/operations/CrService.java
  19. 11 11
      server/src/main/java/password/pwm/util/operations/PasswordUtility.java
  20. 10 6
      server/src/main/java/password/pwm/util/operations/cr/NMASCrOperator.java

+ 1 - 1
server/pom.xml

@@ -652,7 +652,7 @@
         <dependency>
             <groupId>com.github.ldapchai</groupId>
             <artifactId>ldapchai</artifactId>
-            <version>0.6.11</version>
+            <version>0.7.0</version>
         </dependency>
         <dependency>
             <groupId>commons-net</groupId>

+ 2 - 2
server/src/main/java/password/pwm/bean/TelemetryPublishBean.java

@@ -22,7 +22,7 @@
 
 package password.pwm.bean;
 
-import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.DirectoryVendor;
 import lombok.Builder;
 import lombok.Getter;
 
@@ -39,7 +39,7 @@ public class TelemetryPublishBean implements Serializable {
     private final String instanceHash;
     private final String siteDescription;
     private final Instant installTime;
-    private final List<ChaiProvider.DIRECTORY_VENDOR> ldapVendor;
+    private final List<DirectoryVendor> ldapVendor;
     private final Map<String,String> statistics;
     private final List<String> configuredSettings;
     private final String versionBuild;

+ 9 - 8
server/src/main/java/password/pwm/health/LDAPStatusChecker.java

@@ -33,6 +33,7 @@ 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;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
@@ -230,7 +231,7 @@ public class LDAPStatusChecker implements HealthChecker {
             LOGGER.trace(SessionLabel.HEALTH_SESSION_LABEL, "beginning process to check ldap test user password read/write operations for profile " + ldapProfile.getIdentifier());
             try {
                 final boolean readPwdEnabled = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.EDIRECTORY_READ_USER_PWD)
-                        && theUser.getChaiProvider().getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY;
+                        && theUser.getChaiProvider().getDirectoryVendor() == DirectoryVendor.EDIRECTORY;
 
                 if (readPwdEnabled) {
                     try {
@@ -395,7 +396,7 @@ public class LDAPStatusChecker implements HealthChecker {
         final List<HealthRecord> returnRecords = new ArrayList<>();
         ChaiProvider chaiProvider = null;
         try{
-            ChaiProvider.DIRECTORY_VENDOR directoryVendor = null;
+            final DirectoryVendor directoryVendor;
             try {
                 final String proxyDN = ldapProfile.readSettingAsString(PwmSetting.LDAP_PROXY_USER_DN);
                 final PasswordData proxyPW = ldapProfile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD);
@@ -439,7 +440,7 @@ public class LDAPStatusChecker implements HealthChecker {
                 return returnRecords;
             }
 
-            if (directoryVendor != null && directoryVendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+            if (directoryVendor != null && directoryVendor == DirectoryVendor.ACTIVE_DIRECTORY) {
                 returnRecords.addAll(checkAd(pwmApplication, config, ldapProfile));
             }
 
@@ -538,7 +539,7 @@ public class LDAPStatusChecker implements HealthChecker {
 
         LOGGER.trace(SessionLabel.HEALTH_SESSION_LABEL,"beginning check for replica vendor sameness");
         boolean errorReachingServer = false;
-        final Map<String,ChaiProvider.DIRECTORY_VENDOR> replicaVendorMap = new HashMap<>();
+        final Map<String, DirectoryVendor> replicaVendorMap = new HashMap<>();
 
         try {
             for (final LdapProfile ldapProfile : pwmApplication.getConfig().getLdapProfiles().values()) {
@@ -549,7 +550,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);
-                    replicaVendorMap.put(chaiConfiguration.getSetting(ChaiSetting.BIND_URLS),loopProvider.getDirectoryVendor());
+                    replicaVendorMap.put(chaiConfiguration.getSetting(ChaiSetting.BIND_URLS), loopProvider.getDirectoryVendor());
                 }
             }
         } catch (Exception e) {
@@ -558,12 +559,12 @@ public class LDAPStatusChecker implements HealthChecker {
         }
 
         final ArrayList<HealthRecord> healthRecords = new ArrayList<>();
-        final Set<ChaiProvider.DIRECTORY_VENDOR> discoveredVendors = new HashSet<>(replicaVendorMap.values());
+        final Set<DirectoryVendor> discoveredVendors = new HashSet<>(replicaVendorMap.values());
 
         if (discoveredVendors.size() >= 2) {
             final StringBuilder vendorMsg = new StringBuilder();
-            for (final Iterator<Map.Entry<String,ChaiProvider.DIRECTORY_VENDOR>> iterator = replicaVendorMap.entrySet().iterator(); iterator.hasNext(); ) {
-                final Map.Entry<String,ChaiProvider.DIRECTORY_VENDOR> entry = iterator.next();
+            for (final Iterator<Map.Entry<String, DirectoryVendor>> iterator = replicaVendorMap.entrySet().iterator(); iterator.hasNext(); ) {
+                final Map.Entry<String, DirectoryVendor> entry = iterator.next();
                 final String key = entry.getKey();
                 vendorMsg.append(key).append("=").append(entry.getValue().toString());
                 if (iterator.hasNext()) {

+ 8 - 19
server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java

@@ -231,7 +231,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
                     Collections.singletonList(guestRegistrationBean.getUpdateUserIdentity())
             );
 
-            final Date expirationDate = readExpirationFromRequest(pwmRequest);
+            final Instant expirationDate = readExpirationFromRequest(pwmRequest);
 
             // Update user attributes
             LdapOperationsHelper.writeFormValuesToLdap(pwmApplication, pwmSession.getSessionManager().getMacroMachine(pwmApplication), theGuest, formValues, false);
@@ -344,9 +344,9 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
                 }
                 final String expirationAttribute = config.readSettingAsString(PwmSetting.GUEST_EXPIRATION_ATTRIBUTE);
                 if (expirationAttribute != null && expirationAttribute.length() > 0) {
-                    final Date expiration = guestUserInfo.readDateAttribute(expirationAttribute);
+                    final Instant expiration = guestUserInfo.readDateAttribute(expirationAttribute);
                     if (expiration != null) {
-                        guBean.setUpdateUserExpirationDate(expiration.toInstant());
+                        guBean.setUpdateUserExpirationDate(expiration);
                     }
                 }
 
@@ -395,7 +395,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
                     pwmRequest, guestUserForm, locale);
 
             //read the expiration date from the request.
-            final Date expirationDate = readExpirationFromRequest(pwmRequest);
+            final Instant expirationDate = readExpirationFromRequest(pwmRequest);
 
             // see if the values meet form requirements.
             FormUtility.validateFormValues(config, formValues, locale);
@@ -440,17 +440,6 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
             final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(pwmApplication, pwmSession.getLabel(), userIdentity, theUser, locale);
             final PasswordData newPassword = RandomPasswordGenerator.createRandomPassword(pwmSession.getLabel(), passwordPolicy, pwmApplication);
             theUser.setPassword(newPassword.getStringValue());
-            /*
-            final UserInfoBean guestUserInfoBean = new UserInfoBean();
-            final UserStatusReader userStatusReader = new UserStatusReader(pwmApplication);
-            userStatusReader.populateUserInfoBean(
-                    pwmSession.getLabel(),
-                    guestUserInfoBean,
-                    pwmSession.getSessionStateBean().getLocale(),
-                    userIdentity,
-                    theUser.getChaiProvider()
-            );
-            */
 
 
             {  // execute configured actions
@@ -487,7 +476,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
         }
     }
 
-    private static Date readExpirationFromRequest(
+    private static Instant readExpirationFromRequest(
             final PwmRequest pwmRequest
     )
             throws PwmOperationalException, ChaiUnavailableException, ChaiOperationException, PwmUnrecoverableException
@@ -518,15 +507,15 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
 
         final long durationValueMs = durationValueDays * 24 * 60 * 60 * 1000;
         final long futureDateMs = System.currentTimeMillis() + durationValueMs;
-        final Date futureDate = new Date(futureDateMs);
+        final Instant futureDate = Instant.ofEpochMilli(futureDateMs);
 
-        if (expirationDate.after(futureDate)) {
+        if (expirationDate.after(Date.from(futureDate))) {
             final String errorMsg = "expiration date must be sooner than " + futureDate.toString();
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_FIELD_REQUIRED,errorMsg));
         }
 
         LOGGER.trace(pwmRequest, "read expiration date as " + expirationDate.toString());
-        return expirationDate;
+        return expirationDate.toInstant();
     }
 
     private static String determineUserDN(final Map<FormConfiguration, String> formValues, final Configuration config)

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

@@ -89,8 +89,13 @@ public class ConfigGuideUtils {
         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);
         try {
-            final ChaiConfiguration chaiConfiguration = new ChaiConfiguration(ldapUrl, form.get(ConfigGuideFormField.PARAM_LDAP_PROXY_DN), form.get(ConfigGuideFormField.PARAM_LDAP_PROXY_PW));
-            chaiConfiguration.setSetting(ChaiSetting.PROMISCUOUS_SSL,"true");
+            final ChaiConfiguration chaiConfiguration = ChaiConfiguration.builder(
+                    ldapUrl,
+                    form.get(ConfigGuideFormField.PARAM_LDAP_PROXY_DN),
+                    form.get(ConfigGuideFormField.PARAM_LDAP_PROXY_PW)
+            )
+                    .setSetting(ChaiSetting.PROMISCUOUS_SSL,"true")
+                    .build();
             final ChaiProvider chaiProvider = ChaiProviderFactory.createProvider(chaiConfiguration);
             if (doSchemaExtension) {
                 return SchemaManager.extendSchema(chaiProvider);

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

@@ -55,7 +55,6 @@ import java.io.IOException;
 import java.io.Serializable;
 import java.time.Instant;
 import java.util.Collections;
-import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
@@ -137,8 +136,8 @@ public class HelpdeskDetailInfoBean implements Serializable {
         }
 
         try {
-            final Date lastLoginTime = theUser.readLastLoginTime();
-            detailInfo.setLastLoginTime(lastLoginTime == null ? null : lastLoginTime.toInstant());
+            final Instant lastLoginTime = theUser.readLastLoginTime();
+            detailInfo.setLastLoginTime(lastLoginTime);
         } catch (Exception e) {
             LOGGER.error(pwmRequest, "unexpected error reading last login time for user '" + userIdentity + "', " + e.getMessage());
         }

+ 8 - 6
server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java

@@ -30,6 +30,7 @@ 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;
 import password.pwm.PwmApplication;
 import password.pwm.bean.EmailItemBean;
@@ -174,7 +175,7 @@ class NewUserUtils {
         {
             final String settingValue = pwmApplication.getConfig().readAppProperty(AppProperty.NEWUSER_LDAP_USE_TEMP_PW);
             if ("auto".equalsIgnoreCase(settingValue)) {
-                useTempPw = chaiProvider.getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY;
+                useTempPw = chaiProvider.getDirectoryVendor() == DirectoryVendor.ACTIVE_DIRECTORY;
             } else {
                 useTempPw = Boolean.parseBoolean(settingValue);
             }
@@ -201,7 +202,7 @@ class NewUserUtils {
             }
 
             // add AD-specific attributes
-            if (ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY == chaiProvider.getDirectoryVendor()) {
+            if (DirectoryVendor.ACTIVE_DIRECTORY == chaiProvider.getDirectoryVendor()) {
                 try {
                     NewUserUtils.LOGGER.debug(pwmSession,
                             "setting userAccountControl attribute to enable account " + theUser.getEntryDN());
@@ -217,9 +218,10 @@ class NewUserUtils {
             try { // bind as user
                 NewUserUtils.LOGGER.debug(pwmSession,
                         "attempting bind as user to then allow changing to requested password for new user entry: " + newUserDN);
-                final ChaiConfiguration chaiConfiguration = new ChaiConfiguration(chaiProvider.getChaiConfiguration());
-                chaiConfiguration.setSetting(ChaiSetting.BIND_DN, newUserDN);
-                chaiConfiguration.setSetting(ChaiSetting.BIND_PASSWORD, temporaryPassword.getStringValue());
+                final ChaiConfiguration chaiConfiguration = ChaiConfiguration.builder(chaiProvider.getChaiConfiguration())
+                        .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);
                 bindAsUser.changePassword(temporaryPassword.getStringValue(), userPassword.getStringValue());
@@ -243,7 +245,7 @@ class NewUserUtils {
             }
 
             // add AD-specific attributes
-            if (ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY == chaiProvider.getDirectoryVendor()) {
+            if (DirectoryVendor.ACTIVE_DIRECTORY == chaiProvider.getDirectoryVendor()) {
                 try {
                     theUser.writeStringAttribute("userAccountControl", "512");
                 } catch (ChaiOperationException e) {

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

@@ -27,6 +27,8 @@ 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.DirectoryVendor;
+import com.novell.ldapchai.provider.SearchScope;
 import com.novell.ldapchai.util.ChaiUtility;
 import com.novell.ldapchai.util.SearchHelper;
 import password.pwm.AppProperty;
@@ -150,7 +152,7 @@ public class LdapBrowser {
 
         final HashMap<String, Boolean> returnMap = new HashMap<>();
         final ChaiProvider chaiProvider = getChaiProvider(profile);
-        if ((dn == null || dn.isEmpty()) && chaiProvider.getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+        if ((dn == null || dn.isEmpty()) && chaiProvider.getDirectoryVendor() == DirectoryVendor.ACTIVE_DIRECTORY) {
             final Set<String> adRootDNList = adRootDNList(profile);
             for (final String rootDN : adRootDNList) {
                 returnMap.put(rootDN, true);
@@ -163,7 +165,7 @@ public class LdapBrowser {
                 searchHelper.setFilter("(objectclass=*)");
                 searchHelper.setMaxResults(getMaxSizeLimit());
                 searchHelper.setAttributes("subordinateCount");
-                searchHelper.setSearchScope(ChaiProvider.SEARCH_SCOPE.ONE);
+                searchHelper.setSearchScope(SearchScope.ONE);
                 results = chaiProvider.searchMultiValues(dn, searchHelper);
 
             }
@@ -180,7 +182,7 @@ public class LdapBrowser {
                     searchHelper.setFilter("(objectclass=*)");
                     searchHelper.setMaxResults(1);
                     searchHelper.setAttributes(Collections.emptyList());
-                    searchHelper.setSearchScope(ChaiProvider.SEARCH_SCOPE.ONE);
+                    searchHelper.setSearchScope(SearchScope.ONE);
                     try {
                         final Map<String, Map<String, String>> subSearchResults = chaiProvider.search(resultDN, searchHelper);
                         hasSubs = !subSearchResults.isEmpty();
@@ -197,7 +199,7 @@ public class LdapBrowser {
     private Set<String> adRootDNList(final String profile) throws ChaiUnavailableException, ChaiOperationException, PwmUnrecoverableException {
         final ChaiProvider chaiProvider = getChaiProvider(profile);
         final Set<String> adRootValues = new HashSet<>();
-        if (chaiProvider.getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+        if (chaiProvider.getDirectoryVendor() == DirectoryVendor.ACTIVE_DIRECTORY) {
             final ChaiEntry chaiEntry = ChaiUtility.getRootDSE(chaiProvider);
             adRootValues.addAll(chaiEntry.readMultiStringAttribute("namingContexts"));
         }

+ 2 - 0
server/src/main/java/password/pwm/ldap/LdapConnectionService.java

@@ -24,6 +24,7 @@ package password.pwm.ldap;
 
 import com.google.gson.reflect.TypeToken;
 import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.ChaiProviderFactory;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.config.option.DataStorageMethod;
@@ -56,6 +57,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();
 
     public STATUS status()
     {

+ 35 - 31
server/src/main/java/password/pwm/ldap/LdapOperationsHelper.java

@@ -33,6 +33,7 @@ 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.SearchScope;
 import com.novell.ldapchai.util.SearchHelper;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
@@ -65,7 +66,6 @@ import java.security.cert.X509Certificate;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -501,32 +501,38 @@ public class LdapOperationsHelper {
     )
             throws PwmUnrecoverableException
     {
-        final ChaiConfiguration chaiConfig = new ChaiConfiguration(ldapURLs, userDN, userPassword == null ? null : userPassword.getStringValue());
+        final ChaiConfiguration.ChaiConfigurationBuilder configBuilder = ChaiConfiguration.builder(
+                ldapURLs,
+                userDN,
+                userPassword == null
+                        ? null
+                        : userPassword.getStringValue()
+        );
 
-        chaiConfig.setSetting(ChaiSetting.PROMISCUOUS_SSL, config.readAppProperty(AppProperty.LDAP_PROMISCUOUS_ENABLE));
+        configBuilder.setSetting(ChaiSetting.PROMISCUOUS_SSL, config.readAppProperty(AppProperty.LDAP_PROMISCUOUS_ENABLE));
         {
             final boolean enableNmasExtensions = Boolean.parseBoolean(config.readAppProperty(AppProperty.LDAP_EXTENSIONS_NMAS_ENABLE));
-            chaiConfig.setSetting(ChaiSetting.EDIRECTORY_ENABLE_NMAS, Boolean.toString(enableNmasExtensions));
+            configBuilder.setSetting(ChaiSetting.EDIRECTORY_ENABLE_NMAS, Boolean.toString(enableNmasExtensions));
         }
 
-        chaiConfig.setSetting(ChaiSetting.CR_CHAI_STORAGE_ATTRIBUTE, ldapProfile.readSettingAsString(PwmSetting.CHALLENGE_USER_ATTRIBUTE));
-        chaiConfig.setSetting(ChaiSetting.CR_ALLOW_DUPLICATE_RESPONSES, Boolean.toString(config.readSettingAsBoolean(PwmSetting.CHALLENGE_ALLOW_DUPLICATE_RESPONSES)));
-        chaiConfig.setSetting(ChaiSetting.CR_CASE_INSENSITIVE, Boolean.toString(config.readSettingAsBoolean(PwmSetting.CHALLENGE_CASE_INSENSITIVE)));
+        configBuilder.setSetting(ChaiSetting.CR_CHAI_STORAGE_ATTRIBUTE, ldapProfile.readSettingAsString(PwmSetting.CHALLENGE_USER_ATTRIBUTE));
+        configBuilder.setSetting(ChaiSetting.CR_ALLOW_DUPLICATE_RESPONSES, Boolean.toString(config.readSettingAsBoolean(PwmSetting.CHALLENGE_ALLOW_DUPLICATE_RESPONSES)));
+        configBuilder.setSetting(ChaiSetting.CR_CASE_INSENSITIVE, Boolean.toString(config.readSettingAsBoolean(PwmSetting.CHALLENGE_CASE_INSENSITIVE)));
         {
             final String setting = config.readAppProperty(AppProperty.SECURITY_RESPONSES_HASH_ITERATIONS);
             if (setting != null && setting.length() > 0) {
                 final int intValue = Integer.parseInt(setting);
-                chaiConfig.setSetting(ChaiSetting.CR_CHAI_SALT_COUNT, Integer.toString(intValue));
+                configBuilder.setSetting(ChaiSetting.CR_CHAI_SALT_COUNT, Integer.toString(intValue));
             }
         }
 
-        chaiConfig.setSetting(ChaiSetting.JNDI_ENABLE_POOL, "false"); // can cause issues with previous password authentication
+        configBuilder.setSetting(ChaiSetting.JNDI_ENABLE_POOL, "false"); // can cause issues with previous password authentication
 
-        chaiConfig.setSetting(ChaiSetting.CR_DEFAULT_FORMAT_TYPE, Answer.FormatType.SHA1_SALT.toString());
+        configBuilder.setSetting(ChaiSetting.CR_DEFAULT_FORMAT_TYPE, Answer.FormatType.SHA1_SALT.toString());
         final String storageMethodString = config.readSettingAsString(PwmSetting.CHALLENGE_STORAGE_HASHED);
         try {
             final Answer.FormatType formatType = Answer.FormatType.valueOf(storageMethodString);
-            chaiConfig.setSetting(ChaiSetting.CR_DEFAULT_FORMAT_TYPE, formatType.toString());
+            configBuilder.setSetting(ChaiSetting.CR_DEFAULT_FORMAT_TYPE, formatType.toString());
         } catch (Exception e) {
             LOGGER.warn("unknown CR storage format type '" + storageMethodString + "' ");
         }
@@ -534,27 +540,27 @@ public class LdapOperationsHelper {
         final List<X509Certificate> ldapServerCerts = ldapProfile.readSettingAsCertificate(PwmSetting.LDAP_SERVER_CERTS);
         if (ldapServerCerts != null && ldapServerCerts.size() > 0) {
             final X509TrustManager tm = new X509Utils.CertMatchingTrustManager(config, ldapServerCerts);
-            chaiConfig.setTrustManager(new X509TrustManager[]{tm});
+            configBuilder.setTrustManager(new X509TrustManager[]{tm});
         }
 
         final String idleTimeoutMsString = config.readAppProperty(AppProperty.LDAP_CONNECTION_TIMEOUT);
-        chaiConfig.setSetting(ChaiSetting.LDAP_CONNECT_TIMEOUT,idleTimeoutMsString);
+        configBuilder.setSetting(ChaiSetting.LDAP_CONNECT_TIMEOUT,idleTimeoutMsString);
 
         // set the watchdog idle timeout.
         final int idleTimeoutMs = (int)config.readSettingAsLong(PwmSetting.LDAP_IDLE_TIMEOUT) * 1000;
         if (idleTimeoutMs > 0) {
-            chaiConfig.setSetting(ChaiSetting.WATCHDOG_ENABLE, "true");
-            chaiConfig.setSetting(ChaiSetting.WATCHDOG_IDLE_TIMEOUT, idleTimeoutMsString);
-            chaiConfig.setSetting(ChaiSetting.WATCHDOG_CHECK_FREQUENCY, Long.toString(5 * 1000));
+            configBuilder.setSetting(ChaiSetting.WATCHDOG_ENABLE, "true");
+            configBuilder.setSetting(ChaiSetting.WATCHDOG_IDLE_TIMEOUT, idleTimeoutMsString);
+            configBuilder.setSetting(ChaiSetting.WATCHDOG_CHECK_FREQUENCY, Long.toString(5 * 1000));
         } else {
-            chaiConfig.setSetting(ChaiSetting.WATCHDOG_ENABLE, "false");
+            configBuilder.setSetting(ChaiSetting.WATCHDOG_ENABLE, "false");
         }
 
-        chaiConfig.setSetting(ChaiSetting.LDAP_SEARCH_PAGING_ENABLE, config.readAppProperty(AppProperty.LDAP_SEARCH_PAGING_ENABLE));
-        chaiConfig.setSetting(ChaiSetting.LDAP_SEARCH_PAGING_SIZE, config.readAppProperty(AppProperty.LDAP_SEARCH_PAGING_SIZE));
+        configBuilder.setSetting(ChaiSetting.LDAP_SEARCH_PAGING_ENABLE, config.readAppProperty(AppProperty.LDAP_SEARCH_PAGING_ENABLE));
+        configBuilder.setSetting(ChaiSetting.LDAP_SEARCH_PAGING_SIZE, config.readAppProperty(AppProperty.LDAP_SEARCH_PAGING_SIZE));
 
         if (config.readSettingAsBoolean(PwmSetting.AD_ENFORCE_PW_HISTORY_ON_SET)) {
-            chaiConfig.setSetting(ChaiSetting.AD_SET_POLICY_HINTS_ON_PW_SET,"true");
+            configBuilder.setSetting(ChaiSetting.AD_SET_POLICY_HINTS_ON_PW_SET,"true");
         }
 
         // write out any configured values;
@@ -568,20 +574,20 @@ public class LdapOperationsHelper {
                 if (theSetting == null) {
                     LOGGER.warn("ignoring unknown chai setting '" + key + "'");
                 } else {
-                    chaiConfig.setSetting(theSetting, entry.getValue());
+                    configBuilder.setSetting(theSetting, entry.getValue());
                 }
             }
         }
 
         // set ldap referrals
-        chaiConfig.setSetting(ChaiSetting.LDAP_FOLLOW_REFERRALS,String.valueOf(config.readSettingAsBoolean(PwmSetting.LDAP_FOLLOW_REFERRALS)));
+        configBuilder.setSetting(ChaiSetting.LDAP_FOLLOW_REFERRALS,String.valueOf(config.readSettingAsBoolean(PwmSetting.LDAP_FOLLOW_REFERRALS)));
 
         // enable wire trace;
         if (config.readSettingAsBoolean(PwmSetting.LDAP_ENABLE_WIRE_TRACE)) {
-            chaiConfig.setSetting(ChaiSetting.WIRETRACE_ENABLE, "true");
+            configBuilder.setSetting(ChaiSetting.WIRETRACE_ENABLE, "true");
         }
 
-        return chaiConfig;
+        return configBuilder.build();
     }
 
     /**
@@ -608,7 +614,7 @@ public class LdapOperationsHelper {
 
         if (updateAttribute != null && updateAttribute.length() > 0) {
             try {
-                theUser.writeDateAttribute(updateAttribute, new Date());
+                theUser.writeDateAttribute(updateAttribute, Instant.now());
                 LOGGER.debug(sessionLabel, "wrote pwdLastModified update attribute for " + theUser.getEntryDN());
                 success = true;
             } catch (ChaiOperationException e) {
@@ -623,7 +629,7 @@ public class LdapOperationsHelper {
             throws ChaiUnavailableException, ChaiOperationException
     {
         final SearchHelper searchHelper = new SearchHelper();
-        searchHelper.setSearchScope(ChaiProvider.SEARCH_SCOPE.BASE);
+        searchHelper.setSearchScope(SearchScope.BASE);
         searchHelper.setFilter("(objectClass=*)");
         final Map<String, Map<String, List<String>>> results = chaiEntry.getChaiProvider().searchMultiValues(chaiEntry.getEntryDN(), searchHelper);
         if (!results.isEmpty()) {
@@ -688,14 +694,12 @@ public class LdapOperationsHelper {
 
     public static Instant readPasswordExpirationTime(final ChaiUser theUser) {
         try {
-            Date ldapPasswordExpirationTime = theUser.readPasswordExpirationDate();
-            if (ldapPasswordExpirationTime != null && ldapPasswordExpirationTime.getTime() < 0) {
+            Instant ldapPasswordExpirationTime = theUser.readPasswordExpirationDate();
+            if (ldapPasswordExpirationTime != null && ldapPasswordExpirationTime.toEpochMilli() < 0) {
                 // If ldapPasswordExpirationTime is less than 0, this may indicate an extremely late date, past the epoch.
                 ldapPasswordExpirationTime = null;
             }
-            return ldapPasswordExpirationTime == null
-                    ? null
-                    : ldapPasswordExpirationTime.toInstant();
+            return ldapPasswordExpirationTime;
         } catch (Exception e) {
             LOGGER.warn("error reading password expiration time: " + e.getMessage());
         }

+ 8 - 4
server/src/main/java/password/pwm/ldap/LdapPermissionTester.java

@@ -24,14 +24,14 @@ package password.pwm.ldap;
 
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiException;
-import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.SearchScope;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
-import password.pwm.config.value.data.UserPermission;
 import password.pwm.config.profile.LdapProfile;
+import password.pwm.config.value.data.UserPermission;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
@@ -137,7 +137,11 @@ public class LdapPermissionTester {
             try {
                 LOGGER.trace(pwmSession, "checking ldap to see if " + userIdentity + " matches group '" + groupDN + "' using filter '" + filterString + "'");
                 final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
-                final Map<String, Map<String, String>> results = theUser.getChaiProvider().search(theUser.getEntryDN(), filterString, Collections.<String>emptySet(), ChaiProvider.SEARCH_SCOPE.BASE);
+                final Map<String, Map<String, String>> results = theUser.getChaiProvider().search(
+                        theUser.getEntryDN(),
+                        filterString,
+                        Collections.<String>emptySet(), SearchScope.BASE
+                );
                 if (results.size() == 1 && results.keySet().contains(theUser.getEntryDN())) {
                     result = true;
                 }
@@ -182,7 +186,7 @@ public class LdapPermissionTester {
             try {
                 LOGGER.trace(pwmSession, "checking ldap to see if " + userIdentity + " matches '" + filterString + "'");
                 final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
-                final Map<String, Map<String, String>> results = theUser.getChaiProvider().search(theUser.getEntryDN(), filterString, Collections.<String>emptySet(), ChaiProvider.SEARCH_SCOPE.BASE);
+                final Map<String, Map<String, String>> results = theUser.getChaiProvider().search(theUser.getEntryDN(), filterString, Collections.emptySet(), SearchScope.BASE);
                 if (results.size() == 1 && results.keySet().contains(theUser.getEntryDN())) {
                     result = true;
                 }

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

@@ -33,7 +33,6 @@ import password.pwm.util.operations.otp.OTPUserRecord;
 
 import java.time.Instant;
 import java.util.Collection;
-import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -84,7 +83,7 @@ public interface UserInfo {
 
     String readStringAttribute(String attribute) throws PwmUnrecoverableException;
 
-    Date readDateAttribute(String attribute) throws PwmUnrecoverableException;
+    Instant readDateAttribute(String attribute) throws PwmUnrecoverableException;
 
     List<String> readMultiStringAttribute(String attribute) throws PwmUnrecoverableException;
 

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

@@ -37,7 +37,6 @@ import password.pwm.util.operations.otp.OTPUserRecord;
 import java.time.Instant;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -96,10 +95,10 @@ public class UserInfoBean implements UserInfo {
     }
 
     @Override
-    public Date readDateAttribute(final String attribute) throws PwmUnrecoverableException
+    public Instant readDateAttribute(final String attribute) throws PwmUnrecoverableException
     {
         if (attributes.containsKey(attribute)) {
-            return EdirEntries.convertZuluToDate(attributes.get(attribute));
+            return EdirEntries.convertZuluToInstant(attributes.get(attribute));
         }
         return null;
     }

+ 5 - 11
server/src/main/java/password/pwm/ldap/UserInfoReader.java

@@ -28,6 +28,7 @@ import com.novell.ldapchai.exception.ChaiException;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.SearchScope;
 import password.pwm.PwmApplication;
 import password.pwm.bean.PasswordStatus;
 import password.pwm.bean.ResponseInfoBean;
@@ -66,7 +67,6 @@ import password.pwm.util.operations.otp.OTPUserRecord;
 import java.time.Instant;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -153,10 +153,7 @@ public class UserInfoReader implements UserInfo {
     public Instant getLastLdapLoginTime() throws PwmUnrecoverableException
     {
         try {
-            final Date lastLoginTime = chaiUser.readLastLoginTime();
-            return lastLoginTime == null
-                    ? null
-                    : lastLoginTime.toInstant();
+            return chaiUser.readLastLoginTime();
         } catch (ChaiOperationException e) {
             LOGGER.warn(sessionLabel, "error reading user's last ldap login time: " + e.getMessage());
         } catch (ChaiUnavailableException e) {
@@ -491,10 +488,7 @@ public class UserInfoReader implements UserInfo {
     public Instant getAccountExpirationTime() throws PwmUnrecoverableException
     {
         try {
-            final Date accountExpireDate = chaiUser.readAccountExpirationDate();
-            return accountExpireDate == null
-                    ? null
-                    : accountExpireDate.toInstant();
+            return chaiUser.readAccountExpirationDate();
         } catch (ChaiOperationException e) {
             LOGGER.warn(sessionLabel, "error reading user's account expiration time: " + e.getMessage());
         } catch (ChaiUnavailableException e) {
@@ -554,7 +548,7 @@ public class UserInfoReader implements UserInfo {
     }
 
     @Override
-    public Date readDateAttribute(final String attribute)
+    public Instant readDateAttribute(final String attribute)
             throws PwmUnrecoverableException
     {
         try {
@@ -610,7 +604,7 @@ public class UserInfoReader implements UserInfo {
                         chaiUser.getEntryDN(),
                         "(objectclass=*)",
                         uncachedAttributes,
-                        ChaiProvider.SEARCH_SCOPE.BASE
+                        SearchScope.BASE
                 );
             } catch (ChaiOperationException e) {
                 final String msg = "ldap operational error while reading user data" + e.getMessage();

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

@@ -33,6 +33,7 @@ import com.novell.ldapchai.exception.ImpossiblePasswordPolicyException;
 import com.novell.ldapchai.impl.oracleds.entry.OracleDSEntries;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiSetting;
+import com.novell.ldapchai.provider.DirectoryVendor;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
@@ -64,6 +65,7 @@ import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.operations.PasswordUtility;
 
+import java.time.Instant;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
@@ -191,17 +193,17 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
                 testCredentials(userIdentity, password);
             } catch (PwmOperationalException e) {
                 boolean permitAuthDespiteError = false;
-                final ChaiProvider.DIRECTORY_VENDOR vendor = pwmApplication.getProxyChaiProvider(
+                final DirectoryVendor vendor = pwmApplication.getProxyChaiProvider(
                         userIdentity.getLdapProfileID()).getDirectoryVendor();
                 if (PwmError.PASSWORD_NEW_PASSWORD_REQUIRED == e.getError()) {
-                    if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+                    if (vendor == DirectoryVendor.ACTIVE_DIRECTORY) {
                         if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
                             log(PwmLogLevel.INFO,
                                     "auth bind failed, but will allow login due to 'must change password on next login AD error', error: " + e.getErrorInformation().toDebugStr());
                             allowBindAsUser = false;
                             permitAuthDespiteError = true;
                         }
-                    } else if (vendor == ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS) {
+                    } else if (vendor == DirectoryVendor.ORACLE_DS) {
                         if (pwmApplication.getConfig().readSettingAsBoolean(
                                 PwmSetting.ORACLE_DS_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
                             log(PwmLogLevel.INFO,
@@ -211,7 +213,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
                         }
                     }
                 } else if (PwmError.PASSWORD_EXPIRED == e.getError()) { // handle ad case where password is expired
-                    if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+                    if (vendor == DirectoryVendor.ACTIVE_DIRECTORY) {
                         if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
                             if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_EXPIRED)) {
                                 throw e;
@@ -423,7 +425,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
             return null;
         }
 
-        if (ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
+        if (DirectoryVendor.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
             return null;
         }
 
@@ -435,11 +437,11 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
 
 
         if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
-            final Date date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
+            final Instant date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
 
             final boolean enforceFromForgotten = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.CHALLENGE_ENFORCE_MINIMUM_PASSWORD_LIFETIME);
             if (enforceFromForgotten) {
-                if (new Date().before(date)) {
+                if (Instant.now().isBefore(date)) {
                     final String errorMsg = "change not permitted until " + JavaHelper.toIsoDate(
                             date);
                     throw new PwmUnrecoverableException(
@@ -463,7 +465,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
         }
 
         // oracle DS special case: passwordAllowChangeTime handler
-        if (ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
+        if (DirectoryVendor.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
             return;
         }
 
@@ -483,7 +485,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
                 final boolean PostTempUseCurrentTime = Boolean.parseBoolean(pwmApplication.getConfig().readAppProperty(AppProperty.LDAP_ORACLE_POST_TEMPPW_USE_CURRENT_TIME));
                 if (PostTempUseCurrentTime) {
                     log(PwmLogLevel.TRACE, "a new value for passwordAllowChangeTime attribute to user " + chaiUser.getEntryDN() + " has appeared, will replace with current time value");
-                    final String newTimeValue = OracleDSEntries.convertDateToZulu(new Date());
+                    final String newTimeValue = OracleDSEntries.convertDateToZulu(Instant.now());
                     final Set<String> values = new HashSet<>(Collections.singletonList(newTimeValue));
                     chaiProvider.writeStringAttribute(chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME, values, true);
                     log(PwmLogLevel.TRACE, "wrote attribute value '" + newTimeValue + "' for passwordAllowChangeTime attribute on user " + chaiUser.getEntryDN());

+ 6 - 5
server/src/main/java/password/pwm/ldap/schema/SchemaManager.java

@@ -24,6 +24,7 @@ package password.pwm.ldap.schema;
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.DirectoryVendor;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
@@ -37,11 +38,11 @@ import java.util.Map;
 public class SchemaManager {
     private static final PwmLogger LOGGER = PwmLogger.forClass(SchemaManager.class);
 
-    private static final Map<ChaiProvider.DIRECTORY_VENDOR, Class<? extends SchemaExtender>> IMPLEMENTATIONS;
+    private static final Map<DirectoryVendor, Class<? extends SchemaExtender>> IMPLEMENTATIONS;
 
     static {
-        final Map<ChaiProvider.DIRECTORY_VENDOR, Class<? extends SchemaExtender>> implMap = new HashMap<>();
-        implMap.put(ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY, EdirSchemaExtender.class);
+        final Map<DirectoryVendor, Class<? extends SchemaExtender>> implMap = new HashMap<>();
+        implMap.put(DirectoryVendor.EDIRECTORY, EdirSchemaExtender.class);
         IMPLEMENTATIONS = Collections.unmodifiableMap(implMap);
     }
 
@@ -50,7 +51,7 @@ public class SchemaManager {
             throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, "provider is not connected"));
         }
         try {
-            if (chaiProvider.getDirectoryVendor() != ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY) {
+            if (chaiProvider.getDirectoryVendor() != DirectoryVendor.EDIRECTORY) {
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, "directory vendor is not supported"));
             }
             final List<String> urls = chaiProvider.getChaiConfiguration().bindURLsAsList();
@@ -58,7 +59,7 @@ public class SchemaManager {
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, "provider used for schema extension must have only a single ldap url defined"));
             }
 
-            final ChaiProvider.DIRECTORY_VENDOR vendor = chaiProvider.getDirectoryVendor();
+            final DirectoryVendor vendor = chaiProvider.getDirectoryVendor();
             final Class<? extends SchemaExtender> implClass = IMPLEMENTATIONS.get(vendor);
             final SchemaExtender schemaExtenderImpl = implClass.newInstance();
             schemaExtenderImpl.init(chaiProvider);

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

@@ -22,7 +22,7 @@
 
 package password.pwm.svc.telemetry;
 
-import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.DirectoryVendor;
 import lombok.Builder;
 import lombok.Getter;
 import password.pwm.AppProperty;
@@ -256,7 +256,7 @@ public class TelemetryService implements PwmService {
             }
         }
 
-        final Set<ChaiProvider.DIRECTORY_VENDOR> ldapVendors = new LinkedHashSet<>();
+        final Set<DirectoryVendor> ldapVendors = new LinkedHashSet<>();
         for (final LdapProfile ldapProfile : config.getLdapProfiles().values()) {
             try {
                 ldapVendors.add(ldapProfile.getProxyChaiProvider(pwmApplication).getDirectoryVendor());

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

@@ -32,7 +32,7 @@ import com.novell.ldapchai.exception.ChaiException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiValidationException;
 import com.novell.ldapchai.impl.edir.NmasCrFactory;
-import com.novell.ldapchai.provider.ChaiProvider;
+import com.novell.ldapchai.provider.DirectoryVendor;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.bean.ResponseInfoBean;
@@ -132,7 +132,7 @@ public class CrService implements PwmService {
 
         if (config.readSettingAsBoolean(PwmSetting.EDIRECTORY_READ_CHALLENGE_SET)) {
             try {
-                if (theUser.getChaiProvider().getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY) {
+                if (theUser.getChaiProvider().getDirectoryVendor() == DirectoryVendor.EDIRECTORY) {
                     if (policy != null && policy.getChaiPasswordPolicy() != null) {
                         returnSet = NmasCrFactory.readAssignedChallengeSet(theUser.getChaiProvider(), policy.getChaiPasswordPolicy(), locale);
                     }

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

@@ -34,6 +34,7 @@ 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;
 import password.pwm.AppProperty;
 import password.pwm.Permission;
@@ -44,10 +45,8 @@ import password.pwm.bean.PasswordStatus;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SmsItemBean;
 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.value.data.UserPermission;
 import password.pwm.config.option.HelpdeskClearResponseMode;
 import password.pwm.config.option.MessageSendMethod;
 import password.pwm.config.profile.ForgottenPasswordProfile;
@@ -57,6 +56,8 @@ import password.pwm.config.profile.ProfileType;
 import password.pwm.config.profile.ProfileUtility;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.config.profile.PwmPasswordRule;
+import password.pwm.config.value.data.ActionConfiguration;
+import password.pwm.config.value.data.UserPermission;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmDataValidationException;
 import password.pwm.error.PwmError;
@@ -91,7 +92,6 @@ import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -271,7 +271,7 @@ public class PasswordUtility {
 
         boolean setPasswordWithoutOld = false;
         if (oldPassword == null) {
-            if (pwmSession.getSessionManager().getActor(pwmApplication).getChaiProvider().getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+            if (pwmSession.getSessionManager().getActor(pwmApplication).getChaiProvider().getDirectoryVendor() == DirectoryVendor.ACTIVE_DIRECTORY) {
                 setPasswordWithoutOld = true;
             }
         }
@@ -1030,10 +1030,10 @@ public class PasswordUtility {
     {
         // fetch last password modification time from pwm last update attribute operation
         try {
-            final Date chaiReadDate = theUser.readPasswordModificationDate();
+            final Instant chaiReadDate = theUser.readPasswordModificationDate();
             if (chaiReadDate != null) {
                 LOGGER.trace(sessionLabel, "read last user password change timestamp (via chai) as: " + JavaHelper.toIsoDate(chaiReadDate));
-                return chaiReadDate.toInstant();
+                return chaiReadDate;
             }
         } catch (ChaiOperationException e) {
             LOGGER.error(sessionLabel, "unexpected error reading password last modified timestamp: " + e.getMessage());
@@ -1043,9 +1043,9 @@ public class PasswordUtility {
         final String pwmLastSetAttr = ldapProfile.readSettingAsString(PwmSetting.PASSWORD_LAST_UPDATE_ATTRIBUTE);
         if (pwmLastSetAttr != null && pwmLastSetAttr.length() > 0) {
             try {
-                final Date pwmPwdLastModified = theUser.readDateAttribute(pwmLastSetAttr);
+                final Instant pwmPwdLastModified = theUser.readDateAttribute(pwmLastSetAttr);
                 LOGGER.trace(sessionLabel, "read pwmPasswordChangeTime as: " + (pwmPwdLastModified == null ? "n/a" : JavaHelper.toIsoDate(pwmPwdLastModified)));
-                return pwmPwdLastModified == null ? null : pwmPwdLastModified.toInstant();
+                return pwmPwdLastModified;
             } catch (ChaiOperationException e) {
                 LOGGER.error(sessionLabel, "error parsing password last modified PWM password value for user " + theUser.getEntryDN() + "; error: " + e.getMessage());
             }
@@ -1067,11 +1067,11 @@ public class PasswordUtility {
 
         // for oracle DS; this check is also handled in UserAuthenticator.
         try {
-            if (ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS == chaiUser.getChaiProvider().getDirectoryVendor()) {
+            if (DirectoryVendor.ORACLE_DS == chaiUser.getChaiProvider().getDirectoryVendor()) {
                 final String oracleDS_PrePasswordAllowChangeTime = chaiUser.readStringAttribute("passwordAllowChangeTime");
                 if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
-                    final Date date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
-                    if (new Date().before(date)) {
+                    final Instant date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
+                    if (Instant.now().isBefore(date)) {
                         LOGGER.debug("discovered oracleds allowed change time is set to: " + JavaHelper.toIsoDate(date) + ", won't permit password change");
                         final String errorMsg = "change not permitted until " + JavaHelper.toIsoDate(date);
                         final ErrorInformation errorInformation = new ErrorInformation(PwmError.PASSWORD_TOO_SOON, errorMsg);

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

@@ -44,6 +44,7 @@ import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiProviderFactory;
 import com.novell.ldapchai.provider.ChaiProviderImplementor;
 import com.novell.ldapchai.provider.ChaiSetting;
+import com.novell.ldapchai.provider.DirectoryVendor;
 import com.novell.ldapchai.provider.JLDAPProviderImpl;
 import com.novell.security.nmas.client.NMASCallback;
 import com.novell.security.nmas.client.NMASCompletionCallback;
@@ -244,7 +245,7 @@ public class NMASCrOperator implements CrOperator {
         pwmApplication.getIntruderManager().convenience().checkUserIdentity(userIdentity);
 
         try {
-            if (theUser.getChaiProvider().getDirectoryVendor() != ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY) {
+            if (theUser.getChaiProvider().getDirectoryVendor() != DirectoryVendor.EDIRECTORY) {
                 return null;
             }
 
@@ -266,7 +267,7 @@ public class NMASCrOperator implements CrOperator {
             throws PwmUnrecoverableException
     {
         try {
-            if (theUser.getChaiProvider().getDirectoryVendor() != ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY) {
+            if (theUser.getChaiProvider().getDirectoryVendor() != DirectoryVendor.EDIRECTORY) {
                 LOGGER.debug("skipping request to read NMAS responses for " + userIdentity + ", directory type is not eDirectory");
                 return null;
             }
@@ -290,7 +291,7 @@ public class NMASCrOperator implements CrOperator {
             throws PwmUnrecoverableException
     {
         try {
-            if (theUser.getChaiProvider().getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY) {
+            if (theUser.getChaiProvider().getDirectoryVendor() == DirectoryVendor.EDIRECTORY) {
                 NmasCrFactory.clearResponseSet(theUser);
                 LOGGER.info("cleared responses for user " + theUser.getEntryDN() + " using NMAS method ");
             }
@@ -311,7 +312,7 @@ public class NMASCrOperator implements CrOperator {
             throws PwmUnrecoverableException
     {
         try {
-            if (theUser.getChaiProvider().getDirectoryVendor() == ChaiProvider.DIRECTORY_VENDOR.NOVELL_EDIRECTORY) {
+            if (theUser.getChaiProvider().getDirectoryVendor() == DirectoryVendor.EDIRECTORY) {
 
                 final NmasResponseSet nmasResponseSet = NmasCrFactory.newNmasResponseSet(
                         responseInfoBean.getCrMap(),
@@ -389,9 +390,12 @@ public class NMASCrOperator implements CrOperator {
             final List<String> ldapURLs = ldapProfile.readSettingAsStringArray(PwmSetting.LDAP_SERVER_URLS);
             final String proxyDN = ldapProfile.readSettingAsString(PwmSetting.LDAP_PROXY_USER_DN);
             final PasswordData proxyPW = ldapProfile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD);
-            chaiConfiguration = LdapOperationsHelper.createChaiConfiguration(config, ldapProfile, ldapURLs, proxyDN,
+            final ChaiConfiguration newChaiConfig = LdapOperationsHelper.createChaiConfiguration(config, ldapProfile, ldapURLs, proxyDN,
                     proxyPW);
-            chaiConfiguration.setSetting(ChaiSetting.PROVIDER_IMPLEMENTATION, JLDAPProviderImpl.class.getName());
+
+            chaiConfiguration = ChaiConfiguration.builder(newChaiConfig)
+                    .setSetting(ChaiSetting.PROVIDER_IMPLEMENTATION, JLDAPProviderImpl.class.getName())
+                    .build();
 
             cycle();
         }