ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stoa...@apache.org
Subject [2/2] ambari git commit: AMBARI-15554. Ambari LDAP integration cannot handle LDAP directories with multiple entries for the same user. (stoader)
Date Mon, 28 Mar 2016 11:04:42 GMT
AMBARI-15554. Ambari LDAP integration cannot handle LDAP directories with multiple entries for the same user. (stoader)


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

Branch: refs/heads/trunk
Commit: 71b4c624fb219bb1626c238322bda6c2e5589f72
Parents: d0672c2
Author: Toader, Sebastian <stoader@hortonworks.com>
Authored: Mon Mar 28 13:04:17 2016 +0200
Committer: Toader, Sebastian <stoader@hortonworks.com>
Committed: Mon Mar 28 13:04:17 2016 +0200

----------------------------------------------------------------------
 ambari-server/conf/unix/log4j.properties        |   3 +
 ambari-server/pom.xml                           |   1 +
 .../server/api/UserNameOverrideFilter.java      | 137 ++++++++
 .../server/configuration/Configuration.java     |  47 +++
 .../ambari/server/controller/AmbariServer.java  |   2 +
 .../authorization/AmbariAuthentication.java     | 214 ++++++++++++
 .../AmbariLdapAuthenticationProvider.java       |  47 ++-
 .../AmbariLdapAuthoritiesPopulator.java         |   2 +
 .../AmbariLdapBindAuthenticator.java            |  29 +-
 .../security/authorization/AmbariLdapUtils.java |  43 +++
 .../authorization/AuthorizationHelper.java      |  38 ++-
 ...ateLdapUserFoundAuthenticationException.java |  51 +++
 .../authorization/LdapServerProperties.java     |  44 ++-
 .../webapp/WEB-INF/spring-security.xml          |   1 +
 .../server/api/UserNameOverrideFilterTest.java  | 196 +++++++++++
 .../server/configuration/ConfigurationTest.java |  56 ++++
 .../server/security/AmbariLdapUtilsTest.java    |  87 +++++
 .../authorization/AmbariAuthenticationTest.java | 333 +++++++++++++++++++
 ...henticationProviderForDuplicateUserTest.java | 100 ++++++
 .../AmbariLdapAuthenticationProviderTest.java   |  50 ++-
 .../AmbariLdapBindAuthenticatorTest.java        | 136 ++++++++
 .../authorization/AuthorizationHelperTest.java  | 116 ++++++-
 .../authorization/LdapServerPropertiesTest.java |  23 +-
 .../TestAmbariLdapAuthoritiesPopulator.java     |  42 +++
 ambari-server/src/test/resources/users.ldif     |   1 +
 .../resources/users_with_duplicate_uid.ldif     |  20 ++
 ambari-web/app/controllers/login_controller.js  |   8 +-
 ambari-web/app/router.js                        |   4 +-
 .../test/controllers/login_controller_test.js   |  26 +-
 29 files changed, 1816 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/conf/unix/log4j.properties
----------------------------------------------------------------------
diff --git a/ambari-server/conf/unix/log4j.properties b/ambari-server/conf/unix/log4j.properties
index 2ee32d4..ab3ea0b 100644
--- a/ambari-server/conf/unix/log4j.properties
+++ b/ambari-server/conf/unix/log4j.properties
@@ -73,3 +73,6 @@ log4j.appender.eclipselink.layout.ConversionPattern=%m%n
 log4j.logger.org.apache.hadoop.yarn.client=WARN
 log4j.logger.org.apache.slider.common.tools.SliderUtils=WARN
 log4j.logger.org.apache.ambari.server.security.authorization=WARN
+
+log4j.logger.org.apache.ambari.server.security.authorization.AuthorizationHelper=INFO
+log4j.logger.org.apache.ambari.server.security.authorization.AmbariLdapBindAuthenticator=INFO

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml
index 1e44517..857e554 100644
--- a/ambari-server/pom.xml
+++ b/ambari-server/pom.xml
@@ -293,6 +293,7 @@
             <exclude>src/test/resources/TestAmbaryServer.samples/**</exclude>
             <exclude>src/test/resources/*.txt</exclude>
             <exclude>src/test/resources/users_for_dn_with_space.ldif</exclude>
+            <exclude>src/test/resources/users_with_duplicate_uid.ldif</exclude>
 
             <!--Velocity log -->
             <exclude>**/velocity.log*</exclude>

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/api/UserNameOverrideFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/UserNameOverrideFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/api/UserNameOverrideFilter.java
new file mode 100644
index 0000000..04de12c
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/UserNameOverrideFilter.java
@@ -0,0 +1,137 @@
+/**
+ * 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.api;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+
+/**
+ * This filter overrides usernames found in request url.
+ */
+public class UserNameOverrideFilter implements Filter {
+
+  // Regex for extracting user name component from the user related api request Uris
+  private final static Pattern USER_NAME_IN_URI_REGEXP = Pattern.compile("(?<pre>.*/users/)(?<username>[^/]+)(?<post>(/.*)?)");
+
+  /**
+   * Called by the web container to indicate to a filter that it is
+   * being placed into service.
+   *
+   * <p>The servlet container calls the init
+   * method exactly once after instantiating the filter. The init
+   * method must complete successfully before the filter is asked to do any
+   * filtering work.
+   *
+   * <p>The web container cannot place the filter into service if the init
+   * method either
+   * <ol>
+   * <li>Throws a ServletException
+   * <li>Does not return within a time period defined by the web container
+   * </ol>
+   *
+   * @param filterConfig
+   */
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+
+  }
+
+  /**
+   * The <code>doFilter</code> method of the Filter is called by the
+   * container each time a request/response pair is passed through the
+   * chain due to a client request for a resource at the end of the chain.
+   * The FilterChain passed in to this method allows the Filter to pass
+   * on the request and response to the next entity in the chain.
+   *
+   * Verify if this a user related request by checking that the Uri of the request contains
+   * username and resolves the username to actual ambari user name if username
+   * is a login alias.
+   *
+   * @param request
+   * @param response
+   * @param chain
+   */
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    if (request instanceof HttpServletRequest) {
+      final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+      Matcher userNameMatcher = getUserNameMatcher(httpServletRequest.getRequestURI());
+
+      if (userNameMatcher.find()) {
+        String userNameFromUri = URLDecoder.decode(userNameMatcher.group("username"), "UTF-8");
+        final String userName = AuthorizationHelper.resolveLoginAliasToUserName(userNameFromUri);
+
+        if (!userNameFromUri.equals(userName)) {
+          final String requestUriOverride = String.format("%s%s%s", userNameMatcher.group("pre"), userName, userNameMatcher.group("post"));
+
+          request = new HttpServletRequestWrapper(httpServletRequest) {
+            @Override
+            public String getRequestURI() {
+              return requestUriOverride;
+            }
+          };
+
+        }
+      }
+    }
+
+    chain.doFilter(request, response);
+  }
+
+  /**
+   * Returns a {@link Matcher} created from {@link #USER_NAME_IN_URI_REGEXP} for the
+   * provided requestUri.
+   * @param requestUri the Uri the Matcher is created for.
+   * @return the matcher
+   */
+  protected Matcher getUserNameMatcher(String requestUri) {
+    return USER_NAME_IN_URI_REGEXP.matcher(requestUri);
+  }
+
+  /**
+   * Called by the web container to indicate to a filter that it is being
+   * taken out of service.
+   *
+   * <p>This method is only called once all threads within the filter's
+   * doFilter method have exited or after a timeout period has passed.
+   * After the web container calls this method, it will not call the
+   * doFilter method again on this instance of the filter.
+   *
+   * <p>This method gives the filter an opportunity to clean up any
+   * resources that are being held (for example, memory, file handles,
+   * threads) and make sure that any persistent state is synchronized
+   * with the filter's current state in memory.
+   */
+  @Override
+  public void destroy() {
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index bf18325..1d30f1c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -188,6 +188,30 @@ public class Configuration {
   public static final String LDAP_GROUP_NAMING_ATTR_KEY = "authentication.ldap.groupNamingAttr";
   public static final String LDAP_GROUP_MEMEBERSHIP_ATTR_KEY = "authentication.ldap.groupMembershipAttr";
   public static final String LDAP_ADMIN_GROUP_MAPPING_RULES_KEY = "authorization.ldap.adminGroupMappingRules";
+  /**
+   * When authentication through LDAP is enabled then Ambari Server uses this filter to lookup
+   * the user in LDAP based on the provided ambari user name.
+   *
+   * If it is not set then the default {@link #LDAP_USER_SEARCH_FILTER_DEFAULT} is used.
+   */
+  public static final String LDAP_USER_SEARCH_FILTER_KEY = "authentication.ldap.userSearchFilter";
+
+  /**
+   * When authentication through LDAP is enabled there might be cases when {@link #LDAP_USER_SEARCH_FILTER_KEY}
+   * may match multiple users in LDAP. In such cases the user is prompted to provide additional info, e.g. the domain
+   * he or she wants ot log in upon login beside the username. This filter will be used by Ambari Server to lookup
+   * users in LDAP if the login name the user logs in contains additional information beside ambari user name.
+   *
+   * If it is not not set then the default {@link #LDAP_ALT_USER_SEARCH_FILTER_DEFAULT} is used.
+   *
+   * <p>
+   *   Note: Currently this filter will only be used by Ambari Server if the user login name
+   *   is in the username@domain format (e.g. user1@x.y.com) which is the userPrincipalName
+   *   format used in AD.
+   * </p>
+   */
+  public static final String LDAP_ALT_USER_SEARCH_FILTER_KEY = "authentication.ldap.alternateUserSearchFilter"; //TODO: we'll need a more generic solution to support any login name format
+
   public static final String LDAP_GROUP_SEARCH_FILTER_KEY = "authorization.ldap.groupSearchFilter";
   public static final String LDAP_REFERRAL_KEY = "authentication.ldap.referral";
   public static final String LDAP_PAGINATION_ENABLED_KEY = "authentication.ldap.pagination.enabled";
@@ -459,6 +483,25 @@ public class Configuration {
   private static final String LDAP_GROUP_NAMING_ATTR_DEFAULT = "cn";
   private static final String LDAP_GROUP_MEMBERSHIP_ATTR_DEFAULT = "member";
   private static final String LDAP_ADMIN_GROUP_MAPPING_RULES_DEFAULT = "Ambari Administrators";
+  /**
+   * When authentication through LDAP is enabled then Ambari Server uses this filter by default to lookup
+   * the user in LDAP if one not provided in the config via {@link #LDAP_USER_SEARCH_FILTER_KEY}.
+   */
+  protected static final String LDAP_USER_SEARCH_FILTER_DEFAULT = "(&({usernameAttribute}={0})(objectClass={userObjectClass}))";
+
+  /**
+   * When authentication through LDAP is enabled Ambari Server uses this filter by default to lookup
+   * the user in LDAP when the user provides beside user name additional information.
+   * This filter can be overridden through {@link #LDAP_ALT_USER_SEARCH_FILTER_KEY}.
+   *
+   * <p>
+   *   Note: Currently the use of alternate user search filter is triggered only if the user login name
+   *   is in the username@domain format (e.g. user1@x.y.com) which is the userPrincipalName
+   *   format used in AD.
+   * </p>
+   */
+  protected static final String LDAP_ALT_USER_SEARCH_FILTER_DEFAULT = "(&(userPrincipalName={0})(objectClass={userObjectClass}))"; //TODO: we'll need a more generic solution to support any login name format
+
   private static final String LDAP_GROUP_SEARCH_FILTER_DEFAULT = "";
   private static final String LDAP_REFERRAL_DEFAULT = "follow";
 
@@ -1661,6 +1704,10 @@ public class Configuration {
       getProperty(LDAP_GROUP_NAMING_ATTR_KEY, LDAP_GROUP_NAMING_ATTR_DEFAULT));
     ldapServerProperties.setAdminGroupMappingRules(properties.getProperty(
       LDAP_ADMIN_GROUP_MAPPING_RULES_KEY, LDAP_ADMIN_GROUP_MAPPING_RULES_DEFAULT));
+    ldapServerProperties.setUserSearchFilter(properties.getProperty(
+      LDAP_USER_SEARCH_FILTER_KEY, LDAP_USER_SEARCH_FILTER_DEFAULT));
+    ldapServerProperties.setAlternateUserSearchFilter(properties.getProperty(
+      LDAP_ALT_USER_SEARCH_FILTER_KEY, LDAP_ALT_USER_SEARCH_FILTER_DEFAULT));
     ldapServerProperties.setGroupSearchFilter(properties.getProperty(
       LDAP_GROUP_SEARCH_FILTER_KEY, LDAP_GROUP_SEARCH_FILTER_DEFAULT));
     ldapServerProperties.setReferralMethod(properties.getProperty(

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/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 076f850..4b769e3 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
@@ -41,6 +41,7 @@ import org.apache.ambari.server.agent.rest.AgentResource;
 import org.apache.ambari.server.api.AmbariErrorHandler;
 import org.apache.ambari.server.api.AmbariPersistFilter;
 import org.apache.ambari.server.api.MethodOverrideFilter;
+import org.apache.ambari.server.api.UserNameOverrideFilter;
 import org.apache.ambari.server.api.rest.BootStrapResource;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.KeyService;
@@ -363,6 +364,7 @@ public class AmbariServer {
       root.addEventListener(new RequestContextListener());
 
       root.addFilter(new FilterHolder(springSecurityFilter), "/api/*", DISPATCHER_TYPES);
+      root.addFilter(new FilterHolder(new UserNameOverrideFilter()), "/api/v1/users/*", DISPATCHER_TYPES);
 
       // session-per-request strategy for agents
       agentroot.addFilter(new FilterHolder(injector.getInstance(AmbariPersistFilter.class)), "/agent/*", DISPATCHER_TYPES);

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java
new file mode 100644
index 0000000..98b97b2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java
@@ -0,0 +1,214 @@
+/**
+ * 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 java.security.Principal;
+import java.util.Collection;
+import java.util.Objects;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.User;
+
+/**
+ * This class is a wrapper for authentication objects to
+ * provide functionality for resolving login aliases to
+ * ambari user names.
+ */
+public final class AmbariAuthentication implements Authentication {
+  private final Authentication authentication;
+  private final Object principalOverride;
+
+  public AmbariAuthentication(Authentication authentication) {
+    this.authentication = authentication;
+    this.principalOverride = getPrincipalOverride();
+  }
+
+
+
+  /**
+   * Set by an <code>AuthenticationManager</code> to indicate the authorities that the principal has been
+   * granted. Note that classes should not rely on this value as being valid unless it has been set by a trusted
+   * <code>AuthenticationManager</code>.
+   * <p>
+   * Implementations should ensure that modifications to the returned collection
+   * array do not affect the state of the Authentication object, or use an unmodifiable instance.
+   * </p>
+   *
+   * @return the authorities granted to the principal, or an empty collection if the token has not been authenticated.
+   * Never null.
+   */
+  @Override
+  public Collection<? extends GrantedAuthority> getAuthorities() {
+    return authentication.getAuthorities();
+  }
+
+  /**
+   * The credentials that prove the principal is correct. This is usually a password, but could be anything
+   * relevant to the <code>AuthenticationManager</code>. Callers are expected to populate the credentials.
+   *
+   * @return the credentials that prove the identity of the <code>Principal</code>
+   */
+  @Override
+  public Object getCredentials() {
+    return authentication.getCredentials();
+  }
+
+  /**
+   * Stores additional details about the authentication request. These might be an IP address, certificate
+   * serial number etc.
+   *
+   * @return additional details about the authentication request, or <code>null</code> if not used
+   */
+  @Override
+  public Object getDetails() {
+    return authentication.getDetails();
+  }
+
+  /**
+   * The identity of the principal being authenticated. In the case of an authentication request with username and
+   * password, this would be the username. Callers are expected to populate the principal for an authentication
+   * request.
+   * <p>
+   * The <tt>AuthenticationManager</tt> implementation will often return an <tt>Authentication</tt> containing
+   * richer information as the principal for use by the application. Many of the authentication providers will
+   * create a {@code UserDetails} object as the principal.
+   *
+   * @return the <code>Principal</code> being authenticated or the authenticated principal after authentication.
+   */
+  @Override
+  public Object getPrincipal() {
+    if (principalOverride != null) {
+      return principalOverride;
+    }
+
+    return authentication.getPrincipal();
+  }
+
+  /**
+   * Used to indicate to {@code AbstractSecurityInterceptor} whether it should present the
+   * authentication token to the <code>AuthenticationManager</code>. Typically an <code>AuthenticationManager</code>
+   * (or, more often, one of its <code>AuthenticationProvider</code>s) will return an immutable authentication token
+   * after successful authentication, in which case that token can safely return <code>true</code> to this method.
+   * Returning <code>true</code> will improve performance, as calling the <code>AuthenticationManager</code> for
+   * every request will no longer be necessary.
+   * <p>
+   * For security reasons, implementations of this interface should be very careful about returning
+   * <code>true</code> from this method unless they are either immutable, or have some way of ensuring the properties
+   * have not been changed since original creation.
+   *
+   * @return true if the token has been authenticated and the <code>AbstractSecurityInterceptor</code> does not need
+   * to present the token to the <code>AuthenticationManager</code> again for re-authentication.
+   */
+  @Override
+  public boolean isAuthenticated() {
+    return authentication.isAuthenticated();
+  }
+
+  /**
+   * See {@link #isAuthenticated()} for a full description.
+   * <p>
+   * Implementations should <b>always</b> allow this method to be called with a <code>false</code> parameter,
+   * as this is used by various classes to specify the authentication token should not be trusted.
+   * If an implementation wishes to reject an invocation with a <code>true</code> parameter (which would indicate
+   * the authentication token is trusted - a potential security risk) the implementation should throw an
+   * {@link IllegalArgumentException}.
+   *
+   * @param isAuthenticated <code>true</code> if the token should be trusted (which may result in an exception) or
+   *                        <code>false</code> if the token should not be trusted
+   * @throws IllegalArgumentException if an attempt to make the authentication token trusted (by passing
+   *                                  <code>true</code> as the argument) is rejected due to the implementation being immutable or
+   *                                  implementing its own alternative approach to {@link #isAuthenticated()}
+   */
+  @Override
+  public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
+    authentication.setAuthenticated(isAuthenticated);
+  }
+
+  /**
+   * Returns the name of this principal.
+   *
+   * @return the name of this principal.
+   */
+  @Override
+  public String getName() {
+    if (principalOverride != null)
+    {
+      if (principalOverride instanceof UserDetails) {
+        return ((UserDetails) principalOverride).getUsername();
+      }
+
+      return principalOverride.toString();
+    }
+
+    return authentication.getName();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    AmbariAuthentication that = (AmbariAuthentication) o;
+    return Objects.equals(authentication, that.authentication) &&
+      Objects.equals(principalOverride, that.principalOverride);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(authentication, principalOverride);
+  }
+
+  /**
+   * Returns a principal object that is to be used
+   * to override the original principal object
+   * returned by the inner {@link #authentication} object.
+   *
+   * <p>The purpose of overriding the origin principal is to provide
+   * and object that resolves the contained user name to ambari user name in case
+   * the original user name is a login alias.</p>
+   *
+   * @return principal override of the original one is of type {@link UserDetails},
+   * if the original one is a login alias name than the user name the login alias resolves to
+   * otherwise <code>null</code>
+   */
+  private Object getPrincipalOverride() {
+    Object principal = authentication.getPrincipal();
+
+    if (principal instanceof UserDetails) {
+      UserDetails user = (UserDetails)principal;
+
+      principal =
+        new User(
+          AuthorizationHelper.resolveLoginAliasToUserName(user.getUsername()),
+          user.getPassword(),
+          user.isEnabled(),
+          user.isAccountNonExpired(),
+          user.isCredentialsNonExpired(),
+          user.isAccountNonLocked(),
+          user.getAuthorities());
+    } else if ( !(principal instanceof Principal) && principal != null ){
+      String username = principal.toString();
+      principal = AuthorizationHelper.resolveLoginAliasToUserName(username);
+    } else {
+      principal = null;
+    }
+
+    return principal;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/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 20cf2fd..7b2a95c 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
@@ -19,10 +19,12 @@ package org.apache.ambari.server.security.authorization;
 
 import com.google.inject.Inject;
 import java.util.List;
+
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.security.ClientSecurityType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.dao.IncorrectResultSizeDataAccessException;
 import org.springframework.ldap.core.support.LdapContextSource;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -45,6 +47,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
 
   private ThreadLocal<LdapServerProperties> ldapServerProperties = new ThreadLocal<LdapServerProperties>();
   private ThreadLocal<LdapAuthenticationProvider> providerThreadLocal = new ThreadLocal<LdapAuthenticationProvider>();
+  private ThreadLocal<String> ldapUserSearchFilterThreadLocal = new ThreadLocal<>();
 
   @Inject
   public AmbariLdapAuthenticationProvider(Configuration configuration, AmbariLdapAuthoritiesPopulator authoritiesPopulator) {
@@ -54,10 +57,13 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
 
   @Override
   public Authentication authenticate(Authentication authentication) throws AuthenticationException {
-
     if (isLdapEnabled()) {
+      String username = getUserName(authentication);
+
       try {
-        return loadLdapAuthenticationProvider().authenticate(authentication);
+        Authentication auth = loadLdapAuthenticationProvider(username).authenticate(authentication);
+
+        return new AmbariAuthentication(auth);
       } catch (AuthenticationException e) {
         LOG.debug("Got exception during LDAP authentification attempt", e);
         // Try to help in troubleshooting
@@ -73,6 +79,8 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
           }
         }
         throw e;
+      } catch (IncorrectResultSizeDataAccessException multipleUsersFound) {
+        throw new DuplicateLdapUserFoundAuthenticationException(String.format("Login Failed: Please append your domain to your username and try again.  Example: %s@domain", username));
       }
     } else {
       return null;
@@ -89,9 +97,14 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
    * Reloads LDAP Context Source and depending objects if properties were changed
    * @return corresponding LDAP authentication provider
    */
-  LdapAuthenticationProvider loadLdapAuthenticationProvider() {
-    if (reloadLdapServerProperties()) {
-      LOG.info("LDAP Properties changed - rebuilding Context");
+  LdapAuthenticationProvider loadLdapAuthenticationProvider(String userName) {
+    boolean ldapConfigPropertiesChanged = reloadLdapServerProperties();
+
+    String ldapUserSearchFilter = getLdapUserSearchFilter(userName);
+
+    if (ldapConfigPropertiesChanged|| !ldapUserSearchFilter.equals(ldapUserSearchFilterThreadLocal.get())) {
+
+      LOG.info("Either LDAP Properties or user search filter changed - rebuilding Context");
       LdapContextSource springSecurityContextSource = new LdapContextSource();
       List<String> ldapUrls = ldapServerProperties.get().getLdapUrls();
       springSecurityContextSource.setUrls(ldapUrls.toArray(new String[ldapUrls.size()]));
@@ -111,18 +124,17 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
 
       //TODO change properties
       String userSearchBase = ldapServerProperties.get().getUserSearchBase();
-      String userSearchFilter = ldapServerProperties.get().getUserSearchFilter();
-
-      FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter, springSecurityContextSource);
+      FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch(userSearchBase, ldapUserSearchFilter, springSecurityContextSource);
 
       AmbariLdapBindAuthenticator bindAuthenticator = new AmbariLdapBindAuthenticator(springSecurityContextSource, configuration);
       bindAuthenticator.setUserSearch(userSearch);
 
       LdapAuthenticationProvider authenticationProvider = new LdapAuthenticationProvider(bindAuthenticator, authoritiesPopulator);
-
       providerThreadLocal.set(authenticationProvider);
     }
 
+    ldapUserSearchFilterThreadLocal.set(ldapUserSearchFilter);
+
     return providerThreadLocal.get();
   }
 
@@ -136,6 +148,16 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
   }
 
   /**
+   * Extracts the user name from the passed authentication object.
+   * @param authentication
+   * @return
+   */
+  protected String getUserName(Authentication authentication) {
+    UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken)authentication;
+    return userToken.getName();
+  }
+
+  /**
    * Reloads LDAP Server properties from configuration
    *
    * @return true if properties were reloaded
@@ -149,4 +171,11 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider
     }
     return false;
   }
+
+
+  private String getLdapUserSearchFilter(String userName) {
+    return ldapServerProperties.get()
+      .getUserSearchFilter(AmbariLdapUtils.isUserPrincipalNameFormat(userName));
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/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 fc7f73a..7df8dc3 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
@@ -60,6 +60,8 @@ public class AmbariLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
 
   @Override
   public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
+    username = AuthorizationHelper.resolveLoginAliasToUserName(username);
+
     log.info("Get authorities for user " + username + " from local DB");
 
     UserEntity user;

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java
index ed68c01..c63ea92 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java
@@ -18,7 +18,14 @@
 package org.apache.ambari.server.security.authorization;
 
 
+import java.util.List;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+
 import org.apache.ambari.server.configuration.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.ldap.core.AttributesMapper;
 import org.springframework.ldap.core.DirContextOperations;
 import org.springframework.ldap.core.LdapTemplate;
@@ -26,16 +33,13 @@ import org.springframework.ldap.core.support.BaseLdapPathContextSource;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.ldap.authentication.BindAuthenticator;
 
-import java.util.*;
-import javax.naming.*;
-import javax.naming.directory.Attributes;
-
 
 /**
  * An authenticator which binds as a user and checks if user should get ambari
  * admin authorities according to LDAP group membership
  */
 public class AmbariLdapBindAuthenticator extends BindAuthenticator {
+  private static final Logger LOG = LoggerFactory.getLogger(AmbariLdapBindAuthenticator.class);
 
   private Configuration configuration;
 
@@ -51,8 +55,23 @@ public class AmbariLdapBindAuthenticator extends BindAuthenticator {
   public DirContextOperations authenticate(Authentication authentication) {
 
     DirContextOperations user = super.authenticate(authentication);
+    setAmbariAdminAttr(user);
 
-    return setAmbariAdminAttr(user);
+    // Users stored locally in ambari are matched against LDAP users by the ldap attribute configured to be used as user name.
+    // (e.g. uid, sAMAccount -> ambari user name )
+    String ldapUserName = user.getStringAttribute(configuration.getLdapServerProperties().getUsernameAttribute());
+    String loginName  = authentication.getName(); // user login name the user has logged in
+
+    if (!ldapUserName.equals(loginName)) {
+      // if authenticated user name is different from ldap user name than user has logged in
+      // with a login name that is different (e.g. user principal name) from the ambari user name stored in
+      // ambari db. In this case add the user login name  as login alias for ambari user name.
+      LOG.info("User with {}='{}' logged in with login alias '{}'", ldapUserName, loginName);
+
+      AuthorizationHelper.addLoginNameAlias(ldapUserName, loginName);
+    }
+
+    return user;
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java
new file mode 100644
index 0000000..ffebd45
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.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.authorization;
+
+import java.util.regex.Pattern;
+
+/**
+ * Provides utility methods for LDAP related functionality
+ */
+public class AmbariLdapUtils {
+
+  /**
+   * Regexp to verify if user login name beside user contains domain information as well (User principal name format).
+   */
+  private static final Pattern UPN_FORMAT = Pattern.compile(".+@\\w+(\\.\\w+)*");
+
+  /**
+   * Returns true if the given user name contains domain name as well (e.g. username@domain)
+   * @param loginName the login name to verify if it contains domain information.
+   * @return
+   */
+  public static boolean isUserPrincipalNameFormat(String loginName) {
+    return UPN_FORMAT.matcher(loginName).matches();
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
index 0c675b8..15ef8d0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
@@ -28,6 +28,9 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
 
 import java.util.*;
 
@@ -42,7 +45,7 @@ public class AuthorizationHelper {
    * Converts collection of RoleEntities to collection of GrantedAuthorities
    */
   public Collection<GrantedAuthority> convertPrivilegesToAuthorities(Collection<PrivilegeEntity> privilegeEntities) {
-    Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(privilegeEntities.size());
+    Set<GrantedAuthority> authorities = new HashSet<>(privilegeEntities.size());
 
     for (PrivilegeEntity privilegeEntity : privilegeEntities) {
       authorities.add(new AmbariGrantedAuthority(privilegeEntity));
@@ -247,4 +250,37 @@ public class AuthorizationHelper {
     SecurityContext context = SecurityContextHolder.getContext();
     return (context == null) ? null : context.getAuthentication();
   }
+
+  /**
+   * There are cases when users log-in with a login name that is
+   * define in LDAP and which do not correspond to the user name stored
+   * locally in ambari. These external login names act as an alias to
+   * ambari users name. This method stores in the current http session a mapping
+   * of alias user name to local ambari user name to make possible resolving
+   * login alias to ambari user name.
+   * @param ambariUserName ambari user name for which the alias is to be stored in the session
+   * @param loginAlias the alias for the ambari user name.
+   */
+  public static void addLoginNameAlias(String ambariUserName, String loginAlias) {
+    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+    if (attr != null) {
+      LOG.info("Adding login alias '{}' for user name '{}'", loginAlias, ambariUserName);
+      attr.setAttribute(loginAlias, ambariUserName, RequestAttributes.SCOPE_SESSION);
+    }
+  }
+
+  /**
+   * Looks up the provided loginAlias in the current http session and return the ambari
+   * user name that the alias is defined for.
+   * @param loginAlias the login alias to resolve to ambari user name
+   * @return the ambari user name if the alias is found otherwise returns the passed in loginAlias
+   */
+  public static String resolveLoginAliasToUserName(String loginAlias) {
+    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+    if (attr != null && attr.getAttribute(loginAlias, RequestAttributes.SCOPE_SESSION) != null) {
+      return (String)attr.getAttribute(loginAlias, RequestAttributes.SCOPE_SESSION);
+    }
+
+    return loginAlias;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/DuplicateLdapUserFoundAuthenticationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/DuplicateLdapUserFoundAuthenticationException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/DuplicateLdapUserFoundAuthenticationException.java
new file mode 100644
index 0000000..dd5c754
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/DuplicateLdapUserFoundAuthenticationException.java
@@ -0,0 +1,51 @@
+/**
+ * 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;
+
+/**
+ * This exception signals that duplicate user entries were found in LDAP during authentication.
+ * The filter used to match user entry in LDAP that corresponds to the user being authenticated
+ * should be refined to match only one entry.
+ */
+public class DuplicateLdapUserFoundAuthenticationException extends AuthenticationException {
+
+  /**
+   * Constructs an {@code DuplicateLdapUserFoundAuthenticationException} with the specified message.
+   *
+   * @param msg the detail message
+   */
+  public DuplicateLdapUserFoundAuthenticationException(String msg) {
+    super(msg);
+  }
+
+  /**
+   * Constructs an {@code DuplicateLdapUserFoundAuthenticationException} with the specified message and root cause.
+   *
+   * @param msg the detail message
+   * @param t   the root cause
+   */
+  public DuplicateLdapUserFoundAuthenticationException(String msg, Throwable t) {
+    super(msg, t);
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java
index 8eeaf35..17432d0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java
@@ -53,7 +53,8 @@ public class LdapServerProperties {
   private String userSearchBase = "";
 
   private String groupSearchFilter;
-  private static final String userSearchFilter = "(&({attribute}={0})(objectClass={userObjectClass}))";
+  private String userSearchFilter;
+  private String alternateUserSearchFilter; // alternate user search filter to be used when users use their alternate login id (e.g. User Principal Name)
 
   //LDAP pagination properties
   private boolean paginationEnabled = true;
@@ -137,10 +138,17 @@ public class LdapServerProperties {
     this.userSearchBase = userSearchBase;
   }
 
-  public String getUserSearchFilter() {
-    return userSearchFilter
-      .replace("{attribute}", usernameAttribute)
-      .replace("{userObjectClass}", userObjectClass);
+  /**
+   * Returns the LDAP filter to search users by.
+   * @param useAlternateUserSearchFilter if true than return LDAP filter that expects user name in
+   *                                  User Principal Name format to filter users constructed from {@value org.apache.ambari.server.configuration.Configuration#LDAP_ALT_USER_SEARCH_FILTER_KEY}.
+   *                                  Otherwise the filter is constructed from {@value org.apache.ambari.server.configuration.Configuration#LDAP_USER_SEARCH_FILTER_KEY}
+   * @return the LDAP filter string
+   */
+  public String getUserSearchFilter(boolean useAlternateUserSearchFilter) {
+    String filter = useAlternateUserSearchFilter ? alternateUserSearchFilter : userSearchFilter;
+
+    return resolveUserSearchFilterPlaceHolders(filter);
   }
 
   public String getUsernameAttribute() {
@@ -199,6 +207,15 @@ public class LdapServerProperties {
     this.groupSearchFilter = groupSearchFilter;
   }
 
+
+  public void setUserSearchFilter(String userSearchFilter) {
+    this.userSearchFilter = userSearchFilter;
+  }
+
+  public void setAlternateUserSearchFilter(String alternateUserSearchFilter) {
+    this.alternateUserSearchFilter = alternateUserSearchFilter;
+  }
+
   public boolean isGroupMappingEnabled() {
     return groupMappingEnabled;
   }
@@ -288,6 +305,10 @@ public class LdapServerProperties {
 
     if (paginationEnabled != that.isPaginationEnabled()) return false;
 
+    if (userSearchFilter != null ? !userSearchFilter.equals(that.userSearchFilter) : that.userSearchFilter != null) return false;
+    if (alternateUserSearchFilter != null ? !alternateUserSearchFilter.equals(that.alternateUserSearchFilter) : that.alternateUserSearchFilter != null) return false;
+
+
     return true;
   }
 
@@ -311,7 +332,20 @@ public class LdapServerProperties {
     result = 31 * result + (groupSearchFilter != null ? groupSearchFilter.hashCode() : 0);
     result = 31 * result + (dnAttribute != null ? dnAttribute.hashCode() : 0);
     result = 31 * result + (referralMethod != null ? referralMethod.hashCode() : 0);
+    result = 31 * result + (userSearchFilter != null ? userSearchFilter.hashCode() : 0);
+    result = 31 * result + (alternateUserSearchFilter != null ? alternateUserSearchFilter.hashCode() : 0);
     return result;
   }
 
+  /**
+   * Resolves known placeholders found within the given ldap user search ldap filter
+   * @param filter
+   * @return returns the filter with the resolved placeholders.
+   */
+  protected String resolveUserSearchFilterPlaceHolders(String filter) {
+    return filter
+      .replace("{usernameAttribute}", usernameAttribute)
+      .replace("{userObjectClass}", userObjectClass);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/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 3bbc785..8b44b94 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
@@ -49,5 +49,6 @@
 
   <beans:bean id="basicFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
     <beans:constructor-arg ref="authenticationManager"/>
+    <beans:constructor-arg ref="ambariEntryPoint"/>
   </beans:bean>
 </beans:beans>

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/test/java/org/apache/ambari/server/api/UserNameOverrideFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/UserNameOverrideFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/UserNameOverrideFilterTest.java
new file mode 100644
index 0000000..1e0fe90
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/UserNameOverrideFilterTest.java
@@ -0,0 +1,196 @@
+/**
+ * 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.api;
+
+import java.net.URLEncoder;
+import java.util.regex.Matcher;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+import org.easymock.Capture;
+import org.easymock.EasyMockRule;
+import org.easymock.EasyMockSupport;
+import org.easymock.Mock;
+import org.easymock.MockType;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+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.same;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(PowerMockRunner.class)               // Allow mocking static methods
+@PrepareForTest(AuthorizationHelper.class)    // This class has a static method that will be mocked
+public class UserNameOverrideFilterTest extends EasyMockSupport {
+
+  @Rule
+  public EasyMockRule mocks = new EasyMockRule(this);
+
+  @Mock(type = MockType.NICE)
+  private HttpServletRequest userRelatedRequest;
+
+  @Mock(type = MockType.NICE)
+  private ServletResponse response;
+
+  @Mock(type = MockType.NICE)
+  private FilterChain filterChain;
+
+  private UserNameOverrideFilter filter = new UserNameOverrideFilter();
+
+
+  @Test
+  public void testGetUserNameMatcherNoUserNameInUri() throws Exception {
+    // Given
+    String uri = "/aaa/bbb";
+
+    // When
+    Matcher m = filter.getUserNameMatcher(uri);
+    boolean isMatch = m.matches();
+
+    // Then
+    assertFalse(isMatch);
+  }
+
+  @Test
+  public void testGetUserNameMatcherNoPostInUri() throws Exception {
+    // Given
+    String uri = "/aaa/users/user1@domain";
+
+    // When
+    Matcher m = filter.getUserNameMatcher(uri);
+    boolean isMatch = m.find();
+
+    String pre = isMatch ? m.group("pre") : null;
+    String userName = isMatch ? m.group("username") : null;
+    String post = isMatch ? m.group("post") : null;
+
+
+    // Then
+    assertTrue(isMatch);
+
+    assertEquals("/aaa/users/", pre);
+    assertEquals("user1@domain", userName);
+    assertEquals("", post);
+  }
+
+
+
+  @Test
+  public void testGetUserNameMatcherPostInUri() throws Exception {
+    // Given
+    String uri = "/aaa/users/user1@domain/privileges";
+
+    // When
+    Matcher m = filter.getUserNameMatcher(uri);
+    boolean isMatch = m.find();
+
+    String pre = isMatch ? m.group("pre") : null;
+    String userName = isMatch ? m.group("username") : null;
+    String post = isMatch ? m.group("post") : null;
+
+
+    // Then
+    assertTrue(isMatch);
+
+    assertEquals("/aaa/users/", pre);
+    assertEquals("user1@domain", userName);
+    assertEquals("/privileges", post);
+  }
+
+  @Test
+  public void testDoFilterNoUserNameInUri() throws Exception {
+    // Given
+    expect(userRelatedRequest.getRequestURI()).andReturn("/test/test1").anyTimes();
+    filterChain.doFilter(same(userRelatedRequest), same(response));
+    expectLastCall();
+
+    replayAll();
+
+    // When
+    filter.doFilter(userRelatedRequest, response, filterChain);
+
+    // Then
+
+    verifyAll();
+  }
+
+  @Test
+  public void testDoFilterWithUserNameInUri() throws Exception {
+    // Given
+    expect(userRelatedRequest.getRequestURI()).andReturn("/test/users/testUserName/test1").anyTimes();
+
+    // filterChain should be invoked with the same req and resp as the OverrideUserName filter doesn't change these
+    filterChain.doFilter(same(userRelatedRequest), same(response));
+    expectLastCall();
+
+    replayAll();
+
+    // When
+    filter.doFilter(userRelatedRequest, response, filterChain);
+
+    // Then
+
+    verifyAll();
+  }
+
+  @Test
+  public void testDoFilterWithLoginAliasInUri() throws Exception {
+    // Given
+    expect(userRelatedRequest.getRequestURI()).andReturn(String.format("/test/users/%s/test1", URLEncoder.encode("testLoginAlias@testdomain.com", "UTF-8"))).anyTimes();
+
+    Capture<ServletRequest> requestCapture = Capture.newInstance();
+    filterChain.doFilter(capture(requestCapture), same(response));
+    expectLastCall();
+
+    PowerMock.mockStatic(AuthorizationHelper.class);
+    expect(AuthorizationHelper.resolveLoginAliasToUserName(eq("testLoginAlias@testdomain.com"))).andReturn("testuser1");
+
+    PowerMock.replay(AuthorizationHelper.class);
+    replayAll();
+
+    // When
+    filter.doFilter(userRelatedRequest, response, filterChain);
+
+    // Then
+    HttpServletRequest updatedRequest = (HttpServletRequest)requestCapture.getValue();
+    assertEquals("testLoginAlias@testdomain.com login alias in the request Uri should be resolved to testuser1 user name !", "/test/users/testuser1/test1", updatedRequest.getRequestURI());
+
+    PowerMock.verify(AuthorizationHelper.class);
+    verifyAll();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    resetAll();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java b/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
index 3ecb5aa..99ec786 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
@@ -658,4 +658,60 @@ public class ConfigurationTest {
     Assert.assertEquals(actualCacheEnabledConfig, Configuration.SERVER_HRC_STATUS_SUMMARY_CACHE_ENABLED_DEFAULT);
   }
 
+  @Test
+  public void testLdapUserSearchFilterDefault() throws Exception {
+    // Given
+    final Properties ambariProperties = new Properties();
+    final Configuration configuration = new Configuration(ambariProperties);
+
+    // When
+    String actualLdapUserSearchFilter = configuration.getLdapServerProperties().getUserSearchFilter(false);
+
+    // Then
+    Assert.assertEquals("(&(uid={0})(objectClass=person))", actualLdapUserSearchFilter);
+  }
+
+  @Test
+  public void testLdapUserSearchFilter() throws Exception {
+    // Given
+    final Properties ambariProperties = new Properties();
+    final Configuration configuration = new Configuration(ambariProperties);
+    ambariProperties.setProperty(Configuration.LDAP_USERNAME_ATTRIBUTE_KEY, "test_uid");
+    ambariProperties.setProperty(Configuration.LDAP_USER_SEARCH_FILTER_KEY, "{usernameAttribute}={0}");
+
+    // When
+    String actualLdapUserSearchFilter = configuration.getLdapServerProperties().getUserSearchFilter(false);
+
+    // Then
+    Assert.assertEquals("test_uid={0}", actualLdapUserSearchFilter);
+  }
+
+  @Test
+  public void testAlternateLdapUserSearchFilterDefault() throws Exception {
+    // Given
+    final Properties ambariProperties = new Properties();
+    final Configuration configuration = new Configuration(ambariProperties);
+
+    // When
+    String actualLdapUserSearchFilter = configuration.getLdapServerProperties().getUserSearchFilter(true);
+
+    // Then
+    Assert.assertEquals("(&(userPrincipalName={0})(objectClass=person))", actualLdapUserSearchFilter);
+  }
+
+  @Test
+  public void testAlternatLdapUserSearchFilter() throws Exception {
+    // Given
+    final Properties ambariProperties = new Properties();
+    final Configuration configuration = new Configuration(ambariProperties);
+    ambariProperties.setProperty(Configuration.LDAP_USERNAME_ATTRIBUTE_KEY, "test_uid");
+    ambariProperties.setProperty(Configuration.LDAP_ALT_USER_SEARCH_FILTER_KEY, "{usernameAttribute}={5}");
+
+    // When
+    String actualLdapUserSearchFilter = configuration.getLdapServerProperties().getUserSearchFilter(true);
+
+    // Then
+    Assert.assertEquals("test_uid={5}", actualLdapUserSearchFilter);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java
new file mode 100644
index 0000000..b2778ae
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java
@@ -0,0 +1,87 @@
+/**
+ * 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;
+
+import org.apache.ambari.server.security.authorization.AmbariLdapUtils;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+public class AmbariLdapUtilsTest {
+
+  @Test
+  public void testIsUserPrincipalNameFormat_True() throws Exception {
+    // Given
+    String testLoginName = "testuser@domain1.d_1.com";
+
+    // When
+    boolean isUserPrincipalNameFormat = AmbariLdapUtils.isUserPrincipalNameFormat(testLoginName);
+
+    // Then
+    assertTrue(isUserPrincipalNameFormat);
+  }
+
+  @Test
+  public void testIsUserPrincipalNameFormatMultipleAtSign_True() throws Exception {
+    // Given
+    String testLoginName = "@testuser@domain1.d_1.com";
+
+    // When
+    boolean isUserPrincipalNameFormat = AmbariLdapUtils.isUserPrincipalNameFormat(testLoginName);
+
+    // Then
+    assertTrue(isUserPrincipalNameFormat);
+  }
+
+  @Test
+  public void testIsUserPrincipalNameFormat_False() throws Exception {
+    // Given
+    String testLoginName = "testuser";
+
+    // When
+    boolean isUserPrincipalNameFormat = AmbariLdapUtils.isUserPrincipalNameFormat(testLoginName);
+
+    // Then
+    assertFalse(isUserPrincipalNameFormat);
+  }
+
+  @Test
+  public void testIsUserPrincipalNameFormatWithAtSign_False() throws Exception {
+    // Given
+    String testLoginName = "@testuser";
+
+    // When
+    boolean isUserPrincipalNameFormat = AmbariLdapUtils.isUserPrincipalNameFormat(testLoginName);
+
+    // Then
+    assertFalse(isUserPrincipalNameFormat);
+  }
+
+  @Test
+  public void testIsUserPrincipalNameFormatWithAtSign1_False() throws Exception {
+    // Given
+    String testLoginName = "testuser@";
+
+    // When
+    boolean isUserPrincipalNameFormat = AmbariLdapUtils.isUserPrincipalNameFormat(testLoginName);
+
+    // Then
+    assertFalse(isUserPrincipalNameFormat);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java
new file mode 100644
index 0000000..19656b1
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java
@@ -0,0 +1,333 @@
+/**
+ * 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 java.security.Principal;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.easymock.EasyMockRule;
+import org.easymock.EasyMockSupport;
+import org.easymock.Mock;
+import org.easymock.MockType;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.verify;
+
+public class AmbariAuthenticationTest extends EasyMockSupport {
+
+  @Rule
+  public EasyMockRule mocks = new EasyMockRule(this);
+
+  @Mock(type = MockType.NICE)
+  private ServletRequestAttributes servletRequestAttributes;
+
+  @Mock(type = MockType.NICE)
+  private Authentication testAuthentication;
+
+  @Before
+  public void setUp() {
+    resetAll();
+
+    RequestContextHolder.setRequestAttributes(servletRequestAttributes);
+
+  }
+
+  @Test
+  public void testGetPrincipalNoOverride() throws Exception {
+    // Given
+    Principal origPrincipal = new Principal() {
+      @Override
+      public String getName() {
+        return "user";
+      }
+    };
+
+    Authentication authentication = new TestingAuthenticationToken(origPrincipal, "password");
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    Object principal = ambariAuthentication.getPrincipal();
+
+    // Then
+    assertSame(origPrincipal, principal);
+  }
+
+
+  @Test
+  public void testGetPrincipal() throws Exception {
+    // Given
+    Authentication authentication = new TestingAuthenticationToken("user", "password");
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    Object principal = ambariAuthentication.getPrincipal();
+
+    // Then
+    assertEquals("user", principal);
+  }
+
+  @Test
+  public void testGetPrincipalWithLoginAlias() throws Exception {
+    // Given
+    Authentication authentication = new TestingAuthenticationToken("loginAlias", "password");
+    expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION)))
+      .andReturn("user").atLeastOnce();
+
+    replayAll();
+
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    verifyAll();
+    Object principal = ambariAuthentication.getPrincipal();
+
+    // Then
+    assertEquals("user", principal);
+  }
+
+  @Test
+  public void testGetUserDetailPrincipal() throws Exception {
+    // Given
+    UserDetails userDetails = new User("user", "password", Collections.<GrantedAuthority>emptyList());
+    Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword());
+
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    Object principal = ambariAuthentication.getPrincipal();
+
+    // Then
+    assertEquals(userDetails, principal);
+  }
+
+  @Test
+  public void testGetUserDetailPrincipalWithLoginAlias() throws Exception {
+    // Given
+    UserDetails userDetails = new User("loginAlias", "password", Collections.<GrantedAuthority>emptyList());
+    Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword());
+
+    expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION)))
+      .andReturn("user").atLeastOnce();
+
+    replayAll();
+
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    Object principal = ambariAuthentication.getPrincipal();
+
+    // Then
+    verify();
+    UserDetails expectedUserDetails = new User("user", "password", Collections.<GrantedAuthority>emptyList()); // user detail with login alias resolved
+
+    assertEquals(expectedUserDetails, principal);
+  }
+
+
+
+  @Test
+  public void testGetNameNoOverride () throws Exception {
+    // Given
+    Principal origPrincipal = new Principal() {
+      @Override
+      public String getName() {
+        return "user1";
+      }
+    };
+    Authentication authentication = new TestingAuthenticationToken(origPrincipal, "password");
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    String name = ambariAuthentication.getName();
+
+    // Then
+    assertEquals("user1", name);
+  }
+
+  @Test
+  public void testGetName() throws Exception {
+    // Given
+    Authentication authentication = new TestingAuthenticationToken("user", "password");
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    String name = ambariAuthentication.getName();
+
+    // Then
+    assertEquals("user", name);
+  }
+
+  @Test
+  public void testGetNameWithLoginAlias() throws Exception {
+    // Given
+    Authentication authentication = new TestingAuthenticationToken("loginAlias", "password");
+    expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION)))
+      .andReturn("user").atLeastOnce();
+
+    replayAll();
+
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    String name = ambariAuthentication.getName();
+
+    // Then
+    verifyAll();
+    assertEquals("user", name);
+  }
+
+  @Test
+  public void testGetNameWithUserDetailsPrincipal() throws Exception {
+    // Given
+    UserDetails userDetails = new User("user", "password", Collections.<GrantedAuthority>emptyList());
+    Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword());
+
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    String name = ambariAuthentication.getName();
+
+    // Then
+    assertEquals("user", name);
+  }
+
+  @Test
+  public void testGetNameWithUserDetailsPrincipalWithLoginAlias() throws Exception {
+    // Given
+    UserDetails userDetails = new User("loginAlias", "password", Collections.<GrantedAuthority>emptyList());
+    Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword());
+
+    expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION)))
+      .andReturn("user").atLeastOnce();
+
+    replayAll();
+
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    String name = ambariAuthentication.getName();
+
+    // Then
+    verifyAll();
+    assertEquals("user", name);
+  }
+
+  @Test
+  public void testGetAuthorities() throws Exception {
+    // Given
+    Authentication authentication = new TestingAuthenticationToken("user", "password", "test_role");
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    Collection<?>  grantedAuthorities =  ambariAuthentication.getAuthorities();
+
+    // Then
+    Collection<?> expectedAuthorities = authentication.getAuthorities();
+
+    assertSame(expectedAuthorities, grantedAuthorities);
+  }
+
+  @Test
+  public void testGetCredentials() throws Exception {
+    // Given
+    String passord = "password";
+    Authentication authentication = new TestingAuthenticationToken("user", passord);
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    Object credentials = ambariAuthentication.getCredentials();
+
+    // Then
+    assertSame(passord, credentials);
+  }
+
+  @Test
+  public void testGetDetails() throws Exception {
+    // Given
+    TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password");
+    authentication.setDetails("test auth details");
+    Authentication ambariAuthentication = new AmbariAuthentication(authentication);
+
+    // When
+    Object authDetails = ambariAuthentication.getDetails();
+
+    // Then
+    Object expecteAuthDetails = authentication.getDetails();
+
+    assertSame(expecteAuthDetails, authDetails);
+  }
+
+  @Test
+  public void testIsAuthenticated() throws Exception {
+    // Given
+    expect(testAuthentication.isAuthenticated()).andReturn(false).once();
+
+    replayAll();
+
+    Authentication ambariAuthentication = new AmbariAuthentication(testAuthentication);
+
+    // When
+    ambariAuthentication.isAuthenticated();
+
+    // Then
+    verifyAll();
+  }
+
+  @Test
+  public void setTestAuthentication() throws Exception {
+    // Given
+    testAuthentication.setAuthenticated(true);
+    expectLastCall().once();
+
+    replayAll();
+
+    Authentication ambariAuthentication = new AmbariAuthentication(testAuthentication);
+
+    // When
+    ambariAuthentication.setAuthenticated(true);
+
+    // Then
+    verifyAll();
+  }
+
+  @Test
+  public void testEquals() throws Exception {
+    EqualsVerifier.forClass(AmbariAuthentication.class)
+      .verify();
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java
new file mode 100644
index 0000000..f5d1412
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java
@@ -0,0 +1,100 @@
+/**
+ * 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 java.util.Properties;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.ApplyLdifFiles;
+import org.apache.directory.server.core.annotations.ContextEntry;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.annotations.CreatePartition;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.easymock.EasyMockRule;
+import org.easymock.Mock;
+import org.easymock.MockType;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+
+import com.google.inject.Inject;
+
+@RunWith(FrameworkRunner.class)
+@CreateDS(allowAnonAccess = true,
+  name = "Test",
+  partitions = {
+    @CreatePartition(name = "Root",
+      suffix = "dc=apache,dc=org",
+      contextEntry = @ContextEntry(
+        entryLdif =
+            "dn: dc=apache,dc=org\n" +
+            "dc: apache\n" +
+            "objectClass: top\n" +
+            "objectClass: domain\n\n" +
+            "dn: dc=ambari,dc=apache,dc=org\n" +
+            "dc: ambari\n" +
+            "objectClass: top\n" +
+            "objectClass: domain\n\n"))
+  })
+@CreateLdapServer(allowAnonymousAccess = true,
+  transports = {@CreateTransport(protocol = "LDAP", port = 33389)})
+@ApplyLdifFiles("users_with_duplicate_uid.ldif")
+public class AmbariLdapAuthenticationProviderForDuplicateUserTest extends AmbariLdapAuthenticationProviderBaseTest {
+
+  @Rule
+  public EasyMockRule mocks = new EasyMockRule(this);
+
+  @Mock(type = MockType.NICE)
+  private AmbariLdapAuthoritiesPopulator authoritiesPopulator;
+
+  private AmbariLdapAuthenticationProvider authenticationProvider;
+
+  @Before
+  public void setUp() {
+    Properties properties = new Properties();
+    properties.setProperty(Configuration.CLIENT_SECURITY_KEY, "ldap");
+    properties.setProperty(Configuration.SERVER_PERSISTENCE_TYPE_KEY, "in-memory");
+    properties.setProperty(Configuration.METADATA_DIR_PATH,"src/test/resources/stacks");
+    properties.setProperty(Configuration.SERVER_VERSION_FILE,"src/test/resources/version");
+    properties.setProperty(Configuration.OS_VERSION_KEY,"centos5");
+    properties.setProperty(Configuration.SHARED_RESOURCES_DIR_KEY, "src/test/resources/");
+    properties.setProperty(Configuration.LDAP_BASE_DN_KEY, "dc=apache,dc=org");
+
+    Configuration configuration = new Configuration(properties);
+
+    authenticationProvider = new AmbariLdapAuthenticationProvider(configuration, authoritiesPopulator);
+  }
+
+  @Test(expected = DuplicateLdapUserFoundAuthenticationException.class)
+  public void testAuthenticateDuplicateUser() throws Exception {
+    // Given
+    Authentication authentication = new UsernamePasswordAuthenticationToken("user_dup", "password");
+
+    // When
+    authenticationProvider.authenticate(authentication);
+
+    // Then
+    // DuplicateLdapUserFoundAuthenticationException should be thrown
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java
index d48be85..b26494c 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java
@@ -49,6 +49,8 @@ import org.slf4j.Logger;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
+
 import static org.easymock.EasyMock.*;
 
 import static org.junit.Assert.*;
@@ -90,6 +92,7 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati
     injector.injectMembers(this);
     injector.getInstance(GuiceJpaInitializer.class);
     configuration.setClientSecurityType(ClientSecurityType.LDAP);
+    configuration.setProperty(Configuration.LDAP_ALT_USER_SEARCH_FILTER_KEY, "(&(mail={0})(objectClass={userObjectClass}))");
   }
 
   @After
@@ -116,7 +119,7 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati
     expect(exception.getCause()).andReturn(exception).atLeastOnce();
 
     expect(provider.isLdapEnabled()).andReturn(true);
-    expect(provider.loadLdapAuthenticationProvider()).andThrow(exception);
+    expect(provider.loadLdapAuthenticationProvider("notFound")).andThrow(exception);
     // Logging call
     Logger log = createNiceMock(Logger.class);
     provider.LOG = log;
@@ -155,7 +158,7 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati
     expect(exception.getCause()).andReturn(cause).atLeastOnce();
 
     expect(provider.isLdapEnabled()).andReturn(true);
-    expect(provider.loadLdapAuthenticationProvider()).andThrow(exception);
+    expect(provider.loadLdapAuthenticationProvider("notFound")).andThrow(exception);
     // Logging call
     Logger log = createNiceMock(Logger.class);
     provider.LOG = log;
@@ -189,4 +192,47 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati
     Authentication auth = authenticationProvider.authenticate(authentication);
     Assert.assertTrue(auth == null);
   }
+
+  @Test
+  public void testAuthenticateLoginAlias() throws Exception {
+    // Given
+    assertNull("User alread exists in DB", userDAO.findLdapUserByName("allowedUser"));
+    Authentication authentication = new UsernamePasswordAuthenticationToken("allowedUser@ambari.apache.org", "password");
+
+
+    // When
+    Authentication result = authenticationProvider.authenticate(authentication);
+
+    // Then
+    assertTrue(result.isAuthenticated());
+  }
+
+  @Test(expected = BadCredentialsException.class)
+  public void testBadCredentialsForMissingLoginAlias() throws Exception {
+    // Given
+    assertNull("User alread exists in DB", userDAO.findLdapUserByName("allowedUser"));
+    Authentication authentication = new UsernamePasswordAuthenticationToken("missingloginalias@ambari.apache.org", "password");
+
+
+    // When
+    authenticationProvider.authenticate(authentication);
+
+    // Then
+    // BadCredentialsException should be thrown due to no user with 'missingloginalias@ambari.apache.org'  is found in ldap
+  }
+
+
+  @Test(expected = BadCredentialsException.class)
+  public void testBadCredentialsBadPasswordForLoginAlias() throws Exception {
+    // Given
+    assertNull("User alread exists in DB", userDAO.findLdapUserByName("allowedUser"));
+    Authentication authentication = new UsernamePasswordAuthenticationToken("allowedUser@ambari.apache.org", "bad_password");
+
+
+    // When
+    authenticationProvider.authenticate(authentication);
+
+    // Then
+    // BadCredentialsException should be thrown due to wrong password
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/71b4c624/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java
new file mode 100644
index 0000000..27e62e2
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java
@@ -0,0 +1,136 @@
+
+/**
+ * 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 java.util.Properties;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.ApplyLdifFiles;
+import org.apache.directory.server.core.annotations.ContextEntry;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.annotations.CreatePartition;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.easymock.EasyMockRule;
+import org.easymock.Mock;
+import org.easymock.MockType;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.support.LdapContextSource;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
+import org.springframework.security.ldap.search.LdapUserSearch;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.junit.Assert.assertEquals;
+
+
+@RunWith(FrameworkRunner.class)
+@CreateDS(allowAnonAccess = true,
+  name = "Test",
+  partitions = {
+    @CreatePartition(name = "Root",
+      suffix = "dc=apache,dc=org",
+      contextEntry = @ContextEntry(
+        entryLdif =
+          "dn: dc=apache,dc=org\n" +
+            "dc: apache\n" +
+            "objectClass: top\n" +
+            "objectClass: domain\n\n" +
+            "dn: dc=ambari,dc=apache,dc=org\n" +
+            "dc: ambari\n" +
+            "objectClass: top\n" +
+            "objectClass: domain\n\n"))
+  })
+@CreateLdapServer(allowAnonymousAccess = true,
+  transports = {@CreateTransport(protocol = "LDAP", port = 33389)})
+@ApplyLdifFiles("users.ldif")
+public class AmbariLdapBindAuthenticatorTest extends AmbariLdapAuthenticationProviderBaseTest {
+
+  @Rule
+  public EasyMockRule mocks = new EasyMockRule(this);
+
+  @Mock(type = MockType.NICE)
+  private ServletRequestAttributes servletRequestAttributes;
+
+  @Before
+  public void setUp() {
+    resetAll();
+  }
+
+  @Test
+  public void testAuthenticateWithLoginAlias() throws Exception {
+    // Given
+
+    LdapContextSource ldapCtxSource = new LdapContextSource();
+    ldapCtxSource.setUrls(new String[] {"ldap://localhost:33389"});
+    ldapCtxSource.setBase("dc=ambari,dc=apache,dc=org");
+    ldapCtxSource.afterPropertiesSet();
+
+    Properties properties = new Properties();
+    properties.setProperty(Configuration.CLIENT_SECURITY_KEY, "ldap");
+    properties.setProperty(Configuration.SERVER_PERSISTENCE_TYPE_KEY, "in-memory");
+    properties.setProperty(Configuration.METADATA_DIR_PATH,"src/test/resources/stacks");
+    properties.setProperty(Configuration.SERVER_VERSION_FILE,"src/test/resources/version");
+    properties.setProperty(Configuration.OS_VERSION_KEY,"centos5");
+    properties.setProperty(Configuration.SHARED_RESOURCES_DIR_KEY, "src/test/resources/");
+    properties.setProperty(Configuration.LDAP_BASE_DN_KEY, "dc=ambari,dc=apache,dc=org");
+
+    Configuration configuration = new Configuration(properties);
+
+    AmbariLdapBindAuthenticator bindAuthenticator = new AmbariLdapBindAuthenticator(ldapCtxSource, configuration);
+
+    LdapUserSearch userSearch = new FilterBasedLdapUserSearch("", "(&(cn={0})(objectClass=person))", ldapCtxSource);
+    bindAuthenticator.setUserSearch(userSearch);
+
+    // JohnSmith is a login alias for deniedUser username
+    String loginAlias = "JohnSmith";
+    String userName = "deniedUser";
+
+    Authentication authentication = new UsernamePasswordAuthenticationToken(loginAlias, "password");
+
+    RequestContextHolder.setRequestAttributes(servletRequestAttributes);
+
+    servletRequestAttributes.setAttribute(eq(loginAlias), eq(userName), eq(RequestAttributes.SCOPE_SESSION));
+    expectLastCall().once();
+
+    replayAll();
+
+    // When
+
+    DirContextOperations user = bindAuthenticator.authenticate(authentication);
+
+    // Then
+
+    verifyAll();
+
+    String ldapUserNameAttribute = configuration.getLdapServerProperties().getUsernameAttribute();
+
+    assertEquals(userName, user.getStringAttribute(ldapUserNameAttribute));
+  }
+}


Mime
View raw message