From a7de79150052dc08056f1b9dd0d8158ca5930deb Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Tue, 17 Feb 2015 13:25:07 +0000
Subject: [PATCH] SSDM-1532: Configurable session time out in case of no-login.
 No login text appears also in GWT client.

SVN: 33469
---
 .../authentication/DefaultSessionManager.java | 29 +++++++++++++++----
 .../DefaultSessionManagerTest.java            |  2 +-
 .../web/client/ICommonClientService.java      |  4 +--
 .../web/client/ICommonClientServiceAsync.java |  2 +-
 .../application/ui/BasicLoginCallback.java    |  8 +++--
 .../web/server/CommonClientService.java       | 10 +++----
 .../generic/server/AbstractServer.java        |  9 +++---
 .../generic/server/OpenBisSessionManager.java | 24 +++++++++++----
 .../source/java/genericApplicationContext.xml |  1 +
 9 files changed, 61 insertions(+), 28 deletions(-)

diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java b/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java
index 7b926f31b0d..55e4ba9177c 100644
--- a/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java
+++ b/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.authentication;
 
+import java.io.File;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -53,6 +54,8 @@ import ch.systemsx.cisd.common.spring.ExposablePropertyPlaceholderConfigurer;
  */
 public class DefaultSessionManager<T extends BasicSession> implements ISessionManager<T>
 {
+    public static final File NO_LOGIN_FILE = new File("./etc/nologin.html");
+    
     private static final String LOGOUT_PREFIX = "LOGOUT: ";
 
     private static final String LOGIN_PREFIX_TEMPLATE = "(%dms) LOGIN: ";
@@ -101,7 +104,7 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa
         }
 
         /**
-         * Sets the time of last activity (used to determine whether the session {@link #hasExpired()}.
+         * Sets the time of last activity (used to determine whether the session {@link #hasExpired(Long)}.
          */
         void touch()
         {
@@ -111,9 +114,11 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa
         /**
          * Returns <code>true</code> if the session has expired.
          */
-        boolean hasExpired()
+        boolean hasExpired(Long sessionExpirationTimeOrNull)
         {
-            return System.currentTimeMillis() - lastActiveTime > session.getSessionExpirationTime();
+            long sessionExpirationTime = sessionExpirationTimeOrNull == null ?
+                    session.getSessionExpirationTime() : sessionExpirationTimeOrNull;
+            return System.currentTimeMillis() - lastActiveTime > sessionExpirationTime;
         }
     }
 
@@ -134,6 +139,8 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa
     /** The time after which an inactive session will be expired (in milliseconds). */
     private final int sessionExpirationPeriodMillis;
 
+    private final int sessionExpirationPeriodMillisNoLogin;
+    
     private final boolean tryEmailAsUserName;
 
     public DefaultSessionManager(final ISessionFactory<T> sessionFactory,
@@ -142,13 +149,14 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa
             final IRemoteHostProvider remoteHostProvider, final int sessionExpirationPeriodMinutes)
     {
         this(sessionFactory, prefixGenerator, authenticationService, remoteHostProvider,
-                sessionExpirationPeriodMinutes, false);
+                sessionExpirationPeriodMinutes, 0, false);
     }
 
     public DefaultSessionManager(final ISessionFactory<T> sessionFactory,
             final ILogMessagePrefixGenerator<T> prefixGenerator,
             final IAuthenticationService authenticationService,
-            final IRemoteHostProvider remoteHostProvider, final int sessionExpirationPeriodMinutes,
+            final IRemoteHostProvider remoteHostProvider, final int sessionExpirationPeriodMinutes, 
+            final int sessionExpirationPeriodMinutesNoLogin,
             final boolean tryEmailAsUserName)
     {
 
@@ -165,6 +173,14 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa
         this.remoteHostProvider = remoteHostProvider;
         this.sessionExpirationPeriodMillis =
                 (int) (sessionExpirationPeriodMinutes * DateUtils.MILLIS_PER_MINUTE);
+        if (sessionExpirationPeriodMinutesNoLogin > 0) 
+        {
+            this.sessionExpirationPeriodMillisNoLogin 
+                = (int) (sessionExpirationPeriodMinutesNoLogin * DateUtils.MILLIS_PER_MINUTE);
+        } else
+        {
+            this.sessionExpirationPeriodMillisNoLogin = sessionExpirationPeriodMillis;
+        }
         this.tryEmailAsUserName = tryEmailAsUserName;
 
         operationLog.info(String.format("Authentication service: '%s'", authenticationService
@@ -341,7 +357,8 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa
 
     private boolean doSessionExpiration(final FullSession<T> session)
     {
-        return session != null && session.hasExpired();
+        Long expTimeOrNull = NO_LOGIN_FILE.exists() ? (long) sessionExpirationPeriodMillisNoLogin : null;
+        return session != null && session.hasExpired(expTimeOrNull);
     }
 
     private void logAuthenticed(final T session, final long timeToLoginMillis)
diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java
index 2144b4701f6..fded9c79073 100644
--- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java
+++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java
@@ -103,7 +103,7 @@ public class DefaultSessionManagerTest
     private ISessionManager<BasicSession> createSessionManager(int sessionExpiration)
     {
         return new DefaultSessionManager(sessionFactory, prefixGenerator, authenticationService,
-                remoteHostProvider, sessionExpiration, true);
+                remoteHostProvider, sessionExpiration, 0, true);
     }
 
     @AfterMethod
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
index 58d3e4accf0..98d47191a62 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
@@ -137,9 +137,9 @@ public interface ICommonClientService extends IClientService
     /**
      * Keeps the logged in user session alive.
      * 
-     * @return 'true' if session was successfully prolonged, 'false' otherwise
+     * @return <code>null</code> if session was successfully prolonged, otherwise the reason for failure.
      */
-    public Boolean keepSessionAlive() throws UserFailureException;
+    public String keepSessionAlive() throws UserFailureException;
 
     /**
      * Returns a list of all spaces.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
index 59dac0eb1ac..182d99d7c16 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
@@ -133,7 +133,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.api.IManagedUiAction;
 public interface ICommonClientServiceAsync extends IClientServiceAsync
 {
     /** @see ICommonClientService#keepSessionAlive() */
-    public void keepSessionAlive(final AsyncCallback<Boolean> asyncCallback);
+    public void keepSessionAlive(final AsyncCallback<String> asyncCallback);
 
     /** @see ICommonClientService#listSpaces(DefaultResultSetConfig) */
     public void listSpaces(DefaultResultSetConfig<String, TableModelRowWithObject<Space>> criteria,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/BasicLoginCallback.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/BasicLoginCallback.java
index e58680a5ab5..49e681dddfd 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/BasicLoginCallback.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/BasicLoginCallback.java
@@ -107,15 +107,16 @@ public class BasicLoginCallback extends AbstractAsyncCallback<SessionContext>
                 {
                     // callback will cancel keeping session alive if something went wrong
                     // or user logged out
-                    AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>()
+                    AsyncCallback<String> callback = new AsyncCallback<String>()
                         {
 
                             @Override
-                            public void onSuccess(Boolean result)
+                            public void onSuccess(String reasonOrNull)
                             {
-                                if (result == false)
+                                if (reasonOrNull != null)
                                 {
                                     cancel();
+                                    MessageBox.alert("Session Expiration", reasonOrNull, null);
                                 }
                             }
 
@@ -123,6 +124,7 @@ public class BasicLoginCallback extends AbstractAsyncCallback<SessionContext>
                             public void onFailure(Throwable caught)
                             {
                                 cancel();
+                                MessageBox.alert("Server Connection", "Connection to the server is broken.", null);
                             }
                         };
                     viewContext.getCommonService().keepSessionAlive(callback);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
index 4505790890b..961b036d23e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
@@ -2488,17 +2488,17 @@ public final class CommonClientService extends AbstractClientService implements
     // --
 
     @Override
-    public Boolean keepSessionAlive()
+    public String keepSessionAlive()
             throws ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException
     {
         try
         {
             commonServer.keepSessionAlive(getSessionToken());
-            return true;
-        } catch (final InvalidSessionException e)
+            return null;
+        } catch (final Exception e)
         {
-            // most probable cause - user logged out
-            return false;
+            String reason = getDisabledText();
+            return reason == null ? "Session expired. Please, login again." : reason;
         }
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java
index 351a12f82b3..614b133ea50 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java
@@ -16,7 +16,6 @@
 
 package ch.systemsx.cisd.openbis.generic.server;
 
-import java.io.File;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -35,6 +34,7 @@ import org.apache.commons.lang.StringUtils;
 import org.springframework.dao.DataAccessException;
 import org.springframework.transaction.annotation.Transactional;
 
+import ch.systemsx.cisd.authentication.DefaultSessionManager;
 import ch.systemsx.cisd.authentication.IPrincipalProvider;
 import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.authentication.Principal;
@@ -467,7 +467,7 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp
     @Override
     public final SessionContextDTO tryAuthenticate(final String user, final String password)
     {
-        if (tryGetDisabledText() != null)
+        if (DefaultSessionManager.NO_LOGIN_FILE.exists())
         {
             throw new UserFailureException("Login is disabled by the administrator.");
         } 
@@ -1037,12 +1037,11 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp
 
     protected static String tryGetDisabledText()
     {
-        File noLoginFile = new File("./etc/nologin.html");
-        if (noLoginFile.exists() == false)
+        if (DefaultSessionManager.NO_LOGIN_FILE.exists() == false)
         {
             return null;
         }
-        return FileUtilities.loadToString(noLoginFile).trim();
+        return FileUtilities.loadToString(DefaultSessionManager.NO_LOGIN_FILE).trim();
     }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java
index 43980d0cd26..f6c982653eb 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java
@@ -34,22 +34,36 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
  */
 public class OpenBisSessionManager extends DefaultSessionManager<Session> implements IOpenBisSessionManager
 {
+    private static final int DEFAULT_SESSION_EXPIRATION_PERIOD_FOR_NO_LOGIN = 10;
+    
+    private static final int getSessionExpirationPeriodMinutesForNoLogin(String property)
+    {
+        try
+        {
+            return Integer.parseInt(property);
+        } catch (NumberFormatException ex)
+        {
+            return DEFAULT_SESSION_EXPIRATION_PERIOD_FOR_NO_LOGIN;
+        }
+    }
+    
     IDAOFactory daoFactory;
 
     public OpenBisSessionManager(ISessionFactory<Session> sessionFactory, ILogMessagePrefixGenerator<Session> prefixGenerator,
             IAuthenticationService authenticationService, IRemoteHostProvider remoteHostProvider, int sessionExpirationPeriodMinutes,
-            boolean tryEmailAsUserName, IDAOFactory daoFactory)
+            String sessionExpirationPeriodMinutesForNoLogin, boolean tryEmailAsUserName, IDAOFactory daoFactory)
     {
-        super(sessionFactory, prefixGenerator, authenticationService, remoteHostProvider, sessionExpirationPeriodMinutes, tryEmailAsUserName);
+        super(sessionFactory, prefixGenerator, authenticationService, remoteHostProvider, sessionExpirationPeriodMinutes, 
+                getSessionExpirationPeriodMinutesForNoLogin(sessionExpirationPeriodMinutesForNoLogin), tryEmailAsUserName);
         this.daoFactory = daoFactory;
     }
 
     public OpenBisSessionManager(ISessionFactory<Session> sessionFactory, ILogMessagePrefixGenerator<Session> prefixGenerator,
             IAuthenticationService authenticationService, IRemoteHostProvider remoteHostProvider, int sessionExpirationPeriodMinutes,
-            IDAOFactory daoFactory)
+            String sessionExpirationPeriodMinutesForNoLogin, IDAOFactory daoFactory)
     {
-        super(sessionFactory, prefixGenerator, authenticationService, remoteHostProvider, sessionExpirationPeriodMinutes);
-        this.daoFactory = daoFactory;
+        this(sessionFactory, prefixGenerator, authenticationService, remoteHostProvider, sessionExpirationPeriodMinutes,
+                sessionExpirationPeriodMinutesForNoLogin, false, daoFactory);
     }
 
     @Override
diff --git a/openbis/source/java/genericApplicationContext.xml b/openbis/source/java/genericApplicationContext.xml
index 0af786405f4..ad181ae6fb3 100644
--- a/openbis/source/java/genericApplicationContext.xml
+++ b/openbis/source/java/genericApplicationContext.xml
@@ -125,6 +125,7 @@
         </constructor-arg>
         <!-- The time after which an inactive session is expired by the service (in minutes). -->
         <constructor-arg value="${session-timeout}" />
+        <constructor-arg value="${session-timeout-no-login}" />
         <!-- Enable login with email addresses -->
         <constructor-arg value="true" />
         <constructor-arg ref="dao-factory"/>
-- 
GitLab