← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 1714: STQC security fixes: Adds 10min lockout on 5 incorrect logins. Will add a UI and captcha

 

------------------------------------------------------------
revno: 1714
committer: Saptarshi <sunbiz@xxxxxxxxx>
branch nick: trunk
timestamp: Wed 2010-03-31 01:08:42 +0200
message:
  STQC security fixes: Adds 10min lockout on 5 incorrect logins. Will add a UI and captcha
removed:
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.html
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.html
added:
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.jsp
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.jsp
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditStore.java
  dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/DefaultUserAuditService.java
  dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/hibernate/HibernateUserAuditStore.java
  dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription.
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditService.java	2010-03-08 07:37:39 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditService.java	2010-03-30 23:08:42 +0000
@@ -32,12 +32,20 @@
  */
 public interface UserAuditService
 {
-    final int TIMEFRAME_NUMBER_OF_HOURS = 1;
-    final int MAX_NUMBER_OF_ATTEMPTS = 3;
+    final int TIMEFRAME_MINUTES = 10; //TODO: through System Settings
+    final int MAX_NUMBER_OF_ATTEMPTS = 5; //TODO: through System Settings
     
     void registerLoginSuccess( String username );
     
     void registerLogout( String username );
     
     void registerLoginFailure( String username );
+
+    void resetLockoutTimeframe( String username );
+
+    int getLoginFailures( String username );
+
+    int getMaxAttempts();
+
+    int getLockoutTimeframe();
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditStore.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditStore.java	2010-03-08 07:37:39 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/useraudit/UserAuditStore.java	2010-03-30 23:08:42 +0000
@@ -43,5 +43,7 @@
     
     void deleteLoginFailures( String username );
     
+    void resetLoginFailures( String username, Date date );
+    
     int getLoginFailures( String username, Date date );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/DefaultUserAuditService.java'
--- dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/DefaultUserAuditService.java	2010-03-08 07:37:39 +0000
+++ dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/DefaultUserAuditService.java	2010-03-30 23:08:42 +0000
@@ -40,48 +40,76 @@
 public class DefaultUserAuditService
     implements UserAuditService
 {
+
     private static final Log log = LogFactory.getLog( DefaultUserAuditService.class );
-    
+
     private UserAuditStore userAuditStore;
-    
+
     public void setUserAuditStore( UserAuditStore userAuditStore )
     {
         this.userAuditStore = userAuditStore;
     }
 
+    @Override
     public void registerLoginSuccess( String username )
     {
-        log.info( "User login success: '" + username + "'" );        
+        log.info( "User login success: '" + username + "'" );
+
+        resetLockoutTimeframe( username );
     }
 
+    @Override
     public void registerLogout( String username )
     {
         log.info( "User logout: '" + username + "'" );
     }
 
     @Transactional
+    @Override
     public void registerLoginFailure( String username )
     {
         log.info( "User login failure: '" + username + "'" );
-        
+
         userAuditStore.saveLoginFailure( new LoginFailure( username, new Date() ) );
-        
+
         int no = userAuditStore.getLoginFailures( username, getDate() );
-        
+
         if ( no >= MAX_NUMBER_OF_ATTEMPTS )
         {
             log.info( "Max number of login attempts exceeded: '" + username + "'" );
-            
-            userAuditStore.deleteLoginFailures( username );
         }
     }
-    
+
     private Date getDate()
     {
-        Calendar cal = Calendar.getInstance();        
-        cal.clear();
-        cal.add( Calendar.HOUR, TIMEFRAME_NUMBER_OF_HOURS * -1 );
-        
+        Calendar cal = Calendar.getInstance();
+        cal.add( Calendar.MINUTE, TIMEFRAME_MINUTES * -1 );
         return cal.getTime();
     }
+
+    @Transactional
+    @Override
+    public int getLoginFailures( String username )
+    {
+        int no = userAuditStore.getLoginFailures( username, getDate() );
+        return no;
+    }
+
+    @Override
+    public int getMaxAttempts()
+    {
+        return MAX_NUMBER_OF_ATTEMPTS;
+    }
+
+    @Override
+    public int getLockoutTimeframe()
+    {
+        return TIMEFRAME_MINUTES;
+    }
+
+    @Override
+    public void resetLockoutTimeframe( String username )
+    {
+        userAuditStore.resetLoginFailures( username, getDate() );
+    }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/hibernate/HibernateUserAuditStore.java'
--- dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/hibernate/HibernateUserAuditStore.java	2010-03-08 07:37:39 +0000
+++ dhis-2/dhis-services/dhis-service-administration/src/main/java/org/hisp/dhis/useraudit/hibernate/HibernateUserAuditStore.java	2010-03-30 23:08:42 +0000
@@ -29,6 +29,7 @@
 
 import java.util.Collection;
 import java.util.Date;
+import org.hibernate.Query;
 
 import org.hibernate.Session;
 import org.hibernate.SessionFactory;
@@ -41,43 +42,67 @@
 public class HibernateUserAuditStore
     implements UserAuditStore
 {
+
     private SessionFactory sessionFactory;
-    
+
     public void setSessionFactory( SessionFactory sessionFactory )
     {
         this.sessionFactory = sessionFactory;
     }
 
+    @Override
     public void saveLoginFailure( LoginFailure login )
     {
         sessionFactory.getCurrentSession().save( login );
     }
-    
-    @SuppressWarnings( "unchecked" )
+
+    @SuppressWarnings("unchecked")
+    @Override
     public Collection<LoginFailure> getAllLoginFailures()
     {
         return sessionFactory.getCurrentSession().createCriteria( LoginFailure.class ).list();
     }
-    
+
+    @Override
     public void deleteLoginFailures( String username )
     {
         String hql = "delete from LoginFailure where username = :username";
-        
+
         sessionFactory.getCurrentSession().createQuery( hql ).setString( "username", username ).executeUpdate();
     }
-        
+
+    @Override
     public int getLoginFailures( String username, Date date )
     {
         Session session = sessionFactory.getCurrentSession();
-        
-        String hql = "delete from LoginFailure where date < :date";
-        
-        session.createQuery( hql ).setDate( "date", date ).executeUpdate();
-        
-        hql = "select count(*) from LoginFailure where username = :username";
-        
-        Long no = (Long) session.createQuery( hql ).setString( "username", username ).uniqueResult();
-        
+
+        String hql = "select count(*) from LoginFailure where username = :username and date > :date";
+
+        Query q = session.createQuery( hql );
+
+        q.setString( "username", username );
+
+        q.setTimestamp( "date", date );
+
+        Long no = (Long) q.list().get( 0 );
+
         return no.intValue();
     }
+
+    //TODO: create GUI for reset and accurate logging
+    @Override
+    public void resetLoginFailures( String username, Date date )
+    {
+        Session session = sessionFactory.getCurrentSession();
+
+        String hql = "delete from LoginFailure where username = :username and date > :date";
+
+        Query q = session.createQuery( hql );
+
+        q.setString( "username", username );
+
+        q.setTimestamp( "date", date );
+
+        q.executeUpdate();
+    }
 }

=== removed file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.html'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.html	2010-03-12 16:38:18 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.html	1970-01-01 00:00:00 +0000
@@ -1,39 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd";>
-<html>
-    <head>
-        <title>DHIS 2</title>
-        <script type="text/javascript" src="../util/jquery.js"></script>
-        <script type="text/javascript">
-            $(document).ready(function() {
-                $('#j_username').focus();
-            });
-        </script>
-        <link type="text/css" rel="stylesheet" media="screen" href="../css/login.css">
-        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    </head>
-    <body>
-        <div class="loginField" align="center">
-            <p><img alt="" src="logo_banner.png"></p>
-            <form action="../../dhis-web-commons-security/login.action" method="post">
-                <table>
-                    <tr>
-                        <td colspan="2" style="height:40px"></td>
-                    </tr>
-                    <tr>
-                        <td><label for="j_username">Username</label></td>
-                        <td><input type="text" id="j_username" name="j_username" style="width:18em" autocomplete="off"></td>
-                    </tr>
-                    <tr>
-                        <td><label for="j_password">Password</label></td>
-                        <td><input type="password" id="j_password" name="j_password" style="width:18em" autocomplete="off"></td>
-                    </tr>
-                    <tr>
-                        <td></td>
-                        <td><input type="submit" value="Login" style="width:9em">
-                            <input type="reset" value="Clear" style="width:9em"></td>
-                    </tr>
-                </table>
-            </form>
-        </div>
-    </body>
-</html>

=== added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.jsp'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.jsp	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/login.jsp	2010-03-30 23:08:42 +0000
@@ -0,0 +1,59 @@
+<jsp:useBean id="userAuditService" type="org.hisp.dhis.useraudit.UserAuditService" scope="application" />
+<jsp:useBean id="userAuditStore" type="org.hisp.dhis.useraudit.UserAuditStore" scope="application" />
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd";>
+<html>
+    <head>
+        <title>DHIS 2</title>
+        <script type="text/javascript" src="../util/jquery.js"></script>
+        <script type="text/javascript">
+            $(document).ready(function() {
+                $('#j_username').focus();
+            });
+        </script>
+        <link type="text/css" rel="stylesheet" media="screen" href="../css/login.css">
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    </head>
+    <body>
+        <div class="loginField" align="center">
+            <p><img alt="" src="logo_banner.png"></p>
+                <%
+                    Object obj = session.getAttribute( "SPRING_SECURITY_LAST_USERNAME" );
+                    boolean formVisible = true;
+                    if( obj != null )
+                    {
+                        String username = obj.toString();
+                        if( userAuditService.getLoginFailures(username) >= userAuditService.getMaxAttempts() )
+                        {
+                            formVisible = false;
+                %>
+            <span class="loginMessage">Maximum Tries exceeded... Please try after <%=userAuditService.getLockoutTimeframe() %> mins</span>
+            <%
+                        }
+                    }
+            %>
+            <% if(formVisible){%>
+            <form action="../../dhis-web-commons-security/login.action" method="post">
+                <table>
+                    <tr>
+                        <td colspan="2" style="height:40px"></td>
+                    </tr>
+                    <tr>
+                        <td><label for="j_username">Username</label></td>
+                        <td><input type="text" id="j_username" name="j_username" style="width:18em" autocomplete="off"></td>
+                    </tr>
+                    <tr>
+                        <td><label for="j_password">Password</label></td>
+                        <td><input type="password" id="j_password" name="j_password" style="width:18em" autocomplete="off"></td>
+                    </tr>
+                    <tr>
+                        <td></td>
+                        <td><input type="submit" value="Login" style="width:9em">
+                            <input type="reset" value="Clear" style="width:9em"></td>
+                    </tr>
+                </table>
+            </form>
+            <% } %>
+        </div>
+    </body>
+</html>

=== removed file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.html'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.html	2009-12-10 22:00:36 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.html	1970-01-01 00:00:00 +0000
@@ -1,42 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd";>
-<html>
-    <head>
-        <title>DHIS 2</title>
-        <script type="text/javascript" src="../util/jquery.js"></script>
-        <script type="text/javascript">
-            $(document).ready(function() {
-                $('#j_username').focus();
-            });
-        </script>
-        <link type="text/css" rel="stylesheet" media="screen" href="../css/login.css">
-        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    </head>
-    <body>
-        <div class="loginField" align="center">
-            <p><img alt="" src="logo_banner.png"></p>
-            <form action="../../dhis-web-commons-security/login.action" method="post">
-                <table>
-                    <tr>
-                        <td colspan="2" style="height:40px"></td>
-                    </tr>
-                    <tr>
-                        <td><label for="j_username">Username</label></td>
-                        <td><input type="text" id="j_username" name="j_username" style="width:18em"></td>
-                    </tr>
-                    <tr>
-                        <td><label for="j_password">Password</label></td>
-                        <td><input type="password" id="j_password" name="j_password" style="width:18em"></td>
-                    </tr>
-                    <tr>
-                        <td></td>
-                        <td><input type="submit" value="Login" style="width:9em">
-                            <input type="reset" value="Clear" style="width:9em"></td>
-                    </tr>
-                    <tr>
-                        <td colspan="2" style="height:40px">
-                    </tr>
-                </table>
-            </form>
-            <span class="loginMessage">Wrong username or password. Please try again.</span> </div>
-    </body>
-</html>

=== added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.jsp'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.jsp	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/security/loginfailed.jsp	2010-03-30 23:08:42 +0000
@@ -0,0 +1,63 @@
+<jsp:useBean id="userAuditService" type="org.hisp.dhis.useraudit.UserAuditService" scope="application" />
+<jsp:useBean id="userAuditStore" type="org.hisp.dhis.useraudit.UserAuditStore" scope="application" />
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd";>
+<html>
+    <head>
+        <title>DHIS 2</title>
+        <script type="text/javascript" src="../util/jquery.js"></script>
+        <script type="text/javascript">
+            $(document).ready(function() {
+                $('#j_username').focus();
+            });
+        </script>
+        <link type="text/css" rel="stylesheet" media="screen" href="../css/login.css">
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    </head>
+    <body>
+        <div class="loginField" align="center">
+            <p><img alt="" src="logo_banner.png"></p>
+                <%
+                    Object obj = session.getAttribute( "SPRING_SECURITY_LAST_USERNAME" );
+                    boolean formVisible = true;
+                    if( obj != null )
+                    {
+                        String username = obj.toString();
+                        if( userAuditService.getLoginFailures(username) >= userAuditService.getMaxAttempts() )
+                        {
+                            formVisible = false;
+                %>
+            <span class="loginMessage">Maximum Tries exceeded... Please try after <%=userAuditService.getLockoutTimeframe() %> mins</span>
+            <%
+                        }
+                    }
+            %>
+            <% if( formVisible ){%>
+            <form action="../../dhis-web-commons-security/login.action" method="post">
+                <table>
+                    <tr>
+                        <td colspan="2" style="height:40px"></td>
+                    </tr>
+                    <tr>
+                        <td><label for="j_username">Username</label></td>
+                        <td><input type="text" id="j_username" name="j_username" style="width:18em" autocomplete="off"></td>
+                    </tr>
+                    <tr>
+                        <td><label for="j_password">Password</label></td>
+                        <td><input type="password" id="j_password" name="j_password" style="width:18em" autocomplete="off"></td>
+                    </tr>
+                    <tr>
+                        <td></td>
+                        <td><input type="submit" value="Login" style="width:9em">
+                            <input type="reset" value="Clear" style="width:9em"></td>
+                    </tr>
+                    <tr>
+                        <td colspan="2" style="height:40px">
+                    </tr>
+                </table>
+            </form>
+            <span class="loginMessage">Wrong username or password. Please try again.</span>
+            <% } %>
+        </div>
+    </body>
+</html>

=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml	2010-03-25 04:15:30 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml	2010-03-30 23:08:42 +0000
@@ -278,7 +278,7 @@
 	<bean id="authenticationProcessingFilter"
 		class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter">
 		<property name="authenticationManager" ref="authenticationManager" />
-		<property name="authenticationFailureUrl" value="/dhis-web-commons/security/loginfailed.html" />
+		<property name="authenticationFailureUrl" value="/dhis-web-commons/security/loginfailed.jsp" />
 		<property name="defaultTargetUrl" value="/dhis-web-commons-security/loggedIn.action" />
 		<property name="filterProcessesUrl" value="/dhis-web-commons-security/login.action" />
 		<property name="alwaysUseDefaultTargetUrl" value="true" />
@@ -296,7 +296,7 @@
 	</bean>
 
 	<bean id="userAuditLogoutFilter" class="org.hisp.dhis.security.filter.UserAuditLogoutFilter">
-		<property name="userAuditService" ref="org.hisp.dhis.useraudit.UserAuditService"/>
+		<property name="userAuditService" ref="org.hisp.dhis.useraudit.UserAuditService"/>
 	</bean>
 
 	<bean id="automaticAccessFilter" class="org.hisp.dhis.security.filter.AutomaticAccessFilter">
@@ -314,7 +314,20 @@
 
     <bean id="requiredLoginFilter" class="org.hisp.dhis.security.filter.RequiredLoginFilter">
         <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
-        <property name="loginPageUrl" value="/dhis-web-commons/security/login.html" />
+        <property name="loginPageUrl" value="/dhis-web-commons/security/login.jsp" />
+    </bean>
+
+    <bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
+        <property name="attributes">
+            <map>
+                <entry key="userAuditService">
+                    <ref bean="org.hisp.dhis.useraudit.UserAuditService"/>
+                </entry>
+                <entry key="userAuditStore">
+                    <ref bean="org.hisp.dhis.useraudit.UserAuditStore"/>
+                </entry>
+            </map>
+        </property>
     </bean>
 
     <!-- Security : Listener -->