From 2d6a02664dd1d6d70b5c51329955dde3fe5c0add Mon Sep 17 00:00:00 2001
From: brinn <brinn>
Date: Wed, 23 Jan 2013 19:33:16 +0000
Subject: [PATCH] Make the CachingAuthenticationService available as a Spring
 bean and make it the default in the template service.properties. Change
 timeout and caching durations to support the formats fo
 DateTimeUtils.parseDurationString().

SVN: 28173
---
 .../crowd/CrowdConfiguration.java             |  18 ++-
 .../CachingAuthenticationConfiguration.java   | 150 ++++++++++++++++++
 .../file/CachingAuthenticationService.java    |  59 +++++--
 .../ldap/LDAPDirectoryConfiguration.java      |  21 ++-
 ...uthenticationServiceInvalidLoginTests.java |   2 +
 ...hingAuthenticationServiceSuccessTests.java |   2 +
 common/source/java/genericCommonContext.xml   |  24 ++-
 openbis/dist/server/service.properties        |  26 ++-
 8 files changed, 262 insertions(+), 40 deletions(-)
 create mode 100644 authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationConfiguration.java

diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdConfiguration.java b/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdConfiguration.java
index 6f0c159ffa7..97cdd3f2965 100644
--- a/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdConfiguration.java
+++ b/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdConfiguration.java
@@ -18,6 +18,8 @@ package ch.systemsx.cisd.authentication.crowd;
 
 import org.apache.commons.lang.StringUtils;
 
+import ch.systemsx.cisd.common.time.DateTimeUtils;
+
 /**
  * A configuration object for Crowd.
  * 
@@ -80,9 +82,10 @@ public class CrowdConfiguration
     {
         return Integer.toString(port);
     }
-    
+
     /**
-     * Sets the port (as String) that the Crowd service is running on. Only set if a positive integer.
+     * Sets the port (as String) that the Crowd service is running on. Only set if a positive
+     * integer.
      */
     public void setPortStr(String portStr)
     {
@@ -111,7 +114,7 @@ public class CrowdConfiguration
             return null;
         }
     }
-    
+
     /**
      * Returns the application name that this application sends to the Crowd service.
      */
@@ -151,7 +154,7 @@ public class CrowdConfiguration
     }
 
     /**
-     * Returns the timeout, i.e.  how long to wait for a result from Crowd (in ms).
+     * Returns the timeout, i.e. how long to wait for a result from Crowd (in ms).
      */
     public int getTimeout()
     {
@@ -167,7 +170,8 @@ public class CrowdConfiguration
     }
 
     /**
-     * Sets the timeout, i.e. how long to wait for a result from Crowd (as String, in s).
+     * Sets the timeout, i.e. how long to wait for a result from Crowd as a String in a format
+     * understood by {@link DateTimeUtils#parseDurationToMillis(String)}.
      */
     public void setTimeoutStr(String timeoutStr)
     {
@@ -175,14 +179,14 @@ public class CrowdConfiguration
         {
             try
             {
-                setTimeout(Integer.parseInt(timeoutStr) * 1000);
+                setTimeout((int) DateTimeUtils.parseDurationToMillis(timeoutStr));
             } catch (NumberFormatException ex)
             {
                 // Not set.
             }
         }
     }
-    
+
     /**
      * Returns the timeout, i.e. how long to wait for a result from Crowd (as String, in s).
      */
diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationConfiguration.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationConfiguration.java
new file mode 100644
index 00000000000..0c0c03c18fa
--- /dev/null
+++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationConfiguration.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.authentication.file;
+
+import org.apache.commons.lang.StringUtils;
+
+import ch.systemsx.cisd.authentication.IAuthenticationService;
+import ch.systemsx.cisd.common.time.DateTimeUtils;
+
+/**
+ * A configuration object for the {@link CachingAuthenticationService}.
+ * 
+ * @author Bernd Rinn
+ */
+public class CachingAuthenticationConfiguration
+{
+    private String passwordCacheFile;
+
+    private IAuthenticationService delegate;
+
+    private long cacheTime = CachingAuthenticationService.CACHE_TIME_MILLIS;
+
+    private long cacheTimeNoRevalidation =
+            CachingAuthenticationService.CACHE_TIME_MILLIS_NO_REVALIDATION;
+
+    /**
+     * Returns the path of the password cache file.
+     */
+    public String getPasswordCacheFile()
+    {
+        return passwordCacheFile;
+    }
+
+    /**
+     * Sets the path of the password cache file.
+     */
+    public void setPasswordCacheFile(String passwordCacheFile)
+    {
+        this.passwordCacheFile = passwordCacheFile;
+    }
+
+    /**
+     * Returns the delegate authentication service.
+     */
+    public IAuthenticationService getDelegate()
+    {
+        return delegate;
+    }
+
+    /**
+     * Sets the delegate authentication service.
+     */
+    public void setDelegate(IAuthenticationService delegate)
+    {
+        this.delegate = delegate;
+    }
+
+    /**
+     * Returns the cache time (in ms).
+     */
+    public long getCacheTime()
+    {
+        return cacheTime;
+    }
+
+    /**
+     * Sets the cache time (in ms).
+     */
+    public void setCacheTime(long cacheTime)
+    {
+        this.cacheTime = cacheTime;
+    }
+
+    /**
+     * Returns the cache time (in s, as String).
+     */
+    public String getCacheTimeStr()
+    {
+        return Long.toString(getCacheTime() / 1000);
+    }
+
+    /**
+     * Sets the cache time as String in a format understood by
+     * {@link DateTimeUtils#parseDurationToMillis(String)}.
+     */
+    public void setCacheTimeStr(String cacheTimeStr)
+    {
+        if (isResolved(cacheTimeStr))
+        {
+            setCacheTime(DateTimeUtils.parseDurationToMillis(cacheTimeStr));
+        }
+    }
+
+    /**
+     * Returns the time to return cache value without triggering revalidation (in ms).
+     */
+    public long getCacheTimeNoRevalidation()
+    {
+        return cacheTimeNoRevalidation;
+    }
+
+    /**
+     * Sets the time to return cache value without triggering revalidation (in ms).
+     */
+    public void setCacheTimeNoRevalidation(long cacheTimeNoRevalidation)
+    {
+        this.cacheTimeNoRevalidation = cacheTimeNoRevalidation;
+    }
+
+    /**
+     * Returns the time to return cache value without triggering revalidation (in s, as String).
+     */
+    public String getCacheTimeNoRevalidationStr()
+    {
+        return Long.toString(getCacheTimeNoRevalidation() / 1000);
+    }
+
+    /**
+     * Sets the time to return cache value without triggering revalidation as String in a format
+     * understood by {@link DateTimeUtils#parseDurationToMillis(String)}..
+     */
+    public void setCacheTimeNoRevalidationStr(String cacheTimeNoRevalidationStr)
+    {
+        if (isResolved(cacheTimeNoRevalidationStr))
+        {
+            setCacheTimeNoRevalidation(DateTimeUtils
+                    .parseDurationToMillis(cacheTimeNoRevalidationStr));
+        }
+    }
+
+    private static boolean isResolved(String name)
+    {
+        return StringUtils.isNotBlank(name) && name.startsWith("${") == false;
+    }
+
+}
diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationService.java
index d9ca05b94a0..af4294eb60a 100644
--- a/authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationService.java
+++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationService.java
@@ -31,6 +31,7 @@ import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.common.time.DateTimeUtils;
 import ch.systemsx.cisd.common.utilities.ITimeProvider;
 import ch.systemsx.cisd.common.utilities.SystemTimeProvider;
 
@@ -67,9 +68,9 @@ public class CachingAuthenticationService implements IAuthenticationService
 
     public final static long ONE_HOUR = 60 * ONE_MINUTE;
 
-    private final static long CACHE_TIME_MILLIS_NO_REVALIDATION = ONE_HOUR;
+    public final static long CACHE_TIME_MILLIS_NO_REVALIDATION = ONE_HOUR;
 
-    private final static long CACHE_TIME_MILLIS = 28 * ONE_HOUR;
+    public final static long CACHE_TIME_MILLIS = 28 * ONE_HOUR;
 
     private static final Logger operationLog =
             LogFactory.getLogger(LogCategory.OPERATION, CachingAuthenticationService.class);
@@ -211,7 +212,7 @@ public class CachingAuthenticationService implements IAuthenticationService
                         userId));
             }
         }
-        
+
         @Override
         public void run()
         {
@@ -230,7 +231,7 @@ public class CachingAuthenticationService implements IAuthenticationService
         }
     }
 
-    /** A simple interface to authenticate by one type of id. */
+    /** A interface with one method to authenticate by one type of id (userid or email). */
     interface IAuthenticator
     {
         Principal tryGetAndAuthenticate(String id, String passwordOrNull);
@@ -252,20 +253,22 @@ public class CachingAuthenticationService implements IAuthenticationService
 
     private final ITimeProvider timeProvider;
 
-    CachingAuthenticationService(IAuthenticationService delegate,
+    private final boolean caching;
+
+    public CachingAuthenticationService(IAuthenticationService delegate,
             String passwordCacheFileName)
     {
         this(delegate, createUserStore(passwordCacheFileName));
     }
 
-    CachingAuthenticationService(IAuthenticationService authenticationService,
+    public CachingAuthenticationService(IAuthenticationService authenticationService,
             IUserStore<UserCacheEntry> store)
     {
         this(authenticationService, store, CACHE_TIME_MILLIS_NO_REVALIDATION,
                 CACHE_TIME_MILLIS);
     }
 
-    CachingAuthenticationService(IAuthenticationService delegate,
+    public CachingAuthenticationService(IAuthenticationService delegate,
             String passwordCacheFileName,
             long cacheTimeNoRevalidationMillis,
             long cacheTimeMillis)
@@ -274,7 +277,7 @@ public class CachingAuthenticationService implements IAuthenticationService
                 cacheTimeNoRevalidationMillis, cacheTimeMillis);
     }
 
-    CachingAuthenticationService(IAuthenticationService delegate,
+    public CachingAuthenticationService(IAuthenticationService delegate,
             IUserStore<UserCacheEntry> userStore,
             long cacheTimeNoRevalidationMillis,
             long cacheTimeMillis)
@@ -283,6 +286,12 @@ public class CachingAuthenticationService implements IAuthenticationService
                 SystemTimeProvider.SYSTEM_TIME_PROVIDER);
     }
 
+    public CachingAuthenticationService(CachingAuthenticationConfiguration config)
+    {
+        this(config.getDelegate(), createUserStore(config.getPasswordCacheFile()), config
+                .getCacheTimeNoRevalidation(), config.getCacheTime());
+    }
+
     // For unit tests.
     CachingAuthenticationService(IAuthenticationService delegate,
             IUserStore<UserCacheEntry> userStore,
@@ -315,18 +324,36 @@ public class CachingAuthenticationService implements IAuthenticationService
         this.cacheTimeMillis = cacheTimeMillis;
         this.validationQueue = new LinkedBlockingQueue<ValidationRequest>();
         this.timeProvider = timeProvider;
-        if (startRevalidationThread)
+        this.caching = (cacheTimeMillis > 0);
+        if (startRevalidationThread && caching)
         {
             final Thread t = new Thread(new RevalidationRunnable());
             t.setName(getClass().getSimpleName() + " - Validator");
             t.setDaemon(true);
             t.start();
         }
+        if (operationLog.isInfoEnabled())
+        {
+            if (caching)
+            {
+                operationLog.info(String.format(
+                        "Caching authentication results for %s, revalidating after %s.",
+                        DateTimeUtils.renderDuration(cacheTimeMillis),
+                        DateTimeUtils.renderDuration(cacheTimeNoRevalidationMillis)));
+            } else
+            {
+                operationLog.info("Authentication caching is switched off.");
+            }
+        }
     }
 
     static IUserStore<UserCacheEntry> createUserStore(
             final String passwordCacheFileName)
     {
+        if (StringUtils.isBlank(passwordCacheFileName))
+        {
+            return null;
+        }
         final ILineStore lineStore =
                 new FileBasedLineStore(new File(passwordCacheFileName), "Password cache file");
         return new LineBasedUserStore<UserCacheEntry>(lineStore,
@@ -386,9 +413,13 @@ public class CachingAuthenticationService implements IAuthenticationService
                 {
                     return null;
                 }
-                final UserCacheEntry user =
-                        new UserCacheEntry(p, passwordOrNull, timeProvider.getTimeInMilliseconds());
-                userStore.addOrUpdateUser(user);
+                if (caching)
+                {
+                    final UserCacheEntry user =
+                            new UserCacheEntry(p, passwordOrNull,
+                                    timeProvider.getTimeInMilliseconds());
+                    userStore.addOrUpdateUser(user);
+                }
                 return p;
             }
             default:
@@ -409,7 +440,7 @@ public class CachingAuthenticationService implements IAuthenticationService
 
     private CacheEntryStatus getStatus(UserCacheEntry entry, boolean requirePassword, long now)
     {
-        if (entry == null)
+        if (entry == null || caching == false)
         {
             return CacheEntryStatus.NO_ENTRY;
         }
@@ -579,7 +610,7 @@ public class CachingAuthenticationService implements IAuthenticationService
     @Override
     public boolean isConfigured()
     {
-        return delegate.isConfigured();
+        return (userStore != null) && (delegate != null) && delegate.isConfigured();
     }
 
 }
diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java
index 51bffb56957..45730730a99 100644
--- a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java
+++ b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPDirectoryConfiguration.java
@@ -20,6 +20,8 @@ import javax.naming.Context;
 
 import org.apache.commons.lang.StringUtils;
 
+import ch.systemsx.cisd.common.time.DateTimeUtils;
+
 /**
  * The configuration for an LDAP directory server. Example:
  * 
@@ -335,13 +337,14 @@ public final class LDAPDirectoryConfiguration
     }
 
     /**
-     * Set the read timeout (in s).
+     * Set the read timeout as String in a format understood by
+     * {@link DateTimeUtils#parseDurationToMillis(String)}.
      */
-    public void setTimeoutStr(String timeoutMillis)
+    public void setTimeoutStr(String timeoutStr)
     {
-        if (isResolved(timeoutMillis))
+        if (isResolved(timeoutStr))
         {
-            this.timeout = Long.parseLong(timeoutMillis) * 1000L;
+            this.timeout = DateTimeUtils.parseDurationToMillis(timeoutStr);
         }
     }
 
@@ -364,13 +367,15 @@ public final class LDAPDirectoryConfiguration
     }
 
     /**
-     * Set the time to wait after failure before retrying (in ms).
+     * Set the time to wait after failure before retrying as a String in a format understood by
+     * {@link DateTimeUtils#parseDurationToMillis(String)}.
      */
-    public void setTimeToWaitAfterFailureStr(String timeToWaitOnFailureMillis)
+    public void setTimeToWaitAfterFailureStr(String timeToWaitOnFailureStr)
     {
-        if (isResolved(timeToWaitOnFailureMillis))
+        if (isResolved(timeToWaitOnFailureStr))
         {
-            this.timeToWaitAfterFailure = Long.parseLong(timeToWaitOnFailureMillis) * 1000L;
+            this.timeToWaitAfterFailure =
+                    DateTimeUtils.parseDurationToMillis(timeToWaitOnFailureStr);
         }
     }
 
diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceInvalidLoginTests.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceInvalidLoginTests.java
index 2cebf278273..1b6b390f709 100644
--- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceInvalidLoginTests.java
+++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceInvalidLoginTests.java
@@ -37,6 +37,7 @@ import org.testng.annotations.Test;
 import ch.systemsx.cisd.authentication.IAuthenticationService;
 import ch.systemsx.cisd.authentication.Principal;
 import ch.systemsx.cisd.common.filesystem.FileUtilities;
+import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.utilities.ITimeProvider;
 
 /**
@@ -96,6 +97,7 @@ public class CachingAuthenticationServiceInvalidLoginTests
     @BeforeClass
     public void setUp()
     {
+        LogInitializer.init();
         FileUtilities.deleteRecursively(workingDirectory);
         workingDirectory.mkdirs();
         context = new Mockery();
diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceSuccessTests.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceSuccessTests.java
index 1cbc9842f92..20ba4fccc80 100644
--- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceSuccessTests.java
+++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/CachingAuthenticationServiceSuccessTests.java
@@ -37,6 +37,7 @@ import org.testng.annotations.Test;
 import ch.systemsx.cisd.authentication.IAuthenticationService;
 import ch.systemsx.cisd.authentication.Principal;
 import ch.systemsx.cisd.common.filesystem.FileUtilities;
+import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.utilities.ITimeProvider;
 
 /**
@@ -103,6 +104,7 @@ public class CachingAuthenticationServiceSuccessTests
     @BeforeClass
     public void setUp()
     {
+        LogInitializer.init();
         FileUtilities.deleteRecursively(workingDirectory);
         workingDirectory.mkdirs();
         context = new Mockery();
diff --git a/common/source/java/genericCommonContext.xml b/common/source/java/genericCommonContext.xml
index ac6e0a8a304..f862d3cf6d8 100644
--- a/common/source/java/genericCommonContext.xml
+++ b/common/source/java/genericCommonContext.xml
@@ -76,7 +76,7 @@
         <property name="timeoutStr" value="${ldap.timeout}" />
         <property name="timeToWaitAfterFailureStr" value="${ldap.timeToWaitAfterFailure}" />
     </bean>
-
+    
     <bean id="ldap-authentication-service"
         class="ch.systemsx.cisd.authentication.ldap.LDAPAuthenticationService">
         <constructor-arg ref="ldap-directory-configuration" />
@@ -97,7 +97,8 @@
         </constructor-arg>
     </bean>
     
-    <bean id="file-ldap-authentication-service" class = "ch.systemsx.cisd.authentication.stacked.StackedAuthenticationService">
+    <bean id="file-ldap-authentication-service" 
+    			class = "ch.systemsx.cisd.authentication.stacked.StackedAuthenticationService">
         <constructor-arg>
             <list>
                 <ref bean="file-authentication-service" />
@@ -106,7 +107,8 @@
         </constructor-arg>
     </bean>
     
-    <bean id="stacked-authentication-service" class = "ch.systemsx.cisd.authentication.stacked.StackedAuthenticationService">
+    <bean id="ldap-crowd-authentication-service" 
+    			class = "ch.systemsx.cisd.authentication.stacked.StackedAuthenticationService">
         <constructor-arg>
             <list>
                 <ref bean="ldap-authentication-service" />
@@ -114,7 +116,23 @@
             </list>
         </constructor-arg>
     </bean>
+
+    <bean id="authentication-cache-configuration"
+    		class="ch.systemsx.cisd.authentication.file.CachingAuthenticationConfiguration">
+        <property name="delegate" ref="ldap-crowd-authentication-service" />
+        <property name="passwordCacheFile" value="etc/password_cache" />
+        <property name="cacheTimeStr" value="${authentication.cache.time}" />
+        <property name="cacheTimeNoRevalidationStr" value="${authentication.cache.time-no-revalidation}" />
+    </bean>
+
+    <bean id="file-ldap-crowd-caching-authentication-service" 
+    			class = "ch.systemsx.cisd.authentication.file.CachingAuthenticationService">
+        <constructor-arg ref="authentication-cache-configuration" />
+    </bean>
     
+    <!-- Keep this for backward compatibility with old service.properties files. -->
+    <alias name="ldap-crowd-authentication-service" alias="stacked-authentication-service"/>
+        
     <!-- 
         // Implementation of IRequestContextProvider
     -->
diff --git a/openbis/dist/server/service.properties b/openbis/dist/server/service.properties
index b5258b60310..bc20f392eb9 100644
--- a/openbis/dist/server/service.properties
+++ b/openbis/dist/server/service.properties
@@ -31,10 +31,20 @@ database.active-connections-log-interval =
 # 'crowd-authentication-service'
 # 'file-crowd-authentication-service'
 # 'file-ldap-authentication-service'
-# 'stacked-authentication-service' : ldap - crowd
-# For a detailed description please have a look at the Installation and Administrator
+# 'ldap-crowd-authentication-service'
+# 'file-ldap-crowd-caching-authentication-service'
+# For a detailed description, have a look at the Installation and Administrator
 # Guide of the openBIS Server: https://wiki-bsse.ethz.ch/x/oYIUBQ 
-authentication-service = file-authentication-service
+authentication-service = file-ldap-crowd-caching-authentication-service
+
+# ---------------------------------------------------------------------------
+# Caching configuration (only used with 'file-ldap-crowd-caching-authentication-service')
+# ---------------------------------------------------------------------------
+# The time that the authentication cache keeps entries. Default: 28h
+authentication.cache.time = 28h
+# The time that the authentication cache does not perform re-validation on a cache entry. 
+# Default: 1h
+authentication.cache.time-no-revalidation = 1h
 
 # ---------------------------------------------------------------------------
 # Crowd configuration
@@ -45,9 +55,9 @@ authentication-service = file-authentication-service
 crowd.service.host = 
 # The Crowd service port. Default: 443
 crowd.service.port =
-# The timeout (in s) to wait for a Crowd query to return, -1 for "wait indefinitely". Default: 10. 
+# The timeout (in s) to wait for a Crowd query to return, -1 for "wait indefinitely". Default: 10s. 
 crowd.service.timeout =
-# The Crowd application name.
+# The Crowd application name. The value 'openbis' is just a suggestion.
 # Mandatory. 
 crowd.application.name = openbis
 # The Crowd application password. 
@@ -96,10 +106,10 @@ ldap.queryEmailForAliases = true
 ldap.queryTemplate =
 # The number of times a failed LDAP query is retried at the max. Default: 1.
 ldap.maxRetries = 
-# The timeout (in s) to wait for an LDAP query to return, -1 for "wait indefinitely". Default: 10. 
+# The timeout (in s) to wait for an LDAP query to return, -1 for "wait indefinitely". Default: 10s. 
 ldap.timeout = 
-# Time time (in s) to wait after a failure before retrying the query. Default: 10. 
-ldap.timeToWaitAfterFailure=
+# The time (in s) to wait after a failure before retrying the query. Default: 10s. 
+ldap.timeToWaitAfterFailure =
 
 # ---------------------------------------------------------------------------
 # Anonymous login configuration (optional)
-- 
GitLab