浏览代码

ng-storage updates
misc fixes

Jason Rivard 6 年之前
父节点
当前提交
9dd4bc28b2

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

@@ -77,19 +77,12 @@ public class UserIdentity implements Serializable, Comparable
         {
             return null;
         }
-        if ( configuration.getLdapProfiles().containsKey( this.getLdapProfileID() ) )
-        {
-            return configuration.getLdapProfiles().get( this.getLdapProfileID() );
-        }
-        else
-        {
-            return null;
-        }
+        return configuration.getLdapProfiles().getOrDefault( this.getLdapProfileID(), null );
     }
 
     public String toString( )
     {
-        return "UserIdentity" + JsonUtil.serialize( this );
+        return toDisplayString();
     }
 
     public String toObfuscatedKey( final PwmApplication pwmApplication )

+ 0 - 143
server/src/main/java/password/pwm/config/stored/NGStorageEngineImpl.java

@@ -1,143 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.config.stored;
-
-import password.pwm.bean.UserIdentity;
-import password.pwm.config.StoredValue;
-
-import java.time.Instant;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-class NGStorageEngineImpl implements StorageEngine
-{
-    private final Map<StoredConfigReference, StoredValue> values = new TreeMap<>();
-    private final Map<StoredConfigReference, ValueMetaData> metaValues = new TreeMap<>();
-    private final ConfigChangeLog changeLog;
-
-    private final ReentrantReadWriteLock bigLock = new ReentrantReadWriteLock();
-    private boolean writeLocked;
-
-    NGStorageEngineImpl(
-            final Map<StoredConfigReference, StoredValue> values,
-            final Map<StoredConfigReference, ValueMetaData> metaValues
-    )
-    {
-        this.values.putAll( values );
-        this.metaValues.putAll( metaValues );
-        changeLog = new ConfigChangeLogImpl( this );
-    }
-
-    public ConfigChangeLog changeLog( )
-    {
-        return changeLog;
-    }
-
-    public StoredValue read( final StoredConfigReference storedConfigReference )
-    {
-        bigLock.readLock().lock();
-        try
-        {
-            return values.get( storedConfigReference );
-        }
-        finally
-        {
-            bigLock.readLock().unlock();
-        }
-    }
-
-    public ValueMetaData readMetaData( final StoredConfigReference storedConfigReference )
-    {
-        return metaValues.get( storedConfigReference );
-    }
-
-    public void write( final StoredConfigReference reference, final StoredValue value, final UserIdentity userIdentity )
-    {
-        checkWriteLock();
-        bigLock.writeLock().lock();
-        try
-        {
-            if ( values.containsKey( reference ) )
-            {
-                changeLog.updateChangeLog( reference, values.get( reference ), value );
-            }
-            else
-            {
-                changeLog.updateChangeLog( reference, value );
-            }
-            values.put( reference, value );
-            final ValueMetaData valueMetaData = new ValueMetaData( Instant.now(), userIdentity );
-            metaValues.put( reference, valueMetaData );
-        }
-        finally
-        {
-            bigLock.writeLock().unlock();
-        }
-    }
-
-    public void reset( final StoredConfigReference reference, final UserIdentity userIdentity )
-    {
-        checkWriteLock();
-        bigLock.writeLock().lock();
-        try
-        {
-            if ( values.containsKey( reference ) )
-            {
-                changeLog.updateChangeLog( reference, values.get( reference ), null );
-            }
-            else
-            {
-                changeLog.updateChangeLog( reference, null );
-            }
-            values.remove( reference );
-            if ( metaValues.containsKey( reference ) )
-            {
-                final ValueMetaData valueMetaData = new ValueMetaData( Instant.now(), userIdentity );
-                metaValues.put( reference, valueMetaData );
-            }
-        }
-        finally
-        {
-            bigLock.writeLock().unlock();
-        }
-    }
-
-    private void checkWriteLock( )
-    {
-        if ( writeLocked )
-        {
-            throw new IllegalStateException( "attempt to modify writeLock configuration" );
-        }
-    }
-
-    public boolean isWriteLocked( )
-    {
-        return writeLocked;
-    }
-
-    public void writeLock( )
-    {
-        writeLocked = true;
-    }
-}

+ 1 - 1
server/src/main/java/password/pwm/config/stored/StorageEngine.java

@@ -25,7 +25,7 @@ package password.pwm.config.stored;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.StoredValue;
 
-interface StorageEngine
+public interface StorageEngine
 {
     StoredValue read( StoredConfigReference storedConfigReference );
 

+ 5 - 32
server/src/main/java/password/pwm/config/stored/StoredConfigReferenceBean.java

@@ -22,15 +22,18 @@
 
 package password.pwm.config.stored;
 
+import lombok.Value;
+
 import java.io.Serializable;
 
-class StoredConfigReferenceBean implements StoredConfigReference, Serializable, Comparable
+@Value
+public class StoredConfigReferenceBean implements StoredConfigReference, Serializable, Comparable
 {
     private RecordType recordType;
     private String recordID;
     private String profileID;
 
-    StoredConfigReferenceBean( final RecordType type, final String recordID, final String profileID )
+    public StoredConfigReferenceBean( final RecordType type, final String recordID, final String profileID )
     {
         if ( type == null )
         {
@@ -47,36 +50,6 @@ class StoredConfigReferenceBean implements StoredConfigReference, Serializable,
         this.profileID = profileID;
     }
 
-    public RecordType getRecordType( )
-    {
-        return recordType;
-    }
-
-    public String getRecordID( )
-    {
-        return recordID;
-    }
-
-    @Override
-    public String getProfileID( )
-    {
-        return profileID;
-    }
-
-    @Override
-    public boolean equals( final Object o )
-    {
-        return o != null
-                && o instanceof StoredConfigReference
-                && toString().equals( o.toString() );
-
-    }
-
-    @Override
-    public int hashCode( )
-    {
-        return toString().hashCode();
-    }
 
     @Override
     public String toString( )

+ 2 - 0
server/src/main/java/password/pwm/config/stored/ValueMetaData.java

@@ -22,6 +22,7 @@
 
 package password.pwm.config.stored;
 
+import lombok.Builder;
 import lombok.Value;
 import password.pwm.bean.UserIdentity;
 
@@ -29,6 +30,7 @@ import java.io.Serializable;
 import java.time.Instant;
 
 @Value
+@Builder( toBuilder = true )
 public class ValueMetaData implements Serializable
 {
     private Instant modifyDate;

+ 101 - 0
server/src/main/java/password/pwm/config/stored/ng/NGStorageEngineImpl.java

@@ -0,0 +1,101 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.config.stored.ng;
+
+import password.pwm.bean.UserIdentity;
+import password.pwm.config.StoredValue;
+import password.pwm.config.stored.StoredConfigReference;
+import password.pwm.config.stored.ValueMetaData;
+
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+
+class NGStorageEngineImpl
+{
+    private final Map<StoredConfigReference, StoredValue> storedValues = new HashMap<>();
+    private final Map<StoredConfigReference, ValueMetaData> metaValues = new HashMap<>();
+
+    NGStorageEngineImpl()
+    {
+    }
+
+    NGStorageEngineImpl(
+            final Map<StoredConfigReference, StoredValue> storedValues,
+            final Map<StoredConfigReference, ValueMetaData> metaValues
+    )
+    {
+        this.storedValues.putAll( storedValues );
+        this.metaValues.putAll( metaValues );
+    }
+
+    StoredValue read( final StoredConfigReference storedConfigReference )
+    {
+        return storedValues.get( storedConfigReference );
+    }
+
+    ValueMetaData readMetaData( final StoredConfigReference storedConfigReference )
+    {
+        return metaValues.get( storedConfigReference );
+    }
+
+    void writeMetaData( final StoredConfigReference storedConfigReference, final ValueMetaData valueMetaData )
+    {
+        metaValues.put( storedConfigReference, valueMetaData );
+    }
+
+    void write( final StoredConfigReference reference, final StoredValue value, final UserIdentity userIdentity )
+    {
+        if ( reference != null )
+        {
+            if ( value != null )
+            {
+                storedValues.put( reference, value );
+            }
+
+            updateUserIdentity( reference, userIdentity );
+        }
+    }
+
+    void reset( final StoredConfigReference reference, final UserIdentity userIdentity )
+    {
+        if ( reference != null )
+        {
+            storedValues.remove( reference );
+            updateUserIdentity( reference, userIdentity );
+        }
+    }
+
+    private void updateUserIdentity(
+            final StoredConfigReference reference,
+            final UserIdentity userIdentity
+    )
+    {
+        metaValues.put(
+                reference,
+                ValueMetaData.builder().modifyDate( Instant.now() )
+                        .userIdentity( userIdentity )
+                        .build() );
+
+    }
+}

+ 17 - 11
server/src/main/java/password/pwm/config/stored/NGStoredConfiguration.java → server/src/main/java/password/pwm/config/stored/ng/NGStoredConfiguration.java

@@ -20,12 +20,17 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-package password.pwm.config.stored;
+package password.pwm.config.stored.ng;
 
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingCategory;
 import password.pwm.config.StoredValue;
+import password.pwm.config.stored.ConfigurationProperty;
+import password.pwm.config.stored.StoredConfigReference;
+import password.pwm.config.stored.StoredConfigReferenceBean;
+import password.pwm.config.stored.StoredConfiguration;
+import password.pwm.config.stored.ValueMetaData;
 import password.pwm.config.value.StringValue;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.java.JavaHelper;
@@ -33,20 +38,19 @@ import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 
 import java.time.Instant;
-import java.util.Map;
 
-class NGStoredConfiguration implements StoredConfiguration
+public class NGStoredConfiguration implements StoredConfiguration
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( NGStoredConfiguration.class );
     private final PwmSecurityKey configurationSecurityKey;
-    private final StorageEngine engine;
+    private final NGStorageEngineImpl engine;
+    private boolean readOnly = false;
 
     NGStoredConfiguration(
-            final Map<StoredConfigReference, StoredValue> values,
-            final Map<StoredConfigReference, ValueMetaData> metaValues,
+            final NGStorageEngineImpl storageEngine,
             final PwmSecurityKey pwmSecurityKey )
     {
-        engine = new NGStorageEngineImpl( values, metaValues );
+        engine = storageEngine;
         configurationSecurityKey = pwmSecurityKey;
     }
 
@@ -65,7 +69,9 @@ class NGStoredConfiguration implements StoredConfiguration
         return ( String ) storedValue.toNativeObject();
     }
 
-    public void writeConfigProperty( final ConfigurationProperty configurationProperty, final String value )
+    public void writeConfigProperty(
+            final ConfigurationProperty configurationProperty,
+            final String value )
     {
         final StoredConfigReference storedConfigReference = new StoredConfigReferenceBean(
                 StoredConfigReference.RecordType.PROPERTY,
@@ -73,7 +79,7 @@ class NGStoredConfiguration implements StoredConfiguration
                 null
         );
         final StoredValue storedValue = new StringValue( value );
-        engine.write( storedConfigReference, storedValue, null );
+        engine.write( storedConfigReference, storedValue, null  );
     }
 
     public void resetSetting( final PwmSetting setting, final String profileID, final UserIdentity userIdentity )
@@ -158,13 +164,13 @@ class NGStoredConfiguration implements StoredConfiguration
     @Override
     public boolean isLocked( )
     {
-        return engine.isWriteLocked();
+        return readOnly;
     }
 
     @Override
     public void lock( )
     {
-        engine.writeLock();
+        readOnly = true;
     }
 
     @Override

+ 20 - 18
server/src/main/java/password/pwm/config/stored/NGStoredConfigurationFactory.java → server/src/main/java/password/pwm/config/stored/ng/NGStoredConfigurationFactory.java

@@ -20,11 +20,15 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-package password.pwm.config.stored;
+package password.pwm.config.stored.ng;
 
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
+import password.pwm.config.stored.StoredConfigReference;
+import password.pwm.config.stored.StoredConfigReferenceBean;
+import password.pwm.config.stored.StoredConfiguration;
+import password.pwm.config.stored.ValueMetaData;
 import password.pwm.config.value.StringValue;
 import password.pwm.config.value.ValueFactory;
 import password.pwm.error.PwmUnrecoverableException;
@@ -38,8 +42,6 @@ import password.pwm.util.secure.PwmSecurityKey;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.time.Instant;
-import java.util.LinkedHashMap;
-import java.util.Map;
 
 
 public class NGStoredConfigurationFactory
@@ -59,12 +61,10 @@ public class NGStoredConfigurationFactory
 
     private static class XmlEngine
     {
-
         static NGStoredConfiguration fromXmlImpl( final InputStream inputStream )
                 throws PwmUnrecoverableException
         {
-            final Map<StoredConfigReference, StoredValue> values = new LinkedHashMap<>();
-            final Map<StoredConfigReference, ValueMetaData> metaData = new LinkedHashMap<>();
+            final NGStorageEngineImpl storageEngine = new NGStorageEngineImpl();
 
             final XmlDocument inputDocument = XmlFactory.getFactory().parseXml( inputStream );
             final XmlElement rootElement = inputDocument.getRootElement();
@@ -79,22 +79,21 @@ public class NGStoredConfigurationFactory
                 {
                     for ( final XmlElement propertyElement : loopElement.getChildren( StoredConfiguration.XML_ELEMENT_PROPERTY ) )
                     {
-                        readInterestingElement( propertyElement, pwmSecurityKey, values, metaData );
+                        readInterestingElement( propertyElement, pwmSecurityKey, storageEngine );
                     }
                 }
                 else if ( StoredConfiguration.XML_ELEMENT_SETTING.equals( loopElement.getName() ) )
                 {
-                    readInterestingElement( loopElement, pwmSecurityKey, values, metaData );
+                    readInterestingElement( loopElement, pwmSecurityKey, storageEngine );
                 }
             }
-            return new NGStoredConfiguration( values, metaData, readSecurityKey( rootElement ) );
+            return new NGStoredConfiguration( storageEngine, pwmSecurityKey );
         }
 
         static void readInterestingElement(
                 final XmlElement loopElement,
                 final PwmSecurityKey pwmSecurityKey,
-                final Map<StoredConfigReference, StoredValue> values,
-                final Map<StoredConfigReference, ValueMetaData> metaData
+                final NGStorageEngineImpl engine
         )
         {
             final StoredConfigReference reference = referenceForElement( loopElement );
@@ -107,7 +106,7 @@ public class NGStoredConfigurationFactory
                         final StoredValue storedValue = readSettingValue( reference, loopElement, pwmSecurityKey );
                         if ( storedValue != null )
                         {
-                            values.put( reference, storedValue );
+                            engine.write( reference, storedValue, null );
                         }
                     }
                     break;
@@ -115,17 +114,17 @@ public class NGStoredConfigurationFactory
                     case PROPERTY:
                     {
                         final StoredValue storedValue = readPropertyValue( reference, loopElement );
+                        if ( storedValue != null )
+                        {
+                            engine.write( reference, storedValue, null );
+                        }
                     }
                     break;
 
                     default:
                         throw new IllegalArgumentException( "unimplemented setting recordtype in reader" );
                 }
-                final ValueMetaData valueMetaData = readValueMetaData( loopElement );
-                if ( valueMetaData != null )
-                {
-                    metaData.put( reference, valueMetaData );
-                }
+                engine.writeMetaData( reference, readValueMetaData( loopElement ) );
             }
         }
 
@@ -246,7 +245,10 @@ public class NGStoredConfigurationFactory
                     ? new UserIdentity( modifyUser, modifyUserProfile )
                     : null;
 
-            return new ValueMetaData( modifyDate, userIdentity );
+            return ValueMetaData.builder()
+                    .modifyDate( modifyDate )
+                    .userIdentity( userIdentity )
+                    .build();
         }
     }
 

+ 23 - 31
server/src/main/java/password/pwm/http/filter/ConfigAccessFilter.java

@@ -96,10 +96,17 @@ public class ConfigAccessFilter extends AbstractPwmFilter
             }
         }
 
-        final ConfigManagerBean configManagerBean = pwmRequest.getPwmApplication().getSessionStateService().getBean( pwmRequest, ConfigManagerBean.class );
-        if ( checkAuthentication( pwmRequest, configManagerBean ) == ProcessStatus.Continue )
+        try
         {
-            filterChain.doFilter();
+            final ConfigManagerBean configManagerBean = pwmRequest.getPwmApplication().getSessionStateService().getBean( pwmRequest, ConfigManagerBean.class );
+            if ( checkAuthentication( pwmRequest, configManagerBean ) == ProcessStatus.Continue )
+            {
+                filterChain.doFilter();
+            }
+        }
+        catch ( PwmUnrecoverableException e )
+        {
+            pwmRequest.respondWithError( e.getErrorInformation() );
         }
     }
 
@@ -120,21 +127,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter
         final ConfigurationReader runningConfigReader = ContextManager.getContextManager( pwmRequest.getHttpServletRequest().getSession() ).getConfigReader();
         final StoredConfigurationImpl storedConfig = runningConfigReader.getStoredConfiguration();
 
-        if ( !checkIfAuthIsRequired( pwmRequest, storedConfig ) )
-        {
-            return ProcessStatus.Continue;
-        }
-
-        if ( !storedConfig.hasPassword() )
-        {
-            final String errorMsg = "config file does not have a configuration password";
-            final ErrorInformation errorInformation = new ErrorInformation( PwmError.CONFIG_FORMAT_ERROR, errorMsg, new String[]
-                    {
-                            errorMsg,
-                    }
-            );
-            return denyAndError( pwmRequest, errorInformation );
-        }
+        checkPreconditions( pwmRequest, storedConfig );
 
         if ( configManagerBean.isPasswordVerified() )
         {
@@ -252,15 +245,22 @@ public class ConfigAccessFilter extends AbstractPwmFilter
     }
 
 
-    private static boolean checkIfAuthIsRequired(
+    private static void checkPreconditions(
             final PwmRequest pwmRequest,
             final StoredConfigurationImpl storedConfig
     )
             throws PwmUnrecoverableException
     {
-        if ( storedConfig.hasPassword() )
+
+        if ( !storedConfig.hasPassword() )
         {
-            return true;
+            final String errorMsg = "config file does not have a configuration password";
+            final ErrorInformation errorInformation = new ErrorInformation( PwmError.CONFIG_FORMAT_ERROR, errorMsg, new String[]
+                    {
+                            errorMsg,
+                    }
+            );
+            throw new PwmUnrecoverableException( errorInformation );
         }
 
         if ( PwmApplicationMode.RUNNING == pwmRequest.getPwmApplication().getApplicationMode() )
@@ -274,14 +274,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter
             {
                 throw new PwmUnrecoverableException( PwmError.ERROR_UNAUTHORIZED );
             }
-        }
-
-        if ( PwmApplicationMode.CONFIGURATION != pwmRequest.getPwmApplication().getApplicationMode() )
-        {
-            return true;
-        }
-
-        return false;
+         }
     }
 
     private static boolean persistentLoginEnabled(
@@ -410,8 +403,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter
     private static ProcessStatus denyAndError( final PwmRequest pwmRequest, final ErrorInformation errorInformation )
             throws ServletException, PwmUnrecoverableException, IOException
     {
-        pwmRequest.setAttribute( PwmRequestAttribute.PwmErrorInfo, errorInformation );
-        forwardToJsp( pwmRequest );
+        pwmRequest.respondWithError( errorInformation );
         return ProcessStatus.Halt;
     }
 

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -628,7 +628,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
         final boolean otpPassed;
         if ( otpUserRecord != null )
         {
-            LOGGER.info( pwmRequest, () -> "checking entered OTP" );
+            LOGGER.info( pwmRequest, () -> "checking entered OTP for user " + userInfo.getUserIdentity().toDisplayString() );
             try
             {
                 // forces service to use proxy account to update (write) updated otp record if necessary.

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

@@ -339,7 +339,12 @@ public class HelpdeskServlet extends ControlledPwmServlet
         String userID = null;
         try
         {
-            userID = pwmSession.getUserInfo().getUsername();
+            final UserInfo deletedUserInfo = UserInfoFactory.newUserInfoUsingProxy(
+                    pwmApplication,
+                    pwmSession.getLabel(),
+                    userIdentity,
+                    pwmRequest.getLocale() );
+            userID = deletedUserInfo.getUsername();
         }
         catch ( PwmUnrecoverableException e )
         {

+ 7 - 3
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchDataReader.java

@@ -281,7 +281,8 @@ class PeopleSearchDataReader
 
         userDetailBean.setLinks( makeUserDetailLinks( userIdentity ) );
 
-        LOGGER.trace( pwmRequest, () -> "finished building userDetail result in " + TimeDuration.fromCurrent( startTime ).asCompactString() );
+        LOGGER.trace( pwmRequest, () -> "finished building userDetail result of " + userIdentity
+                + " in " + TimeDuration.fromCurrent( startTime ).asCompactString() );
         storeDataInCache( pwmRequest.getPwmApplication(), cacheKey, userDetailBean );
         return userDetailBean;
     }
@@ -480,6 +481,7 @@ class PeopleSearchDataReader
     )
             throws PwmUnrecoverableException
     {
+        final Instant startTime = Instant.now();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final boolean enabled = peopleSearchConfiguration.isPhotosEnabled( pwmRequest.getUserInfoIfLoggedIn(), pwmRequest.getSessionLabel() );
         if ( !enabled )
@@ -492,7 +494,8 @@ class PeopleSearchDataReader
             final boolean hasPermission = LdapPermissionTester.testUserPermissions( pwmApplication, pwmRequest.getSessionLabel(), userIdentity, permissions );
             if ( !hasPermission )
             {
-                LOGGER.debug( pwmRequest, () -> "user " + userIdentity.toString() + " failed photo query filter, denying photo view" );
+                LOGGER.debug( pwmRequest, () -> "user " + userIdentity + " failed photo query filter, denying photo view ("
+                        + TimeDuration.compactFromCurrent( startTime ) + ")" );
                 return null;
             }
         }
@@ -512,7 +515,8 @@ class PeopleSearchDataReader
             }
             catch ( PwmOperationalException e )
             {
-                LOGGER.debug( pwmRequest, () -> "determined " + userIdentity + " does not have photo data available while generating detail data" );
+                LOGGER.debug( pwmRequest, () -> "determined " + userIdentity.toDisplayString() + " does not have photo data available while generating detail data ("
+                        + TimeDuration.compactFromCurrent( startTime ) + ")" );
                 return null;
             }
         }

+ 11 - 3
server/src/main/java/password/pwm/svc/node/LDAPNodeDataService.java

@@ -86,7 +86,8 @@ class LDAPNodeDataService implements NodeDataServiceProvider
         }
         catch ( ChaiException e )
         {
-            throw new PwmUnrecoverableException( PwmError.ERROR_LDAP_DATA_ERROR, "error reading node service data: " + e.getMessage() );
+            throw new PwmUnrecoverableException( PwmError.ERROR_LDAP_DATA_ERROR, "error reading node service data "
+                    + ldapHelper.debugInfo() + ", error: " + e.getMessage() );
         }
 
         return returnData;
@@ -116,7 +117,8 @@ class LDAPNodeDataService implements NodeDataServiceProvider
         }
         catch ( ChaiException e )
         {
-            throw new PwmUnrecoverableException( PwmError.ERROR_LDAP_DATA_ERROR, "error writing node service data: " + e.getMessage() );
+            throw new PwmUnrecoverableException( PwmError.ERROR_LDAP_DATA_ERROR, "error writing node service data "
+                    + ldapHelper.debugInfo() + ", error: " + e.getMessage() );
         }
 
     }
@@ -148,7 +150,8 @@ class LDAPNodeDataService implements NodeDataServiceProvider
                 }
                 catch ( ChaiException e )
                 {
-                    throw new PwmUnrecoverableException( PwmError.ERROR_LDAP_DATA_ERROR, "error purging node service data: " + e.getMessage() );
+                    throw new PwmUnrecoverableException( PwmError.ERROR_LDAP_DATA_ERROR, "error purging node service data "
+                          + ldapHelper.debugInfo() + ", error: " + e.getMessage() );
                 }
             }
         }
@@ -189,5 +192,10 @@ class LDAPNodeDataService implements NodeDataServiceProvider
         {
             return new LDAPHelper( pwmApplication );
         }
+
+        String debugInfo()
+        {
+            return "user '" + this.userIdentity.toDisplayString() + "' attribute '" + attr  + "'";
+        }
     }
 }

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

@@ -178,6 +178,7 @@ public class XodusLocalDB implements LocalDBProvider
         environmentConfig.setEnvCloseForcedly( true );
         environmentConfig.setMemoryUsage( 50 * 1024 * 1024 );
         environmentConfig.setEnvGatherStatistics( true );
+        environmentConfig.setGcUtilizationFromScratch( true );
 
         for ( final Map.Entry<String, String> entry : initParameters.entrySet() )
         {