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

rlevas pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new b6a3341  [AMBARI-24985] Handle requests from a configured trusted 
proxy to identify a proxied user using Kerberos
b6a3341 is described below

commit b6a33413e518a20e186609996b34671d33573c51
Author: Robert Levas <rle...@users.noreply.github.com>
AuthorDate: Mon Dec 3 12:17:10 2018 -0500

    [AMBARI-24985] Handle requests from a configured trusted proxy to identify 
a proxied user using Kerberos
    
    * [AMBARI-24985] Handle requests from a configured trusted proxy to 
identify a proxied user using Kerberos
    
    * [AMBARI-24985] Handle requests from a configured trusted proxy to 
identify a proxied user using Kerberos
---
 .../ambari/server/api/predicate/QueryLexer.java    |   2 +
 .../configuration/spring/ApiSecurityConfig.java    |  24 +-
 .../AmbariBasicAuthenticationFilter.java           |  28 +-
 .../AmbariLocalAuthenticationProvider.java         |   2 +-
 .../AmbariProxiedUserDetailsImpl.java              |  96 +++++++
 .../authentication/AmbariProxyUserDetails.java     |  41 +++
 .../authentication/AmbariProxyUserDetailsImpl.java |  50 ++++
 .../security/authentication/AmbariUserDetails.java |  67 +----
 ...UserDetails.java => AmbariUserDetailsImpl.java} |  10 +-
 .../jwt/AmbariJwtAuthenticationProvider.java       |   3 +-
 .../AmbariAuthToLocalUserDetailsService.java       |  50 ++--
 .../AmbariKerberosAuthenticationFilter.java        |  14 +-
 .../AmbariKerberosAuthenticationProvider.java      | 126 ++++++++
 .../kerberos/AmbariProxiedUserDetailsService.java  | 320 +++++++++++++++++++++
 .../AmbariProxyUserKerberosDetailsImpl.java        |  40 +++
 .../pam/AmbariPamAuthenticationProvider.java       |   3 +-
 .../tproxy/AmbariTProxyConfiguration.java          |   2 +-
 .../tproxy/TrustedProxyAuthenticationDetails.java  | 102 +++++++
 .../TrustedProxyAuthenticationDetailsSource.java   |  36 +++
 ...stedProxyAuthenticationNotAllowedException.java |  32 +++
 .../AmbariLdapAuthenticationProvider.java          |   3 +-
 .../AmbariUserAuthorizationFilter.java             |   3 +-
 .../apache/ambari/server/utils/RequestUtils.java   | 122 ++++++++
 .../server/security/SecurityHelperImplTest.java    |   4 +-
 .../server/security/TestAuthenticationFactory.java |   4 +-
 .../AmbariKerberosAuthenticationFilterTest.java    |   8 +
 .../AmbariProxiedUserDetailsServiceTest.java       | 204 +++++++++++++
 .../authorization/AuthorizationHelperTest.java     |   4 +-
 .../ambari/server/utils/RequestUtilsTest.java      | 172 ++++++++++-
 29 files changed, 1443 insertions(+), 129 deletions(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
index b72bc48..ba42398 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
@@ -53,6 +53,7 @@ public class QueryLexer {
   public static final String QUERY_FROM      = "from";
   public static final String QUERY_MINIMAL   = "minimal_response";
   public static final String QUERY_SORT      = "sortBy";
+  public static final String QUERY_DOAS      = "doAs";
 
   /**
    * All valid deliminators.
@@ -216,6 +217,7 @@ public class QueryLexer {
     SET_IGNORE.add(QUERY_FROM);
     SET_IGNORE.add(QUERY_MINIMAL);
     SET_IGNORE.add(QUERY_SORT);
+    SET_IGNORE.add(QUERY_DOAS);
     SET_IGNORE.add("_");
   }
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/spring/ApiSecurityConfig.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/spring/ApiSecurityConfig.java
index 9401d1a..c551e5e 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/spring/ApiSecurityConfig.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/spring/ApiSecurityConfig.java
@@ -22,7 +22,9 @@ import 
org.apache.ambari.server.security.authentication.AmbariDelegatingAuthenti
 import 
org.apache.ambari.server.security.authentication.AmbariLocalAuthenticationProvider;
 import 
org.apache.ambari.server.security.authentication.jwt.AmbariJwtAuthenticationProvider;
 import 
org.apache.ambari.server.security.authentication.kerberos.AmbariAuthToLocalUserDetailsService;
+import 
org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosAuthenticationProvider;
 import 
org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosTicketValidator;
+import 
org.apache.ambari.server.security.authentication.kerberos.AmbariProxiedUserDetailsService;
 import 
org.apache.ambari.server.security.authentication.pam.AmbariPamAuthenticationProvider;
 import 
org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter;
 import 
org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider;
@@ -37,7 +39,6 @@ import 
org.springframework.security.config.annotation.authentication.builders.Au
 import 
org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import 
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import 
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import 
org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
 import 
org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import 
org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
 
@@ -67,15 +68,14 @@ public class ApiSecurityConfig extends 
WebSecurityConfigurerAdapter{
                                              AmbariLocalAuthenticationProvider 
ambariLocalAuthenticationProvider,
                                              AmbariLdapAuthenticationProvider 
ambariLdapAuthenticationProvider,
                                              
AmbariInternalAuthenticationProvider ambariInternalAuthenticationProvider,
-                                             
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider
+                                             
AmbariKerberosAuthenticationProvider ambariKerberosAuthenticationProvider
   ) {
     auth.authenticationProvider(ambariJwtAuthenticationProvider)
         .authenticationProvider(ambariPamAuthenticationProvider)
         .authenticationProvider(ambariLocalAuthenticationProvider)
         .authenticationProvider(ambariLdapAuthenticationProvider)
         .authenticationProvider(ambariInternalAuthenticationProvider)
-        .authenticationProvider(kerberosServiceAuthenticationProvider);
-
+        .authenticationProvider(ambariKerberosAuthenticationProvider);
   }
 
   @Override
@@ -98,17 +98,13 @@ public class ApiSecurityConfig extends 
WebSecurityConfigurerAdapter{
   }
 
   @Bean
-  public KerberosServiceAuthenticationProvider 
kerberosServiceAuthenticationProvider(
+  public AmbariKerberosAuthenticationProvider 
ambariKerberosAuthenticationProvider(
       AmbariKerberosTicketValidator ambariKerberosTicketValidator,
-      AmbariAuthToLocalUserDetailsService userDetailsService) {
-
-    KerberosServiceAuthenticationProvider 
kerberosServiceAuthenticationProvider =
-        new KerberosServiceAuthenticationProvider();
+      AmbariAuthToLocalUserDetailsService authToLocalUserDetailsService,
+      AmbariProxiedUserDetailsService proxiedUserDetailsService) {
 
-    
kerberosServiceAuthenticationProvider.setTicketValidator(ambariKerberosTicketValidator);
-    
kerberosServiceAuthenticationProvider.setUserDetailsService(userDetailsService);
-
-    return kerberosServiceAuthenticationProvider;
+    return new 
AmbariKerberosAuthenticationProvider(authToLocalUserDetailsService,
+        proxiedUserDetailsService,
+        ambariKerberosTicketValidator);
   }
-
 }
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 32ab3a8..f7936a1 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
@@ -25,6 +25,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.ambari.server.security.AmbariEntryPoint;
+import org.apache.ambari.server.utils.RequestUtils;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.core.annotation.Order;
@@ -63,7 +65,7 @@ public class AmbariBasicAuthenticationFilter extends 
BasicAuthenticationFilter i
                                          AmbariAuthenticationEventHandler 
eventHandler) {
     super(authenticationManager, ambariEntryPoint);
 
-    if(eventHandler == null) {
+    if (eventHandler == null) {
       throw new IllegalArgumentException("The AmbariAuthenticationEventHandler 
must not be null");
     }
 
@@ -90,8 +92,28 @@ public class AmbariBasicAuthenticationFilter extends 
BasicAuthenticationFilter i
    */
   @Override
   public boolean shouldApply(HttpServletRequest httpServletRequest) {
+
+    if (LOG.isDebugEnabled()) {
+      RequestUtils.logRequestHeadersAndQueryParams(httpServletRequest, LOG);
+    }
+
     String header = httpServletRequest.getHeader("Authorization");
-    return (header != null) && header.startsWith("Basic ");
+    if ((header != null) && header.startsWith("Basic ")) {
+      // If doAs is sent as a query parameter, ignore the basic auth header.
+      // This logic is here to help deal with a potential issue when Knox is 
the trusted proxy and it
+      // forwards the original request's Authorization header (for Basic Auth) 
when Kerberos authentication
+      // is required.
+      String doAsQueryParameterValue = 
RequestUtils.getQueryStringParameterValue(httpServletRequest, "doAs");
+      if (StringUtils.isEmpty(doAsQueryParameterValue)) {
+        return true;
+      } else {
+        LOG.warn("The 'doAs' query parameter was provided; however, the 
BasicAuth header is found. " +
+            "Ignoring the BasicAuth header hoping to negotiate Kerberos 
authentication.");
+        return false;
+      }
+    } else {
+      return false;
+    }
   }
 
   @Override
@@ -104,7 +126,7 @@ public class AmbariBasicAuthenticationFilter extends 
BasicAuthenticationFilter i
    *
    * @param httpServletRequest  the request
    * @param httpServletResponse the response
-   * @param filterChain           the Spring filter chain
+   * @param filterChain         the Spring filter chain
    * @throws IOException
    * @throws ServletException
    */
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java
index b8958cd..f8bfd4b 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java
@@ -93,7 +93,7 @@ public class AmbariLocalAuthenticationProvider extends 
AmbariAuthenticationProvi
           }
         }
 
-        AmbariUserDetails userDetails = new 
AmbariUserDetails(users.getUser(userEntity), password, 
users.getUserAuthorities(userEntity));
+        AmbariUserDetails userDetails = new 
AmbariUserDetailsImpl(users.getUser(userEntity), password, 
users.getUserAuthorities(userEntity));
         return new AmbariUserAuthentication(password, userDetails, true);
       }
     }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxiedUserDetailsImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxiedUserDetailsImpl.java
new file mode 100644
index 0000000..0ec0b5a
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxiedUserDetailsImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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 java.util.Collection;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * AmbariProxiedUserDetailsImpl is an {@link AmbariUserDetails} implementation 
that contains details
+ * about the proxied user as well as the proxy user.
+ * <p>
+ * This data can be helpful with logging information about the user performing 
an action.
+ */
+public class AmbariProxiedUserDetailsImpl implements AmbariUserDetails {
+
+  /**
+   * Details about the acting user.
+   * <p>
+   * This user was specified as the doAs or proxied user during a trusted 
proxy authentication attempt
+   */
+  private final UserDetails proxiedUserDetails;
+
+  /**
+   * Details about the proxy user that was authenticated.
+   * <p>
+   * This user that authenticated wth Ambari but specified a doAs or proxied 
user to be used as the acting user for operations.
+   */
+  private final AmbariProxyUserDetails proxyUserDetails;
+
+  public AmbariProxiedUserDetailsImpl(UserDetails proxiedUserDetails, 
AmbariProxyUserDetails proxyUserDetails) {
+    this.proxiedUserDetails = proxiedUserDetails;
+    this.proxyUserDetails = proxyUserDetails;
+  }
+
+  @Override
+  public Integer getUserId() {
+    return (proxiedUserDetails instanceof AmbariUserDetails) ? 
((AmbariUserDetails) proxiedUserDetails).getUserId() : null;
+  }
+
+  @Override
+  public Collection<? extends GrantedAuthority> getAuthorities() {
+    return (proxiedUserDetails == null) ? null : 
proxiedUserDetails.getAuthorities();
+  }
+
+  @Override
+  public String getPassword() {
+    return (proxiedUserDetails == null) ? null : 
proxiedUserDetails.getPassword();
+  }
+
+  @Override
+  public String getUsername() {
+    return (proxiedUserDetails == null) ? null : 
proxiedUserDetails.getUsername();
+  }
+
+  @Override
+  public boolean isAccountNonExpired() {
+    return (proxiedUserDetails != null) && 
proxiedUserDetails.isAccountNonExpired();
+  }
+
+  @Override
+  public boolean isAccountNonLocked() {
+    return (proxiedUserDetails != null) && 
proxiedUserDetails.isAccountNonLocked();
+  }
+
+  @Override
+  public boolean isCredentialsNonExpired() {
+    return (proxiedUserDetails != null) && 
proxiedUserDetails.isCredentialsNonExpired();
+  }
+
+  @Override
+  public boolean isEnabled() {
+    return (proxiedUserDetails != null) && proxiedUserDetails.isEnabled();
+  }
+
+  public AmbariProxyUserDetails getProxyUserDetails() {
+    return proxyUserDetails;
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetails.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetails.java
new file mode 100644
index 0000000..9026654
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetails.java
@@ -0,0 +1,41 @@
+/*
+ * 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.apache.ambari.server.security.authorization.UserAuthenticationType;
+
+/**
+ * AmbariProxyUserDetails contains information about the proxy user 
authenticated during a trusted
+ * proxy authentication attempt.
+ */
+public interface AmbariProxyUserDetails {
+  /**
+   * Returns the local username of the authenticated proxy user.
+   *
+   * @return the local username of the authenticated proxy user
+   */
+  String getUsername();
+
+  /**
+   * Returns the authentication type used to peform authentication.
+   *
+   * @return a {@link UserAuthenticationType}
+   */
+  UserAuthenticationType getAuthenticationType();
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetailsImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetailsImpl.java
new file mode 100644
index 0000000..8353035
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetailsImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.apache.ambari.server.security.authorization.UserAuthenticationType;
+
+/**
+ * AmbariProxyUserDetailsImpl is a general implementation of a {@link 
AmbariProxyUserDetails}.
+ */
+public class AmbariProxyUserDetailsImpl implements AmbariProxyUserDetails {
+  private final String username;
+  private final UserAuthenticationType authenticationType;
+
+  /**
+   * Constructor
+   *
+   * @param username           the local username
+   * @param authenticationType the {@link UserAuthenticationType}
+   */
+  public AmbariProxyUserDetailsImpl(String username, UserAuthenticationType 
authenticationType) {
+    this.username = username;
+    this.authenticationType = authenticationType;
+  }
+
+  @Override
+  public String getUsername() {
+    return username;
+  }
+
+  @Override
+  public UserAuthenticationType getAuthenticationType() {
+    return authenticationType;
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
index e2c40d4..8f32b25 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
@@ -17,70 +17,13 @@
  */
 package org.apache.ambari.server.security.authentication;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.ambari.server.security.authorization.User;
-import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 
 /**
- * AmbariUserDetails is an implementation of {@link UserDetails} that contains 
information about
- * an authenticated user needed specifically by Ambari.  For example, the 
user's <code>userId</code>.
- * <p>
- * Ideally instances of this class are used as the value returned by {@link 
org.springframework.security.core.Authentication#getPrincipal()}
+ * AmbariUserDetails implementations are extensions of {@link UserDetails} 
that contain information
+ * about the authenticated user that is needed specifically by Ambari.  For 
example, the user's
+ * <code>userId</code>.
  */
-public class AmbariUserDetails implements UserDetails {
-
-  private final User user;
-  private final String password;
-  private final Collection<? extends GrantedAuthority> grantedAuthorities;
-
-  public AmbariUserDetails(User user, String password, Collection<? extends 
GrantedAuthority> grantedAuthorities) {
-    this.user = user;
-    this.password = password;
-    this.grantedAuthorities = (grantedAuthorities == null)
-        ? Collections.emptyList()
-        : Collections.unmodifiableCollection(new 
ArrayList<>(grantedAuthorities));
-  }
-
-  @Override
-  public Collection<? extends GrantedAuthority> getAuthorities() {
-    return grantedAuthorities;
-  }
-
-  @Override
-  public String getPassword() {
-    return password;
-  }
-
-  @Override
-  public String getUsername() {
-    return (user == null) ? null : user.getUserName();
-  }
-
-  public Integer getUserId() {
-    return (user == null) ? null : user.getUserId();
-  }
-
-  @Override
-  public boolean isAccountNonExpired() {
-    return true;
-  }
-
-  @Override
-  public boolean isAccountNonLocked() {
-    return true;
-  }
-
-  @Override
-  public boolean isCredentialsNonExpired() {
-    return true;
-  }
-
-  @Override
-  public boolean isEnabled() {
-    return (user != null) && user.isActive();
-  }
+public interface AmbariUserDetails extends UserDetails {
+  Integer getUserId();
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetailsImpl.java
similarity index 84%
copy from 
ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
copy to 
ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetailsImpl.java
index e2c40d4..3c9ea1a 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetailsImpl.java
@@ -15,6 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.ambari.server.security.authentication;
 
 import java.util.ArrayList;
@@ -23,21 +24,19 @@ import java.util.Collections;
 
 import org.apache.ambari.server.security.authorization.User;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.UserDetails;
 
 /**
- * AmbariUserDetails is an implementation of {@link UserDetails} that contains 
information about
- * an authenticated user needed specifically by Ambari.  For example, the 
user's <code>userId</code>.
+ * AmbariUserDetails is an implementation of {@link AmbariUserDetails}
  * <p>
  * Ideally instances of this class are used as the value returned by {@link 
org.springframework.security.core.Authentication#getPrincipal()}
  */
-public class AmbariUserDetails implements UserDetails {
+public class AmbariUserDetailsImpl implements AmbariUserDetails {
 
   private final User user;
   private final String password;
   private final Collection<? extends GrantedAuthority> grantedAuthorities;
 
-  public AmbariUserDetails(User user, String password, Collection<? extends 
GrantedAuthority> grantedAuthorities) {
+  public AmbariUserDetailsImpl(User user, String password, Collection<? 
extends GrantedAuthority> grantedAuthorities) {
     this.user = user;
     this.password = password;
     this.grantedAuthorities = (grantedAuthorities == null)
@@ -60,6 +59,7 @@ public class AmbariUserDetails implements UserDetails {
     return (user == null) ? null : user.getUserName();
   }
 
+  @Override
   public Integer getUserId() {
     return (user == null) ? null : user.getUserId();
   }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java
index 1b80fd1..076e1b7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java
@@ -26,6 +26,7 @@ import 
org.apache.ambari.server.security.authentication.AmbariAuthenticationExce
 import 
org.apache.ambari.server.security.authentication.AmbariAuthenticationProvider;
 import 
org.apache.ambari.server.security.authentication.AmbariUserAuthentication;
 import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import 
org.apache.ambari.server.security.authentication.TooManyLoginFailuresException;
 import org.apache.ambari.server.security.authentication.UserNotFoundException;
 import org.apache.ambari.server.security.authorization.UserAuthenticationType;
@@ -118,7 +119,7 @@ public class AmbariJwtAuthenticationProvider extends 
AmbariAuthenticationProvide
         }
       }
 
-      AmbariUserDetails userDetails = new 
AmbariUserDetails(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
+      AmbariUserDetails userDetails = new 
AmbariUserDetailsImpl(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
       return new 
AmbariUserAuthentication(authentication.getCredentials().toString(), 
userDetails, true);
     } else {
       // The user was not authenticated, fail
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java
index 14c174a..b90a8ca 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java
@@ -28,7 +28,7 @@ import 
org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
 import 
org.apache.ambari.server.security.authentication.AccountDisabledException;
 import 
org.apache.ambari.server.security.authentication.AmbariAuthenticationException;
-import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import 
org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
 import 
org.apache.ambari.server.security.authentication.TooManyLoginFailuresException;
 import org.apache.ambari.server.security.authentication.UserNotFoundException;
@@ -94,22 +94,7 @@ public class AmbariAuthToLocalUserDetailsService implements 
UserDetailsService {
     // If no entries are returned, we have not yet seen this principal.  If 
no, perform an auth-to-local translation
     // to determine what the local username is.
     if (CollectionUtils.isEmpty(entities)) {
-      // Since KerberosName relies on a static variable to hold on to the 
auth-to-local rules, attempt
-      // to protect access to the rule set by blocking other threads from 
changing the rules out from
-      // under us during this operation.  Similar logic is used in 
org.apache.ambari.server.view.ViewContextImpl.getUsername().
-      try {
-        synchronized (KerberosName.class) {
-          KerberosName.setRules(authToLocalRules);
-          username = new KerberosName(principal).getShortName();
-        }
-      } catch (UserNotFoundException e) {
-        throw new UsernameNotFoundException(e.getMessage(), e);
-      } catch (IOException e) {
-        String message = String.format("Failed to translate %s to a local 
username during Kerberos authentication: %s", principal, 
e.getLocalizedMessage());
-        LOG.warn(message);
-        throw new UsernameNotFoundException(message, e);
-      }
-
+      username = translatePrincipalName(principal);
       if (username == null) {
         String message = String.format("Failed to translate %s to a local 
username during Kerberos authentication.", principal);
         LOG.warn(message);
@@ -203,6 +188,35 @@ public class AmbariAuthToLocalUserDetailsService 
implements UserDetailsService {
       }
     }
 
-    return new AmbariUserDetails(new User(userEntity), null, 
users.getUserAuthorities(userEntity));
+    return new AmbariUserDetailsImpl(new User(userEntity), null, 
users.getUserAuthorities(userEntity));
+  }
+
+  /**
+   * Using the auth-to-local rules stored in 
<code>authentication.kerberos.auth_to_local.rules</code>
+   * in the <code>ambari.properties</code> file, translate the supplied 
principal name to a local name.
+   *
+   * @param principalName the principal name to translate
+   * @return a local username
+   */
+  public String translatePrincipalName(String principalName) {
+    if (StringUtils.isNotEmpty(principalName) && principalName.contains("@")) {
+      try {
+        // Since KerberosName relies on a static variable to hold on to the 
auth-to-local rules,
+        // attempt to protect access to the rule set by blocking other threads 
from changing the
+        // rules out from under us during this operation.
+        synchronized (KerberosName.class) {
+          KerberosName.setRules(authToLocalRules);
+          return new KerberosName(principalName).getShortName();
+        }
+      } catch (UserNotFoundException e) {
+        throw new UsernameNotFoundException(e.getMessage(), e);
+      } catch (IOException e) {
+        String message = String.format("Failed to translate %s to a local 
username during Kerberos authentication: %s", principalName, 
e.getLocalizedMessage());
+        LOG.warn(message);
+        throw new UsernameNotFoundException(message, e);
+      }
+    } else {
+      return principalName;
+    }
   }
 }
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 3d77abb..bcc65eb 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
@@ -32,6 +32,10 @@ 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.authentication.tproxy.TrustedProxyAuthenticationDetailsSource;
+import org.apache.ambari.server.utils.RequestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.core.annotation.Order;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
@@ -52,6 +56,8 @@ import org.springframework.stereotype.Component;
 @Order(2)
 public class AmbariKerberosAuthenticationFilter extends 
SpnegoAuthenticationProcessingFilter implements AmbariAuthenticationFilter {
 
+  private static final Logger LOG = 
LoggerFactory.getLogger(AmbariKerberosAuthenticationFilter.class);
+
   /**
    * Ambari authentication event handler
    */
@@ -83,7 +89,7 @@ public class AmbariKerberosAuthenticationFilter extends 
SpnegoAuthenticationProc
 
     kerberosAuthenticationEnabled = (kerberosAuthenticationProperties != null) 
&& kerberosAuthenticationProperties.isKerberosAuthenticationEnabled();
 
-    if(eventHandler == null) {
+    if (eventHandler == null) {
       throw new IllegalArgumentException("The AmbariAuthenticationEventHandler 
must not be null");
     }
 
@@ -91,6 +97,8 @@ public class AmbariKerberosAuthenticationFilter extends 
SpnegoAuthenticationProc
 
     setAuthenticationManager(authenticationManager);
 
+    setAuthenticationDetailsSource(new 
TrustedProxyAuthenticationDetailsSource());
+
     setFailureHandler(new AuthenticationFailureHandler() {
       @Override
       public void onAuthenticationFailure(HttpServletRequest 
httpServletRequest, HttpServletResponse httpServletResponse, 
AuthenticationException e) throws IOException, ServletException {
@@ -131,6 +139,10 @@ public class AmbariKerberosAuthenticationFilter extends 
SpnegoAuthenticationProc
    */
   @Override
   public boolean shouldApply(HttpServletRequest httpServletRequest) {
+    if (LOG.isDebugEnabled()) {
+      RequestUtils.logRequestHeadersAndQueryParams(httpServletRequest, LOG);
+    }
+
     if (kerberosAuthenticationEnabled) {
       String header = httpServletRequest.getHeader("Authorization");
       return (header != null) && (header.startsWith("Negotiate ") || 
header.startsWith("Kerberos "));
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationProvider.java
new file mode 100644
index 0000000..bf5566e
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationProvider.java
@@ -0,0 +1,126 @@
+/*
+ * 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.kerberos;
+
+import javax.inject.Inject;
+
+import 
org.apache.ambari.server.security.authentication.AmbariProxiedUserDetailsImpl;
+import 
org.apache.ambari.server.security.authentication.tproxy.TrustedProxyAuthenticationDetails;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import 
org.springframework.security.kerberos.authentication.KerberosServiceRequestToken;
+import 
org.springframework.security.kerberos.authentication.KerberosTicketValidation;
+import 
org.springframework.security.kerberos.authentication.KerberosTicketValidator;
+
+/**
+ * AmbariKerberosAuthenticationProvider is an {@link 
org.springframework.security.authentication.AuthenticationProvider}
+ * implementation used to authenticate users using a Kerberos ticket.
+ * <p>
+ * In order for this provider to be usable, Kerberos authentication must be 
enabled. This may be done
+ * using the <code>ambari-server setup-kerberos</code> CLI.
+ * <p>
+ * This implementation supports Trusted Proxy authentication.
+ */
+public class AmbariKerberosAuthenticationProvider implements 
AuthenticationProvider, InitializingBean {
+  private static final Logger LOG = 
LoggerFactory.getLogger(AmbariKerberosAuthenticationProvider.class);
+
+  private AmbariAuthToLocalUserDetailsService authToLocalUserDetailsService;
+  private AmbariProxiedUserDetailsService proxiedUserDetailsService;
+  private KerberosTicketValidator ticketValidator;
+
+  @Inject
+  public 
AmbariKerberosAuthenticationProvider(AmbariAuthToLocalUserDetailsService 
authToLocalUserDetailsService,
+                                              AmbariProxiedUserDetailsService 
proxiedUserDetailsService,
+                                              KerberosTicketValidator 
ticketValidator) {
+    this.authToLocalUserDetailsService = authToLocalUserDetailsService;
+    this.proxiedUserDetailsService = proxiedUserDetailsService;
+    this.ticketValidator = ticketValidator;
+  }
+
+  @Override
+  public Authentication authenticate(Authentication authentication) throws 
AuthenticationException {
+
+    if (authentication == null) {
+      throw new BadCredentialsException("Missing credentials");
+    } else if (authentication instanceof KerberosServiceRequestToken) {
+      KerberosServiceRequestToken auth = (KerberosServiceRequestToken) 
authentication;
+      byte[] token = auth.getToken();
+
+      LOG.debug("Validating Kerberos token");
+      KerberosTicketValidation ticketValidation = 
ticketValidator.validateTicket(token);
+      LOG.debug("Kerberos token validated: {}", ticketValidation.username());
+
+      Object requestDetails = authentication.getDetails();
+      UserDetails userDetails;
+
+      if (requestDetails instanceof TrustedProxyAuthenticationDetails) {
+        TrustedProxyAuthenticationDetails trustedProxyAuthenticationDetails = 
(TrustedProxyAuthenticationDetails) requestDetails;
+        String proxiedUserName = trustedProxyAuthenticationDetails.getDoAs();
+
+        if (StringUtils.isNotEmpty(proxiedUserName)) {
+          String localProxyUserName = 
authToLocalUserDetailsService.translatePrincipalName(ticketValidation.username());
+
+          userDetails = new AmbariProxiedUserDetailsImpl(
+              proxiedUserDetailsService.loadProxiedUser(proxiedUserName, 
localProxyUserName, trustedProxyAuthenticationDetails),
+              new 
AmbariProxyUserKerberosDetailsImpl(ticketValidation.username(), 
localProxyUserName));
+        } else {
+          // This is not a trusted proxy request... handle normally
+          userDetails = 
authToLocalUserDetailsService.loadUserByUsername(ticketValidation.username());
+        }
+      } else {
+        // This is not a trusted proxy request... handle normally
+        userDetails = 
authToLocalUserDetailsService.loadUserByUsername(ticketValidation.username());
+      }
+
+      KerberosServiceRequestToken responseAuth = new 
KerberosServiceRequestToken(userDetails, ticketValidation, 
userDetails.getAuthorities(), token);
+      responseAuth.setDetails(requestDetails);
+      return responseAuth;
+    } else {
+      throw new BadCredentialsException(String.format("Unexpected 
Authentication class: %s", authentication.getClass().getName()));
+    }
+  }
+
+  @Override
+  public boolean supports(Class<? extends Object> auth) {
+    return KerberosServiceRequestToken.class.isAssignableFrom(auth);
+  }
+
+  @Override
+  public void afterPropertiesSet() throws Exception {
+  }
+
+  public void 
setAuthToLocalUserDetailsService(AmbariAuthToLocalUserDetailsService 
authToLocalUserDetailsService) {
+    this.authToLocalUserDetailsService = authToLocalUserDetailsService;
+  }
+
+  public void setProxiedUserDetailsService(AmbariProxiedUserDetailsService 
proxiedUserDetailsService) {
+    this.proxiedUserDetailsService = proxiedUserDetailsService;
+  }
+
+  public void setTicketValidator(KerberosTicketValidator ticketValidator) {
+    this.ticketValidator = ticketValidator;
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxiedUserDetailsService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxiedUserDetailsService.java
new file mode 100644
index 0000000..0d11a3c
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxiedUserDetailsService.java
@@ -0,0 +1,320 @@
+/*
+ * 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.kerberos;
+
+import static java.util.stream.Collectors.toSet;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.inject.Inject;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.entities.GroupEntity;
+import org.apache.ambari.server.orm.entities.MemberEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import 
org.apache.ambari.server.security.authentication.AccountDisabledException;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
+import 
org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
+import 
org.apache.ambari.server.security.authentication.TooManyLoginFailuresException;
+import 
org.apache.ambari.server.security.authentication.tproxy.AmbariTProxyConfiguration;
+import 
org.apache.ambari.server.security.authentication.tproxy.TrustedProxyAuthenticationDetails;
+import 
org.apache.ambari.server.security.authentication.tproxy.TrustedProxyAuthenticationNotAllowedException;
+import org.apache.ambari.server.security.authorization.User;
+import org.apache.ambari.server.security.authorization.Users;
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Component;
+
+import com.google.inject.Provider;
+
+/**
+ * AmbariProxiedUserDetailsService is a {@link UserDetailsService} that 
handles proxied users via the
+ * Trusted Proxy protocol.
+ * <p>
+ * If trusted proxy support is enabled and a proxied user is declared, the 
proxy user and remote host is validated.
+ * If all criteria is met and  a user account with the the proxied user's 
username exist, that user
+ * account will be set as the authenticated user.
+ */
+@Component
+public class AmbariProxiedUserDetailsService implements UserDetailsService {
+  private static final Logger LOG = 
LoggerFactory.getLogger(AmbariProxiedUserDetailsService.class);
+
+  private static final Pattern IP_ADDRESS_PATTERN = 
Pattern.compile("^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$");
+  private static final Pattern IP_ADDRESS_RANGE_PATTERN = 
Pattern.compile("^((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})/(\\d{1,2})$");
+
+  @Inject
+  private Provider<AmbariTProxyConfiguration> 
ambariTProxyConfigurationProvider;
+
+  private final Configuration configuration;
+
+  private final Users users;
+
+  /**
+   * Constructor.
+   * <p>
+   * Given the Ambari {@link Configuration}, initializes the {@link 
KerberosName} class using
+   * the <code>auth-to-local</code> rules from {@link 
AmbariKerberosAuthenticationProperties#getAuthToLocalRules()}.
+   *
+   * @param configuration the Ambari configuration data
+   * @param users         the Ambari users access object
+   */
+  AmbariProxiedUserDetailsService(Configuration configuration, Users users) {
+    this.configuration = configuration;
+    this.users = users;
+  }
+
+  @Override
+  public UserDetails loadUserByUsername(String proxiedUserName) throws 
UsernameNotFoundException {
+    return loadProxiedUser(proxiedUserName, null, null);
+  }
+
+  public UserDetails loadProxiedUser(String proxiedUserName,
+                                     String proxyUserName,
+                                     TrustedProxyAuthenticationDetails 
trustedProxyAuthenticationDetails)
+      throws AuthenticationException {
+
+    LOG.info("Proxy user {} specified {} as proxied user.", proxyUserName, 
proxiedUserName);
+
+    if (StringUtils.isEmpty(proxiedUserName)) {
+      String message = "No proxied username was specified.";
+      LOG.warn(message);
+      throw new UsernameNotFoundException(message);
+    }
+
+    if (trustedProxyAuthenticationDetails == null) {
+      String message = "Trusted proxy details have not been provided.";
+      LOG.warn(message);
+      throw new TrustedProxyAuthenticationNotAllowedException(message);
+    }
+
+    AmbariTProxyConfiguration tProxyConfiguration = 
ambariTProxyConfigurationProvider.get();
+
+    // Make sure the trusted proxy support is enabled
+    if (!tProxyConfiguration.isEnabled()) {
+      String message = "Trusted proxy support is not enabled.";
+      LOG.warn(message);
+      throw new TrustedProxyAuthenticationNotAllowedException(message);
+    }
+
+    // Validate the host...
+    if (!validateHost(tProxyConfiguration, proxyUserName, 
trustedProxyAuthenticationDetails.getRemoteAddress())) {
+      String message = String.format("Trusted proxy is not allowed for %s -> 
%s: host match not found.", proxyUserName, proxiedUserName);
+      LOG.warn(message);
+      throw new TrustedProxyAuthenticationNotAllowedException(message);
+    }
+
+    // Validate the user...
+    if (!validateUser(tProxyConfiguration, proxyUserName, proxiedUserName)) {
+      String message = String.format("Trusted proxy is not allowed for %s -> 
%s: user match not found.", proxyUserName, proxiedUserName);
+      LOG.warn(message);
+      throw new TrustedProxyAuthenticationNotAllowedException(message);
+    }
+
+    // Retrieve the userEntity so we can validate the groups now....
+    // We also need this later if all validations succeed.
+    UserEntity userEntity = users.getUserEntity(proxiedUserName);
+    if (userEntity == null) {
+      String message = String.format("Failed to find an account for the 
proxied user, %s.", proxiedUserName);
+      LOG.warn(message);
+      throw new UsernameNotFoundException(message);
+    }
+
+    // Validate the proxied user's groups
+    if (!validateGroup(tProxyConfiguration, proxyUserName, userEntity)) {
+      String message = String.format("Trusted proxy is not allowed for %s -> 
%s: group match not found.", proxyUserName, proxiedUserName);
+      LOG.warn(message);
+      throw new TrustedProxyAuthenticationNotAllowedException(message);
+    }
+
+    return createUserDetails(userEntity);
+  }
+
+  boolean validateGroup(AmbariTProxyConfiguration tProxyConfiguration, String 
proxyUserName, UserEntity userEntity) {
+    String allowedGroups = tProxyConfiguration.getAllowedGroups(proxyUserName);
+
+    if (StringUtils.isNotEmpty(allowedGroups)) {
+      Set<String> groupSpecs = Arrays.stream(allowedGroups.split("\\s*,\\s*"))
+          .map(s -> s.trim().toLowerCase())
+          .collect(toSet());
+
+      if (groupSpecs.contains("*")) {
+        return true;
+      } else {
+        Set<MemberEntity> memberEntities = userEntity.getMemberEntities();
+        if (memberEntities != null) {
+          for (MemberEntity memberEntity : memberEntities) {
+            GroupEntity group = memberEntity.getGroup();
+            if (group != null) {
+              String groupName = group.getGroupName();
+              if (StringUtils.isNotEmpty(groupName) && 
groupSpecs.contains(groupName.toLowerCase())) {
+                return true;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+  boolean validateUser(AmbariTProxyConfiguration tProxyConfiguration, String 
proxyUserName, String proxiedUserName) {
+    String allowedUsers = tProxyConfiguration.getAllowedUsers(proxyUserName);
+
+    if (StringUtils.isNotEmpty(allowedUsers)) {
+      String[] userSpecs = allowedUsers.split("\\s*,\\s*");
+      for (String userSpec : userSpecs) {
+        if ("*".equals(userSpec) || 
(userSpec.equalsIgnoreCase(proxiedUserName))) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  boolean validateHost(AmbariTProxyConfiguration tProxyConfiguration, String 
proxyUserName, String remoteAddress) {
+    String allowedHosts = tProxyConfiguration.getAllowedHosts(proxyUserName);
+
+    if (StringUtils.isNotEmpty(allowedHosts)) {
+      Set<String> hostSpecs = Arrays.stream(allowedHosts.split("\\s*,\\s*"))
+          .map(s -> s.trim().toLowerCase())
+          .collect(toSet());
+
+      if (hostSpecs.contains("*")) {
+        return true;
+      } else {
+        for (String hostSpec : hostSpecs) {
+          if (isIPAddress(hostSpec)) {
+            if (hostSpec.equals(remoteAddress)) {
+              return true;
+            }
+          } else if (isIPAddressRange(hostSpec)) {
+            if (isInIpAddressRange(hostSpec, remoteAddress)) {
+              return true;
+            }
+          } else if (matchesHostname(hostSpec, remoteAddress)) {
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+  boolean matchesHostname(String hostSpec, String remoteAddress) {
+    // We assume this is a hostname... convert it to an IP address
+    try {
+      String ipAddress = getIpAddress(hostSpec);
+      if (StringUtils.isNotEmpty(ipAddress) && 
ipAddress.equals(remoteAddress)) {
+        return true;
+      }
+    } catch (Throwable t) {
+      LOG.warn("Invalid hostname in host specification, skipping: " + 
hostSpec, t);
+    }
+
+    return false;
+  }
+
+  String getIpAddress(String hostname) throws UnknownHostException {
+    InetAddress inetAddress = InetAddress.getByName(hostname);
+    return (inetAddress == null) ? null : inetAddress.getHostAddress();
+  }
+
+  /**
+   * Determines if an IP address is in the subnet specified by the CIDR value.
+   * <p>
+   * The logic for ths method comes from an example found at
+   * 
https://stackoverflow.com/questions/4209760/validate-an-ip-address-with-mask.
+   * <p>
+   * A CIDR is an IP address with a mask, denoting a subnet.  For example, 
<code>192.168.1.0/24</code> contains all
+   * IP addresses in the range of <code>192.168.1.0</code> through 
<code>192.168.1.255</code>. See https://www.ipaddressguide.com/cidr
+   * for an on-line calculator.
+   *
+   * @param cidr      a CIDR
+   * @param ipAddress the IP address to test
+   * @return <code>true</code> if the IP Address is withing the range 
specified by the CIDR; <code>false</code> otherwise.
+   */
+  boolean isInIpAddressRange(String cidr, String ipAddress) {
+    Matcher matcher = IP_ADDRESS_RANGE_PATTERN.matcher(cidr);
+
+    if (matcher.matches() && matcher.groupCount() == 2) {
+      try {
+        String hostSpecIPAddress = matcher.group(1);
+        String hostSpecBits = matcher.group(2);
+        int hostSpecIPAddressInt = ipAddressToInt(hostSpecIPAddress);
+        int remoteAddressInt = ipAddressToInt(ipAddress);
+
+        int mask = -1 << (32 - Integer.valueOf(hostSpecBits));
+        return ((hostSpecIPAddressInt & mask) == (remoteAddressInt & mask));
+      } catch (Throwable t) {
+        LOG.warn("Invalid CIDR in host specification, skipping: " + cidr, t);
+      }
+    }
+
+    return false;
+  }
+
+  private int ipAddressToInt(String s) throws UnknownHostException {
+    InetAddress inetAddress = InetAddress.getByName(s);
+    byte[] b = inetAddress.getAddress();
+    return ((b[0] & 0xFF) << 24) |
+        ((b[1] & 0xFF) << 16) |
+        ((b[2] & 0xFF) << 8) |
+        ((b[3] & 0xFF) << 0);
+  }
+
+  private boolean isIPAddressRange(String hostSpec) {
+    return IP_ADDRESS_RANGE_PATTERN.matcher(hostSpec).matches();
+  }
+
+  private boolean isIPAddress(String hostSpec) {
+    return IP_ADDRESS_PATTERN.matcher(hostSpec).matches();
+  }
+
+  private UserDetails createUserDetails(UserEntity userEntity) {
+    String username = userEntity.getUserName();
+
+    // Ensure the user account is allowed to log in
+    try {
+      users.validateLogin(userEntity, username);
+    } catch (AccountDisabledException | TooManyLoginFailuresException e) {
+      if (configuration.showLockedOutUserMessage()) {
+        throw e;
+      } else {
+        // Do not give away information about the existence or status of a user
+        throw new InvalidUsernamePasswordCombinationException(username, false, 
e);
+      }
+    }
+
+    return new AmbariUserDetailsImpl(new User(userEntity), null, 
users.getUserAuthorities(userEntity));
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxyUserKerberosDetailsImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxyUserKerberosDetailsImpl.java
new file mode 100644
index 0000000..21848c1
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxyUserKerberosDetailsImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.kerberos;
+
+import 
org.apache.ambari.server.security.authentication.AmbariProxyUserDetailsImpl;
+import org.apache.ambari.server.security.authorization.UserAuthenticationType;
+
+/**
+ * AmbariProxyUserKerberosDetailsImpl is a {@link AmbariProxyUserDetailsImpl} 
implementation that
+ * adds allows for the proxy user's principal name to be retrieved if the 
proxy user authenticated
+ * using Kerberos.
+ */
+public class AmbariProxyUserKerberosDetailsImpl extends 
AmbariProxyUserDetailsImpl {
+  private final String principalName;
+
+  public AmbariProxyUserKerberosDetailsImpl(String principalName, String 
localUsername) {
+    super(localUsername, UserAuthenticationType.KERBEROS);
+    this.principalName = principalName;
+  }
+
+  public String getPrincipalName() {
+    return principalName;
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/pam/AmbariPamAuthenticationProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/pam/AmbariPamAuthenticationProvider.java
index d92a5ca..e9e6882 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/pam/AmbariPamAuthenticationProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/pam/AmbariPamAuthenticationProvider.java
@@ -34,6 +34,7 @@ import 
org.apache.ambari.server.security.authentication.AmbariAuthenticationExce
 import 
org.apache.ambari.server.security.authentication.AmbariAuthenticationProvider;
 import 
org.apache.ambari.server.security.authentication.AmbariUserAuthentication;
 import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import 
org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
 import 
org.apache.ambari.server.security.authentication.TooManyLoginFailuresException;
 import org.apache.ambari.server.security.authorization.GroupType;
@@ -163,7 +164,7 @@ public class AmbariPamAuthenticationProvider extends 
AmbariAuthenticationProvide
           synchronizeGroups(unixUser, userEntity);
         }
 
-        AmbariUserDetails userDetails = new 
AmbariUserDetails(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
+        AmbariUserDetails userDetails = new 
AmbariUserDetailsImpl(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
         return new AmbariUserAuthentication(password, userDetails, true);
       }
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/AmbariTProxyConfiguration.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/AmbariTProxyConfiguration.java
index daf3788..7f7e79f 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/AmbariTProxyConfiguration.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/AmbariTProxyConfiguration.java
@@ -71,7 +71,7 @@ public class AmbariTProxyConfiguration extends 
AmbariServerConfiguration {
    * @return <code>true</code> if trusted proxy support is enabed; 
<code>false</code> otherwise
    * @see AmbariServerConfigurationKey#TPROXY_AUTHENTICATION_ENABLED
    */
-  boolean isEnabled() {
+  public boolean isEnabled() {
     return 
Boolean.valueOf(getValue(AmbariServerConfigurationKey.TPROXY_AUTHENTICATION_ENABLED,
 configurationMap));
   }
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationDetails.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationDetails.java
new file mode 100644
index 0000000..b254e667
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationDetails.java
@@ -0,0 +1,102 @@
+/*
+ * 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.tproxy;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ambari.server.utils.RequestUtils;
+import 
org.springframework.security.web.authentication.WebAuthenticationDetails;
+
+/**
+ * TrustedProxyAuthenticationDetails is a {@link WebAuthenticationDetails} 
implementation that
+ * contains information from an {@link HttpServletRequest} that can be used 
for handling trusted
+ * proxy authentication requests.
+ * <p>
+ * If the request is not from a trusted proxy, the internal valiues will be 
null.
+ */
+public class TrustedProxyAuthenticationDetails extends 
WebAuthenticationDetails {
+  private final String doAs;
+  private final String xForwardedContext;
+  private final String xForwardedProto;
+  private final String xForwardedHost;
+  private final String xForwardedFor;
+  private final String xForwardedPort;
+  private final String xForwardedServer;
+
+  /**
+   * Constructor.
+   * <p>
+   * Given an {@link HttpServletRequest}, attempts to retrieve the following 
data:
+   * <ul>
+   * <li>The proxied user from the <code>doAs</code> query parameter</li>
+   * <li>
+   * The following <code>X-Forwarded-*</code> values from the request header 
information
+   * <ul>
+   * <li>X-Forwarded-Context</li>
+   * <li>X-Forwarded-Proto</li>
+   * <li>X-Forwarded-Host</li>
+   * <li>X-Forwarded-For</li>
+   * <li>X-Forwarded-Port</li>
+   * <li>X-Forwarded-Server</li>
+   * </ul>
+   * </li>
+   * </ul>
+   *
+   * @param context the {@link HttpServletRequest} to query
+   */
+  TrustedProxyAuthenticationDetails(HttpServletRequest context) {
+    super(context);
+
+    this.doAs = RequestUtils.getQueryStringParameterValue(context, "doAs");
+    this.xForwardedContext = context.getHeader("X-Forwarded-Context");
+    this.xForwardedProto = context.getHeader("X-Forwarded-Proto");
+    this.xForwardedHost = context.getHeader("X-Forwarded-Host");
+    this.xForwardedFor = context.getHeader("X-Forwarded-For");
+    this.xForwardedPort = context.getHeader("X-Forwarded-Port");
+    this.xForwardedServer = context.getHeader("X-Forwarded-Server");
+  }
+
+  public String getDoAs() {
+    return doAs;
+  }
+
+  public String getXForwardedContext() {
+    return xForwardedContext;
+  }
+
+  public String getXForwardedProto() {
+    return xForwardedProto;
+  }
+
+  public String getXForwardedHost() {
+    return xForwardedHost;
+  }
+
+  public String getXForwardedFor() {
+    return xForwardedFor;
+  }
+
+  public String getXForwardedPort() {
+    return xForwardedPort;
+  }
+
+  public String getXForwardedServer() {
+    return xForwardedServer;
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationDetailsSource.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationDetailsSource.java
new file mode 100644
index 0000000..3f0af65
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationDetailsSource.java
@@ -0,0 +1,36 @@
+/*
+ * 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.tproxy;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.security.authentication.AuthenticationDetailsSource;
+
+/**
+ * TrustedProxyAuthenticationDetailsSource is an implementation of a {@link 
AuthenticationDetailsSource}
+ * that produces {@link TrustedProxyAuthenticationDetails} instances.
+ *
+ * @see TrustedProxyAuthenticationDetails
+ */
+public class TrustedProxyAuthenticationDetailsSource implements 
AuthenticationDetailsSource<HttpServletRequest, 
TrustedProxyAuthenticationDetails> {
+  @Override
+  public TrustedProxyAuthenticationDetails buildDetails(HttpServletRequest 
httpServletRequest) {
+    return new TrustedProxyAuthenticationDetails(httpServletRequest);
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationNotAllowedException.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationNotAllowedException.java
new file mode 100644
index 0000000..033c18e
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/TrustedProxyAuthenticationNotAllowedException.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.tproxy;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * TrustedProxyAuthenticationNotAllowedException is an AuthenticationException 
implementation to be
+ * thrown a Trusted proxy authentication attempt fails.
+ */
+public class TrustedProxyAuthenticationNotAllowedException extends 
AuthenticationException {
+
+  public TrustedProxyAuthenticationNotAllowedException(String message) {
+    super(message);
+  }
+}
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 deabb2a..7c2f50a 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
@@ -29,6 +29,7 @@ import 
org.apache.ambari.server.security.authentication.AccountDisabledException
 import 
org.apache.ambari.server.security.authentication.AmbariAuthenticationProvider;
 import 
org.apache.ambari.server.security.authentication.AmbariUserAuthentication;
 import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import 
org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
 import 
org.apache.ambari.server.security.authentication.TooManyLoginFailuresException;
 import org.apache.commons.collections.CollectionUtils;
@@ -110,7 +111,7 @@ public class AmbariLdapAuthenticationProvider extends 
AmbariAuthenticationProvid
             }
           }
 
-          AmbariUserDetails userDetails = new 
AmbariUserDetails(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
+          AmbariUserDetails userDetails = new 
AmbariUserDetailsImpl(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
           return new AmbariUserAuthentication(null, userDetails, true);
         }
       } catch (AuthenticationException e) {
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java
index f252b0e..74394e4 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java
@@ -33,6 +33,7 @@ import org.apache.ambari.server.orm.entities.UserEntity;
 import org.apache.ambari.server.scheduler.ExecutionScheduleManager;
 import 
org.apache.ambari.server.security.authentication.AmbariUserAuthentication;
 import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import 
org.apache.ambari.server.security.authorization.internal.InternalTokenClientFilter;
 import 
org.apache.ambari.server.security.authorization.internal.InternalTokenStorage;
 import org.apache.commons.lang.math.NumberUtils;
@@ -83,7 +84,7 @@ public class AmbariUserAuthorizationFilter implements Filter {
             httpResponse.flushBuffer();
             return;
           } else {
-            AmbariUserDetails userDetails = new 
AmbariUserDetails(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
+            AmbariUserDetails userDetails = new 
AmbariUserDetailsImpl(users.getUser(userEntity), null, 
users.getUserAuthorities(userEntity));
             AmbariUserAuthentication authentication = new 
AmbariUserAuthentication(token, userDetails, true);
             
SecurityContextHolder.getContext().setAuthentication(authentication);
             httpResponse.setHeader("User", 
AuthorizationHelper.getAuthenticatedName());
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java 
b/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java
index a3f9660..d0efb97 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java
@@ -18,12 +18,19 @@
 package org.apache.ambari.server.utils;
 
 import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.springframework.util.MultiValueMap;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.util.UriComponentsBuilder;
 
 import com.google.common.collect.ImmutableSet;
 
@@ -96,4 +103,119 @@ public class RequestUtils {
       ((ServletRequestAttributes) 
RequestContextHolder.getRequestAttributes()).getRequest() != null;
   }
 
+  /**
+   * Posts an informational message the the supplied {@link Logger} showing 
the request headers and
+   * query parameters
+   * <p>
+   * For example:
+   * <pre>
+   * ##### HEADERS ########
+   * X-Requested-By = ambari
+   * ...
+   * Accept-Encoding = gzip, deflate
+   * Accept-Language = en-US,en;q=0.9
+   * ######################
+   * ##### PARAMETERS #####
+   * _ = 1543700737939
+   * ######################
+   * </pre>
+   *
+   * @param request the {@link HttpServletRequest} to log
+   * @param logger  the {@link Logger}
+   */
+  public static void logRequestHeadersAndQueryParams(HttpServletRequest 
request, Logger logger) {
+    if (logger != null) {
+      StringBuilder builder;
+
+      builder = new StringBuilder();
+      builder.append("\n##### HEADERS #######");
+      Enumeration<String> headerNames = request.getHeaderNames();
+      while (headerNames.hasMoreElements()) {
+        String name = headerNames.nextElement();
+        Enumeration<String> values = request.getHeaders(name);
+        while (values.hasMoreElements()) {
+          String value = values.nextElement();
+          builder.append("\n\t");
+          builder.append(name);
+          builder.append(" = ");
+          builder.append(value);
+        }
+      }
+      builder.append("\n#####################");
+      builder.append("\n##### PARAMETERS ####");
+      MultiValueMap<String, String> queryParams = 
getQueryStringParameters(request);
+      if (queryParams != null) {
+        for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
+          String name = entry.getKey();
+          List<String> values = entry.getValue();
+          for (String value : values) {
+            builder.append("\n\t");
+            builder.append(name);
+            builder.append(" = ");
+            builder.append(value);
+          }
+        }
+      }
+      builder.append("\n#####################");
+
+      logger.info(builder.toString());
+    }
+  }
+
+  /**
+   * Returns a {@link MultiValueMap} of the parameters parsed from the 
request's query string.  The
+   * returned map will not contain any parameters that may be in the body of 
the request as form data.
+   * <p>
+   * This implementation manually parses the query string rather than use 
{@link HttpServletRequest#getParameterValues(String)}
+   * so that the message body remains intact and available.  Calling {@link 
HttpServletRequest#getParameterValues(String)}
+   * could interfere with processing the body of this request later since the 
body is parsed to find
+   * any form parameters.
+   *
+   * @param request the {@link HttpServletRequest}
+   * @return a Map of query parameters to values
+   */
+  public static MultiValueMap<String, String> 
getQueryStringParameters(HttpServletRequest request) {
+    // Manually parse the query string rather than use 
HttpServletRequest#getParameter so that
+    // the message body remains intact and available.  Calling 
HttpServletRequest#getParameter
+    // could interfere with processing the body of this request later since 
the body needs to be
+    // parsed to find any form parameters.
+    String queryString = request.getQueryString();
+    return (StringUtils.isEmpty(queryString))
+        ? null
+        : 
UriComponentsBuilder.newInstance().query(queryString).build().getQueryParams();
+  }
+
+  /**
+   * Returns a {@link List} of values parsed from the request's query string 
for the given parameter
+   * name.
+   *
+   * @param request the {@link HttpServletRequest}
+   * @return a List of values for the specified query parameter; or 
<code>null</code> if the
+   * requested parameter is not present
+   * @see #getQueryStringParameters(HttpServletRequest)
+   */
+  public static List<String> getQueryStringParameterValues(HttpServletRequest 
request, String parameterName) {
+    MultiValueMap<String, String> valueMap = getQueryStringParameters(request);
+    return ((valueMap == null) || !valueMap.containsKey(parameterName))
+        ? null
+        : valueMap.get(parameterName);
+  }
+
+  /**
+   * Returns the value parsed from the request's query string for the given 
parameter name.
+   * <p>
+   * If more than one value for the given parameter name is found, the first 
value will be retured.
+   *
+   * @param request the {@link HttpServletRequest}
+   * @return the value for the specified query parameter; or <code>null</code> 
if the
+   * requested parameter is not present
+   * @see #getQueryStringParameters(HttpServletRequest)
+   */
+  public static String getQueryStringParameterValue(HttpServletRequest 
request, String parameterName) {
+    MultiValueMap<String, String> valueMap = getQueryStringParameters(request);
+    return ((valueMap == null) || !valueMap.containsKey(parameterName))
+        ? null
+        : valueMap.getFirst(parameterName);
+  }
+
 }
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityHelperImplTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityHelperImplTest.java
index cbe23d0..2384dab 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityHelperImplTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityHelperImplTest.java
@@ -23,7 +23,7 @@ import java.util.Collection;
 import org.apache.ambari.server.orm.entities.PrincipalEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
 import 
org.apache.ambari.server.security.authentication.AmbariUserAuthentication;
-import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import org.apache.ambari.server.security.authorization.User;
 import org.apache.ambari.server.security.authorization.UserName;
 import org.junit.Assert;
@@ -48,7 +48,7 @@ public class SecurityHelperImplTest {
     userEntity.setUserName(UserName.fromString("userName").toString());
     userEntity.setUserId(1);
     User user = new User(userEntity);
-    Authentication auth = new AmbariUserAuthentication(null, new 
AmbariUserDetails(user, null, null));
+    Authentication auth = new AmbariUserAuthentication(null, new 
AmbariUserDetailsImpl(user, null, null));
     ctx.setAuthentication(auth);
 
     // Username is expected to be lowercase
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/security/TestAuthenticationFactory.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/security/TestAuthenticationFactory.java
index f43e5e5..10277f2 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/security/TestAuthenticationFactory.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/security/TestAuthenticationFactory.java
@@ -30,7 +30,7 @@ import org.apache.ambari.server.orm.entities.ResourceEntity;
 import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
 import 
org.apache.ambari.server.security.authentication.AmbariUserAuthentication;
-import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority;
 import org.apache.ambari.server.security.authorization.ResourceType;
 import org.apache.ambari.server.security.authorization.RoleAuthorization;
@@ -427,6 +427,6 @@ public class TestAuthenticationFactory {
     userEntity.setUserName(username);
     userEntity.setPrincipal(principal);
 
-    return new AmbariUserAuthentication(null, new AmbariUserDetails(new 
User(userEntity), null, authorities), true);
+    return new AmbariUserAuthentication(null, new AmbariUserDetailsImpl(new 
User(userEntity), null, authorities), true);
   }
 }
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java
index a0b7aca..f5087a1 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java
@@ -19,12 +19,14 @@
 package org.apache.ambari.server.security.authentication.kerberos;
 
 import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.anyString;
 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 static org.easymock.EasyMock.startsWith;
 
 import java.io.IOException;
 import java.util.List;
@@ -150,8 +152,11 @@ public class AmbariKerberosAuthenticationFilterTest 
extends EasyMockSupport {
     FilterChain filterChain = createMock(FilterChain.class);
 
     expect(request.getHeader("Authorization")).andReturn("Negotiate ").once();
+    
expect(request.getHeader(startsWith("X-Forwarded-"))).andReturn(null).times(6);
     expect(request.getRemoteAddr()).andReturn("1.2.3.4").once();
     expect(request.getSession(false)).andReturn(session).once();
+    expect(request.getQueryString()).andReturn(null).once();
+    expect(request.getParameter(anyString())).andReturn(null).anyTimes();
     expect(session.getId()).andReturn("sessionID").once();
 
     expect(authenticationManager.authenticate(anyObject(Authentication.class)))
@@ -197,8 +202,11 @@ public class AmbariKerberosAuthenticationFilterTest 
extends EasyMockSupport {
     FilterChain filterChain = createMock(FilterChain.class);
 
     expect(request.getHeader("Authorization")).andReturn("Negotiate ").once();
+    
expect(request.getHeader(startsWith("X-Forwarded-"))).andReturn(null).times(6);
     expect(request.getRemoteAddr()).andReturn("1.2.3.4").once();
     expect(request.getSession(false)).andReturn(session).once();
+    expect(request.getQueryString()).andReturn(null).once();
+    expect(request.getParameter(anyString())).andReturn(null).anyTimes();
     expect(session.getId()).andReturn("sessionID").once();
 
     
expect(authenticationManager.authenticate(anyObject(Authentication.class))).andThrow(new
 InvalidUsernamePasswordCombinationException("user")).once();
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxiedUserDetailsServiceTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxiedUserDetailsServiceTest.java
new file mode 100644
index 0000000..ba310d4
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxiedUserDetailsServiceTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.kerberos;
+
+import static org.easymock.EasyMock.expect;
+
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.entities.GroupEntity;
+import org.apache.ambari.server.orm.entities.MemberEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import 
org.apache.ambari.server.security.authentication.tproxy.AmbariTProxyConfiguration;
+import org.apache.ambari.server.security.authorization.Users;
+import org.easymock.EasyMockSupport;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AmbariProxiedUserDetailsServiceTest extends EasyMockSupport {
+  @Test
+  public void testValidateHost() throws UnknownHostException {
+    AmbariProxiedUserDetailsService service = 
createMockBuilder(AmbariProxiedUserDetailsService.class)
+        .withConstructor(createNiceMock(Configuration.class), 
createNiceMock(Users.class))
+        .addMockedMethod("getIpAddress", String.class)
+        .createMock();
+    
expect(service.getIpAddress("host1.example.com")).andReturn("192.168.74.101").anyTimes();
+    
expect(service.getIpAddress("host2.example.com")).andReturn("192.168.74.102").anyTimes();
+
+    AmbariTProxyConfiguration tproxyConfigration = 
createMock(AmbariTProxyConfiguration.class);
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("*").once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("192.168.74.101").once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("host1.example.com").once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("192.168.74.0/24").once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn(null).once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("").once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("192.168.74.102").once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("host2.example.com").once();
+    
expect(tproxyConfigration.getAllowedHosts("proxyUser")).andReturn("192.168.74.1/32").once();
+
+    replayAll();
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "*"
+    Assert.assertTrue(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "192.168.74.101"
+    Assert.assertTrue(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "host1.example.com"
+    Assert.assertTrue(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "192.168.74.0/24"
+    Assert.assertTrue(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = null
+    Assert.assertFalse(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = ""
+    Assert.assertFalse(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "192.168.74.102"
+    Assert.assertFalse(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "host1.example.com"
+    Assert.assertFalse(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "192.168.74.1/32"
+    Assert.assertFalse(service.validateHost(tproxyConfigration, "proxyUser", 
"192.168.74.101"));
+
+    verifyAll();
+  }
+
+  @Test
+  public void testValidateUser() {
+    AmbariProxiedUserDetailsService service = new 
AmbariProxiedUserDetailsService(createNiceMock(Configuration.class), 
createNiceMock(Users.class));
+
+    AmbariTProxyConfiguration tproxyConfigration = 
createMock(AmbariTProxyConfiguration.class);
+    
expect(tproxyConfigration.getAllowedUsers("proxyUser")).andReturn("*").once();
+    
expect(tproxyConfigration.getAllowedUsers("proxyUser")).andReturn("validUser").once();
+    
expect(tproxyConfigration.getAllowedUsers("proxyUser")).andReturn("validuser").once();
+    
expect(tproxyConfigration.getAllowedUsers("proxyUser")).andReturn("validUser, 
tom, *").once();
+    
expect(tproxyConfigration.getAllowedUsers("proxyUser")).andReturn(null).once();
+    
expect(tproxyConfigration.getAllowedUsers("proxyUser")).andReturn("").once();
+    
expect(tproxyConfigration.getAllowedUsers("proxyUser")).andReturn("notValidUser").once();
+
+    replayAll();
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "*"
+    Assert.assertTrue(service.validateUser(tproxyConfigration, "proxyUser", 
"validUser"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "validUser"
+    Assert.assertTrue(service.validateUser(tproxyConfigration, "proxyUser", 
"validUser"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "validuser"  (different case)
+    Assert.assertTrue(service.validateUser(tproxyConfigration, "proxyUser", 
"validUser"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "validUser, tom, *"
+    Assert.assertTrue(service.validateUser(tproxyConfigration, "proxyUser", 
"validUser"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = null
+    Assert.assertFalse(service.validateUser(tproxyConfigration, "proxyUser", 
"validUser"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = ""
+    Assert.assertFalse(service.validateUser(tproxyConfigration, "proxyUser", 
"validUser"));
+
+    // ambari.tproxy.proxyuser.proxyUser.users = "notValidUser"
+    Assert.assertFalse(service.validateUser(tproxyConfigration, "proxyUser", 
"validUser"));
+
+    verifyAll();
+  }
+
+  @Test
+  public void testValidateGroup() {
+    AmbariProxiedUserDetailsService service = new 
AmbariProxiedUserDetailsService(createNiceMock(Configuration.class), 
createNiceMock(Users.class));
+
+    AmbariTProxyConfiguration tproxyConfigration = 
createMock(AmbariTProxyConfiguration.class);
+    
expect(tproxyConfigration.getAllowedGroups("proxyUser")).andReturn("*").once();
+    
expect(tproxyConfigration.getAllowedGroups("proxyUser")).andReturn("validGroup").once();
+    
expect(tproxyConfigration.getAllowedGroups("proxyUser")).andReturn("validgroup").once();
+    
expect(tproxyConfigration.getAllowedGroups("proxyUser")).andReturn("validGroup, 
*").once();
+    
expect(tproxyConfigration.getAllowedGroups("proxyUser")).andReturn("").once();
+    
expect(tproxyConfigration.getAllowedGroups("proxyUser")).andReturn(null).once();
+    
expect(tproxyConfigration.getAllowedGroups("proxyUser")).andReturn("notValidGroup").once();
+
+    Set<MemberEntity> memberEntities = new HashSet<>();
+    memberEntities.add(createMockMemberEntity("validGroup"));
+    memberEntities.add(createMockMemberEntity("users"));
+
+    // Null Group name - maybe this is not possible
+    memberEntities.add(createMockMemberEntity(null));
+
+    // Null Group - maybe this is not possible
+    MemberEntity memberEntity = createMock(MemberEntity.class);
+    expect(memberEntity.getGroup()).andReturn(null).anyTimes();
+    memberEntities.add(memberEntity);
+
+    UserEntity userEntity = createMock(UserEntity.class);
+    
expect(userEntity.getMemberEntities()).andReturn(memberEntities).anyTimes();
+
+    replayAll();
+
+    // ambari.tproxy.proxyuser.proxyUser.groups = "*"
+    Assert.assertTrue(service.validateGroup(tproxyConfigration, "proxyUser", 
userEntity));
+
+    // ambari.tproxy.proxyuser.proxyUser.groups = "validGroup"
+    Assert.assertTrue(service.validateGroup(tproxyConfigration, "proxyUser", 
userEntity));
+
+    // ambari.tproxy.proxyuser.proxyUser.groups = "validgroup"  (different 
case)
+    Assert.assertTrue(service.validateGroup(tproxyConfigration, "proxyUser", 
userEntity));
+
+    // ambari.tproxy.proxyuser.proxyUser.groups = "validGroup, *"
+    Assert.assertTrue(service.validateGroup(tproxyConfigration, "proxyUser", 
userEntity));
+
+    // ambari.tproxy.proxyuser.proxyUser.groups = null
+    Assert.assertFalse(service.validateGroup(tproxyConfigration, "proxyUser", 
userEntity));
+
+    // ambari.tproxy.proxyuser.proxyUser.groups = ""
+    Assert.assertFalse(service.validateGroup(tproxyConfigration, "proxyUser", 
userEntity));
+
+    // ambari.tproxy.proxyuser.proxyUser.groups = "notValidGroup"
+    Assert.assertFalse(service.validateGroup(tproxyConfigration, "proxyUser", 
userEntity));
+
+    verifyAll();
+  }
+
+  @Test
+  public void testIsInIpAddressRange() {
+    AmbariProxiedUserDetailsService service = new 
AmbariProxiedUserDetailsService(createNiceMock(Configuration.class), 
createNiceMock(Users.class));
+
+    Assert.assertTrue(service.isInIpAddressRange("192.168.74.10/32", 
"192.168.74.10"));
+    Assert.assertFalse(service.isInIpAddressRange("192.168.74.10/32", 
"192.168.74.11"));
+
+    for (int i = 0; i <= 255; i++) {
+      Assert.assertTrue(service.isInIpAddressRange("192.168.1.0/24", 
String.format("192.168.1.%d", i)));
+    }
+    Assert.assertFalse(service.isInIpAddressRange("192.168.1.0/24", 
"192.168.2.100"));
+  }
+
+  private MemberEntity createMockMemberEntity(String groupName) {
+    GroupEntity groupEntity = createMock(GroupEntity.class);
+    expect(groupEntity.getGroupName()).andReturn(groupName).anyTimes();
+
+    MemberEntity memberEntity = createMock(MemberEntity.class);
+    expect(memberEntity.getGroup()).andReturn(groupEntity).anyTimes();
+    return memberEntity;
+  }
+
+}
\ No newline at end of file
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java
index 8ecef22..598f2be 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java
@@ -46,7 +46,7 @@ import 
org.apache.ambari.server.orm.entities.ResourceTypeEntity;
 import org.apache.ambari.server.orm.entities.RoleAuthorizationEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
 import 
org.apache.ambari.server.security.authentication.AmbariUserAuthentication;
-import org.apache.ambari.server.security.authentication.AmbariUserDetails;
+import org.apache.ambari.server.security.authentication.AmbariUserDetailsImpl;
 import org.easymock.EasyMockRule;
 import org.easymock.EasyMockSupport;
 import org.easymock.Mock;
@@ -170,7 +170,7 @@ public class AuthorizationHelperTest  extends 
EasyMockSupport {
     userEntity.setUserId(1);
     userEntity.setPrincipal(principalEntity);
     User user = new User(userEntity);
-    Authentication auth = new AmbariUserAuthentication(null, new 
AmbariUserDetails(user, null, null));
+    Authentication auth = new AmbariUserAuthentication(null, new 
AmbariUserDetailsImpl(user, null, null));
     SecurityContextHolder.getContext().setAuthentication(auth);
 
     userId = AuthorizationHelper.getAuthenticatedId();
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/utils/RequestUtilsTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/utils/RequestUtilsTest.java
index d3dcad3..6452d99 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/utils/RequestUtilsTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/utils/RequestUtilsTest.java
@@ -18,20 +18,22 @@
 package org.apache.ambari.server.utils;
 
 import static org.easymock.EasyMock.anyString;
-import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.assertEquals;
 
+import java.util.List;
+
 import javax.servlet.http.HttpServletRequest;
 
+import org.easymock.EasyMockSupport;
+import org.junit.Assert;
 import org.junit.Test;
+import org.springframework.util.MultiValueMap;
 
-public class RequestUtilsTest {
+public class RequestUtilsTest extends EasyMockSupport {
 
-  public static final String REMOTE_ADDRESS = "12.13.14.15";
-  public static final String REMOTE_ADDRESS_MULTIPLE = 
"12.13.14.15,12.13.14.16";
+  private static final String REMOTE_ADDRESS = "12.13.14.15";
+  private static final String REMOTE_ADDRESS_MULTIPLE = 
"12.13.14.15,12.13.14.16";
 
   @Test
   public void testGetRemoteAddress() {
@@ -42,12 +44,12 @@ public class RequestUtilsTest {
     expect(mockedRequest.getHeader("WL-Proxy-Client-IP")).andReturn("");
     expect(mockedRequest.getHeader("HTTP_CLIENT_IP")).andReturn("unknown");
     
expect(mockedRequest.getHeader("HTTP_X_FORWARDED_FOR")).andReturn(REMOTE_ADDRESS);
-    replay(mockedRequest);
+    replayAll();
     // WHEN
     String remoteAddress = RequestUtils.getRemoteAddress(mockedRequest);
     // THEN
     assertEquals(REMOTE_ADDRESS, remoteAddress);
-    verify(mockedRequest);
+    verifyAll();
   }
 
   @Test
@@ -59,12 +61,12 @@ public class RequestUtilsTest {
     expect(mockedRequest.getHeader("WL-Proxy-Client-IP")).andReturn("");
     expect(mockedRequest.getHeader("HTTP_CLIENT_IP")).andReturn("unknown");
     
expect(mockedRequest.getHeader("HTTP_X_FORWARDED_FOR")).andReturn(REMOTE_ADDRESS_MULTIPLE);
-    replay(mockedRequest);
+    replayAll();
     // WHEN
     String remoteAddress = RequestUtils.getRemoteAddress(mockedRequest);
     // THEN
     assertEquals(REMOTE_ADDRESS, remoteAddress);
-    verify(mockedRequest);
+    verifyAll();
   }
 
   @Test
@@ -72,12 +74,12 @@ public class RequestUtilsTest {
     // GIVEN
     HttpServletRequest mockedRequest = createMock(HttpServletRequest.class);
     
expect(mockedRequest.getHeader("X-Forwarded-For")).andReturn(REMOTE_ADDRESS);
-    replay(mockedRequest);
+    replayAll();
     // WHEN
     String remoteAddress = RequestUtils.getRemoteAddress(mockedRequest);
     // THEN
     assertEquals(REMOTE_ADDRESS, remoteAddress);
-    verify(mockedRequest);
+    verifyAll();
   }
 
   @Test
@@ -86,11 +88,153 @@ public class RequestUtilsTest {
     HttpServletRequest mockedRequest = createMock(HttpServletRequest.class);
     expect(mockedRequest.getHeader(anyString())).andReturn(null).times(5);
     expect(mockedRequest.getRemoteAddr()).andReturn(REMOTE_ADDRESS);
-    replay(mockedRequest);
+    replayAll();
     // WHEN
     String remoteAddress = RequestUtils.getRemoteAddress(mockedRequest);
     // THEN
     assertEquals(REMOTE_ADDRESS, remoteAddress);
-    verify(mockedRequest);
+    verifyAll();
+  }
+
+
+  @Test
+  public void testGetQueryStringParameters() {
+
+    HttpServletRequest request = createMock(HttpServletRequest.class);
+    // Null query string
+    expect(request.getQueryString()).andReturn(null).once();
+    // Empty query string
+    expect(request.getQueryString()).andReturn("").once();
+    // Single parameter query string
+    expect(request.getQueryString()).andReturn("p1=1").once();
+    // Multiple parameter query string
+    expect(request.getQueryString()).andReturn("p1=1&p2=2").once();
+    // Multiple parameter query string, one with multiple values
+    expect(request.getQueryString()).andReturn("p1=1a&p2=2&p1=1b").once();
+
+    replayAll();
+    // Null query string
+    Assert.assertNull(RequestUtils.getQueryStringParameters(request));
+
+    // Empty query string
+    Assert.assertNull(RequestUtils.getQueryStringParameters(request));
+
+    MultiValueMap<String, String> parameters;
+
+    // Single parameter query string
+    parameters = RequestUtils.getQueryStringParameters(request);
+    Assert.assertNotNull(parameters);
+    Assert.assertEquals(1, parameters.size());
+    Assert.assertNotNull(parameters.get("p1"));
+    Assert.assertEquals(1, parameters.get("p1").size());
+    Assert.assertEquals("1", parameters.get("p1").get(0));
+
+    // Multiple  parameter query string
+    parameters = RequestUtils.getQueryStringParameters(request);
+    Assert.assertNotNull(parameters);
+    Assert.assertEquals(2, parameters.size());
+    Assert.assertNotNull(parameters.get("p1"));
+    Assert.assertEquals(1, parameters.get("p1").size());
+    Assert.assertEquals("1", parameters.get("p1").get(0));
+    Assert.assertNotNull(parameters.get("p2"));
+    Assert.assertEquals(1, parameters.get("p2").size());
+    Assert.assertEquals("2", parameters.get("p2").get(0));
+
+    // Multiple parameter query string, one with multiple values
+    parameters = RequestUtils.getQueryStringParameters(request);
+    Assert.assertNotNull(parameters);
+    Assert.assertEquals(2, parameters.size());
+    Assert.assertNotNull(parameters.get("p1"));
+    Assert.assertEquals(2, parameters.get("p1").size());
+    Assert.assertEquals("1a", parameters.get("p1").get(0));
+    Assert.assertEquals("1b", parameters.get("p1").get(1));
+    Assert.assertNotNull(parameters.get("p2"));
+    Assert.assertEquals(1, parameters.get("p2").size());
+    Assert.assertEquals("2", parameters.get("p2").get(0));
+
+    verifyAll();
+  }
+
+  @Test
+  public void testGetQueryStringParameterValues() {
+    HttpServletRequest request = createMock(HttpServletRequest.class);
+    // Null query string
+    expect(request.getQueryString()).andReturn(null).once();
+    // Empty query string
+    expect(request.getQueryString()).andReturn("").once();
+    // Single parameter query string
+    expect(request.getQueryString()).andReturn("p1=1").once();
+    // Multiple parameter query string
+    expect(request.getQueryString()).andReturn("p1=1&p2=2").once();
+    // Multiple parameter query string, one with multiple values
+    expect(request.getQueryString()).andReturn("p1=1a&p2=2&p1=1b").once();
+
+    replayAll();
+    // Null query string
+    Assert.assertNull(RequestUtils.getQueryStringParameterValues(request, 
"p1"));
+
+    // Empty query string
+    Assert.assertNull(RequestUtils.getQueryStringParameterValues(request, 
"p1"));
+
+    List<String> parameterValues;
+
+    // Single parameter query string
+    parameterValues = RequestUtils.getQueryStringParameterValues(request, 
"p1");
+    Assert.assertNotNull(parameterValues);
+    Assert.assertEquals(1, parameterValues.size());
+    Assert.assertEquals("1", parameterValues.get(0));
+
+    // Multiple parameter query string
+    parameterValues = RequestUtils.getQueryStringParameterValues(request, 
"p2");
+    Assert.assertNotNull(parameterValues);
+    Assert.assertEquals(1, parameterValues.size());
+    Assert.assertEquals("2", parameterValues.get(0));
+
+    // Multiple parameter query string, one with multiple values
+    parameterValues = RequestUtils.getQueryStringParameterValues(request, 
"p1");
+    Assert.assertNotNull(parameterValues);
+    Assert.assertEquals(2, parameterValues.size());
+    Assert.assertEquals("1a", parameterValues.get(0));
+    Assert.assertEquals("1b", parameterValues.get(1));
+
+    verifyAll();
+  }
+
+  @Test
+  public void testGetQueryStringParameterValue() {
+    HttpServletRequest request = createMock(HttpServletRequest.class);
+    // Null query string
+    expect(request.getQueryString()).andReturn(null).once();
+    // Empty query string
+    expect(request.getQueryString()).andReturn("").once();
+    // Single parameter query string
+    expect(request.getQueryString()).andReturn("p1=1").once();
+    // Multiple parameter query string
+    expect(request.getQueryString()).andReturn("p1=1&p2=2").once();
+    // Multiple parameter query string, one with multiple values
+    expect(request.getQueryString()).andReturn("p1=1a&p2=2&p1=1b").once();
+
+    replayAll();
+    // Null query string
+    Assert.assertNull(RequestUtils.getQueryStringParameterValue(request, 
"p1"));
+
+    // Empty query string
+    Assert.assertNull(RequestUtils.getQueryStringParameterValue(request, 
"p1"));
+
+    String parameterValue;
+
+    // Single parameter query string
+    parameterValue = RequestUtils.getQueryStringParameterValue(request, "p1");
+    Assert.assertEquals("1", parameterValue);
+
+    // Multiple parameter query string
+    parameterValue = RequestUtils.getQueryStringParameterValue(request, "p2");
+    Assert.assertEquals("2", parameterValue);
+
+    // Multiple parameter query string, one with multiple values
+    parameterValue = RequestUtils.getQueryStringParameterValue(request, "p1");
+    Assert.assertEquals("1a", parameterValue);
+
+    verifyAll();
   }
 }

Reply via email to