You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2018/09/18 23:06:52 UTC

[sling-org-apache-sling-auth-core] branch master updated: SLING-7939 SlingAuthenticator should post an event for login failures

This is an automated email from the ASF dual-hosted git repository.

enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-core.git


The following commit(s) were added to refs/heads/master by this push:
     new c81d411  SLING-7939 SlingAuthenticator should post an event for login failures
c81d411 is described below

commit c81d411a6bbd691e1c9bad6f7581a417eee06ab3
Author: Eric Norman <en...@apache.org>
AuthorDate: Tue Sep 18 16:06:43 2018 -0700

    SLING-7939 SlingAuthenticator should post an event for login failures
---
 .../org/apache/sling/auth/core/AuthConstants.java  |  8 ++
 .../sling/auth/core/impl/SlingAuthenticator.java   | 95 ++++++++++++++++++----
 .../org/apache/sling/auth/core/package-info.java   |  4 +-
 3 files changed, 87 insertions(+), 20 deletions(-)

diff --git a/src/main/java/org/apache/sling/auth/core/AuthConstants.java b/src/main/java/org/apache/sling/auth/core/AuthConstants.java
index 43631f7..a809b26 100644
--- a/src/main/java/org/apache/sling/auth/core/AuthConstants.java
+++ b/src/main/java/org/apache/sling/auth/core/AuthConstants.java
@@ -115,6 +115,14 @@ public final class AuthConstants {
     public static final String TOPIC_LOGIN = "org/apache/sling/auth/core/Authenticator/LOGIN";
 
     /**
+     * The topic for the OSGi event which is sent when a user has logged in successfully.
+     * The event contains at least the {@link org.apache.sling.api.SlingConstants#PROPERTY_USERID}
+     * and the {@link org.apache.sling.auth.core.spi.AuthenticationInfo#AUTH_TYPE}
+     * properties.
+     */
+    public static final String TOPIC_LOGIN_FAILED = "org/apache/sling/auth/core/Authenticator/LOGIN_FAILED";
+
+    /**
      * Any OSGi service may provide a {@code sling.auth.requirements} registration property which is used
      * to dynamically extend the authentication requirements for the {@code AuthenticationSupport}.
      * This may for example be set by AuthenticationHandler implementations providing
diff --git a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
index 7a78f89..89f124c 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
@@ -30,6 +30,7 @@ import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+
 import javax.jcr.SimpleCredentials;
 import javax.security.auth.login.AccountLockedException;
 import javax.security.auth.login.AccountNotFoundException;
@@ -40,6 +41,7 @@ import javax.servlet.ServletRequestListener;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -486,6 +488,8 @@ public class SlingAuthenticator implements Authenticator,
         try {
             postProcess(authInfo, request, response);
         } catch (LoginException e) {
+        	postLoginFailedEvent(request, authInfo, e);
+        	
             handleLoginFailure(request, response, authInfo, e);
             return false;
         }
@@ -833,6 +837,7 @@ public class SlingAuthenticator implements Authenticator,
             return processRequest;
 
         } catch (LoginException re) {
+        	postLoginFailedEvent(request, authInfo, re);
 
             // handle failure feedback before proceeding to handling the
             // failed login internally
@@ -1011,26 +1016,26 @@ public class SlingAuthenticator implements Authenticator,
                 // request authentication information and send 403 (Forbidden)
                 // if no handler can request authentication information.
 
-                AuthenticationHandler.FAILURE_REASON_CODES code = AuthenticationHandler.FAILURE_REASON_CODES.INVALID_LOGIN;
-                String message = "User name and password do not match";
-
-                if (reason.getCause() instanceof CredentialExpiredException) {
-                    // force failure attribute to be set so handlers can
-                    // react to this special circumstance
-                    Object creds = authInfo.get("user.jcr.credentials");
-                    if (creds instanceof SimpleCredentials && ((SimpleCredentials) creds).getAttribute("PasswordHistoryException") != null) {
-                        code = AuthenticationHandler.FAILURE_REASON_CODES.PASSWORD_EXPIRED_AND_NEW_PASSWORD_IN_HISTORY;
-                        message = "Password expired and new password found in password history";
-                    } else {
-                        code = AuthenticationHandler.FAILURE_REASON_CODES.PASSWORD_EXPIRED;
-                        message = "Password expired";
-                    }
-                } else if (reason.getCause() instanceof AccountLockedException) {
-                    code = AuthenticationHandler.FAILURE_REASON_CODES.ACCOUNT_LOCKED;
+                AuthenticationHandler.FAILURE_REASON_CODES code = getFailureReasonFromException(authInfo, reason);
+                String message = null;
+                switch (code) {
+				case ACCOUNT_LOCKED:
                     message = "Account is locked";
-                } else if (reason.getCause() instanceof AccountNotFoundException) {
-                    code = AuthenticationHandler.FAILURE_REASON_CODES.ACCOUNT_NOT_FOUND;
+					break;
+				case ACCOUNT_NOT_FOUND:
                     message = "Account was not found";
+					break;
+				case PASSWORD_EXPIRED:
+                    message = "Password expired";
+					break;
+				case PASSWORD_EXPIRED_AND_NEW_PASSWORD_IN_HISTORY:
+                    message = "Password expired and new password found in password history";
+					break;
+				case UNKNOWN:
+				case INVALID_LOGIN:
+				default:
+					message = "User name and password do not match";
+					break;
                 }
 
                 // preset a reason for the login failure
@@ -1061,6 +1066,39 @@ public class SlingAuthenticator implements Authenticator,
     }
 
     /**
+     * Try to determine the failure reason from the thrown exception
+     */
+    private AuthenticationHandler.FAILURE_REASON_CODES getFailureReasonFromException(final AuthenticationInfo authInfo, Exception reason) {
+        AuthenticationHandler.FAILURE_REASON_CODES code = null;
+        if (reason.getClass().getName().contains("TooManySessionsException")) {
+        	// not a login failure just unavailable service
+        	code = null;
+        } else if (reason instanceof LoginException) {
+            if (reason.getCause() instanceof CredentialExpiredException) {
+                // force failure attribute to be set so handlers can
+                // react to this special circumstance
+                Object creds = authInfo.get("user.jcr.credentials");
+                if (creds instanceof SimpleCredentials && ((SimpleCredentials) creds).getAttribute("PasswordHistoryException") != null) {
+                    code = AuthenticationHandler.FAILURE_REASON_CODES.PASSWORD_EXPIRED_AND_NEW_PASSWORD_IN_HISTORY;
+                } else {
+                    code = AuthenticationHandler.FAILURE_REASON_CODES.PASSWORD_EXPIRED;
+                }
+            } else if (reason.getCause() instanceof AccountLockedException) {
+                code = AuthenticationHandler.FAILURE_REASON_CODES.ACCOUNT_LOCKED;
+            } else if (reason.getCause() instanceof AccountNotFoundException) {
+                code = AuthenticationHandler.FAILURE_REASON_CODES.ACCOUNT_NOT_FOUND;
+            }
+
+            if (code == null) {
+            	// default to invalid login as the reason
+            	code = AuthenticationHandler.FAILURE_REASON_CODES.INVALID_LOGIN;
+            }
+        }
+        
+        return code;
+    }
+    
+    /**
      * Tries to request credentials from the client. The following mechanisms
      * are implemented by this method:
      * <ul>
@@ -1491,6 +1529,27 @@ public class SlingAuthenticator implements Authenticator,
     }
 
     /**
+     * Post an event to let subscribers know that a login failure has occurred.  For examples, subscribers
+     * to the {@link AuthConstants#TOPIC_LOGIN_FAILED} event topic may be used to implement a failed login throttling solution.
+     */
+    private void postLoginFailedEvent(final HttpServletRequest request, final AuthenticationInfo authInfo, Exception reason) {
+        // The reason for the failure may be useful to downstream subscribers.
+        AuthenticationHandler.FAILURE_REASON_CODES reason_code = getFailureReasonFromException(authInfo, reason);
+        //if reason_code is null, it is problem some non-login related failure, so don't send the event
+        if (reason_code != null) {
+        	final Dictionary<String, Object> properties = new Hashtable<String, Object>();
+            properties.put(SlingConstants.PROPERTY_USERID, authInfo.getUser());
+            properties.put(AuthenticationInfo.AUTH_TYPE, authInfo.getAuthType());
+           	properties.put("reason_code", reason_code.name());
+            
+            EventAdmin localEA = this.eventAdmin;
+            if (localEA != null) {
+                localEA.postEvent(new Event(AuthConstants.TOPIC_LOGIN_FAILED, properties));
+            }
+        }
+    }
+
+    /**
      * Ensures the cookie value is properly quoted for transmission to the
      * client.
      * <p>
diff --git a/src/main/java/org/apache/sling/auth/core/package-info.java b/src/main/java/org/apache/sling/auth/core/package-info.java
index bc1ab49..66dee4f 100755
--- a/src/main/java/org/apache/sling/auth/core/package-info.java
+++ b/src/main/java/org/apache/sling/auth/core/package-info.java
@@ -22,9 +22,9 @@
  * of utility functions in the {@link org.apache.sling.auth.core.AuthUtil}
  * class.
  *
- * @version 1.3.2
+ * @version 1.4.0
  */
-@org.osgi.annotation.versioning.Version("1.3.2")
+@org.osgi.annotation.versioning.Version("1.4.0")
 package org.apache.sling.auth.core;