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

amagyar pushed a commit to branch branch-2.6
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-2.6 by this push:
     new 37aa671  AMBARI-25155. Backport knox trusted proxy support for ambari 
for 2.6 (amagyar) (#2821)
37aa671 is described below

commit 37aa6718779215ce345826de0ff62c6d153bbdd0
Author: Attila Magyar <m.magy...@gmail.com>
AuthorDate: Thu Feb 21 15:45:48 2019 +0100

    AMBARI-25155. Backport knox trusted proxy support for ambari for 2.6 
(amagyar) (#2821)
---
 ambari-server/sbin/ambari-server                   |   6 +-
 .../ambari/server/api/predicate/QueryLexer.java    |   2 +
 .../ambari/server/api/services/LogoutService.java  |   3 +-
 .../server/audit/event/AbstractUserAuditEvent.java |  23 ++
 .../audit/event/OperationStatusAuditEvent.java     |  19 +-
 .../server/audit/event/TaskStatusAuditEvent.java   |  22 +-
 .../AmbariBasicAuthenticationFilter.java           |  22 +-
 .../AmbariProxiedUserDetailsImpl.java              |  96 +++++++
 .../authentication/AmbariProxyUserDetails.java     |  32 +++
 .../authentication/AmbariProxyUserDetailsImpl.java |  39 +++
 .../security/authentication/AmbariUserDetails.java |  29 ++
 .../authentication/AmbariUserDetailsImpl.java      |  86 ++++++
 .../AmbariAuthToLocalUserDetailsService.java       |  53 ++--
 .../AmbariKerberosAuthenticationFilter.java        |  10 +
 .../AmbariKerberosAuthenticationProvider.java      | 123 +++++++++
 .../kerberos/AmbariProxiedUserDetailsService.java  | 304 +++++++++++++++++++++
 .../AmbariProxyUserKerberosDetailsImpl.java        |  39 +++
 .../tproxy/AmbariTProxyConfiguration.java          | 121 ++++++++
 .../tproxy/TrustedProxyAuthenticationDetails.java  | 102 +++++++
 .../TrustedProxyAuthenticationDetailsSource.java   |  36 +++
 ...stedProxyAuthenticationNotAllowedException.java |  32 +++
 .../authorization/AmbariAuthorizationFilter.java   |   3 +
 .../authorization/AuthorizationHelper.java         |  48 +++-
 .../server/security/authorization/Users.java       |  36 +++
 .../apache/ambari/server/utils/RequestUtils.java   | 121 +++++++-
 ambari-server/src/main/python/ambari-server.py     |  15 +-
 .../src/main/python/ambari_server/serverUtils.py   |   3 +
 .../src/main/python/ambari_server/setupActions.py  |   1 +
 .../main/python/ambari_server/setupTrustedProxy.py | 124 +++++++++
 .../resources/webapp/WEB-INF/spring-security.xml   |  25 +-
 .../audit/AccessUnauthorizedAuditEventTest.java    |  19 ++
 .../ambari/server/audit/LoginAuditEventTest.java   |  41 +++
 .../ambari/server/audit/LogoutAuditEventTest.java  |  17 ++
 .../audit/OperationStatusAuditEventTest.java       |   4 +-
 .../audit/StartOperationRequestAuditEventTest.java |  18 ++
 .../server/audit/TaskStatusAuditEventTest.java     |   4 +-
 .../AmbariBasicAuthenticationFilterTest.java       |   1 +
 .../AmbariProxiedUserDetailsServiceTest.java       | 204 ++++++++++++++
 ambari-web/app/utils/http_client.js                |   2 +-
 39 files changed, 1801 insertions(+), 84 deletions(-)

diff --git a/ambari-server/sbin/ambari-server b/ambari-server/sbin/ambari-server
index d51cbfa..96352b1 100755
--- a/ambari-server/sbin/ambari-server
+++ b/ambari-server/sbin/ambari-server
@@ -182,6 +182,10 @@ case "${1:-}" in
         echo -e "Purge database history..."
         $PYTHON "$AMBARI_PYTHON_EXECUTABLE" $@
         ;;
+  setup-trusted-proxy)
+         echo -e "Setting up Trusted Proxy support..."
+        $PYTHON "$AMBARI_PYTHON_EXECUTABLE" "$@"
+        ;;
   install-mpack)
         echo -e "Installing management pack"
         $PYTHON "$AMBARI_PYTHON_EXECUTABLE" $@
@@ -200,7 +204,7 @@ case "${1:-}" in
         ;;
   *)
         echo "Usage: $AMBARI_EXECUTABLE
-        
{start|stop|reset|restart|upgrade|status|upgradestack|setup|setup-jce|setup-ldap|sync-ldap|set-current|setup-security|refresh-stack-hash|backup|restore|update-host-names|check-database|enable-stack|setup-sso|db-purge-history|install-mpack|uninstall-mpack|upgrade-mpack|setup-kerberos|setup-pam|migrate-ldap-pam}
 [options]
+        
{start|stop|reset|restart|upgrade|status|upgradestack|setup|setup-jce|setup-ldap|sync-ldap|set-current|setup-security|refresh-stack-hash|backup|restore|update-host-names|check-database|enable-stack|setup-sso|setup-trusted-proxy|db-purge-history|install-mpack|uninstall-mpack|upgrade-mpack|setup-kerberos|setup-pam|migrate-ldap-pam}
 [options]
         Use $AMBARI_PYTHON_EXECUTABLE <action> --help to get details on 
options available.
         Or, simply invoke ambari-server.py --help to print the options."
         exit 1
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 afaae58..68e8102 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
@@ -44,6 +44,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.
@@ -209,6 +210,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/api/services/LogoutService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/LogoutService.java
index 02403f9..88c096e 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/LogoutService.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/LogoutService.java
@@ -29,9 +29,9 @@ import org.apache.ambari.server.audit.AuditLogger;
 import org.apache.ambari.server.audit.event.LogoutAuditEvent;
 import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.utils.RequestUtils;
+import org.springframework.security.core.context.SecurityContextHolder;
 
 import com.google.inject.Inject;
-import org.springframework.security.core.context.SecurityContextHolder;
 
 /**
  * Service performing logout of current user
@@ -64,6 +64,7 @@ public class LogoutService {
       .withTimestamp(System.currentTimeMillis())
       .withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
       .withUserName(AuthorizationHelper.getAuthenticatedName())
+      .withProxyUserName(AuthorizationHelper.getProxyUserName())
       .build();
     auditLogger.log(logoutEvent);
   }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/AbstractUserAuditEvent.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/AbstractUserAuditEvent.java
index 6aab071..9acdfde 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/AbstractUserAuditEvent.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/AbstractUserAuditEvent.java
@@ -19,6 +19,7 @@
 package org.apache.ambari.server.audit.event;
 
 import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+import org.apache.commons.lang.StringUtils;
 
 /**
  * Base class for audit events which are result of user actions. It appends
@@ -39,6 +40,10 @@ public abstract class AbstractUserAuditEvent extends 
AbstractAuditEvent {
      * Ip of the user who started the operation. Note: remote ip might not be 
the original ip (proxies, routers can modify it)
      */
     private String remoteIp;
+    /**
+     * Name of the proxy user if proxied
+     */
+    private String proxyUserName = AuthorizationHelper.getProxyUserName();
 
     /**
      * Appends to audit event details the user name and remote ip of the host
@@ -54,6 +59,12 @@ public abstract class AbstractUserAuditEvent extends 
AbstractAuditEvent {
         .append("), RemoteIp(")
         .append(this.remoteIp)
         .append(")");
+      if (StringUtils.isNotEmpty(this.proxyUserName)){
+        builder
+          .append(", ProxyUser(")
+          .append(this.proxyUserName)
+          .append(")");
+      }
     }
 
     /**
@@ -79,6 +90,18 @@ public abstract class AbstractUserAuditEvent extends 
AbstractAuditEvent {
 
       return (TBuilder) this;
     }
+
+    /**
+     * Sets the proxy user name.
+     *
+     * @param proxyUserName
+     * @return the builder
+     */
+    public TBuilder withProxyUserName(String proxyUserName) {
+      this.proxyUserName = proxyUserName;
+
+      return (TBuilder) this;
+    }
   }
 
   protected AbstractUserAuditEvent() {
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/OperationStatusAuditEvent.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/OperationStatusAuditEvent.java
index fd0068e..63c7efd9 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/OperationStatusAuditEvent.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/OperationStatusAuditEvent.java
@@ -25,9 +25,9 @@ import javax.annotation.concurrent.Immutable;
  * Audit event for tracking operations
  */
 @Immutable
-public class OperationStatusAuditEvent extends AbstractAuditEvent {
+public class OperationStatusAuditEvent extends AbstractUserAuditEvent {
 
-  public static class OperationStatusAuditEventBuilder extends 
AbstractAuditEventBuilder<OperationStatusAuditEvent, 
OperationStatusAuditEventBuilder> {
+  public static class OperationStatusAuditEventBuilder extends 
AbstractUserAuditEventBuilder<OperationStatusAuditEvent, 
OperationStatusAuditEventBuilder> {
 
     /**
      * Request identifier
@@ -44,11 +44,6 @@ public class OperationStatusAuditEvent extends 
AbstractAuditEvent {
      */
     private String operation;
 
-    /**
-     * Name of the logged in user who sent the request
-     */
-    private String userName;
-
     private OperationStatusAuditEventBuilder() {
     }
 
@@ -64,10 +59,9 @@ public class OperationStatusAuditEvent extends 
AbstractAuditEvent {
      */
     @Override
     protected void buildAuditMessage(StringBuilder builder) {
+      super.buildAuditMessage(builder);
       builder
-        .append("User(")
-        .append(this.userName)
-        .append("), Operation(")
+        .append(", Operation(")
         .append(this.operation)
         .append("), Status(")
         .append(this.status)
@@ -91,11 +85,6 @@ public class OperationStatusAuditEvent extends 
AbstractAuditEvent {
       this.operation = operation;
       return this;
     }
-
-    public OperationStatusAuditEventBuilder withUserName(String userName) {
-      this.userName = userName;
-      return this;
-    }
   }
 
   private OperationStatusAuditEvent() {
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/TaskStatusAuditEvent.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/TaskStatusAuditEvent.java
index 1682e74..13a14e6 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/audit/event/TaskStatusAuditEvent.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/audit/event/TaskStatusAuditEvent.java
@@ -25,9 +25,9 @@ import javax.annotation.concurrent.Immutable;
  * Audit event for tracking task status
  */
 @Immutable
-public class TaskStatusAuditEvent extends AbstractAuditEvent {
+public class TaskStatusAuditEvent extends AbstractUserAuditEvent {
 
-  public static class TaskStatusAuditEventBuilder extends 
AbstractAuditEventBuilder<TaskStatusAuditEvent, TaskStatusAuditEventBuilder> {
+  public static class TaskStatusAuditEventBuilder extends 
AbstractUserAuditEventBuilder<TaskStatusAuditEvent, 
TaskStatusAuditEventBuilder> {
 
     /**
      * Request identifier
@@ -59,11 +59,6 @@ public class TaskStatusAuditEvent extends AbstractAuditEvent 
{
      */
     private String details;
 
-    /**
-     * User name
-     */
-    private String userName;
-
     private TaskStatusAuditEventBuilder() {
     }
 
@@ -79,14 +74,9 @@ public class TaskStatusAuditEvent extends AbstractAuditEvent 
{
      */
     @Override
     protected void buildAuditMessage(StringBuilder builder) {
-      builder
-        .append("User(")
-        .append(this.userName)
-        .append("), Operation(")
-        .append(this.operation);
-
+      super.buildAuditMessage(builder);
       if (details != null) {
-        builder.append("), Details(")
+        builder.append(", Details(")
           .append(this.details);
       }
 
@@ -131,10 +121,6 @@ public class TaskStatusAuditEvent extends 
AbstractAuditEvent {
       this.details = details;
       return this;
     }
-    public TaskStatusAuditEventBuilder withUserName(String userName) {
-      this.userName = userName;
-      return this;
-    }
   }
 
   private TaskStatusAuditEvent() {
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 9e83f73..b6e08e8 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
@@ -32,6 +32,7 @@ import org.apache.ambari.server.security.AmbariEntryPoint;
 import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.security.authorization.PermissionHelper;
 import org.apache.ambari.server.utils.RequestUtils;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -99,8 +100,27 @@ 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;
+    }
   }
 
   /**
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..35b16d7
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetails.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;
+
+/**
+ * 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();
+}
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..94bd997
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariProxyUserDetailsImpl.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * AmbariProxyUserDetailsImpl is a general implementation of a {@link 
AmbariProxyUserDetails}.
+ */
+public class AmbariProxyUserDetailsImpl implements AmbariProxyUserDetails {
+  private final String username;
+
+  /**
+   * Constructor
+   * @param username           the local username
+   */
+  public AmbariProxyUserDetailsImpl(String username) {
+    this.username = username;
+  }
+
+  @Override
+  public String getUsername() {
+    return username;
+  }
+}
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
new file mode 100644
index 0000000..c0b00cd
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetails.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.security.authentication;
+
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * 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 interface AmbariUserDetails extends UserDetails {
+  Integer getUserId();
+}
\ No newline at end of file
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetailsImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetailsImpl.java
new file mode 100644
index 0000000..235a545
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserDetailsImpl.java
@@ -0,0 +1,86 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.ambari.server.security.authorization.User;
+import org.springframework.security.core.GrantedAuthority;
+
+/**
+ * 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 AmbariUserDetailsImpl implements AmbariUserDetails {
+
+  private final User user;
+  private final String password;
+  private final Collection<? extends GrantedAuthority> grantedAuthorities;
+
+  public AmbariUserDetailsImpl(User user, String password, Collection<? 
extends GrantedAuthority> grantedAuthorities) {
+    this.user = user;
+    this.password = password;
+    this.grantedAuthorities = (grantedAuthorities == null)
+        ? Collections.<GrantedAuthority>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();
+  }
+
+  @Override
+  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();
+  }
+}
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 6e84233..7d89e7e 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
@@ -61,7 +61,7 @@ public class AmbariAuthToLocalUserDetailsService implements 
UserDetailsService {
    * @param users         the Ambari users access object
    * @throws AmbariException if an error occurs parsing the user-provided 
auth-to-local rules
    */
-  public AmbariAuthToLocalUserDetailsService(Configuration configuration, 
Users users) throws AmbariException {
+  public AmbariAuthToLocalUserDetailsService(Configuration configuration, 
Users users) {
     String authToLocalRules = null;
     List<UserType> orderedUserTypes = null;
 
@@ -89,29 +89,40 @@ public class AmbariAuthToLocalUserDetailsService implements 
UserDetailsService {
 
   @Override
   public UserDetails loadUserByUsername(String principal) throws 
UsernameNotFoundException {
-    try {
-      String username;
-
-      // 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 
chaning the rules out from
-      // under us during this operation.  Similar logic is used in 
org.apache.ambari.server.view.ViewContextImpl.getUsername().
-      synchronized (KerberosName.class) {
-        KerberosName.setRules(authToLocalRules);
-        username = new KerberosName(principal).getShortName();
-      }
+    String 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);
+      throw new UsernameNotFoundException(message);
+    }
+    LOG.info("Translated {} to {} using auth-to-local rules during Kerberos 
authentication.", principal, username);
+    return createUser(username);
+  }
 
-      if (username == null) {
-        String message = String.format("Failed to translate %s to a local 
username during Kerberos authentication.", principal);
+  /**
+   * 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 (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);
+        throw new UsernameNotFoundException(message, e);
       }
-
-      LOG.info("Translated {} to {} using auth-to-local rules during Kerberos 
authentication.", principal, username);
-      return createUser(username);
-    } 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);
+    } 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 a5a3922..2ae25e2 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
@@ -23,9 +23,12 @@ import org.apache.ambari.server.audit.event.AuditEvent;
 import org.apache.ambari.server.audit.event.LoginAuditEvent;
 import org.apache.ambari.server.configuration.Configuration;
 import 
org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter;
+import 
org.apache.ambari.server.security.authentication.tproxy.TrustedProxyAuthenticationDetailsSource;
 import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.security.authorization.PermissionHelper;
 import org.apache.ambari.server.utils.RequestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
@@ -50,6 +53,7 @@ import java.io.IOException;
  */
 public class AmbariKerberosAuthenticationFilter extends 
SpnegoAuthenticationProcessingFilter implements AmbariAuthenticationFilter {
 
+  private static final Logger LOG = 
LoggerFactory.getLogger(AmbariKerberosAuthenticationFilter.class);
   /**
    * Audit logger
    */
@@ -83,6 +87,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 {
@@ -128,6 +134,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..e0e9145
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationProvider.java
@@ -0,0 +1,123 @@
+/*
+ * 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.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;
+
+  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..e558cac
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxiedUserDetailsService.java
@@ -0,0 +1,304 @@
+/*
+ * 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 java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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.AmbariUserDetailsImpl;
+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.authentication.BadCredentialsException;
+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;
+
+/**
+ * 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.
+ */
+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})$");
+  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
+   */
+  public 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 = 
AmbariTProxyConfiguration.fromConfig(configuration);
+
+    // 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 = explode(allowedGroups);
+
+      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;
+  }
+
+  private static Set<String> explode(String commaSeparated) {
+    Set<String> result = new HashSet<>();
+    for (String each : commaSeparated.split("\\s*,\\s*")) {
+      result.add(each.trim().toLowerCase());
+    }
+    return result;
+  }
+
+  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 = explode(allowedHosts);
+
+      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
+    if (!userEntity.getActive()) {
+      LOG.info("User account is disabled: {}", username);
+      // Do not give away information about the existence or status of a user
+      throw new BadCredentialsException("Bad credentials");
+    }
+
+    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..79d74cb
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariProxyUserKerberosDetailsImpl.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * 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);
+    this.principalName = principalName;
+  }
+
+  public String getPrincipalName() {
+    return principalName;
+  }
+}
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
new file mode 100644
index 0000000..29278da
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/tproxy/AmbariTProxyConfiguration.java
@@ -0,0 +1,121 @@
+/*
+ *
+ *  * 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 java.util.Map;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+
+public class AmbariTProxyConfiguration {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(AmbariTProxyConfiguration.class);
+  private static final String TPROXY_AUTHENTICATION_ENABLED = 
"ambari.tproxy.authentication.enabled";
+  private static final String TEMPLATE_PROXY_USER_ALLOWED_HOSTS = 
"ambari.tproxy.proxyuser.%s.hosts";
+  private static final String TEMPLATE_PROXY_USER_ALLOWED_USERS = 
"ambari.tproxy.proxyuser.%s.users";
+  private static final String TEMPLATE_PROXY_USER_ALLOWED_GROUPS = 
"ambari.tproxy.proxyuser.%s.groups";
+  private final ImmutableMap<String, String> configurationMap;
+
+  public static AmbariTProxyConfiguration fromConfig(Configuration 
configuration) {
+    return new AmbariTProxyConfiguration(configuration.getAmbariProperties());
+  }
+
+  /**
+   * Constructor
+   * <p>
+   * Copies the given configuration propery map into an {@link ImmutableMap} 
and pulls out propery
+   * values upon request.
+   *
+   * @param configurationMap a map of property names to values
+   */
+  AmbariTProxyConfiguration(Map<String, String> configurationMap) {
+    ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+
+    if (configurationMap != null) {
+      builder.putAll(configurationMap);
+    }
+
+    this.configurationMap = builder.build();
+  }
+
+  /**
+   * Returns an immutable map of the contained properties
+   *
+   * @return an immutable map property names to values
+   */
+  public Map<String, String> toMap() {
+    return configurationMap;
+  }
+
+  /**
+   * Determines of tristed proxy support is enabled based on the configuration 
data.
+   *
+   * @return <code>true</code> if trusted proxy support is enabed; 
<code>false</code> otherwise
+   */
+  public boolean isEnabled() {
+    return Boolean.valueOf(getValue(TPROXY_AUTHENTICATION_ENABLED, 
configurationMap, "false"));
+  }
+
+  public String getAllowedHosts(String proxyUser) {
+    return getValue(String.format(TEMPLATE_PROXY_USER_ALLOWED_HOSTS, 
proxyUser), configurationMap, "");
+  }
+
+  public String getAllowedUsers(String proxyUser) {
+    return getValue(String.format(TEMPLATE_PROXY_USER_ALLOWED_USERS, 
proxyUser), configurationMap, "");
+  }
+
+  public String getAllowedGroups(String proxyUser) {
+    return getValue(String.format(TEMPLATE_PROXY_USER_ALLOWED_GROUPS, 
proxyUser), configurationMap, "");
+  }
+
+  protected String getValue(String propertyName, Map<String, String> 
configurationMap, String defaultValue) {
+    if ((configurationMap != null) && 
configurationMap.containsKey(propertyName)) {
+      return configurationMap.get(propertyName);
+    } else {
+      LOGGER.debug("Ambari server configuration property [{}] hasn't been set; 
using default value", propertyName);
+      return defaultValue;
+    }
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    return new EqualsBuilder()
+      .append(configurationMap, ((AmbariTProxyConfiguration) 
o).configurationMap)
+      .isEquals();
+  }
+
+  @Override
+  public int hashCode() {
+    return new HashCodeBuilder(17, 37)
+      .append(configurationMap)
+      .toHashCode();
+  }
+}
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/AmbariAuthorizationFilter.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
index f0dea59..b9bf6ce 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
@@ -178,6 +178,7 @@ public class AmbariAuthorizationFilter implements Filter {
         if(auditLogger.isEnabled()) {
           LoginAuditEvent loginAuditEvent = LoginAuditEvent.builder()
             .withUserName(internalAuthenticationToken.getName())
+            
.withProxyUserName(AuthorizationHelper.getProxyUserName(internalAuthenticationToken))
             .withRemoteIp(RequestUtils.getRemoteAddress(httpRequest))
             .withRoles(permissionHelper.getPermissionLabels(authentication))
             .withTimestamp(System.currentTimeMillis()).build();
@@ -260,6 +261,7 @@ public class AmbariAuthorizationFilter implements Filter {
             .withRemoteIp(RequestUtils.getRemoteAddress(httpRequest))
             .withResourcePath(httpRequest.getRequestURI())
             .withUserName(AuthorizationHelper.getAuthenticatedName())
+            .withProxyUserName(AuthorizationHelper.getProxyUserName())
             .withTimestamp(System.currentTimeMillis())
             .build();
           auditLogger.log(auditEvent);
@@ -279,6 +281,7 @@ public class AmbariAuthorizationFilter implements Filter {
           .withRemoteIp(RequestUtils.getRemoteAddress(httpRequest))
           .withResourcePath(httpRequest.getRequestURI())
           .withUserName(AuthorizationHelper.getAuthenticatedName())
+          .withProxyUserName(AuthorizationHelper.getProxyUserName())
           .withTimestamp(System.currentTimeMillis())
           .build();
         auditLogger.log(auditEvent);
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
index e875e8a..ea3446e 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java
@@ -17,16 +17,19 @@
  */
 package org.apache.ambari.server.security.authorization;
 
-import com.google.common.collect.Lists;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 import org.apache.ambari.server.orm.dao.PrivilegeDAO;
 import org.apache.ambari.server.orm.dao.ViewInstanceDAO;
 import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
 import org.apache.ambari.server.orm.entities.ResourceEntity;
 import org.apache.ambari.server.orm.entities.RoleAuthorizationEntity;
+import 
org.apache.ambari.server.security.authentication.AmbariProxiedUserDetailsImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.Authentication;
@@ -37,11 +40,10 @@ import 
org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
 
 /**
  * Provides utility methods for authentication functionality
@@ -341,4 +343,32 @@ public class AuthorizationHelper {
     return authorizationNames;
   }
 
+  /**
+   * Gets the name of the logged-in proxy user, if any.
+   *
+   * @param authentication
+   * @return the name of the logged-in proxy user
+   */
+  public static String getProxyUserName(Authentication authentication) {
+    if (authentication==null){
+      return null;
+    }
+    Object userDetails = authentication.getPrincipal();
+    if (userDetails instanceof AmbariProxiedUserDetailsImpl) {
+      AmbariProxiedUserDetailsImpl ambariProxiedUserDetails = 
(AmbariProxiedUserDetailsImpl) userDetails;
+      return ambariProxiedUserDetails.getProxyUserDetails().getUsername();
+    }
+    return null;
+  }
+
+  /**
+   * Gets the name of the logged-in proxy user, if any.
+   *
+   * @return the name of the logged-in proxy user
+   */
+  public static String getProxyUserName() {
+    SecurityContext securityContext = SecurityContextHolder.getContext();
+    Authentication auth = securityContext.getAuthentication();
+    return getProxyUserName(auth);
+  }
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
index 69c750a..0b869a5 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
@@ -140,6 +140,10 @@ public class Users {
     return (null == userEntity) ? null : new User(userEntity);
   }
 
+  public UserEntity getUserEntity(String userName) {
+    return (userName == null) ? null : userDAO.findUserByName(userName);
+  }
+
   /**
    * Retrieves User then userName is unique in users DB. Will return null if 
there no user with provided userName or
    * there are some users with provided userName but with different types.
@@ -963,6 +967,38 @@ public class Users {
   }
 
   /**
+   * Gets the explicit and implicit authorities for the given user.
+   * <p>
+   * The explicit authorities are the authorities that have be explicitly set 
by assigning roles to
+   * a user.  For example the Cluster Operator role on a given cluster gives 
that the ability to
+   * start and stop services in that cluster, among other privileges for that 
particular cluster.
+   * <p>
+   * The implicit authorities are the authorities that have been given to the 
roles themselves which
+   * in turn are granted to the users that have been assigned those roles. For 
example if the
+   * Cluster User role for a given cluster has been given View User access on 
a specified File View
+   * instance, then all users who have the Cluster User role for that cluster 
will implicitly be
+   * granted View User access on that File View instance.
+   *
+   * @param userEntity the relevant user
+   * @return the users collection of implicit and explicit granted authorities
+   */
+  public Collection<AmbariGrantedAuthority> getUserAuthorities(UserEntity 
userEntity) {
+    if (userEntity == null) {
+      return Collections.emptyList();
+    }
+
+    Collection<PrivilegeEntity> privilegeEntities = 
getUserPrivileges(userEntity);
+
+    Set<AmbariGrantedAuthority> authorities = new 
HashSet<>(privilegeEntities.size());
+
+    for (PrivilegeEntity privilegeEntity : privilegeEntities) {
+      authorities.add(new AmbariGrantedAuthority(privilegeEntity));
+    }
+
+    return authorities;
+  }
+
+  /**
    * Gets the implicit privileges based on the set of roles found in a 
collection of privileges.
    * <p>
    * The implicit privileges are the privileges that have been given to the 
roles themselves which
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 dbb0f11..747a6c6 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
@@ -17,15 +17,26 @@
  */
 package org.apache.ambari.server.utils;
 
-import com.google.common.collect.ImmutableSet;
+import java.nio.charset.Charset;
 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.ambari.server.api.services.Request;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.slf4j.Logger;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 
+import com.google.common.collect.ImmutableSet;
+
 /**
  * The purpose of this helper is to get remote address from an HTTP request
  */
@@ -95,4 +106,110 @@ 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 : 
parseQueryParameters(queryString);
+  }
+
+  private static MultiValueMap<String, String> parseQueryParameters(String 
queryString) {
+    LinkedMultiValueMap result = new LinkedMultiValueMap();
+    List<NameValuePair> params = URLEncodedUtils.parse(queryString, 
Charset.forName("UTF-8"));
+    for (NameValuePair each : params) {
+      result.add(each.getName(), each.getValue());
+    }
+    return result;
+  }
+
+  /**
+   * 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/main/python/ambari-server.py 
b/ambari-server/src/main/python/ambari-server.py
index 3a67532..9477d16 100755
--- a/ambari-server/src/main/python/ambari-server.py
+++ b/ambari-server/src/main/python/ambari-server.py
@@ -42,6 +42,7 @@ from ambari_server.setupHttps import setup_https, 
setup_truststore
 from ambari_server.setupMpacks import install_mpack, uninstall_mpack, 
upgrade_mpack, STACK_DEFINITIONS_RESOURCE_NAME, \
   SERVICE_DEFINITIONS_RESOURCE_NAME, MPACKS_RESOURCE_NAME
 from ambari_server.setupSso import setup_sso
+from ambari_server.setupTrustedProxy import setup_trusted_proxy
 from ambari_server.dbCleanup import db_purge
 from ambari_server.hostUpdate import update_host_names
 from ambari_server.checkDatabase import check_database
@@ -52,7 +53,7 @@ from ambari_server.setupActions import BACKUP_ACTION, 
LDAP_SETUP_ACTION, LDAP_SY
   SETUP_ACTION, SETUP_SECURITY_ACTION, START_ACTION, STATUS_ACTION, 
STOP_ACTION, RESTART_ACTION, UPGRADE_ACTION, \
   SETUP_JCE_ACTION, SET_CURRENT_ACTION, START_ACTION, STATUS_ACTION, 
STOP_ACTION, UPGRADE_ACTION, \
   SETUP_JCE_ACTION, SET_CURRENT_ACTION, ENABLE_STACK_ACTION, SETUP_SSO_ACTION, 
\
-  DB_PURGE_ACTION, INSTALL_MPACK_ACTION, UNINSTALL_MPACK_ACTION, 
UPGRADE_MPACK_ACTION, PAM_SETUP_ACTION, MIGRATE_LDAP_PAM_ACTION, 
KERBEROS_SETUP_ACTION
+  DB_PURGE_ACTION, INSTALL_MPACK_ACTION, UNINSTALL_MPACK_ACTION, 
UPGRADE_MPACK_ACTION, PAM_SETUP_ACTION, MIGRATE_LDAP_PAM_ACTION, 
KERBEROS_SETUP_ACTION, SETUP_TPROXY_ACTION
 from ambari_server.setupSecurity import setup_ldap, sync_ldap, 
setup_master_key, setup_ambari_krb5_jaas, setup_pam, migrate_ldap_pam
 from ambari_server.userInput import get_validated_string_input
 from ambari_server.kerberos_setup import setup_kerberos
@@ -608,6 +609,12 @@ def init_parser_options(parser):
   parser.add_option('--kerberos-spnego-user-types', default="LDAP", help="User 
type search order (comma-delimited)", dest="kerberos_user_types")
   parser.add_option('--kerberos-auth-to-local-rules', default="DEFAULT", 
help="Auth-to-local rules", dest="kerberos_auth_to_local_rules")
 
+  parser.add_option('--tproxy-enabled', default=None, help="Indicates whether 
to enable/disable Trusted Proxy Support", dest="tproxy_enabled")
+  parser.add_option('--tproxy-configuration-file-path', default=None,
+                    help="The path where the Trusted Proxy configuration is 
located. The content is expected to be in JSON format." \
+                         "Sample configuration:[{\"proxyuser\": \"knox\", 
\"hosts\": \"host1\", \"users\": \"user1, user2\", \"groups\": \"group1\"}]",
+                    dest="tproxy_configuration_file_path"
+                    )
 
 @OsFamilyFuncImpl(OSConst.WINSRV_FAMILY)
 def are_cmd_line_db_args_blank(options):
@@ -740,7 +747,8 @@ def create_user_action_map(args, options):
     SETUP_SSO_ACTION: UserActionRestart(setup_sso, options),
     INSTALL_MPACK_ACTION: UserAction(install_mpack, options),
     UNINSTALL_MPACK_ACTION: UserAction(uninstall_mpack, options),
-    UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options)
+    UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options),
+    SETUP_TPROXY_ACTION: UserAction(setup_trusted_proxy, options)
   }
   return action_map
 
@@ -772,7 +780,8 @@ def create_user_action_map(args, options):
         UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options),
         PAM_SETUP_ACTION: UserAction(setup_pam, options),
         MIGRATE_LDAP_PAM_ACTION: UserAction(migrate_ldap_pam, options),
-        KERBEROS_SETUP_ACTION: UserAction(setup_kerberos, options)
+        KERBEROS_SETUP_ACTION: UserAction(setup_kerberos, options),
+        SETUP_TPROXY_ACTION: UserAction(setup_trusted_proxy, options)
       }
   return action_map
 
diff --git a/ambari-server/src/main/python/ambari_server/serverUtils.py 
b/ambari-server/src/main/python/ambari_server/serverUtils.py
index 4621646..04b6149 100644
--- a/ambari-server/src/main/python/ambari_server/serverUtils.py
+++ b/ambari-server/src/main/python/ambari_server/serverUtils.py
@@ -133,3 +133,6 @@ def get_ambari_server_api_base(properties):
     if api_port_prop is not None:
       api_port = api_port_prop
   return '{0}://{1}:{2!s}/api/v1/'.format(api_protocol, SERVER_API_HOST, 
api_port)
+
+def get_value_from_dictionary(properties, key, default_value=None):
+  return properties[key] if properties and key in properties else default_value
\ No newline at end of file
diff --git a/ambari-server/src/main/python/ambari_server/setupActions.py 
b/ambari-server/src/main/python/ambari_server/setupActions.py
index 61d20af..b0aec93 100644
--- a/ambari-server/src/main/python/ambari_server/setupActions.py
+++ b/ambari-server/src/main/python/ambari_server/setupActions.py
@@ -49,3 +49,4 @@ UPGRADE_MPACK_ACTION = "upgrade-mpack"
 PAM_SETUP_ACTION = "setup-pam"
 MIGRATE_LDAP_PAM_ACTION = "migrate-ldap-pam"
 KERBEROS_SETUP_ACTION = "setup-kerberos"
+SETUP_TPROXY_ACTION = "setup-trusted-proxy"
\ No newline at end of file
diff --git a/ambari-server/src/main/python/ambari_server/setupTrustedProxy.py 
b/ambari-server/src/main/python/ambari_server/setupTrustedProxy.py
new file mode 100644
index 0000000..d93542e
--- /dev/null
+++ b/ambari-server/src/main/python/ambari_server/setupTrustedProxy.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+
+'''
+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.
+'''
+
+import ambari_simplejson as json
+import os
+import re
+
+from ambari_commons.exceptions import FatalException, NonFatalException
+from ambari_commons.logging_utils import get_silent, print_info_msg
+from ambari_server.serverConfiguration import get_ambari_properties, 
find_properties_file
+from ambari_server.setupSecurity import REGEX_TRUE_FALSE
+from ambari_server.userInput import get_validated_string_input, get_YN_input
+from ambari_commons.logging_utils import print_error_msg
+from ambari_server.serverUtils import get_value_from_dictionary
+
+TPROXY_SUPPORT_ENABLED = 'ambari.tproxy.authentication.enabled'
+PROXYUSER_HOSTS = 'ambari.tproxy.proxyuser.{}.hosts'
+PROXYUSER_USERS = 'ambari.tproxy.proxyuser.{}.users'
+PROXYUSER_GROUPS = 'ambari.tproxy.proxyuser.{}.groups'
+
+REGEX_ANYTHING = '.*'
+WILDCARD_FOR_ALL = '*'
+
+def populate_tproxy_configuration_property(properties, tproxy_user_name, 
property_name, question_text_qualifier):
+  resolved_property_name = property_name.format(tproxy_user_name)
+  resolved_property_value = get_value_from_dictionary(properties, 
resolved_property_name, WILDCARD_FOR_ALL)
+  resolved_property_value = get_validated_string_input("Allowed {0} for {1} 
({2})? ".format(question_text_qualifier, tproxy_user_name, 
resolved_property_value), resolved_property_value, REGEX_ANYTHING, "Invalid 
input", False)
+  properties[resolved_property_name] = resolved_property_value
+
+def add_new_trusted_proxy_config(properties):
+  tproxy_user_name = get_validated_string_input("The proxy user's (local) 
username? ", None, REGEX_ANYTHING, "Invalid Trusted Proxy User Name", False, 
allowEmpty=False)
+  populate_tproxy_configuration_property(properties, tproxy_user_name, 
PROXYUSER_HOSTS, "hosts")
+  populate_tproxy_configuration_property(properties, tproxy_user_name, 
PROXYUSER_USERS, "users")
+  populate_tproxy_configuration_property(properties, tproxy_user_name, 
PROXYUSER_GROUPS, "groups")
+  return get_YN_input("Add another proxy user [y/n] (n)? ", False)
+
+def parse_trusted_configuration_file(tproxy_configuration_file_path, result):
+  with open(tproxy_configuration_file_path) as tproxy_configuration_file:
+    tproxy_configurations = json.loads(tproxy_configuration_file.read())
+  if tproxy_configurations:
+    for tproxy_configuration in tproxy_configurations:
+      tproxy_user_name = tproxy_configuration['proxyuser']
+      result[PROXYUSER_HOSTS.format(tproxy_user_name)] = 
tproxy_configuration['hosts']
+      result[PROXYUSER_USERS.format(tproxy_user_name)] = 
tproxy_configuration['users']
+      result[PROXYUSER_GROUPS.format(tproxy_user_name)] = 
tproxy_configuration['groups']
+
+def validate_options(options):
+  errors = []
+  if options.tproxy_enabled and not re.match(REGEX_TRUE_FALSE, 
options.tproxy_enabled):
+    errors.append("--tproxy-enabled should be to either 'true' or 'false'")
+  if options.tproxy_configuration_file_path and 
options.tproxy_configuration_file_path is not None:
+    if not os.path.isfile(options.tproxy_configuration_file_path):
+      errors.append("--tproxy-configuration-file-path is set to a non-existing 
file: {}".format(options.tproxy_configuration_file_path))
+  if len(errors) > 0:
+    error_msg = "The following errors occurred while processing your request: 
{0}"
+    raise FatalException(1, error_msg.format(str(errors)))
+
+def remove_existing_tproxy_properties(ambari_properties):
+  for key in (each for each in ambari_properties.propertyNames() if 
each.startswith('ambari.tproxy.proxyuser')):
+    ambari_properties.removeOldProp(key)
+
+def save_ambari_properties(properties):
+  conf_file = find_properties_file()
+  try:
+    with open(conf_file, 'w') as f:
+      properties.store_ordered(f)
+  except Exception, e:
+    print_error_msg('Could not write ambari config file "%s": %s' % 
(conf_file, e))
+
+def update_ambari_properties(ambari_properties, a_dict):
+  for key, value in a_dict.items():
+    ambari_properties.process_pair(key, value)
+
+def input_tproxy_config(options):
+  properties = {}
+  if not options.tproxy_configuration_file_path:
+    interactive_tproxy_input(properties)
+  else:
+    parse_trusted_configuration_file(options.tproxy_configuration_file_path, 
properties)
+  return properties
+
+def interactive_tproxy_input(properties):
+  add_new_trusted_proxy = add_new_trusted_proxy_config(properties)
+  while add_new_trusted_proxy:
+    add_new_trusted_proxy = add_new_trusted_proxy_config(properties)
+
+def setup_trusted_proxy(options):
+  print_info_msg("Setup Trusted Proxy")
+  if get_silent():
+    raise NonFatalException('setup-trusted-proxy is not enabled in silent 
mode.')
+  validate_options(options)
+  ambari_properties = get_ambari_properties()
+
+  if ambari_properties.get_property(TPROXY_SUPPORT_ENABLED) == 'true':
+    print_info_msg('\nTrusted Proxy support is currently enabled.\n')
+    if not get_YN_input('Do you want to disable Trusted Proxy support [y/n] 
(n)? ', False):
+      return
+    ambari_properties.process_pair(TPROXY_SUPPORT_ENABLED, 'false')
+  else:
+    print_info_msg('\nTrusted Proxy support is currently disabled.\n')
+    if not get_YN_input('Do you want to configure Trusted Proxy Support [y/n] 
(y)? ', True):
+      return
+    remove_existing_tproxy_properties(ambari_properties)
+    ambari_properties.process_pair(TPROXY_SUPPORT_ENABLED, 'true')
+    update_ambari_properties(ambari_properties, input_tproxy_config(options))
+
+  save_ambari_properties(ambari_properties)
diff --git 
a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml 
b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
index bdbf0de..e668a6e 100644
--- a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
+++ b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
@@ -35,7 +35,7 @@
     <authentication-provider ref="ambariPamAuthenticationProvider"/>
     <authentication-provider ref="ambariLdapAuthenticationProvider"/>
     <authentication-provider ref="ambariInternalAuthenticationProvider"/>
-    <authentication-provider ref="kerberosServiceAuthenticationProvider"/>
+    <authentication-provider ref="ambariKerberosAuthenticationProvider"/>
   </authentication-manager>
 
   <beans:bean id="ambariEntryPoint" 
class="org.apache.ambari.server.security.AmbariEntryPoint">
@@ -83,15 +83,10 @@
     <beans:constructor-arg ref="permissionHelper"/>
   </beans:bean>
 
-  <beans:bean id="kerberosServiceAuthenticationProvider" 
class="org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider">
-    <beans:property name="ticketValidator">
-      <beans:bean 
class="org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosTicketValidator">
-        <beans:constructor-arg ref="ambariConfiguration"/>
-        <beans:property name="debug" value="false"/>
-      </beans:bean>
-    </beans:property>
-
-    <beans:property name="userDetailsService" 
ref="authToLocalUserDetailsService"/>
+  <beans:bean id="ambariKerberosAuthenticationProvider" 
class="org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosAuthenticationProvider">
+    <beans:constructor-arg ref="authToLocalUserDetailsService"/>
+    <beans:constructor-arg ref="proxiedUserDetailsService"/>
+    <beans:constructor-arg ref="ticketValidator"/>
   </beans:bean>
 
   <beans:bean id="authToLocalUserDetailsService" 
class="org.apache.ambari.server.security.authentication.kerberos.AmbariAuthToLocalUserDetailsService">
@@ -99,4 +94,14 @@
     <beans:constructor-arg ref="ambariUsers"/>
   </beans:bean>
 
+  <beans:bean id="ticketValidator" 
class="org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosTicketValidator">
+    <beans:constructor-arg ref="ambariConfiguration"/>
+    <beans:property name="debug" value="false"/>
+  </beans:bean>
+
+  <beans:bean id="proxiedUserDetailsService" 
class="org.apache.ambari.server.security.authentication.kerberos.AmbariProxiedUserDetailsService">
+    <beans:constructor-arg ref="ambariConfiguration"/>
+    <beans:constructor-arg ref="ambariUsers"/>
+  </beans:bean>
+
 </beans:beans>
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java
index 70e4b64..8a3a6d4 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java
@@ -31,6 +31,7 @@ public class AccessUnauthorizedAuditEventTest {
   public void testAuditMessage() throws Exception {
     // Given
     String testUserName = "USER1";
+    String testProxyUserName = "PROXYUSER1";
     String testRemoteIp = "127.0.0.1";
     String testHttpMethod = "GET";
     String testResourcePath = "/api/v1/hosts";
@@ -39,6 +40,7 @@ public class AccessUnauthorizedAuditEventTest {
       .withTimestamp(System.currentTimeMillis())
       .withRemoteIp(testRemoteIp)
       .withUserName(testUserName)
+      .withProxyUserName(null)
       .withHttpMethodName(testHttpMethod)
       .withResourcePath(testResourcePath)
       .build();
@@ -50,6 +52,23 @@ public class AccessUnauthorizedAuditEventTest {
     String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), 
Operation(%s), ResourcePath(%s), Status(Failed), Reason(Access not 
authorized)", testUserName, testRemoteIp, testHttpMethod, testResourcePath);
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
+
+    evnt = AccessUnauthorizedAuditEvent.builder()
+      .withTimestamp(System.currentTimeMillis())
+      .withRemoteIp(testRemoteIp)
+      .withUserName(testUserName)
+      .withProxyUserName(testProxyUserName)
+      .withHttpMethodName(testHttpMethod)
+      .withResourcePath(testResourcePath)
+      .build();
+
+    // When
+    actualAuditMessage = evnt.getAuditMessage();
+
+    // Then
+    expectedAuditMessage = String.format("User(%s), RemoteIp(%s), 
ProxyUser(PROXYUSER1), Operation(%s), ResourcePath(%s), Status(Failed), 
Reason(Access not authorized)", testUserName, testRemoteIp, testHttpMethod, 
testResourcePath);
+
+    assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
   }
 
 
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
index a146176..3d998cc 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java
@@ -39,6 +39,7 @@ public class LoginAuditEventTest {
     // Given
     String testUserName = "USER1";
     String testRemoteIp = "127.0.0.1";
+    String testProxyUserName = "PROXYUSER1";
 
     Map<String, List<String>> roles = new HashMap<>();
     roles.put("a", Arrays.asList("r1", "r2", "r3"));
@@ -47,6 +48,7 @@ public class LoginAuditEventTest {
       .withTimestamp(System.currentTimeMillis())
       .withRemoteIp(testRemoteIp)
       .withUserName(testUserName)
+      .withProxyUserName(null)
       .withRoles(roles)
       .build();
 
@@ -61,6 +63,24 @@ public class LoginAuditEventTest {
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
 
+    evnt = LoginAuditEvent.builder()
+      .withTimestamp(System.currentTimeMillis())
+      .withRemoteIp(testRemoteIp)
+      .withUserName(testUserName)
+      .withProxyUserName(testProxyUserName)
+      .withRoles(roles)
+      .build();
+
+    // When
+    actualAuditMessage = evnt.getAuditMessage();
+
+    roleMessage = System.lineSeparator() + "    a: r1, r2, r3" + 
System.lineSeparator();
+
+    // Then
+    expectedAuditMessage = String.format("User(%s), RemoteIp(%s), 
ProxyUser(%s), Operation(User login), Roles(%s), Status(Success)",
+      testUserName, testRemoteIp, testProxyUserName, roleMessage);
+
+    assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
   }
 
   @Test
@@ -68,6 +88,7 @@ public class LoginAuditEventTest {
     // Given
     String testUserName = "USER1";
     String testRemoteIp = "127.0.0.1";
+    String testProxyUserName = "PROXYUSER1";
     String reason = "Bad credentials";
 
     Map<String, List<String>> roles = new HashMap<>();
@@ -78,6 +99,7 @@ public class LoginAuditEventTest {
       .withRemoteIp(testRemoteIp)
       .withUserName(testUserName)
       .withRoles(roles)
+      .withProxyUserName(null)
       .withReasonOfFailure(reason)
       .build();
 
@@ -92,6 +114,25 @@ public class LoginAuditEventTest {
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
 
+    evnt = LoginAuditEvent.builder()
+      .withTimestamp(System.currentTimeMillis())
+      .withRemoteIp(testRemoteIp)
+      .withUserName(testUserName)
+      .withProxyUserName(testProxyUserName)
+      .withRoles(roles)
+      .withReasonOfFailure(reason)
+      .build();
+
+    // When
+    actualAuditMessage = evnt.getAuditMessage();
+
+    roleMessage = System.lineSeparator() + "    a: r1, r2, r3" + 
System.lineSeparator();
+
+    // Then
+    expectedAuditMessage = String.format("User(%s), RemoteIp(%s), 
ProxyUser(%s), Operation(User login), Roles(%s), Status(Failed), Reason(%s)",
+      testUserName, testRemoteIp, testProxyUserName, roleMessage, reason);
+
+    assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
   }
 
   @Test
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java
index 7fb6fef..5248a94 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java
@@ -32,11 +32,13 @@ public class LogoutAuditEventTest {
     // Given
     String testUserName = "USER1";
     String testRemoteIp = "127.0.0.1";
+    String testProxyUserName = "PROXYUSER1";
 
     LogoutAuditEvent evnt = LogoutAuditEvent.builder()
       .withTimestamp(System.currentTimeMillis())
       .withRemoteIp(testRemoteIp)
       .withUserName(testUserName)
+      .withProxyUserName(null)
       .build();
 
     // When
@@ -48,6 +50,21 @@ public class LogoutAuditEventTest {
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
 
+    evnt = LogoutAuditEvent.builder()
+      .withTimestamp(System.currentTimeMillis())
+      .withRemoteIp(testRemoteIp)
+      .withUserName(testUserName)
+      .withProxyUserName(testProxyUserName)
+      .build();
+
+    // When
+    actualAuditMessage = evnt.getAuditMessage();
+
+    // Then
+    expectedAuditMessage = String.format("User(%s), RemoteIp(%s), 
ProxyUser(%s), Operation(Logout), Status(Success)",
+      testUserName, testRemoteIp, testProxyUserName);
+
+    assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
   }
 
   @Test
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java
index 038cebc..f88b7d7 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java
@@ -33,11 +33,13 @@ public class OperationStatusAuditEventTest {
     // Given
     Long testRequestId = 100L;
     String testStatus = "IN PROGRESS";
+    String testRemoteIp = "127.0.0.1";
 
     OperationStatusAuditEvent evnt = OperationStatusAuditEvent.builder()
       .withTimestamp(System.currentTimeMillis())
       .withRequestId(testRequestId.toString())
       .withStatus(testStatus)
+      .withRemoteIp(testRemoteIp)
       .withUserName("testuser")
       .withRequestContext("Start Service")
       .build();
@@ -46,7 +48,7 @@ public class OperationStatusAuditEventTest {
     String actualAuditMessage = evnt.getAuditMessage();
 
     // Then
-    String expectedAuditMessage = String.format("User(testuser), 
Operation(Start Service), Status(%s), RequestId(%s)", testStatus, 
testRequestId);
+    String expectedAuditMessage = String.format("User(testuser), 
RemoteIp(127.0.0.1), Operation(Start Service), Status(%s), RequestId(%s)", 
testStatus, testRequestId);
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
   }
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java
index a2097d5..8b12d86 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java
@@ -35,11 +35,13 @@ public class StartOperationRequestAuditEventTest {
     String testRemoteIp = "127.0.0.1";
     String testRequestDetails = "{ \"key\": \"value\"}";
     Long testRequestId = 100L;
+    String testProxyUserName = "PROXYUSER1";
 
     StartOperationRequestAuditEvent evnt = 
StartOperationRequestAuditEvent.builder()
       .withTimestamp(System.currentTimeMillis())
       .withRemoteIp(testRemoteIp)
       .withUserName(testUserName)
+      .withProxyUserName(null)
       .withOperation(testRequestDetails)
       .withRequestId(testRequestId.toString())
       .build();
@@ -52,6 +54,22 @@ public class StartOperationRequestAuditEventTest {
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
 
+    evnt = StartOperationRequestAuditEvent.builder()
+      .withTimestamp(System.currentTimeMillis())
+      .withRemoteIp(testRemoteIp)
+      .withUserName(testUserName)
+      .withProxyUserName(testProxyUserName)
+      .withOperation(testRequestDetails)
+      .withRequestId(testRequestId.toString())
+      .build();
+
+    // When
+    actualAuditMessage = evnt.getAuditMessage();
+
+    // Then
+    expectedAuditMessage = String.format("User(%s), RemoteIp(%s), 
ProxyUser(%s), Operation(%s), RequestId(%d), Status(Successfully queued)", 
testUserName, testRemoteIp, testProxyUserName, testRequestDetails, 
testRequestId);
+
+    assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
   }
 
   @Test
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/TaskStatusAuditEventTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/TaskStatusAuditEventTest.java
index 8f769c7..7755468 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/audit/TaskStatusAuditEventTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/audit/TaskStatusAuditEventTest.java
@@ -31,6 +31,7 @@ public class TaskStatusAuditEventTest {
   public void testAuditMessage() throws Exception {
     // Given
     String testUserName = "USER1";
+    String testRemoteIp = "127.0.0.1";
     String testOperation = "START MYCOMPONENT";
     String testRequestDetails = "Start MyComponent";
     String testHostName = "ambari.example.com";
@@ -41,6 +42,7 @@ public class TaskStatusAuditEventTest {
     TaskStatusAuditEvent event = TaskStatusAuditEvent.builder()
       .withTimestamp(System.currentTimeMillis())
       .withUserName(testUserName)
+      .withRemoteIp(testRemoteIp)
       .withOperation(testOperation)
       .withRequestId(testRequestId.toString())
       .withDetails(testRequestDetails)
@@ -53,7 +55,7 @@ public class TaskStatusAuditEventTest {
     String actualAuditMessage = event.getAuditMessage();
 
     // Then
-    String expectedAuditMessage = String.format("User(%s), Operation(%s), 
Details(%s), Status(%s), RequestId(%d), TaskId(%d), Hostname(%s)", 
testUserName, testOperation, testRequestDetails, testStatus, testRequestId, 
testTaskId, testHostName);
+    String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), 
Details(%s), Status(%s), RequestId(%d), TaskId(%d), Hostname(%s)", 
testUserName, testRemoteIp, testRequestDetails, testStatus, testRequestId, 
testTaskId, testHostName);
 
     assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
 
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
index 3df81f9..bafd931 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
@@ -72,6 +72,7 @@ public class AmbariBasicAuthenticationFilterTest extends 
EasyMockSupport {
     FilterChain filterChain = createMock(FilterChain.class);
     expect(request.getHeader("Authorization")).andReturn("Basic 
").andReturn(null);
     
expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4").anyTimes();
+    expect(request.getQueryString()).andReturn(null).anyTimes();
     expect(mockedAuditLogger.isEnabled()).andReturn(true).anyTimes();
     mockedAuditLogger.log(anyObject(AuditEvent.class));
     expectLastCall().times(1);
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-web/app/utils/http_client.js 
b/ambari-web/app/utils/http_client.js
index 0113679..7b7f21d 100644
--- a/ambari-web/app/utils/http_client.js
+++ b/ambari-web/app/utils/http_client.js
@@ -68,7 +68,7 @@ App.HttpClient = Em.Object.create({
     xhr.open(method, url + (url.indexOf('?') >= 0 ? '&_=' : '?_=') + curTime, 
true);
     if (isGetAsPost) {
       xhr.setRequestHeader("X-Http-Method-Override", "GET");
-      xhr.setRequestHeader("Content-type", 
"application/x-www-form-urlencoded");
+      xhr.setRequestHeader("Content-type", "text/plain");
     }
     xhr.send(params);
 

Reply via email to