ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rle...@apache.org
Subject [4/4] ambari git commit: AMBARI-21216. Add support for consecutive login failure accounting (rlevas)
Date Thu, 22 Jun 2017 17:30:09 GMT
AMBARI-21216. Add support for consecutive login failure accounting (rlevas)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/f760516c
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/f760516c
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/f760516c

Branch: refs/heads/branch-feature-AMBARI-20859
Commit: f760516c24478b19d4e579cb67702d9d43251eaa
Parents: ac5008d
Author: Robert Levas <rlevas@hortonworks.com>
Authored: Thu Jun 22 13:29:51 2017 -0400
Committer: Robert Levas <rlevas@hortonworks.com>
Committed: Thu Jun 22 13:29:51 2017 -0400

----------------------------------------------------------------------
 .../server/audit/event/LoginAuditEvent.java     |  20 ++
 .../ambari/server/controller/AmbariServer.java  |   2 +
 .../ambari/server/orm/entities/UserEntity.java  |  32 ++++
 .../AmbariAuthenticationEventHandler.java       |  64 +++++++
 .../AmbariAuthenticationEventHandlerImpl.java   | 152 +++++++++++++++
 .../AmbariAuthenticationException.java          |  43 +++++
 .../AmbariBasicAuthenticationFilter.java        |  92 ++++------
 .../AmbariJWTAuthenticationFilter.java          |  71 +++----
 ...lidUsernamePasswordCombinationException.java |  32 ++++
 .../authentication/UserNotFoundException.java   |  15 +-
 .../AmbariKerberosAuthenticationFilter.java     |  70 +++----
 .../AmbariLdapAuthenticationProvider.java       |   7 +-
 .../AmbariLdapAuthoritiesPopulator.java         |   3 +-
 .../authorization/AmbariLocalUserProvider.java  |   9 +-
 ...lidUsernamePasswordCombinationException.java |  34 ----
 .../server/security/authorization/Users.java    | 167 ++++++++++++++++-
 .../AmbariInternalAuthenticationProvider.java   |   2 +-
 .../jwt/JwtAuthenticationFilter.java            |   2 +-
 .../main/resources/Ambari-DDL-Derby-CREATE.sql  |   1 +
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |   1 +
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |   1 +
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |   1 +
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql |   1 +
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |   1 +
 .../webapp/WEB-INF/spring-security.xml          |   9 +-
 .../server/audit/LoginAuditEventTest.java       |  36 +++-
 .../AmbariBasicAuthenticationFilterTest.java    | 136 ++++++++------
 .../AmbariJWTAuthenticationFilterTest.java      | 160 +++++++++-------
 .../AmbariKerberosAuthenticationFilterTest.java | 183 ++++++++++++++-----
 ...ariAuthorizationProviderDisableUserTest.java |   1 +
 ...uthenticationProviderForDNWithSpaceTest.java |   1 +
 .../AmbariLdapAuthenticationProviderTest.java   |   1 +
 .../AmbariLocalUserProviderTest.java            |   1 +
 33 files changed, 977 insertions(+), 374 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/audit/event/LoginAuditEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/LoginAuditEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/LoginAuditEvent.java
index 9583b84..9be216a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/LoginAuditEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/LoginAuditEvent.java
@@ -51,6 +51,11 @@ public class LoginAuditEvent extends AbstractUserAuditEvent {
     private String reasonOfFailure;
 
     /**
+     * Number of consecutive failed authentication attempts since the last successful attempt
+     */
+    private Integer consecutiveFailures;
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -73,6 +78,9 @@ public class LoginAuditEvent extends AbstractUserAuditEvent {
       if (reasonOfFailure != null) {
         builder.append("), Reason(")
           .append(reasonOfFailure);
+
+        builder.append("), Consecutive failures(")
+            .append((consecutiveFailures == null) ? "UNKNOWN USER" : String.valueOf(consecutiveFailures));
       }
       builder.append(")");
     }
@@ -95,6 +103,18 @@ public class LoginAuditEvent extends AbstractUserAuditEvent {
     }
 
     /**
+     * Set the number of consecutive authentication failures since the last successful authentication
+     * attempt
+     *
+     * @param consecutiveFailures the number of consecutive authentication failures
+     * @return this builder
+     */
+    public LoginAuditEventBuilder withConsecutiveFailures(Integer consecutiveFailures) {
+      this.consecutiveFailures = consecutiveFailures;
+      return this;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index 01920f8..8173655 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -98,6 +98,7 @@ import org.apache.ambari.server.security.AmbariServerSecurityHeaderFilter;
 import org.apache.ambari.server.security.AmbariViewsSecurityHeaderFilter;
 import org.apache.ambari.server.security.CertificateManager;
 import org.apache.ambari.server.security.SecurityFilter;
+import org.apache.ambari.server.security.authentication.AmbariAuthenticationEventHandlerImpl;
 import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider;
 import org.apache.ambari.server.security.authorization.AmbariLocalUserProvider;
 import org.apache.ambari.server.security.authorization.AmbariPamAuthenticationProvider;
@@ -327,6 +328,7 @@ public class AmbariServer {
 
       factory.registerSingleton("guiceInjector", injector);
       factory.registerSingleton("ambariConfiguration", injector.getInstance(Configuration.class));
+      factory.registerSingleton("ambariAuthenticationEventHandler", injector.getInstance(AmbariAuthenticationEventHandlerImpl.class));
       factory.registerSingleton("ambariUsers", injector.getInstance(Users.class));
       factory.registerSingleton("passwordEncoder",
         injector.getInstance(PasswordEncoder.class));

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
index 66e9003..c679fff 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UserEntity.java
@@ -42,6 +42,7 @@ import javax.persistence.TableGenerator;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
 
 import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
@@ -85,6 +86,10 @@ public class UserEntity {
   @Column(name = "local_username")
   private String localUsername;
 
+  @Version
+  @Column(name = "version")
+  private Long version;
+
   @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
   private Set<MemberEntity> memberEntities = new HashSet<>();
 
@@ -214,6 +219,31 @@ public class UserEntity {
     this.createTime = createTime;
   }
 
+  /**
+   * Returns the version number of the relevant data stored in the database.
+   * <p>
+   * This is used to help ensure that collisions updatin the relevant data in the database are
+   * handled properly via Optimistic locking.
+   *
+   * @return a version number
+   */
+  public Long getVersion() {
+    return version;
+  }
+
+  /**
+   * Sets the version number of the relevant data stored in the database.
+   * <p>
+   * This is used to help ensure that collisions updatin the relevant data in the database are
+   * handled properly via Optimistic locking.  It is recommended that this value is <b>not</b>
+   * manually updated, else issues may occur when persisting the data.
+   *
+   * @param version a version number
+   */
+  public void setVersion(Long version) {
+    this.version = version;
+  }
+
   public Set<MemberEntity> getMemberEntities() {
     return memberEntities;
   }
@@ -297,6 +327,7 @@ public class UserEntity {
       equalsBuilder.append(consecutiveFailures, that.consecutiveFailures);
       equalsBuilder.append(active, that.active);
       equalsBuilder.append(createTime, that.createTime);
+      equalsBuilder.append(version, that.version);
       return equalsBuilder.isEquals();
     }
   }
@@ -311,6 +342,7 @@ public class UserEntity {
     hashCodeBuilder.append(consecutiveFailures);
     hashCodeBuilder.append(active);
     hashCodeBuilder.append(createTime);
+    hashCodeBuilder.append(version);
     return hashCodeBuilder.toHashCode();
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandler.java
new file mode 100644
index 0000000..037fc13
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandler.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.security.authentication;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+
+/**
+ * AmbariAuthenticationEventHandler is an interface to be implemented by classes used to track Ambari
+ * user authentication attempts.
+ */
+public interface AmbariAuthenticationEventHandler {
+  /**
+   * The event callback called when a successful authentication attempt has occurred.
+   *
+   * @param filter          the Authentication filer used for authentication
+   * @param servletRequest  the request
+   * @param servletResponse the response
+   * @param result          the authentication result
+   */
+  void onSuccessfulAuthentication(AmbariAuthenticationFilter filter, HttpServletRequest servletRequest,
+                                  HttpServletResponse servletResponse, Authentication result);
+
+  /**
+   * The event callback called when a failed authentication attempt has occurred.
+   *
+   * @param filter          the Authentication filer used for authentication
+   * @param servletRequest  the request
+   * @param servletResponse the response
+   * @param cause           the exception used to declare the cause for the failure
+   */
+  void onUnsuccessfulAuthentication(AmbariAuthenticationFilter filter, HttpServletRequest servletRequest,
+                                    HttpServletResponse servletResponse, AmbariAuthenticationException cause);
+
+  /**
+   * The event callback called just before an authentication attempt.
+   *
+   * @param filter          the Authentication filer used for authentication
+   * @param servletRequest  the request
+   * @param servletResponse the response
+   */
+  void beforeAttemptAuthentication(AmbariAuthenticationFilter filter, ServletRequest servletRequest,
+                                   ServletResponse servletResponse);
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java
new file mode 100644
index 0000000..3a5a66b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.security.authentication;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ambari.server.audit.AuditLogger;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.LoginAuditEvent;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+import org.apache.ambari.server.security.authorization.PermissionHelper;
+import org.apache.ambari.server.security.authorization.Users;
+import org.apache.ambari.server.utils.RequestUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * AmbariAuthenticationEventHandlerImpl is the default {@link AmbariAuthenticationEventHandler}
+ * implementation.
+ * <p>
+ * This implementation tracks authentication attempts using the Ambari {@link AuditLogger} and
+ * ensures that the relevant user's consecutive authentication failure count is properly tracked.
+ * <p>
+ * Upon an authentication failure, the user's consecutive authentication failure count is incremented
+ * by <code>1</code> and upon a successful authentication, the user's consecutive authentication failure count
+ * is reset to <code>0</code>.
+ */
+@Singleton
+public class AmbariAuthenticationEventHandlerImpl implements AmbariAuthenticationEventHandler {
+  private static final Logger LOG = LoggerFactory.getLogger(AmbariAuthenticationEventHandlerImpl.class);
+  /**
+   * Audit logger
+   */
+  @Inject
+  private AuditLogger auditLogger;
+
+  /**
+   * PermissionHelper to help create audit entries
+   */
+  @Inject
+  private PermissionHelper permissionHelper;
+
+  @Inject
+  private Users users;
+
+  @Override
+  public void onSuccessfulAuthentication(AmbariAuthenticationFilter filter, HttpServletRequest servletRequest, HttpServletResponse servletResponse, Authentication result) {
+    String username = (result == null) ? null : result.getName();
+
+    // Using the Ambari audit logger, log this event (if enabled)
+    if (auditLogger.isEnabled()) {
+      AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
+          .withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
+          .withUserName(username)
+          .withTimestamp(System.currentTimeMillis())
+          .withRoles(permissionHelper.getPermissionLabels(result))
+          .build();
+      auditLogger.log(loginSucceededAuditEvent);
+    }
+
+    // Reset the user's consecutive authentication failure count to 0.
+    if (!StringUtils.isEmpty(username)) {
+      LOG.debug("Successfully authenticated {}", username);
+      users.clearConsecutiveAuthenticationFailures(username);
+    } else {
+      LOG.warn("Successfully authenticated an unknown user");
+    }
+  }
+
+  @Override
+  public void onUnsuccessfulAuthentication(AmbariAuthenticationFilter filter, HttpServletRequest servletRequest, HttpServletResponse servletResponse, AmbariAuthenticationException cause) {
+    String username;
+    String message;
+    String logMessage;
+    Integer consecutiveFailures = null;
+
+    if (cause == null) {
+      username = null;
+      message = "Unknown cause";
+    } else {
+      username = cause.getUsername();
+      message = cause.getLocalizedMessage();
+    }
+
+    // Increment the user's consecutive authentication failure count.
+    if (!StringUtils.isEmpty(username)) {
+      consecutiveFailures = users.incrementConsecutiveAuthenticationFailures(username);
+      logMessage = String.format("Failed to authenticate %s (attempt #%d): %s", username, consecutiveFailures, message);
+    } else {
+      logMessage = String.format("Failed to authenticate an unknown user: %s", message);
+    }
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug(logMessage, cause);
+    } else {
+      LOG.info(logMessage);
+    }
+
+    // Using the Ambari audit logger, log this event (if enabled)
+    if (auditLogger.isEnabled()) {
+      AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
+          .withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
+          .withTimestamp(System.currentTimeMillis())
+          .withReasonOfFailure("Invalid username/password combination")
+          .withConsecutiveFailures(consecutiveFailures)
+          .withUserName(username)
+          .build();
+      auditLogger.log(loginFailedAuditEvent);
+    }
+  }
+
+  @Override
+  public void beforeAttemptAuthentication(AmbariAuthenticationFilter filter, ServletRequest servletRequest, ServletResponse servletResponse) {
+    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+
+    // Using the Ambari audit logger, log this event (if enabled)
+    if (auditLogger.isEnabled() && filter.shouldApply(httpServletRequest) && (AuthorizationHelper.getAuthenticatedName() == null)) {
+      AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
+          .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
+          .withTimestamp(System.currentTimeMillis())
+          .withReasonOfFailure("Authentication required")
+          .withUserName(null)
+          .build();
+      auditLogger.log(loginFailedAuditEvent);
+    }
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationException.java
new file mode 100644
index 0000000..fb18b9c
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationException.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.security.authentication;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * AmbariAuthenticationException is an AuthenticationException implementation to be thrown
+ * when the user fails to authenticate with Ambari.
+ */
+public class AmbariAuthenticationException extends AuthenticationException {
+  private final String username;
+
+  public AmbariAuthenticationException(String username, String message) {
+    super(message);
+    this.username = username;
+  }
+
+  public AmbariAuthenticationException(String username, String message, Throwable throwable) {
+    super(message, throwable);
+    this.username = username;
+  }
+
+  public String getUsername() {
+    return username;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java
index ac3e15f..3667012 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java
@@ -26,13 +26,7 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.ambari.server.audit.AuditLogger;
-import org.apache.ambari.server.audit.event.AuditEvent;
-import org.apache.ambari.server.audit.event.LoginAuditEvent;
 import org.apache.ambari.server.security.AmbariEntryPoint;
-import org.apache.ambari.server.security.authorization.AuthorizationHelper;
-import org.apache.ambari.server.security.authorization.PermissionHelper;
-import org.apache.ambari.server.utils.RequestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -53,31 +47,25 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
 public class AmbariBasicAuthenticationFilter extends BasicAuthenticationFilter implements AmbariAuthenticationFilter {
   private static final Logger LOG = LoggerFactory.getLogger(AmbariBasicAuthenticationFilter.class);
 
-  /**
-   * Audit logger
-   */
-  private AuditLogger auditLogger;
-
-  /**
-   * PermissionHelper to help create audit entries
-   */
-  private PermissionHelper permissionHelper;
+  private final AmbariAuthenticationEventHandler eventHandler;
 
   /**
    * Constructor.
    *
-   * @param authenticationManager the Spring authencation manager
+   * @param authenticationManager the Spring authentication manager
    * @param ambariEntryPoint      the Spring entry point
-   * @param auditLogger           an Audit Logger
-   * @param permissionHelper      a permission helper
+   * @param eventHandler          the authentication event handler
    */
   public AmbariBasicAuthenticationFilter(AuthenticationManager authenticationManager,
                                          AmbariEntryPoint ambariEntryPoint,
-                                         AuditLogger auditLogger,
-                                         PermissionHelper permissionHelper) {
+                                         AmbariAuthenticationEventHandler eventHandler) {
     super(authenticationManager, ambariEntryPoint);
-    this.auditLogger = auditLogger;
-    this.permissionHelper = permissionHelper;
+
+    if(eventHandler == null) {
+      throw new IllegalArgumentException("The AmbariAuthenticationEventHandler must not be null");
+    }
+
+    this.eventHandler = eventHandler;
   }
 
   /**
@@ -115,16 +103,9 @@ public class AmbariBasicAuthenticationFilter extends BasicAuthenticationFilter i
    */
   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
-    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
-
-    if (auditLogger.isEnabled() && shouldApply(httpServletRequest) && (AuthorizationHelper.getAuthenticatedName() == null)) {
-      AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
-          .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
-          .withTimestamp(System.currentTimeMillis())
-          .withReasonOfFailure("Authentication required")
-          .withUserName(null)
-          .build();
-      auditLogger.log(loginFailedAuditEvent);
+
+    if (eventHandler != null) {
+      eventHandler.beforeAttemptAuthentication(this, servletRequest, servletResponse);
     }
 
     super.doFilter(servletRequest, servletResponse, chain);
@@ -142,14 +123,9 @@ public class AmbariBasicAuthenticationFilter extends BasicAuthenticationFilter i
   protected void onSuccessfulAuthentication(HttpServletRequest servletRequest,
                                             HttpServletResponse servletResponse,
                                             Authentication authResult) throws IOException {
-    if (auditLogger.isEnabled()) {
-      AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
-          .withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
-          .withUserName(authResult.getName())
-          .withTimestamp(System.currentTimeMillis())
-          .withRoles(permissionHelper.getPermissionLabels(authResult))
-          .build();
-      auditLogger.log(loginSucceededAuditEvent);
+
+    if (eventHandler != null) {
+      eventHandler.onSuccessfulAuthentication(this, servletRequest, servletResponse, authResult);
     }
   }
 
@@ -158,28 +134,30 @@ public class AmbariBasicAuthenticationFilter extends BasicAuthenticationFilter i
    *
    * @param servletRequest  the request
    * @param servletResponse the response
-   * @param authExecption   the exception, if any, causing the unsuccessful authentication attempt
+   * @param authException   the exception, if any, causing the unsuccessful authentication attempt
    * @throws IOException
    */
   @Override
   protected void onUnsuccessfulAuthentication(HttpServletRequest servletRequest,
                                               HttpServletResponse servletResponse,
-                                              AuthenticationException authExecption) throws IOException {
-    String header = servletRequest.getHeader("Authorization");
-    String username = null;
-    try {
-      username = getUsernameFromAuth(header, getCredentialsCharset(servletRequest));
-    } catch (Exception e) {
-      LOG.warn("Error occurred during decoding authorization header.", e);
-    }
-    if (auditLogger.isEnabled()) {
-      AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
-          .withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
-          .withTimestamp(System.currentTimeMillis())
-          .withReasonOfFailure("Invalid username/password combination")
-          .withUserName(username)
-          .build();
-      auditLogger.log(loginFailedAuditEvent);
+                                              AuthenticationException authException) throws IOException {
+    if (eventHandler != null) {
+      AmbariAuthenticationException cause;
+      if (authException instanceof AmbariAuthenticationException) {
+        cause = (AmbariAuthenticationException) authException;
+      } else {
+        String header = servletRequest.getHeader("Authorization");
+        String username = null;
+        try {
+          username = getUsernameFromAuth(header, getCredentialsCharset(servletRequest));
+        } catch (Exception e) {
+          LOG.warn("Error occurred during decoding authorization header.", e);
+        }
+
+        cause = new AmbariAuthenticationException(username, authException.getMessage(), authException);
+      }
+
+      eventHandler.onUnsuccessfulAuthentication(this, servletRequest, servletResponse, cause);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java
index fca8b29..3d35578 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java
@@ -27,15 +27,9 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.ambari.server.audit.AuditLogger;
-import org.apache.ambari.server.audit.event.AuditEvent;
-import org.apache.ambari.server.audit.event.LoginAuditEvent;
 import org.apache.ambari.server.configuration.Configuration;
-import org.apache.ambari.server.security.authorization.AuthorizationHelper;
-import org.apache.ambari.server.security.authorization.PermissionHelper;
 import org.apache.ambari.server.security.authorization.Users;
 import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationFilter;
-import org.apache.ambari.server.utils.RequestUtils;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.AuthenticationEntryPoint;
@@ -51,14 +45,9 @@ import org.springframework.security.web.AuthenticationEntryPoint;
 public class AmbariJWTAuthenticationFilter extends JwtAuthenticationFilter implements AmbariAuthenticationFilter {
 
   /**
-   * Audit logger
+   * Ambari authentication event handler
    */
-  private AuditLogger auditLogger;
-
-  /**
-   * PermissionHelper to help create audit entries
-   */
-  private PermissionHelper permissionHelper;
+  private final AmbariAuthenticationEventHandler eventHandler;
 
 
   /**
@@ -67,17 +56,19 @@ public class AmbariJWTAuthenticationFilter extends JwtAuthenticationFilter imple
    * @param ambariEntryPoint the Spring entry point
    * @param configuration    the Ambari configuration
    * @param users            the Ambari users object
-   * @param auditLogger      an Audit Logger
-   * @param permissionHelper a permission helper
+   * @param eventHandler     the Ambari authentication event handler
    */
   public AmbariJWTAuthenticationFilter(AuthenticationEntryPoint ambariEntryPoint,
                                        Configuration configuration,
                                        Users users,
-                                       AuditLogger auditLogger,
-                                       PermissionHelper permissionHelper) {
+                                       AmbariAuthenticationEventHandler eventHandler) {
     super(configuration, ambariEntryPoint, users);
-    this.auditLogger = auditLogger;
-    this.permissionHelper = permissionHelper;
+
+    if(eventHandler == null) {
+      throw new IllegalArgumentException("The AmbariAuthenticationEventHandler must not be null");
+    }
+
+    this.eventHandler = eventHandler;
   }
 
   /**
@@ -91,16 +82,9 @@ public class AmbariJWTAuthenticationFilter extends JwtAuthenticationFilter imple
    */
   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
-    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
-
-    if (auditLogger.isEnabled() && shouldApply(httpServletRequest) && (AuthorizationHelper.getAuthenticatedName() == null)) {
-      AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
-          .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
-          .withTimestamp(System.currentTimeMillis())
-          .withReasonOfFailure("Authentication required")
-          .withUserName(null)
-          .build();
-      auditLogger.log(loginFailedAuditEvent);
+
+    if (eventHandler != null) {
+      eventHandler.beforeAttemptAuthentication(this, servletRequest, servletResponse);
     }
 
     super.doFilter(servletRequest, servletResponse, chain);
@@ -108,32 +92,23 @@ public class AmbariJWTAuthenticationFilter extends JwtAuthenticationFilter imple
 
   @Override
   protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException {
-    if (auditLogger.isEnabled()) {
-      AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
-          .withRemoteIp(RequestUtils.getRemoteAddress(request))
-          .withUserName(authResult.getName())
-          .withTimestamp(System.currentTimeMillis())
-          .withRoles(permissionHelper.getPermissionLabels(authResult))
-          .build();
-      auditLogger.log(loginSucceededAuditEvent);
+    if (eventHandler != null) {
+      eventHandler.onSuccessfulAuthentication(this, request, response, authResult);
     }
   }
 
   @Override
   protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
-    if (auditLogger.isEnabled()) {
-      String username = null;
-      if (authException instanceof UserNotFoundException) {
-        username = ((UserNotFoundException) authException).getUsername();
+    if (eventHandler != null) {
+      AmbariAuthenticationException cause;
+
+      if (authException instanceof AmbariAuthenticationException) {
+        cause = (AmbariAuthenticationException) authException;
+      } else {
+        cause = new AmbariAuthenticationException(null, authException.getMessage(), authException);
       }
 
-      AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
-          .withRemoteIp(RequestUtils.getRemoteAddress(request))
-          .withTimestamp(System.currentTimeMillis())
-          .withReasonOfFailure(authException.getLocalizedMessage())
-          .withUserName(username)
-          .build();
-      auditLogger.log(loginFailedAuditEvent);
+      eventHandler.onUnsuccessfulAuthentication(this, request, response, cause);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/InvalidUsernamePasswordCombinationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/InvalidUsernamePasswordCombinationException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/InvalidUsernamePasswordCombinationException.java
new file mode 100644
index 0000000..cb1babd
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/InvalidUsernamePasswordCombinationException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.security.authentication;
+
+public class InvalidUsernamePasswordCombinationException extends AmbariAuthenticationException {
+
+  public static final String MESSAGE = "Unable to sign in. Invalid username/password combination.";
+
+  public InvalidUsernamePasswordCombinationException(String username) {
+    super(username, MESSAGE);
+  }
+
+  public InvalidUsernamePasswordCombinationException(String username, Throwable t) {
+    super(username, MESSAGE, t);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java
index f6c4bcf..0f2fbb6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java
@@ -18,26 +18,17 @@
 
 package org.apache.ambari.server.security.authentication;
 
-import org.springframework.security.core.AuthenticationException;
-
 /**
  * AuthenticationUserNotFoundException is an AuthenticationException implementation to be thrown
  * when the user specified in an authentication attempt is not found in the Ambari user database.
  */
-public class UserNotFoundException extends AuthenticationException {
-  private final String username;
+public class UserNotFoundException extends AmbariAuthenticationException {
 
   public UserNotFoundException(String username, String message) {
-    super(message);
-    this.username = username;
+    super(username, message);
   }
 
   public UserNotFoundException(String username, String message, Throwable throwable) {
-    super(message, throwable);
-    this.username = username;
-  }
-
-  public String getUsername() {
-    return username;
+    super(username, message, throwable);
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java
index 1b001ec..23fa171 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java
@@ -28,13 +28,10 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.ambari.server.audit.AuditLogger;
-import org.apache.ambari.server.audit.event.AuditEvent;
-import org.apache.ambari.server.audit.event.LoginAuditEvent;
 import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.authentication.AmbariAuthenticationEventHandler;
+import org.apache.ambari.server.security.authentication.AmbariAuthenticationException;
 import org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter;
-import org.apache.ambari.server.security.authorization.AuthorizationHelper;
-import org.apache.ambari.server.security.authorization.PermissionHelper;
-import org.apache.ambari.server.utils.RequestUtils;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
@@ -52,9 +49,9 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand
 public class AmbariKerberosAuthenticationFilter extends SpnegoAuthenticationProcessingFilter implements AmbariAuthenticationFilter {
 
   /**
-   * Audit logger
+   * Ambari authentication event handler
    */
-  private final AuditLogger auditLogger;
+  private final AmbariAuthenticationEventHandler eventHandler;
 
   /**
    * A Boolean value indicating whether Kerberos authentication is enabled or not.
@@ -70,30 +67,37 @@ public class AmbariKerberosAuthenticationFilter extends SpnegoAuthenticationProc
    * @param authenticationManager the Spring authentication manager
    * @param entryPoint            the Spring entry point
    * @param configuration         the Ambari configuration data
-   * @param auditLogger           an audit logger
-   * @param permissionHelper      a permission helper to aid in audit logging
+   * @param eventHandler          the Ambari authentication event handler
    */
-  public AmbariKerberosAuthenticationFilter(AuthenticationManager authenticationManager, final AuthenticationEntryPoint entryPoint, Configuration configuration, final AuditLogger auditLogger, final PermissionHelper permissionHelper) {
+  public AmbariKerberosAuthenticationFilter(AuthenticationManager authenticationManager,
+                                            final AuthenticationEntryPoint entryPoint,
+                                            Configuration configuration,
+                                            final AmbariAuthenticationEventHandler eventHandler) {
     AmbariKerberosAuthenticationProperties kerberosAuthenticationProperties = (configuration == null)
         ? null
         : configuration.getKerberosAuthenticationProperties();
 
     kerberosAuthenticationEnabled = (kerberosAuthenticationProperties != null) && kerberosAuthenticationProperties.isKerberosAuthenticationEnabled();
 
-    this.auditLogger = auditLogger;
+    if(eventHandler == null) {
+      throw new IllegalArgumentException("The AmbariAuthenticationEventHandler must not be null");
+    }
+
+    this.eventHandler = eventHandler;
 
     setAuthenticationManager(authenticationManager);
 
     setFailureHandler(new AuthenticationFailureHandler() {
       @Override
       public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
-        if (auditLogger.isEnabled()) {
-          AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
-              .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
-              .withTimestamp(System.currentTimeMillis())
-              .withReasonOfFailure(e.getLocalizedMessage())
-              .build();
-          auditLogger.log(loginFailedAuditEvent);
+        if (eventHandler != null) {
+          AmbariAuthenticationException cause;
+          if (e instanceof AmbariAuthenticationException) {
+            cause = (AmbariAuthenticationException) e;
+          } else {
+            cause = new AmbariAuthenticationException(null, e.getLocalizedMessage(), e);
+          }
+          eventHandler.onUnsuccessfulAuthentication(AmbariKerberosAuthenticationFilter.this, httpServletRequest, httpServletResponse, cause);
         }
 
         entryPoint.commence(httpServletRequest, httpServletResponse, e);
@@ -103,14 +107,8 @@ public class AmbariKerberosAuthenticationFilter extends SpnegoAuthenticationProc
     setSuccessHandler(new AuthenticationSuccessHandler() {
       @Override
       public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
-        if (auditLogger.isEnabled()) {
-          AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
-              .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
-              .withUserName(authentication.getName())
-              .withTimestamp(System.currentTimeMillis())
-              .withRoles(permissionHelper.getPermissionLabels(authentication))
-              .build();
-          auditLogger.log(loginSucceededAuditEvent);
+        if (eventHandler != null) {
+          eventHandler.onSuccessfulAuthentication(AmbariKerberosAuthenticationFilter.this, httpServletRequest, httpServletResponse, authentication);
         }
       }
     });
@@ -152,22 +150,10 @@ public class AmbariKerberosAuthenticationFilter extends SpnegoAuthenticationProc
    */
   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
-    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
-
-    if (shouldApply(httpServletRequest)) {
-      if (auditLogger.isEnabled() && (AuthorizationHelper.getAuthenticatedName() == null)) {
-        AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
-            .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
-            .withTimestamp(System.currentTimeMillis())
-            .withReasonOfFailure("Authentication required")
-            .withUserName(null)
-            .build();
-        auditLogger.log(loginFailedAuditEvent);
-      }
-
-      super.doFilter(servletRequest, servletResponse, filterChain);
-    } else {
-      filterChain.doFilter(servletRequest, servletResponse);
+    if (eventHandler != null) {
+      eventHandler.beforeAttemptAuthentication(AmbariKerberosAuthenticationFilter.this, servletRequest, servletResponse);
     }
+
+    super.doFilter(servletRequest, servletResponse, filterChain);
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java
index 6137b68..caff735 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java
@@ -24,6 +24,7 @@ import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
 import org.apache.ambari.server.security.ClientSecurityType;
+import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -93,7 +94,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
                 "connecting to LDAP server) are invalid.", e);
           }
         }
-        throw new InvalidUsernamePasswordCombinationException(e);
+        throw new InvalidUsernamePasswordCombinationException(username, e);
       } catch (IncorrectResultSizeDataAccessException multipleUsersFound) {
         String message = configuration.isLdapAlternateUserSearchEnabled() ?
           String.format("Login Failed: Please append your domain to your username and try again.  Example: %s@domain", username) :
@@ -204,7 +205,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
     // lookup is case insensitive, so no need for string comparison
     if (userEntity == null) {
       LOG.info("user not found ('{}')", userName);
-      throw new InvalidUsernamePasswordCombinationException();
+      throw new InvalidUsernamePasswordCombinationException(userName);
     }
 
     if (!userEntity.getActive()) {
@@ -221,7 +222,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
       LOG.debug("Failed to find LDAP authentication entry for {})", userName);
     }
 
-    throw new InvalidUsernamePasswordCombinationException();
+    throw new InvalidUsernamePasswordCombinationException(userName);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthoritiesPopulator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthoritiesPopulator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthoritiesPopulator.java
index 5c482a1..4331f59 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthoritiesPopulator.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthoritiesPopulator.java
@@ -25,6 +25,7 @@ import org.apache.ambari.server.orm.dao.PrivilegeDAO;
 import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.ldap.core.DirContextOperations;
@@ -71,7 +72,7 @@ public class AmbariLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
       return Collections.emptyList();
     }
     if(!user.getActive()){
-      throw new InvalidUsernamePasswordCombinationException();
+      throw new InvalidUsernamePasswordCombinationException(username);
     }
 
     Collection<PrivilegeEntity> privilegeEntities = users.getUserPrivileges(user);

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
index 517efe4..2c8bf12 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
@@ -22,6 +22,7 @@ import java.util.List;
 import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -66,17 +67,17 @@ public class AmbariLocalUserProvider extends AbstractUserDetailsAuthenticationPr
 
     if (userEntity == null) {
       LOG.info("user not found");
-      throw new InvalidUsernamePasswordCombinationException();
+      throw new InvalidUsernamePasswordCombinationException(userName);
     }
 
     if (!userEntity.getActive()) {
       LOG.debug("User account is disabled");
-      throw new InvalidUsernamePasswordCombinationException();
+      throw new InvalidUsernamePasswordCombinationException(userName);
     }
 
     if (authentication.getCredentials() == null) {
       LOG.debug("Authentication failed: no credentials provided");
-      throw new InvalidUsernamePasswordCombinationException();
+      throw new InvalidUsernamePasswordCombinationException(userName);
     }
 
     List<UserAuthenticationEntity> authenticationEntities = userEntity.getAuthenticationEntities();
@@ -98,7 +99,7 @@ public class AmbariLocalUserProvider extends AbstractUserDetailsAuthenticationPr
 
     // The user was not authenticated, fail
     LOG.debug("Authentication failed: password does not match stored value");
-    throw new InvalidUsernamePasswordCombinationException();
+    throw new InvalidUsernamePasswordCombinationException(userName);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/InvalidUsernamePasswordCombinationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/InvalidUsernamePasswordCombinationException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/InvalidUsernamePasswordCombinationException.java
deleted file mode 100644
index db82381..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/InvalidUsernamePasswordCombinationException.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.ambari.server.security.authorization;
-
-import org.springframework.security.core.AuthenticationException;
-
-public class InvalidUsernamePasswordCombinationException extends AuthenticationException {
-
-  public static final String MESSAGE = "Unable to sign in. Invalid username/password combination.";
-
-  public InvalidUsernamePasswordCombinationException() {
-    super(MESSAGE);
-  }
-
-  public InvalidUsernamePasswordCombinationException(Throwable t) {
-    super(MESSAGE, t);
-  }
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
index 35eb255..de12a16 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import java.util.Set;
 
 import javax.persistence.EntityManager;
+import javax.persistence.OptimisticLockException;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.hooks.HookContextFactory;
@@ -71,6 +72,11 @@ public class Users {
 
   private static final Logger LOG = LoggerFactory.getLogger(Users.class);
 
+  /**
+   * The maximum number of retries when handling OptimisticLockExceptions
+   */
+  private static final int MAX_RETRIES = 10;
+
   @Inject
   private Provider<EntityManager> entityManagerProvider;
 
@@ -238,9 +244,17 @@ public class Users {
    * @param active     true if active; false if not active
    * @throws AmbariException if user does not exist
    */
-  public synchronized void setUserActive(UserEntity userEntity, boolean active) throws AmbariException {
-    userEntity.setActive(active);
-    userDAO.merge(userEntity);
+  public synchronized void setUserActive(UserEntity userEntity, final boolean active) throws AmbariException {
+    if(userEntity != null) {
+      Command command = new Command() {
+        @Override
+        public void perform(UserEntity userEntity) {
+          userEntity.setActive(active);
+        }
+      };
+
+      safelyUpdateUserEntity(userEntity, command, MAX_RETRIES);
+    }
   }
 
   /**
@@ -1252,13 +1266,148 @@ public class Users {
   }
 
   /**
+   * Increments the named user's consecutive authentication failure count by <code>1</code>.
+   * <p>
+   * This operation is safe when concurrent authentication attempts by the same username are made
+   * due to {@link UserEntity#version} and optimistic locking.
+   *
+   * @param username the user's username
+   * @return the updated number of consecutive authentication failures; or null if the user does not exist
+   */
+  public Integer incrementConsecutiveAuthenticationFailures(String username) {
+    return incrementConsecutiveAuthenticationFailures(getUserEntity(username));
+  }
+
+  /**
+   * Increments the named user's consecutive authentication failure count by <code>1</code>.
+   * <p>
+   * This operation is safe when concurrent authentication attempts by the same username are made
+   * due to {@link UserEntity#version} and optimistic locking.
+   *
+   * @param userEntity the user
+   * @return the updated number of consecutive authentication failures; or null if the user does not exist
+   */
+  public Integer incrementConsecutiveAuthenticationFailures(UserEntity userEntity) {
+    if (userEntity != null) {
+      Command command = new Command() {
+        @Override
+        public void perform(UserEntity userEntity) {
+          userEntity.incrementConsecutiveFailures();
+        }
+      };
+
+      userEntity = safelyUpdateUserEntity(userEntity, command, MAX_RETRIES);
+    }
+
+    return (userEntity == null) ? null : userEntity.getConsecutiveFailures();
+  }
+
+  /**
+   * Resets the named user's consecutive authentication failure count to <code>0</code>.
+   * <p>
+   * This operation is safe when concurrent authentication attempts by the same username are made
+   * due to {@link UserEntity#version} and optimistic locking.
+   *
+   * @param username the user's username
+   */
+  public void clearConsecutiveAuthenticationFailures(String username) {
+    clearConsecutiveAuthenticationFailures(getUserEntity(username));
+  }
+
+  /**
+   * Resets the named user's consecutive authentication failure count to <code>0</code>.
+   * <p>
+   * This operation is safe when concurrent authentication attempts by the same username are made
+   * due to {@link UserEntity#version} and optimistic locking.
+   *
+   * @param userEntity the user
+   */
+  public void clearConsecutiveAuthenticationFailures(UserEntity userEntity) {
+    if (userEntity != null) {
+      if (userEntity.getConsecutiveFailures() != 0) {
+        Command command = new Command() {
+          @Override
+          public void perform(UserEntity userEntity) {
+            userEntity.setConsecutiveFailures(0);
+          }
+        };
+
+        safelyUpdateUserEntity(userEntity, command, MAX_RETRIES);
+      }
+    }
+  }
+
+  /***
+   * Attempts to update the specified {@link UserEntity} while handling {@link OptimisticLockException}s
+   * by obtaining the latest version of the {@link UserEntity} and retrying the operation.
+   *
+   * If the maximum number of retries is exceeded, then the operation will fail by rethrowing the last
+   * exception encountered.
+   *
+   *
+   * @param userEntity the user entity
+   * @param command  a command to perform on the user entity object that changes it state thus needing
+   *                 to be persisted
+   */
+  private UserEntity safelyUpdateUserEntity(UserEntity userEntity, Command command, int maxRetries) {
+    int retriesLeft = maxRetries;
+
+    do {
+      try {
+        command.perform(userEntity);
+        userDAO.merge(userEntity);
+
+        // The merge was a success, break out of this loop and return
+        return userEntity;
+      } catch (Throwable t) {
+        Throwable cause = t;
+
+        do {
+          if (cause instanceof OptimisticLockException) {
+            // An OptimisticLockException was caught, refresh the entity and retry.
+            Integer userID = userEntity.getUserId();
+
+            // Find the userEntity record to make sure the object is managed by JPA.  The passed-in
+            // object may be detached, therefore calling reset on it will fail.
+            userEntity = userDAO.findByPK(userID);
+
+            if (userEntity == null) {
+              LOG.warn("Failed to find user with user id of {}.  The user may have been removed. Aborting.", userID);
+              return null;  // return since this user is no longer available.
+            }
+
+            retriesLeft--;
+
+            // The the number of attempts has been exhausted, re-throw the exception
+            if (retriesLeft == 0) {
+              LOG.error("Failed to update the user's ({}) consecutive failures value due to an OptimisticLockException.  Aborting.",
+                  userEntity.getUserName());
+              throw t;
+            } else {
+              LOG.warn("Failed to update the user's ({}) consecutive failures value due to an OptimisticLockException.  {} retries left, retrying...",
+                  userEntity.getUserName(), retriesLeft);
+            }
+
+            break;
+          } else {
+            // Get the cause to see if it is an OptimisticLockException
+            cause = cause.getCause();
+          }
+        } while ((cause != null) && (cause != t)); // We are out of causes
+      }
+    } while (retriesLeft > 0); // We are out of retries
+
+    return userEntity;
+  }
+
+  /**
    * Validator is an interface to be implemented by authentication type specific validators to ensure
    * new user authentication records meet the specific requirements for the relative authentication
    * type.
    */
   private interface Validator {
     /**
-     * Valudate the authentication type specific key meets the requirments for the relative user
+     * Validate the authentication type specific key meets the requirements for the relative user
      * authentication type.
      *
      * @param userEntity the user
@@ -1267,4 +1416,14 @@ public class Users {
      */
     void validate(UserEntity userEntity, String key) throws AmbariException;
   }
+
+  /**
+   * Command is an interface used to perform operations on a {@link UserEntity} while safely updating
+   * a {@link UserEntity} object.
+   *
+   * @see #safelyUpdateUserEntity(UserEntity, Command, int)
+   */
+  private interface Command {
+    void perform(UserEntity userEntity);
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/internal/AmbariInternalAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/internal/AmbariInternalAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/internal/AmbariInternalAuthenticationProvider.java
index c57bdf1..89b6333 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/internal/AmbariInternalAuthenticationProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/internal/AmbariInternalAuthenticationProvider.java
@@ -18,7 +18,7 @@
 
 package org.apache.ambari.server.security.authorization.internal;
 
-import org.apache.ambari.server.security.authorization.InvalidUsernamePasswordCombinationException;
+import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java
index 3c3a446..f42df6c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java
@@ -73,7 +73,7 @@ public class JwtAuthenticationFilter implements AmbariAuthenticationFilter {
   private List<String> audiences = null;
   private String cookieName = "hadoop-jwt";
 
-  private boolean ignoreFailure = true;
+  private boolean ignoreFailure = false;
   private AuthenticationEntryPoint entryPoint;
   private Users users;
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
index 0c86591..78f9aec 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
@@ -266,6 +266,7 @@ CREATE TABLE users (
   display_name VARCHAR(255) NOT NULL,
   local_username VARCHAR(255) NOT NULL,
   create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+  version BIGINT NOT NULL DEFAULT 0,
   CONSTRAINT PK_users PRIMARY KEY (user_id),
   CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_users_0 UNIQUE (user_name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 83b1f48..d3a3650 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -286,6 +286,7 @@ CREATE TABLE users (
   display_name VARCHAR(255) NOT NULL,
   local_username VARCHAR(255) NOT NULL,
   create_time TIMESTAMP DEFAULT NOW(),
+  version BIGINT NOT NULL DEFAULT 0,
   CONSTRAINT PK_users PRIMARY KEY (user_id),
   CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_users_0 UNIQUE (user_name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index 215c01d..a27bc88 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -266,6 +266,7 @@ CREATE TABLE users (
   display_name VARCHAR2(255) NOT NULL,
   local_username VARCHAR2(255) NOT NULL,
   create_time TIMESTAMP NULL,
+  version NUMBER(19) DEFAULT 0 NOT NULL,
   CONSTRAINT PK_users PRIMARY KEY (user_id),
   CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_users_0 UNIQUE (user_name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 40ba709..e56cb04 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -265,6 +265,7 @@ CREATE TABLE users (
   display_name VARCHAR(255) NOT NULL,
   local_username VARCHAR(255) NOT NULL,
   create_time TIMESTAMP DEFAULT NOW(),
+  version BIGINT DEFAULT 0 NOT NULL,
   CONSTRAINT PK_users PRIMARY KEY (user_id),
   CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_users_0 UNIQUE (user_name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
index 35951f1..a1758e3 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
@@ -263,6 +263,7 @@ CREATE TABLE users (
   display_name VARCHAR(255) NOT NULL,
   local_username VARCHAR(255) NOT NULL,
   create_time TIMESTAMP DEFAULT NOW(),
+  version NUMERIC(19) NOT NULL DEFAULT 0,
   CONSTRAINT PK_users PRIMARY KEY (user_id),
   CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_users_0 UNIQUE (user_name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index b7244ab..e794866 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -269,6 +269,7 @@ CREATE TABLE users (
   display_name VARCHAR(255) NOT NULL,
   local_username VARCHAR(255) NOT NULL,
   create_time DATETIME DEFAULT GETDATE(),
+  version BIGINT NOT NULL DEFAULT 0,
   CONSTRAINT PK_users PRIMARY KEY (user_id),
   CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_users_0 UNIQUE (user_name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
index bdbf0de..6650f67 100644
--- a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
+++ b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
@@ -55,24 +55,21 @@
   <beans:bean id="ambariBasicAuthenticationFilter" class="org.apache.ambari.server.security.authentication.AmbariBasicAuthenticationFilter">
     <beans:constructor-arg ref="authenticationManager"/>
     <beans:constructor-arg ref="ambariEntryPoint"/>
-    <beans:constructor-arg ref="auditLogger"/>
-    <beans:constructor-arg ref="permissionHelper"/>
+    <beans:constructor-arg ref="ambariAuthenticationEventHandler"/>
   </beans:bean>
 
   <beans:bean id="ambariJwtAuthenticationFilter" class="org.apache.ambari.server.security.authentication.AmbariJWTAuthenticationFilter">
     <beans:constructor-arg ref="ambariEntryPoint"/>
     <beans:constructor-arg ref="ambariConfiguration"/>
     <beans:constructor-arg ref="ambariUsers"/>
-    <beans:constructor-arg ref="auditLogger"/>
-    <beans:constructor-arg ref="permissionHelper"/>
+    <beans:constructor-arg ref="ambariAuthenticationEventHandler"/>
   </beans:bean>
 
   <beans:bean id="ambariKerberosAuthenticationFilter" class="org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosAuthenticationFilter">
     <beans:constructor-arg ref="authenticationManager"/>
     <beans:constructor-arg ref="ambariEntryPoint"/>
     <beans:constructor-arg ref="ambariConfiguration"/>
-    <beans:constructor-arg ref="auditLogger"/>
-    <beans:constructor-arg ref="permissionHelper"/>
+    <beans:constructor-arg ref="ambariAuthenticationEventHandler"/>
   </beans:bean>
 
   <beans:bean id="ambariAuthorizationFilter" class="org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter">

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
index ac79967..2cff97e 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
@@ -69,6 +69,7 @@ public class LoginAuditEventTest {
     String testUserName = "USER1";
     String testRemoteIp = "127.0.0.1";
     String reason = "Bad credentials";
+    Integer consecutiveFailures = 1;
 
     Map<String, List<String>> roles = new HashMap<>();
     roles.put("a", Arrays.asList("r1", "r2", "r3"));
@@ -79,6 +80,7 @@ public class LoginAuditEventTest {
       .withUserName(testUserName)
       .withRoles(roles)
       .withReasonOfFailure(reason)
+      .withConsecutiveFailures(consecutiveFailures)
       .build();
 
     // When
@@ -87,11 +89,41 @@ public class LoginAuditEventTest {
     String roleMessage = System.lineSeparator() + "    a: r1, r2, r3" + System.lineSeparator();
 
     // Then
-    String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(User login), Roles(%s), Status(Failed), Reason(%s)",
-      testUserName, testRemoteIp, roleMessage, reason);
+    String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(User login), Roles(%s), Status(Failed), Reason(%s), Consecutive failures(%d)",
+      testUserName, testRemoteIp, roleMessage, reason, consecutiveFailures);
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
+  }
+
+  @Test
+  public void testFailedAuditMessageUnknownUser() throws Exception {
+    // Given
+    String testUserName = "USER1";
+    String testRemoteIp = "127.0.0.1";
+    String reason = "Bad credentials";
+
+    Map<String, List<String>> roles = new HashMap<>();
+    roles.put("a", Arrays.asList("r1", "r2", "r3"));
+
+    LoginAuditEvent evnt = LoginAuditEvent.builder()
+      .withTimestamp(System.currentTimeMillis())
+      .withRemoteIp(testRemoteIp)
+      .withUserName(testUserName)
+      .withRoles(roles)
+      .withReasonOfFailure(reason)
+      .withConsecutiveFailures(null)
+      .build();
+
+    // When
+    String actualAuditMessage = evnt.getAuditMessage();
+
+    String roleMessage = System.lineSeparator() + "    a: r1, r2, r3" + System.lineSeparator();
 
+    // Then
+    String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(User login), Roles(%s), Status(Failed), Reason(%s), Consecutive failures(UNKNOWN USER)",
+      testUserName, testRemoteIp, roleMessage, reason);
+
+    assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/ambari/blob/f760516c/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
index 18c4cce..ed4c383 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
@@ -18,110 +18,134 @@
 package org.apache.ambari.server.security.authentication;
 
 import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.getCurrentArguments;
+import static org.easymock.EasyMock.newCapture;
 
 import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
 
-import org.apache.ambari.server.audit.AuditLogger;
-import org.apache.ambari.server.audit.event.AuditEvent;
 import org.apache.ambari.server.security.AmbariEntryPoint;
-import org.apache.ambari.server.security.authorization.PermissionHelper;
+import org.easymock.Capture;
+import org.easymock.CaptureType;
 import org.easymock.EasyMockSupport;
+import org.easymock.IAnswer;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.crypto.codec.Base64;
 
 public class AmbariBasicAuthenticationFilterTest extends EasyMockSupport {
 
-  private AmbariBasicAuthenticationFilter underTest;
-
-  private AuditLogger mockedAuditLogger;
-
-  private PermissionHelper permissionHelper;
+  private AmbariAuthenticationEventHandler eventHandler;
 
   private AmbariEntryPoint entryPoint;
 
+  private AuthenticationManager authenticationManager;
+
   @Before
   public void setUp() {
-    mockedAuditLogger = createMock(AuditLogger.class);
-    permissionHelper = createMock(PermissionHelper.class);
+    SecurityContextHolder.getContext().setAuthentication(null);
+
+    eventHandler = createMock(AmbariAuthenticationEventHandler.class);
     entryPoint = createMock(AmbariEntryPoint.class);
-    underTest = new AmbariBasicAuthenticationFilter(null, entryPoint, mockedAuditLogger, permissionHelper);
+    authenticationManager = createMock(AuthenticationManager.class);
+  }
+
+  @Test (expected = IllegalArgumentException.class)
+  public void ensureNonNullEventHandler() {
+    new AmbariBasicAuthenticationFilter(authenticationManager, entryPoint, null);
   }
 
   @Test
-  public void testDoFilter() throws IOException, ServletException {
-    SecurityContextHolder.getContext().setAuthentication(null);
+  public void testDoFilterSuccessful() throws IOException, ServletException {
+    Capture<? extends AmbariAuthenticationFilter> captureFilter = newCapture(CaptureType.ALL);
+
     // GIVEN
     HttpServletRequest request = createMock(HttpServletRequest.class);
     HttpServletResponse response = createMock(HttpServletResponse.class);
+    HttpSession session = createMock(HttpSession.class);
     FilterChain filterChain = createMock(FilterChain.class);
-    expect(request.getHeader("Authorization")).andReturn("Basic ").andReturn(null);
-    expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4").anyTimes();
-    expect(mockedAuditLogger.isEnabled()).andReturn(true).anyTimes();
-    mockedAuditLogger.log(anyObject(AuditEvent.class));
-    expectLastCall().times(1);
+
+    expect(request.getHeader("Authorization")).andReturn("Basic YWRtaW46YWRtaW4=").once();
+    expect(request.getRemoteAddr()).andReturn("1.2.3.4").once();
+    expect(request.getSession(false)).andReturn(session).once();
+    expect(session.getId()).andReturn("sessionID").once();
+    expect(authenticationManager.authenticate(anyObject(Authentication.class)))
+        .andAnswer(new IAnswer<Authentication>() {
+          @Override
+          public Authentication answer() throws Throwable {
+            return (Authentication) getCurrentArguments()[0];
+          }
+        })
+        .anyTimes();
+
+    eventHandler.beforeAttemptAuthentication(capture(captureFilter), eq(request), eq(response));
+    expectLastCall().once();
+    eventHandler.onSuccessfulAuthentication(capture(captureFilter), eq(request), eq(response), anyObject(Authentication.class));
+    expectLastCall().once();
+
     filterChain.doFilter(request, response);
-    expectLastCall();
-    replayAll();
-    // WHEN
-    underTest.doFilter(request, response, filterChain);
-    // THEN
-    verifyAll();
-  }
+    expectLastCall().once();
 
-  @Test
-  public void testOnSuccessfulAuthentication() throws IOException, ServletException {
-    // GIVEN
-    HttpServletRequest request = createMock(HttpServletRequest.class);
-    HttpServletResponse response = createMock(HttpServletResponse.class);
-    Authentication authentication = createMock(Authentication.class);
-
-    Map<String, List<String>> roles = new HashMap<>();
-    roles.put("a", Arrays.asList("r1", "r2", "r3"));
-    expect(permissionHelper.getPermissionLabels(authentication))
-        .andReturn(roles);
-    expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
-    expect(authentication.getName()).andReturn("admin");
-    expect(mockedAuditLogger.isEnabled()).andReturn(true);
-    mockedAuditLogger.log(anyObject(AuditEvent.class));
-    expectLastCall().times(1);
     replayAll();
     // WHEN
-    underTest.onSuccessfulAuthentication(request, response, authentication);
+    AmbariAuthenticationFilter filter = new AmbariBasicAuthenticationFilter(authenticationManager, entryPoint, eventHandler);
+    filter.doFilter(request, response, filterChain);
     // THEN
     verifyAll();
+
+    List<? extends AmbariAuthenticationFilter> capturedFilters = captureFilter.getValues();
+    for (AmbariAuthenticationFilter capturedFiltered : capturedFilters) {
+      Assert.assertSame(filter, capturedFiltered);
+    }
   }
 
   @Test
-  public void testOnUnsuccessfulAuthentication() throws IOException, ServletException {
+  public void testDoFilterUnsuccessful() throws IOException, ServletException {
+    Capture<? extends AmbariAuthenticationFilter> captureFilter = newCapture(CaptureType.ALL);
+
     // GIVEN
     HttpServletRequest request = createMock(HttpServletRequest.class);
     HttpServletResponse response = createMock(HttpServletResponse.class);
-    AuthenticationException authEx = createMock(AuthenticationException.class);
-    expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
-    expect(request.getHeader("Authorization")).andReturn(
-        "Basic " + new String(Base64.encode("admin:admin".getBytes("UTF-8"))));
-    expect(mockedAuditLogger.isEnabled()).andReturn(true);
-    mockedAuditLogger.log(anyObject(AuditEvent.class));
-    expectLastCall().times(1);
+    HttpSession session = createMock(HttpSession.class);
+    FilterChain filterChain = createMock(FilterChain.class);
+
+    expect(request.getHeader("Authorization")).andReturn("Basic YWRtaW46YWRtaW4=").once();
+    expect(request.getRemoteAddr()).andReturn("1.2.3.4").once();
+    expect(request.getSession(false)).andReturn(session).once();
+    expect(session.getId()).andReturn("sessionID").once();
+    expect(authenticationManager.authenticate(anyObject(Authentication.class))).andThrow(new InvalidUsernamePasswordCombinationException("user")).once();
+
+    eventHandler.beforeAttemptAuthentication(capture(captureFilter), eq(request), eq(response));
+    expectLastCall().once();
+    eventHandler.onUnsuccessfulAuthentication(capture(captureFilter), eq(request), eq(response), anyObject(AmbariAuthenticationException.class));
+    expectLastCall().once();
+
+    entryPoint.commence(eq(request), eq(response), anyObject(AmbariAuthenticationException.class));
+    expectLastCall().once();
+
     replayAll();
     // WHEN
-    underTest.onUnsuccessfulAuthentication(request, response, authEx);
+    AmbariAuthenticationFilter filter = new AmbariBasicAuthenticationFilter(authenticationManager, entryPoint, eventHandler);
+    filter.doFilter(request, response, filterChain);
     // THEN
     verifyAll();
+
+    List<? extends AmbariAuthenticationFilter> capturedFilters = captureFilter.getValues();
+    for (AmbariAuthenticationFilter capturedFiltered : capturedFilters) {
+      Assert.assertSame(filter, capturedFiltered);
+    }
   }
 }


Mime
View raw message