svn commit: r1243917 - in /cocoon/cocoon3/trunk: cocoon-shiro-sample/src/main/java/org/apache/cocoon/shiro/sample/rest/LoginUser.java cocoon-shiro/src/main/java/org/apache/cocoon/shiro/rest/AbstractShiroLogin.java

[email protected]
Newsgroups gmane.text.xml.cocoon.cvs
Message-ID <[email protected]>
Author: thorsten
Date: Tue Feb 14 13:44:08 2012
New Revision: 1243917

URL: http://svn.apache.org/viewvc?rev=1243917&view=rev
Log:
COCOON3-89
Reporter:
    Ajay Deshwal 
Add feature to limit invalid login attempts

Thanks Ajay for your patch

Modified:
    cocoon/cocoon3/trunk/cocoon-shiro-sample/src/main/java/org/apache/cocoon/shiro/sample/rest/LoginUser.java
    cocoon/cocoon3/trunk/cocoon-shiro/src/main/java/org/apache/cocoon/shiro/rest/AbstractShiroLogin.java

Modified: cocoon/cocoon3/trunk/cocoon-shiro-sample/src/main/java/org/apache/cocoon/shiro/sample/rest/LoginUser.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-shiro-sample/src/main/java/org/apache/cocoon/shiro/sample/rest/LoginUser.java?rev=1243917&r1=1243916&r2=1243917&view=diff
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-shiro-sample/src/main/java/org/apache/cocoon/shiro/sample/rest/LoginUser.java (original)
+++ cocoon/cocoon3/trunk/cocoon-shiro-sample/src/main/java/org/apache/cocoon/shiro/sample/rest/LoginUser.java Tue Feb 14 13:44:08 2012
@@ -18,8 +18,12 @@
  */
 package org.apache.cocoon.shiro.sample.rest;
 
+import java.net.MalformedURLException;
+import java.util.Map;
 
 import org.apache.cocoon.rest.controller.annotation.RESTController;
+import org.apache.cocoon.rest.controller.response.RestResponse;
+import org.apache.cocoon.rest.controller.response.URLResponse;
 import org.apache.cocoon.shiro.rest.AbstractShiroLogin;
 
 @RESTController
@@ -28,8 +32,9 @@ public class LoginUser extends AbstractS
 
     private static final String LOGIN_PAGE = "servlet:/screen/login";
 
-    protected String getLoginPage() {
-        return LOGIN_PAGE;
+    protected RestResponse getLoginPage(Map<String, Object> data)
+            throws MalformedURLException {
+        return new URLResponse(LOGIN_PAGE, data);
     }
 
     protected String getDefaultTo() {
@@ -37,7 +42,7 @@ public class LoginUser extends AbstractS
     }
 
     protected String getErrorLogin() {
-        return getLoginPage();
+        return LOGIN_PAGE;
     }
 
 }

Modified: cocoon/cocoon3/trunk/cocoon-shiro/src/main/java/org/apache/cocoon/shiro/rest/AbstractShiroLogin.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-shiro/src/main/java/org/apache/cocoon/shiro/rest/AbstractShiroLogin.java?rev=1243917&r1=1243916&r2=1243917&view=diff
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-shiro/src/main/java/org/apache/cocoon/shiro/rest/AbstractShiroLogin.java (original)
+++ cocoon/cocoon3/trunk/cocoon-shiro/src/main/java/org/apache/cocoon/shiro/rest/AbstractShiroLogin.java Tue Feb 14 13:44:08 2012
@@ -31,6 +31,8 @@ import org.apache.cocoon.rest.controller
 import org.apache.cocoon.rest.controller.response.URLResponse;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.ExcessiveAttemptsException;
 import org.apache.shiro.authc.IncorrectCredentialsException;
 import org.apache.shiro.authc.UnknownAccountException;
 import org.apache.shiro.authc.UsernamePasswordToken;
@@ -42,12 +44,14 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @RESTController
-public abstract class AbstractShiroLogin implements Post, Get{
-    
-    protected abstract String getErrorLogin() ;
-    protected abstract String getDefaultTo();
-    protected abstract String getLoginPage() ;
-    
+public abstract class AbstractShiroLogin implements Post, Get {
+
+    protected final String LOGIN_ATTEMPTS_EXCEEDED = "shiroLoginAttemptExceeded";
+
+    protected final String LOGIN_ATTEMPTS_COUNTER = "shiroLoginAttemptCounter";
+
+    private Integer allowedAttempts = 5;
+
     @RequestParameter
     private String username;
     @RequestParameter
@@ -56,7 +60,16 @@ public abstract class AbstractShiroLogin
     private boolean rememberMe;
     @RequestParameter
     protected String to;
-    protected static final Logger LOG = LoggerFactory.getLogger(AbstractShiroLogin.class);
+
+    protected static final Logger LOG = LoggerFactory
+            .getLogger(AbstractShiroLogin.class);
+
+    protected abstract String getErrorLogin();
+
+    protected abstract String getDefaultTo();
+
+    protected abstract RestResponse getLoginPage(Map<String, Object> data)
+            throws Exception;
 
     public RestResponse doPost() throws Exception {
         // create a UsernamePasswordToken using the
@@ -65,15 +78,35 @@ public abstract class AbstractShiroLogin
                 password, rememberMe);
         Subject subject = SecurityUtils.getSubject();
         boolean error = true;
+        Boolean attemptsExceeded = (Boolean) subject.getSession().getAttribute(
+                LOGIN_ATTEMPTS_EXCEEDED);
+        if (attemptsExceeded == null) {
+            attemptsExceeded = false;
+        }
+        boolean invalidCredentials = false;
+        Map<String, Object> errorData = null;
         try {
-            subject.login(token);
-            error = false;
+            errorData = validatePreLogin();
+            if (errorData == null || errorData.isEmpty()) {
+                subject.login(token);
+                error = false;
+                subject.getSession().removeAttribute(LOGIN_ATTEMPTS_EXCEEDED);
+                subject.getSession().removeAttribute(LOGIN_ATTEMPTS_COUNTER);
+            }
+        } catch (ExcessiveAttemptsException eae) {
+            LOG.error(eae.getMessage());
+            attemptsExceeded = true;
         } catch (UnknownAccountException ex) {
+            invalidCredentials = true;
             LOG.error("UnknownAccountException", ex);
         } catch (IncorrectCredentialsException ex) {
+            invalidCredentials = true;
             // password provided did not match password found in database
             // for the username provided
             LOG.error("IncorrectCredentialsException", ex);
+        } catch (AuthenticationException authEx) {
+            invalidCredentials = true;
+            LOG.error("AuthenticationException", authEx);
         } catch (Exception e) {
             LOG.error("Exception", e);
         } finally {
@@ -81,21 +114,73 @@ public abstract class AbstractShiroLogin
         }
         // clear the information stored in the token
         if (error) {
-            Map<String, Object> data = new HashMap<String, Object>();
-            data.put("error", true);
-            data.put("to", getTo());
-            return getErrorResponse(data);
+            if (invalidCredentials && !attemptsExceeded) {
+                attemptsExceeded = isAttemptsExceeded(subject);
+            }
+            subject.getSession().setAttribute(LOGIN_ATTEMPTS_EXCEEDED,
+                    attemptsExceeded);
+            if (errorData == null) {
+                errorData = new HashMap<String, Object>();
+            }
+            errorData.put("error", true);
+            errorData.put("to", getTo());
+            errorData.put("loginAttemptExceeded", attemptsExceeded);
+            return getErrorResponse(errorData);
         } else {
             return getSuccessResponse();
         }
     }
 
-    protected RestResponse getSuccessResponse(){
-      return new RedirectResponse(getTo());
+    /**
+     * Checks if login attempts exceeded allowed wrong attempts.
+     * 
+     * @param subject
+     *            the shiro subject
+     * @return true, if login attempts exceeded
+     */
+    private boolean isAttemptsExceeded(Subject subject) {
+        Integer attemptCount = (Integer) subject.getSession().getAttribute(
+                LOGIN_ATTEMPTS_COUNTER);
+        if (attemptCount == null) {
+            attemptCount = 0;
+        }
+        if (attemptCount >= getAllowedWrongAttempts()) {
+            return true;
+        } else {
+            attemptCount++;
+        }
+        subject.getSession().setAttribute(LOGIN_ATTEMPTS_COUNTER, attemptCount);
+        return false;
+    }
+
+    /**
+     * Gets the number of maximum allowed wrong login attempts.
+     * 
+     * @return the allowed wrong attempts count
+     */
+    private Integer getAllowedWrongAttempts() {
+        return allowedAttempts;
+    }
+
+    /**
+     * A validation method that is invoked before initiating login. If this
+     * method returns a non-empty map, then login is skipped and map data is
+     * added to UrlResponse. It can be overridden in extending classes to
+     * perform validations before login like captcha.
+     * 
+     * @return the data map that will be passed to error URL Rest response
+     */
+    protected Map<String, Object> validatePreLogin() {
+        return null;
+    }
+
+    protected RestResponse getSuccessResponse() {
+        return new RedirectResponse(getTo());
     }
 
-    protected RestResponse getErrorResponse(Map<String, Object> data) throws MalformedURLException{
-       return new URLResponse(getErrorLogin(), data);
+    protected RestResponse getErrorResponse(Map<String, Object> data)
+            throws MalformedURLException {
+        return new URLResponse(getErrorLogin(), data);
     }
 
     public RestResponse doGet() throws Exception {
@@ -111,7 +196,9 @@ public abstract class AbstractShiroLogin
         Map<String, Object> data = new HashMap<String, Object>();
         data.put("to", getTo());
         data.put("error", false);
-        return new URLResponse(getLoginPage(), data);
+        data.put("loginAttemptExceeded",
+                subject.getSession().getAttribute(LOGIN_ATTEMPTS_EXCEEDED));
+        return getLoginPage(data);
     }
 
     protected String getTo() {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.