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

more pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 18e2947b3 KNOX-3048 - Add support for group-based impersonation (#1043)
18e2947b3 is described below

commit 18e2947b340be776f095921b97128b5a87e5a45d
Author: Sandeep MorĂ© <[email protected]>
AuthorDate: Thu Jun 12 12:01:58 2025 -0700

    KNOX-3048 - Add support for group-based impersonation (#1043)
---
 build.xml                                          |   1 +
 .../filter/CommonIdentityAssertionFilter.java      | 514 ++++++++-------
 .../filter/CommonIdentityAssertionFilterTest.java  | 460 ++++++-------
 gateway-spi/pom.xml                                |  37 ++
 .../knox/gateway/i18n/GatewaySpiMessages.java      |  24 +
 .../apache/knox/gateway/util/AuthFilterUtils.java  | 433 ++++++------
 .../gateway/util/KnoxImpersonationProvider.java    | 295 +++++++++
 .../util/KnoxImpersonationProviderTest.java        | 725 +++++++++++++++++++++
 8 files changed, 1809 insertions(+), 680 deletions(-)

diff --git a/build.xml b/build.xml
index 23a8772a2..fc08f2285 100644
--- a/build.xml
+++ b/build.xml
@@ -423,6 +423,7 @@ Release build file for the Apache Knox Gateway
     <target name="start-debug-gateway" description="Start test gateway server 
enabling remote debugging.">
         <exec executable="java" 
dir="${install.dir}/${gateway-artifact}-${gateway-version}">
             <arg 
value="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"/>
+            <arg 
value="--add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED"/> 
             <arg value="-jar"/>
             <arg value="bin/gateway.jar"/>
         </exec>
diff --git 
a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
 
b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
index f3bb669d7..f82b8b7f1 100644
--- 
a/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
+++ 
b/gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java
@@ -20,6 +20,7 @@ package 
org.apache.knox.gateway.identityasserter.common.filter;
 import static 
org.apache.knox.gateway.identityasserter.common.filter.AbstractIdentityAsserterDeploymentContributor.IMPERSONATION_PARAMS;
 import static 
org.apache.knox.gateway.identityasserter.common.filter.AbstractIdentityAsserterDeploymentContributor.ROLE;
 import static 
org.apache.knox.gateway.identityasserter.common.filter.VirtualGroupMapper.addRequestFunctions;
+import static org.apache.knox.gateway.util.AuthFilterUtils.PROXYGROUP_PREFIX;
 
 import java.io.IOException;
 import java.security.AccessController;
@@ -63,285 +64,304 @@ import 
org.apache.knox.gateway.util.AuthorizationException;
 import org.apache.knox.gateway.util.HttpExceptionUtils;
 
 public class CommonIdentityAssertionFilter extends 
AbstractIdentityAssertionFilter {
-  private static final IdentityAsserterMessages LOG = 
MessagesFactory.get(IdentityAsserterMessages.class);
-
-  public static final String VIRTUAL_GROUP_MAPPING_PREFIX = "group.mapping.";
-  public static final String GROUP_PRINCIPAL_MAPPING = 
"group.principal.mapping";
-  public static final String PRINCIPAL_MAPPING = "principal.mapping";
-  public static final String ADVANCED_PRINCIPAL_MAPPING = 
"expression.principal.mapping";
-  private static final String PRINCIPAL_PARAM = "user.name";
-  private static final String DOAS_PRINCIPAL_PARAM = "doAs";
-  static final String IMPERSONATION_ENABLED_PARAM = 
AuthFilterUtils.PROXYUSER_PREFIX + ".impersonation.enabled";
-
-  private SimplePrincipalMapper mapper = new SimplePrincipalMapper();
-  private final Parser parser = new Parser();
-  private VirtualGroupMapper virtualGroupMapper;
-  /* List of all default and configured impersonation params */
-  protected final List<String> impersonationParamsList = new ArrayList<>();
-  protected boolean impersonationEnabled;
-  private AbstractSyntaxTree expressionPrincipalMapping;
-  private String topologyName;
-
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-    topologyName = (String) 
filterConfig.getServletContext().getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE);
-    String principalMapping = filterConfig.getInitParameter(PRINCIPAL_MAPPING);
-    if (principalMapping == null || principalMapping.isEmpty()) {
-      principalMapping = 
filterConfig.getServletContext().getInitParameter(PRINCIPAL_MAPPING);
-    }
-    String groupPrincipalMapping = 
filterConfig.getInitParameter(GROUP_PRINCIPAL_MAPPING);
-    if (groupPrincipalMapping == null || groupPrincipalMapping.isEmpty()) {
-      groupPrincipalMapping = 
filterConfig.getServletContext().getInitParameter(GROUP_PRINCIPAL_MAPPING);
+    private static final IdentityAsserterMessages LOG = 
MessagesFactory.get(IdentityAsserterMessages.class);
+
+    public static final String VIRTUAL_GROUP_MAPPING_PREFIX = "group.mapping.";
+    public static final String GROUP_PRINCIPAL_MAPPING = 
"group.principal.mapping";
+    public static final String PRINCIPAL_MAPPING = "principal.mapping";
+    public static final String ADVANCED_PRINCIPAL_MAPPING = 
"expression.principal.mapping";
+    private static final String PRINCIPAL_PARAM = "user.name";
+    private static final String DOAS_PRINCIPAL_PARAM = "doAs";
+    static final String IMPERSONATION_ENABLED_PARAM = 
AuthFilterUtils.PROXYUSER_PREFIX + ".impersonation.enabled";
+
+    private SimplePrincipalMapper mapper = new SimplePrincipalMapper();
+    private final Parser parser = new Parser();
+    private VirtualGroupMapper virtualGroupMapper;
+    /* List of all default and configured impersonation params */
+    protected final List<String> impersonationParamsList = new ArrayList<>();
+    protected boolean impersonationEnabled;
+    private AbstractSyntaxTree expressionPrincipalMapping;
+    private String topologyName;
+    private boolean hasProxyGroupParams;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        topologyName = (String) 
filterConfig.getServletContext().getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE);
+        String principalMapping = 
filterConfig.getInitParameter(PRINCIPAL_MAPPING);
+        if (principalMapping == null || principalMapping.isEmpty()) {
+            principalMapping = 
filterConfig.getServletContext().getInitParameter(PRINCIPAL_MAPPING);
+        }
+        String groupPrincipalMapping = 
filterConfig.getInitParameter(GROUP_PRINCIPAL_MAPPING);
+        if (groupPrincipalMapping == null || groupPrincipalMapping.isEmpty()) {
+            groupPrincipalMapping = 
filterConfig.getServletContext().getInitParameter(GROUP_PRINCIPAL_MAPPING);
+        }
+        if (principalMapping != null && !principalMapping.isEmpty() || 
groupPrincipalMapping != null && !groupPrincipalMapping.isEmpty()) {
+            try {
+                mapper.loadMappingTable(principalMapping, 
groupPrincipalMapping);
+            } catch (PrincipalMappingException e) {
+                throw new ServletException("Unable to load principal mapping 
table.", e);
+            }
+        }
+        expressionPrincipalMapping = 
parseAdvancedPrincipalMapping(filterConfig);
+
+        final List<String> initParameterNames = 
AuthFilterUtils.getInitParameterNamesAsList(filterConfig);
+
+        virtualGroupMapper = new 
VirtualGroupMapper(loadVirtualGroups(filterConfig, initParameterNames));
+
+        initImpersonationParamsList(filterConfig);
+        initProxyUserConfiguration(filterConfig, initParameterNames);
     }
-    if (principalMapping != null && !principalMapping.isEmpty() || 
groupPrincipalMapping != null && !groupPrincipalMapping.isEmpty()) {
-      try {
-        mapper.loadMappingTable(principalMapping, groupPrincipalMapping);
-      } catch (PrincipalMappingException e) {
-        throw new ServletException("Unable to load principal mapping table.", 
e);
-      }
+
+    private AbstractSyntaxTree parseAdvancedPrincipalMapping(FilterConfig 
filterConfig) {
+        String expression = 
filterConfig.getInitParameter(ADVANCED_PRINCIPAL_MAPPING);
+        if (StringUtils.isBlank(expression)) {
+            expression = 
filterConfig.getServletContext().getInitParameter(ADVANCED_PRINCIPAL_MAPPING);
+        }
+        return StringUtils.isBlank(expression) ? null : 
parser.parse(expression);
     }
-    expressionPrincipalMapping = parseAdvancedPrincipalMapping(filterConfig);
 
-    final List<String> initParameterNames = 
AuthFilterUtils.getInitParameterNamesAsList(filterConfig);
+    /*
+     * Initialize the impersonation params list.
+     * This list contains query params that needs to be scrubbed
+     * from the outgoing request.
+     */
+    private void initImpersonationParamsList(FilterConfig filterConfig) {
+        String impersonationListFromConfig = 
filterConfig.getInitParameter(IMPERSONATION_PARAMS);
+        if (impersonationListFromConfig == null || 
impersonationListFromConfig.isEmpty()) {
+            impersonationListFromConfig = 
filterConfig.getServletContext().getInitParameter(IMPERSONATION_PARAMS);
+        }
 
-    virtualGroupMapper = new 
VirtualGroupMapper(loadVirtualGroups(filterConfig, initParameterNames));
+        /* Add default impersonation params */
+        impersonationParamsList.add(DOAS_PRINCIPAL_PARAM);
+        impersonationParamsList.add(PRINCIPAL_PARAM);
+
+        if (impersonationListFromConfig != null && 
!impersonationListFromConfig.isEmpty()) {
+            /* Add configured impersonation params */
+            LOG.impersonationConfig(impersonationListFromConfig);
+            final StringTokenizer t = new 
StringTokenizer(impersonationListFromConfig, ",");
+            while (t.hasMoreElements()) {
+                final String token = t.nextToken().trim();
+                if (!impersonationParamsList.contains(token)) {
+                    impersonationParamsList.add(token);
+                }
+            }
+        }
+    }
 
-    initImpersonationParamsList(filterConfig);
-    initProxyUserConfiguration(filterConfig, initParameterNames);
-  }
+    private void initProxyUserConfiguration(FilterConfig filterConfig, 
List<String> initParameterNames) {
+        final String impersonationEnabledValue = 
filterConfig.getInitParameter(IMPERSONATION_ENABLED_PARAM);
+        impersonationEnabled = impersonationEnabledValue == null ? 
Boolean.FALSE : Boolean.parseBoolean(impersonationEnabledValue);
+        hasProxyGroupParams = initParameterNames.stream().anyMatch(name -> 
name.startsWith(PROXYGROUP_PREFIX + "."));
+
+        if (impersonationEnabled) {
+            if (AuthFilterUtils.hasProxyConfig(topologyName, "HadoopAuth")) {
+                LOG.ignoreProxyuserConfig();
+                impersonationEnabled = false; //explicitly set to false to 
avoid redundant authorization attempts at request processing time
+            } else {
+                
AuthFilterUtils.refreshSuperUserGroupsConfiguration(filterConfig, 
initParameterNames, topologyName, ROLE);
+                
filterConfig.getServletContext().setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE,
 Boolean.TRUE);
+            }
+        } else {
+            
filterConfig.getServletContext().setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE,
 Boolean.FALSE);
+        }
+    }
 
-  private AbstractSyntaxTree parseAdvancedPrincipalMapping(FilterConfig 
filterConfig) {
-    String expression = 
filterConfig.getInitParameter(ADVANCED_PRINCIPAL_MAPPING);
-    if (StringUtils.isBlank(expression)) {
-      expression = 
filterConfig.getServletContext().getInitParameter(ADVANCED_PRINCIPAL_MAPPING);
+    boolean isImpersonationEnabled() {
+        return impersonationEnabled;
     }
-    return StringUtils.isBlank(expression) ? null : parser.parse(expression);
-  }
-
-  /*
-   * Initialize the impersonation params list.
-   * This list contains query params that needs to be scrubbed
-   * from the outgoing request.
-   */
-  private void initImpersonationParamsList(FilterConfig filterConfig) {
-    String impersonationListFromConfig = 
filterConfig.getInitParameter(IMPERSONATION_PARAMS);
-    if (impersonationListFromConfig == null || 
impersonationListFromConfig.isEmpty()) {
-      impersonationListFromConfig = 
filterConfig.getServletContext().getInitParameter(IMPERSONATION_PARAMS);
+
+    private Map<String, AbstractSyntaxTree> loadVirtualGroups(FilterConfig 
filterConfig, List<String> initParameterNames) {
+        Map<String, AbstractSyntaxTree> predicateToGroupMapping = new 
HashMap<>();
+        loadVirtualGroupConfig(filterConfig, initParameterNames, 
predicateToGroupMapping);
+        if (predicateToGroupMapping.isEmpty() && 
filterConfig.getServletContext() != null) {
+            loadVirtualGroupConfig(filterConfig.getServletContext(), 
predicateToGroupMapping);
+        }
+        return predicateToGroupMapping;
     }
 
-    /* Add default impersonation params */
-    impersonationParamsList.add(DOAS_PRINCIPAL_PARAM);
-    impersonationParamsList.add(PRINCIPAL_PARAM);
-
-    if (impersonationListFromConfig != null && 
!impersonationListFromConfig.isEmpty()) {
-      /* Add configured impersonation params */
-      LOG.impersonationConfig(impersonationListFromConfig);
-      final StringTokenizer t = new 
StringTokenizer(impersonationListFromConfig, ",");
-      while (t.hasMoreElements()) {
-        final String token = t.nextToken().trim();
-        if (!impersonationParamsList.contains(token)) {
-          impersonationParamsList.add(token);
+    private void loadVirtualGroupConfig(FilterConfig config, List<String> 
initParameterNames, Map<String, AbstractSyntaxTree> result) {
+        for (String paramName : 
virtualGroupParameterNames(initParameterNames)) {
+            addGroup(result, paramName, config.getInitParameter(paramName));
         }
-      }
     }
-  }
-
-  private void initProxyUserConfiguration(FilterConfig filterConfig, 
List<String> initParameterNames) {
-    final String impersonationEnabledValue = 
filterConfig.getInitParameter(IMPERSONATION_ENABLED_PARAM);
-    impersonationEnabled = impersonationEnabledValue == null ? Boolean.FALSE : 
Boolean.parseBoolean(impersonationEnabledValue);
-
-    if (impersonationEnabled) {
-      if (AuthFilterUtils.hasProxyConfig(topologyName, "HadoopAuth")) {
-        LOG.ignoreProxyuserConfig();
-        impersonationEnabled = false; //explicitly set to false to avoid 
redundant authorization attempts at request processing time
-      } else {
-        AuthFilterUtils.refreshSuperUserGroupsConfiguration(filterConfig, 
initParameterNames, topologyName, ROLE);
-        
filterConfig.getServletContext().setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE,
 Boolean.TRUE);
-      }
-    } else {
-      
filterConfig.getServletContext().setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE,
 Boolean.FALSE);
+
+    private void loadVirtualGroupConfig(ServletContext context, Map<String, 
AbstractSyntaxTree> result) {
+        final List<String> contextInitParams = context.getInitParameterNames() 
== null ? Collections.emptyList()
+                : Collections.list(context.getInitParameterNames());
+        for (String paramName : virtualGroupParameterNames(contextInitParams)) 
{
+            addGroup(result, paramName, context.getInitParameter(paramName));
+        }
     }
-  }
 
-  boolean isImpersonationEnabled() {
-    return impersonationEnabled;
-  }
+    private void addGroup(Map<String, AbstractSyntaxTree> result, String 
paramName, String predicate) {
+        try {
+            AbstractSyntaxTree ast = parser.parse(predicate);
+            String groupName = 
paramName.substring(VIRTUAL_GROUP_MAPPING_PREFIX.length()).trim();
+            if (StringUtils.isBlank(groupName)) {
+                LOG.missingVirtualGroupName();
+            } else {
+                result.put(groupName, ast);
+            }
+        } catch (SyntaxException e) {
+            LOG.parseError(paramName, predicate, e);
+        }
+    }
 
-  private Map<String, AbstractSyntaxTree> loadVirtualGroups(FilterConfig 
filterConfig, List<String> initParameterNames) {
-    Map<String, AbstractSyntaxTree> predicateToGroupMapping = new HashMap<>();
-    loadVirtualGroupConfig(filterConfig, initParameterNames, 
predicateToGroupMapping);
-    if (predicateToGroupMapping.isEmpty() && filterConfig.getServletContext() 
!= null) {
-      loadVirtualGroupConfig(filterConfig.getServletContext(), 
predicateToGroupMapping);
+    private static List<String> virtualGroupParameterNames(List<String> 
initParameterNames) {
+        return initParameterNames == null ? new ArrayList<>()
+                : initParameterNames.stream().filter(name -> 
name.startsWith(VIRTUAL_GROUP_MAPPING_PREFIX)).collect(Collectors.toList());
     }
-    return predicateToGroupMapping;
-  }
 
-  private void loadVirtualGroupConfig(FilterConfig config, List<String> 
initParameterNames, Map<String, AbstractSyntaxTree> result) {
-    for (String paramName : virtualGroupParameterNames(initParameterNames)) {
-      addGroup(result, paramName, config.getInitParameter(paramName));
+    @Override
+    public void destroy() {
     }
-  }
 
-  private void loadVirtualGroupConfig(ServletContext context, Map<String, 
AbstractSyntaxTree> result) {
-    final List<String> contextInitParams = context.getInitParameterNames() == 
null ? Collections.emptyList()
-        : Collections.list(context.getInitParameterNames());
-    for (String paramName : virtualGroupParameterNames(contextInitParams)) {
-      addGroup(result, paramName, context.getInitParameter(paramName));
+    /**
+     * Obtain the standard javax.security.auth.Subject, retrieve the caller 
principal, map
+     * to the identity to be asserted as appropriate and create the provider 
specific
+     * assertion token. Add the assertion token to the request.
+     */
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
+            throws IOException, ServletException {
+        Subject subject = Subject.getSubject(AccessController.getContext());
+
+        if (subject == null) {
+            LOG.subjectNotAvailable();
+            throw new IllegalStateException("Required Subject Missing");
+        }
+
+        String principalName = SubjectUtils.getEffectivePrincipalName(subject);
+        String[] groupsForPrincipal = {};
+        /* get groups from non-impersonated user IF groups params are 
configured */
+        if(hasProxyGroupParams) {
+            groupsForPrincipal = getGroupsForPrincipal(principalName, subject, 
request);
+        }
+
+        String mappedPrincipalName = null;
+        try {
+            mappedPrincipalName = handleProxyUserImpersonation(request, 
subject, groupsForPrincipal);
+        } catch(AuthorizationException e) {
+            LOG.hadoopAuthProxyUserFailed(e);
+            
HttpExceptionUtils.createServletExceptionResponse((HttpServletResponse) 
response, HttpServletResponse.SC_FORBIDDEN, e);
+            return;
+        }
+
+
+        // mapping principal name using user principal mapping (if configured)
+        mappedPrincipalName = mapUserPrincipalBase(mappedPrincipalName);
+        mappedPrincipalName = mapUserPrincipal(mappedPrincipalName);
+        if (expressionPrincipalMapping != null) {
+            String result = evalAdvancedPrincipalMapping(request, subject, 
mappedPrincipalName);
+            if (result != null) {
+                mappedPrincipalName = result;
+            }
+        }
+
+        String[] groups = getGroupsForPrincipal(mappedPrincipalName, subject, 
request);
+
+
+
+
+        HttpServletRequestWrapper wrapper = wrapHttpServletRequest(request, 
mappedPrincipalName);
+
+
+        continueChainAsPrincipal(wrapper, response, chain, 
mappedPrincipalName, unique(groups));
     }
-  }
-
-  private void addGroup(Map<String, AbstractSyntaxTree> result, String 
paramName, String predicate) {
-    try {
-      AbstractSyntaxTree ast = parser.parse(predicate);
-      String groupName = 
paramName.substring(VIRTUAL_GROUP_MAPPING_PREFIX.length()).trim();
-      if (StringUtils.isBlank(groupName)) {
-        LOG.missingVirtualGroupName();
-      } else {
-        result.put(groupName, ast);
-      }
-    } catch (SyntaxException e) {
-      LOG.parseError(paramName, predicate, e);
+
+    private String[] getGroupsForPrincipal(String mappedPrincipalName, Subject 
subject, ServletRequest request) {
+        String[] mappedGroups = mapGroupPrincipalsBase(mappedPrincipalName, 
subject);
+        String[] groups = mapGroupPrincipals(mappedPrincipalName, subject);
+        String[] virtualGroups = 
virtualGroupMapper.mapGroups(mappedPrincipalName, combine(subject, groups), 
request).toArray(new String[0]);
+        groups = combineGroupMappings(mappedGroups, groups);
+        groups = combineGroupMappings(virtualGroups, groups);
+        return groups;
     }
-  }
-
-  private static List<String> virtualGroupParameterNames(List<String> 
initParameterNames) {
-    return initParameterNames == null ? new ArrayList<>()
-        : initParameterNames.stream().filter(name -> 
name.startsWith(VIRTUAL_GROUP_MAPPING_PREFIX)).collect(Collectors.toList());
-  }
-
-  @Override
-  public void destroy() {
-  }
-
-  /**
-   * Obtain the standard javax.security.auth.Subject, retrieve the caller 
principal, map
-   * to the identity to be asserted as appropriate and create the provider 
specific
-   * assertion token. Add the assertion token to the request.
-   */
-  @Override
-  public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
-      throws IOException, ServletException {
-    Subject subject = Subject.getSubject(AccessController.getContext());
-
-    if (subject == null) {
-      LOG.subjectNotAvailable();
-      throw new IllegalStateException("Required Subject Missing");
+
+    private String evalAdvancedPrincipalMapping(ServletRequest request, 
Subject subject, String originalPrincipal) {
+        Interpreter interpreter = new Interpreter();
+        interpreter.addConstant("username", originalPrincipal);
+        interpreter.addConstant("groups", groups(subject));
+        addRequestFunctions(request, interpreter);
+        Object mappedPrincipal = interpreter.eval(expressionPrincipalMapping);
+        if (mappedPrincipal instanceof String) {
+            return (String)mappedPrincipal;
+        } else {
+            LOG.invalidAdvancedPrincipalMappingResult(originalPrincipal, 
expressionPrincipalMapping, mappedPrincipal);
+            return null;
+        }
     }
 
-    String mappedPrincipalName = null;
-    try {
-      mappedPrincipalName = handleProxyUserImpersonation(request, subject);
-    } catch(AuthorizationException e) {
-      LOG.hadoopAuthProxyUserFailed(e);
-      HttpExceptionUtils.createServletExceptionResponse((HttpServletResponse) 
response, HttpServletResponse.SC_FORBIDDEN, e);
-      return;
+    private String handleProxyUserImpersonation(ServletRequest request, 
Subject subject, String[] groups) throws AuthorizationException {
+        String principalName = SubjectUtils.getEffectivePrincipalName(subject);
+        if (impersonationEnabled) {
+            final String doAsUser = 
request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS);
+            if (doAsUser != null && !doAsUser.equals(principalName)) {
+                LOG.hadoopAuthDoAsUser(doAsUser, principalName, 
request.getRemoteAddr());
+                if (principalName != null) {
+                    
AuthFilterUtils.authorizeImpersonationRequest((HttpServletRequest) request, 
principalName, doAsUser, topologyName, ROLE, Arrays.asList(groups));
+                    LOG.hadoopAuthProxyUserSuccess();
+                    principalName = doAsUser;
+                }
+            }
+        }
+        return principalName;
     }
 
-    // mapping principal name using user principal mapping (if configured)
-    mappedPrincipalName = mapUserPrincipalBase(mappedPrincipalName);
-    mappedPrincipalName = mapUserPrincipal(mappedPrincipalName);
-    if (expressionPrincipalMapping != null) {
-      String result = evalAdvancedPrincipalMapping(request, subject, 
mappedPrincipalName);
-      if (result != null) {
-        mappedPrincipalName = result;
-      }
+    private Set<String> combine(Subject subject, String[] groups) {
+        Set<String> result = groups(subject);
+        if (groups != null) {
+            result.addAll(Arrays.asList(groups));
+        }
+        return result;
     }
-    String[] mappedGroups = mapGroupPrincipalsBase(mappedPrincipalName, 
subject);
-    String[] groups = mapGroupPrincipals(mappedPrincipalName, subject);
-    String[] virtualGroups = virtualGroupMapper.mapGroups(mappedPrincipalName, 
combine(subject, groups), request).toArray(new String[0]);
-    groups = combineGroupMappings(mappedGroups, groups);
-    groups = combineGroupMappings(virtualGroups, groups);
-
-    HttpServletRequestWrapper wrapper = wrapHttpServletRequest(request, 
mappedPrincipalName);
-
-
-    continueChainAsPrincipal(wrapper, response, chain, mappedPrincipalName, 
unique(groups));
-  }
-
-  private String evalAdvancedPrincipalMapping(ServletRequest request, Subject 
subject, String originalPrincipal) {
-    Interpreter interpreter = new Interpreter();
-    interpreter.addConstant("username", originalPrincipal);
-    interpreter.addConstant("groups", groups(subject));
-    addRequestFunctions(request, interpreter);
-    Object mappedPrincipal = interpreter.eval(expressionPrincipalMapping);
-    if (mappedPrincipal instanceof String) {
-      return (String)mappedPrincipal;
-    } else {
-      LOG.invalidAdvancedPrincipalMappingResult(originalPrincipal, 
expressionPrincipalMapping, mappedPrincipal);
-      return null;
+
+    private static String[] unique(String[] groups) {
+        return new HashSet<>(Arrays.asList(groups)).toArray(new String[0]);
     }
-  }
-
-  private String handleProxyUserImpersonation(ServletRequest request, Subject 
subject) throws AuthorizationException {
-    String principalName = SubjectUtils.getEffectivePrincipalName(subject);
-    if (impersonationEnabled) {
-      final String doAsUser = 
request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS);
-      if (doAsUser != null && !doAsUser.equals(principalName)) {
-        LOG.hadoopAuthDoAsUser(doAsUser, principalName, 
request.getRemoteAddr());
-        if (principalName != null) {
-          AuthFilterUtils.authorizeImpersonationRequest((HttpServletRequest) 
request, principalName, doAsUser, topologyName, ROLE);
-          LOG.hadoopAuthProxyUserSuccess();
-          principalName = doAsUser;
+
+    protected String[] combineGroupMappings(String[] mappedGroups, String[] 
groups) {
+        if (mappedGroups != null && groups != null) {
+            return ArrayUtils.addAll(mappedGroups, groups);
+        }
+        else {
+            return groups != null ? groups : mappedGroups;
         }
-      }
     }
-    return principalName;
-  }
 
-  private Set<String> combine(Subject subject, String[] groups) {
-    Set<String> result = groups(subject);
-    if (groups != null) {
-      result.addAll(Arrays.asList(groups));
+    public HttpServletRequestWrapper wrapHttpServletRequest(
+            ServletRequest request, String mappedPrincipalName) {
+        // wrap the request so that the proper principal is returned
+        // from request methods
+        return new IdentityAsserterHttpServletRequestWrapper(
+                (HttpServletRequest) request,
+                mappedPrincipalName,
+                impersonationParamsList);
     }
-    return result;
-  }
 
-  private static String[] unique(String[] groups) {
-    return new HashSet<>(Arrays.asList(groups)).toArray(new String[0]);
-  }
+    protected String[] mapGroupPrincipalsBase(String mappedPrincipalName, 
Subject subject) {
+        return mapper.mapGroupPrincipal(mappedPrincipalName);
+    }
+
+    protected String mapUserPrincipalBase(String principalName) {
+        return mapper.mapUserPrincipal(principalName);
+    }
 
-  protected String[] combineGroupMappings(String[] mappedGroups, String[] 
groups) {
-    if (mappedGroups != null && groups != null) {
-      return ArrayUtils.addAll(mappedGroups, groups);
+    private Set<String> groups(Subject subject) {
+        return subject.getPrincipals(GroupPrincipal.class).stream()
+                .map(GroupPrincipal::getName)
+                .collect(Collectors.toSet());
     }
-    else {
-      return groups != null ? groups : mappedGroups;
+
+    @Override
+    public String[] mapGroupPrincipals(String mappedPrincipalName, Subject 
subject) {
+        // NOP
+        return null;
+    }
+
+    @Override
+    public String mapUserPrincipal(String principalName) {
+        // NOP
+        return principalName;
     }
-  }
-
-  public HttpServletRequestWrapper wrapHttpServletRequest(
-      ServletRequest request, String mappedPrincipalName) {
-    // wrap the request so that the proper principal is returned
-    // from request methods
-    return new IdentityAsserterHttpServletRequestWrapper(
-        (HttpServletRequest) request,
-        mappedPrincipalName,
-        impersonationParamsList);
-  }
-
-  protected String[] mapGroupPrincipalsBase(String mappedPrincipalName, 
Subject subject) {
-    return mapper.mapGroupPrincipal(mappedPrincipalName);
-  }
-
-  protected String mapUserPrincipalBase(String principalName) {
-    return mapper.mapUserPrincipal(principalName);
-  }
-
-  private Set<String> groups(Subject subject) {
-    return subject.getPrincipals(GroupPrincipal.class).stream()
-            .map(GroupPrincipal::getName)
-            .collect(Collectors.toSet());
-  }
-
-  @Override
-  public String[] mapGroupPrincipals(String mappedPrincipalName, Subject 
subject) {
-    // NOP
-    return null;
-  }
-
-  @Override
-  public String mapUserPrincipal(String principalName) {
-    // NOP
-    return principalName;
-  }
 }
diff --git 
a/gateway-provider-identity-assertion-common/src/test/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilterTest.java
 
b/gateway-provider-identity-assertion-common/src/test/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilterTest.java
index 4d3f9ec74..942065b8a 100644
--- 
a/gateway-provider-identity-assertion-common/src/test/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilterTest.java
+++ 
b/gateway-provider-identity-assertion-common/src/test/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilterTest.java
@@ -61,247 +61,247 @@ import org.junit.Before;
 import org.junit.Test;
 
 public class CommonIdentityAssertionFilterTest {
-  private String username;
-  private CommonIdentityAssertionFilter filter;
-  private Set<String> calculatedGroups = new HashSet<>();
-
-  @Before
-  public void setUp() {
-    filter = new CommonIdentityAssertionFilter() {
-      @Override
-      public String mapUserPrincipal(String principalName) {
-        username = principalName.toUpperCase(Locale.ROOT);
-        return principalName;
-      }
-
-      @Override
-      public String[] mapGroupPrincipals(String principalName, Subject 
subject) {
-        String[] groups = new String[4];
-        int i = 0;
-        for(GroupPrincipal p : subject.getPrincipals(GroupPrincipal.class)) {
-          groups[i] = p.getName().toUpperCase(Locale.ROOT);
-          i++;
+    private String username;
+    private CommonIdentityAssertionFilter filter;
+    private Set<String> calculatedGroups = new HashSet<>();
+
+    @Before
+    public void setUp() {
+        filter = new CommonIdentityAssertionFilter() {
+            @Override
+            public String mapUserPrincipal(String principalName) {
+                username = principalName.toUpperCase(Locale.ROOT);
+                return principalName;
+            }
+
+            @Override
+            public String[] mapGroupPrincipals(String principalName, Subject 
subject) {
+                String[] groups = new String[4];
+                int i = 0;
+                for(GroupPrincipal p : 
subject.getPrincipals(GroupPrincipal.class)) {
+                    groups[i] = p.getName().toUpperCase(Locale.ROOT);
+                    i++;
+                }
+                return groups;
+            }
+
+            @Override
+            protected String[] combineGroupMappings(String[] mappedGroups, 
String[] groups) {
+                
calculatedGroups.addAll(Arrays.asList(super.combineGroupMappings(mappedGroups, 
groups)));
+                return super.combineGroupMappings(mappedGroups, groups);
+            }
+
+            @Override
+            protected void continueChainAsPrincipal(HttpServletRequestWrapper 
request, ServletResponse response, FilterChain chain, String 
mappedPrincipalName, String[] groups) throws IOException, ServletException {
+                assertEquals("Groups should not have duplicates: " + 
Arrays.toString(groups),
+                        new HashSet<>(Arrays.asList(groups)).size(),
+                        groups.length);
+                super.continueChainAsPrincipal(request, response, chain, 
mappedPrincipalName, groups);
+            }
+        };
+        ThreadContext.put(MDC_AUDIT_CONTEXT_KEY, "dummy");
+    }
+
+    @Test
+    public void testSimpleFilter() throws ServletException, IOException {
+        ServletContext servletContext = createMock(ServletContext.class);
+        
EasyMock.expect(servletContext.getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE)).andReturn("topology1").anyTimes();
+        
servletContext.setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE, 
Boolean.FALSE);
+        EasyMock.expectLastCall();
+        EasyMock.replay(servletContext);
+        FilterConfig config = EasyMock.createNiceMock( FilterConfig.class );
+        
EasyMock.expect(config.getServletContext()).andReturn(servletContext).anyTimes();
+        
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.ADVANCED_PRINCIPAL_MAPPING)).
+                andReturn("username").anyTimes();
+        
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING)).
+                andReturn("*=everyone;lmccay=test-virtual-group").once();
+        
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.PRINCIPAL_MAPPING)).
+                andReturn("ljm=lmccay;").once();
+        EasyMock.expect(config.getInitParameterNames()).
+                andReturn(Collections.enumeration(Arrays.asList(
+                        CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING,
+                        CommonIdentityAssertionFilter.PRINCIPAL_MAPPING,
+                        
CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX + 
"test-virtual-group",
+                        
CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX))) // invalid group 
with no name
+                .anyTimes();
+        EasyMock.expect(config.getInitParameter(IMPERSONATION_PARAMS)).
+                andReturn("doAs").anyTimes();
+        
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX
 + "test-virtual-group")).
+                andReturn("(and (username 'lmccay') (and (member 'users') 
(member 'admin')))").anyTimes();
+        
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX)).
+                andReturn("true").anyTimes();
+        EasyMock.replay( config );
+
+        final HttpServletRequest request = EasyMock.createNiceMock( 
HttpServletRequest.class );
+        EasyMock.replay( request );
+
+        final HttpServletResponse response = EasyMock.createNiceMock( 
HttpServletResponse.class );
+        EasyMock.replay( response );
+
+        final FilterChain chain = (req, resp) -> {};
+
+        Subject subject = new Subject();
+        subject.getPrincipals().add(new PrimaryPrincipal("ljm"));
+        subject.getPrincipals().add(new GroupPrincipal("users"));
+        subject.getPrincipals().add(new GroupPrincipal("admin"));
+        try {
+            Subject.doAs(
+                    subject,
+                    (PrivilegedExceptionAction<Object>) () -> {
+                        filter.init(config);
+                        filter.doFilter(request, response, chain);
+                        return null;
+                    });
         }
-        return groups;
-      }
-
-      @Override
-      protected String[] combineGroupMappings(String[] mappedGroups, String[] 
groups) {
-        
calculatedGroups.addAll(Arrays.asList(super.combineGroupMappings(mappedGroups, 
groups)));
-        return super.combineGroupMappings(mappedGroups, groups);
-      }
-
-      @Override
-      protected void continueChainAsPrincipal(HttpServletRequestWrapper 
request, ServletResponse response, FilterChain chain, String 
mappedPrincipalName, String[] groups) throws IOException, ServletException {
-        assertEquals("Groups should not have duplicates: " + 
Arrays.toString(groups),
-                new HashSet<>(Arrays.asList(groups)).size(),
-                groups.length);
-        super.continueChainAsPrincipal(request, response, chain, 
mappedPrincipalName, groups);
-      }
-    };
-    ThreadContext.put(MDC_AUDIT_CONTEXT_KEY, "dummy");
-  }
-
-  @Test
-  public void testSimpleFilter() throws ServletException, IOException {
-    ServletContext servletContext = createMock(ServletContext.class);
-    
EasyMock.expect(servletContext.getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE)).andReturn("topology1").anyTimes();
-    
servletContext.setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE, 
Boolean.FALSE);
-    EasyMock.expectLastCall();
-    EasyMock.replay(servletContext);
-    FilterConfig config = EasyMock.createNiceMock( FilterConfig.class );
-    
EasyMock.expect(config.getServletContext()).andReturn(servletContext).anyTimes();
-    
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.ADVANCED_PRINCIPAL_MAPPING)).
-            andReturn("username").anyTimes();
-    
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING)).
-        andReturn("*=everyone;lmccay=test-virtual-group").once();
-    
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.PRINCIPAL_MAPPING)).
-        andReturn("ljm=lmccay;").once();
-    EasyMock.expect(config.getInitParameterNames()).
-            andReturn(Collections.enumeration(Arrays.asList(
-                    CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING,
-                    CommonIdentityAssertionFilter.PRINCIPAL_MAPPING,
-                    CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX 
+ "test-virtual-group",
-                    
CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX))) // invalid group 
with no name
-            .anyTimes();
-    EasyMock.expect(config.getInitParameter(IMPERSONATION_PARAMS)).
-        andReturn("doAs").anyTimes();
-    
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX
 + "test-virtual-group")).
-            andReturn("(and (username 'lmccay') (and (member 'users') (member 
'admin')))").anyTimes();
-    
EasyMock.expect(config.getInitParameter(CommonIdentityAssertionFilter.VIRTUAL_GROUP_MAPPING_PREFIX)).
-            andReturn("true").anyTimes();
-    EasyMock.replay( config );
-
-    final HttpServletRequest request = EasyMock.createNiceMock( 
HttpServletRequest.class );
-    EasyMock.replay( request );
-
-    final HttpServletResponse response = EasyMock.createNiceMock( 
HttpServletResponse.class );
-    EasyMock.replay( response );
-
-    final FilterChain chain = (req, resp) -> {};
-
-    Subject subject = new Subject();
-    subject.getPrincipals().add(new PrimaryPrincipal("ljm"));
-    subject.getPrincipals().add(new GroupPrincipal("users"));
-    subject.getPrincipals().add(new GroupPrincipal("admin"));
-    try {
-      Subject.doAs(
-        subject,
-              (PrivilegedExceptionAction<Object>) () -> {
-                filter.init(config);
-                filter.doFilter(request, response, chain);
-                return null;
-              });
+        catch (PrivilegedActionException e) {
+            Throwable t = e.getCause();
+            if (t instanceof IOException) {
+                throw (IOException) t;
+            }
+            else if (t instanceof ServletException) {
+                throw (ServletException) t;
+            }
+            else {
+                throw new ServletException(t);
+            }
+        }
+
+        assertEquals("LMCCAY", username);
+        assertTrue("Should be greater than 2", calculatedGroups.size() > 2);
+        assertTrue(calculatedGroups.containsAll(Arrays.asList("everyone", 
"USERS", "ADMIN", "test-virtual-group")));
+        assertFalse(calculatedGroups.contains(""));
     }
-    catch (PrivilegedActionException e) {
-      Throwable t = e.getCause();
-      if (t instanceof IOException) {
-        throw (IOException) t;
-      }
-      else if (t instanceof ServletException) {
-        throw (ServletException) t;
-      }
-      else {
-        throw new ServletException(t);
-      }
+
+    @Test
+    public void testProxyUserImpersonationDisabled() throws Exception {
+        testProxyUserImpersonationEnabled(false);
+
     }
 
-    assertEquals("LMCCAY", username);
-    assertTrue("Should be greater than 2", calculatedGroups.size() > 2);
-    assertTrue(calculatedGroups.containsAll(Arrays.asList("everyone", "USERS", 
"ADMIN", "test-virtual-group")));
-    assertFalse(calculatedGroups.contains(""));
-  }
-
-  @Test
-  public void testProxyUserImpersonationDisabled() throws Exception {
-    testProxyUserImpersonationEnabled(false);
-
-  }
-
-  @Test
-  public void testProxyUserImpersonationEnabled() throws Exception {
-    testProxyUserImpersonationEnabled(true);
-  }
-
-  @Test
-  public void testProxyUserImpersonationEnabledAndConfiguredInHadoopAuth() 
throws Exception {
-    testProxyUserImpersonationEnabled(true, true, Arrays.asList());
-  }
-
-  @Test
-  public void testProxyUserImpersonationFailWithoutProxyUserConfig() throws 
Exception {
-    final String impersonatedUser = "bob";
-
-    // enable impersonation without configuring context/filter expectations
-    testProxyUserImpersonationEnabled(true);
-
-    final HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
-    
EasyMock.expect(request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS)).andReturn(impersonatedUser).once();
-
-    final ByteArrayOutputStream out = new ByteArrayOutputStream();
-    final PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, 
StandardCharsets.UTF_8));
-    final HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-    EasyMock.expect(response.getWriter()).andReturn(writer).anyTimes();
-    EasyMock.replay(request, response);
-
-    final FilterChainWrapper chain = new FilterChainWrapper((req, resp) -> {
-    });
-
-    final Subject subject = new Subject();
-    subject.getPrincipals().add(new PrimaryPrincipal("admin"));
-    Subject.doAs(subject, (PrivilegedExceptionAction<Object>) () -> {
-      filter.doFilter(request, response, chain);
-      return null;
-    });
-    assertTrue(new String(out.toByteArray(), 
StandardCharsets.UTF_8).contains("User: admin is not allowed to impersonate 
bob"));
-  }
-
-  @Test
-  public void testProxyUserImpersonationApplied() throws Exception {
-    final String impersonatedUser = "bob";
-
-    // enable impersonation and configure context/filter expectations
-    final List<String> proxyUserConfig = 
Arrays.asList(AuthFilterUtils.PROXYUSER_PREFIX + ".admin.users", 
AuthFilterUtils.PROXYUSER_PREFIX + ".admin.hosts");
-    testProxyUserImpersonationEnabled(true, false, proxyUserConfig);
-
-    final HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
-    
EasyMock.expect(request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS)).andReturn(impersonatedUser).once();
-    final HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-    EasyMock.replay(request, response);
-
-    final FilterChainWrapper chain = new FilterChainWrapper((req, resp) -> {
-    });
-
-    final Subject subject = new Subject();
-    subject.getPrincipals().add(new PrimaryPrincipal("admin"));
-    Subject.doAs(subject, (PrivilegedExceptionAction<Object>) () -> {
-      filter.doFilter(request, response, chain);
-      return null;
-    });
-    assertTrue(filter.isImpersonationEnabled());
-    final Subject impersonatedSubject = chain.getSubject();
-    assertTrue(SubjectUtils.isImpersonating(impersonatedSubject));
-    assertEquals("admin", 
SubjectUtils.getPrimaryPrincipalName(impersonatedSubject));
-    assertEquals("bob", 
SubjectUtils.getEffectivePrincipalName(impersonatedSubject));
-  }
-
-  private static final class FilterChainWrapper implements FilterChain {
-    private final FilterChain wrappedFilterChain;
-    private Subject subject;
-
-    FilterChainWrapper(FilterChain wrappedFilterChain) {
-      this.wrappedFilterChain = wrappedFilterChain;
+    @Test
+    public void testProxyUserImpersonationEnabled() throws Exception {
+        testProxyUserImpersonationEnabled(true);
     }
 
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response) 
throws IOException, ServletException {
-      this.subject = SubjectUtils.getCurrentSubject();
-      wrappedFilterChain.doFilter(request, response);
+    @Test
+    public void testProxyUserImpersonationEnabledAndConfiguredInHadoopAuth() 
throws Exception {
+        testProxyUserImpersonationEnabled(true, true, Arrays.asList());
     }
 
-    Subject getSubject() {
-      return subject;
+    @Test
+    public void testProxyUserImpersonationFailWithoutProxyUserConfig() throws 
Exception {
+        final String impersonatedUser = "bob";
+
+        // enable impersonation without configuring context/filter expectations
+        testProxyUserImpersonationEnabled(true);
+
+        final HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+        
EasyMock.expect(request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS)).andReturn(impersonatedUser).once();
+
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, 
StandardCharsets.UTF_8));
+        final HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+        EasyMock.expect(response.getWriter()).andReturn(writer).anyTimes();
+        EasyMock.replay(request, response);
+
+        final FilterChainWrapper chain = new FilterChainWrapper((req, resp) -> 
{
+        });
+
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new PrimaryPrincipal("admin"));
+        Subject.doAs(subject, (PrivilegedExceptionAction<Object>) () -> {
+            filter.doFilter(request, response, chain);
+            return null;
+        });
+        assertTrue(new String(out.toByteArray(), 
StandardCharsets.UTF_8).contains("User: admin (auth:SIMPLE) is not allowed to 
impersonate bob"));
     }
-  }
-
-  private void testProxyUserImpersonationEnabled(boolean impersonationEnabled) 
throws Exception {
-    testProxyUserImpersonationEnabled(impersonationEnabled, false, 
Arrays.asList());
-  }
-
-  private void testProxyUserImpersonationEnabled(boolean impersonationEnabled, 
boolean configuredInHadoopAuth, List<String> filterConfigParameterNames)
-      throws Exception {
-    ServletContext servletContext = createMock(ServletContext.class);
-    
EasyMock.expect(servletContext.getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE)).andReturn("topology1").anyTimes();
-    
EasyMock.expect(servletContext.getInitParameter(CommonIdentityAssertionFilter.PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
-    
EasyMock.expect(servletContext.getInitParameter(CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
-    
EasyMock.expect(servletContext.getInitParameter(CommonIdentityAssertionFilter.ADVANCED_PRINCIPAL_MAPPING)).andReturn("username").anyTimes();
-    
EasyMock.expect(servletContext.getInitParameterNames()).andReturn(Collections.enumeration(filterConfigParameterNames)).anyTimes();
-    
EasyMock.expect(servletContext.getInitParameter(IMPERSONATION_PARAMS)).andReturn("doAs").anyTimes();
-    if (!configuredInHadoopAuth) {
-      
servletContext.setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE, 
Boolean.valueOf(impersonationEnabled));
-      EasyMock.expectLastCall();
+
+    @Test
+    public void testProxyUserImpersonationApplied() throws Exception {
+        final String impersonatedUser = "bob";
+
+        // enable impersonation and configure context/filter expectations
+        final List<String> proxyUserConfig = 
Arrays.asList(AuthFilterUtils.PROXYUSER_PREFIX + ".admin.users", 
AuthFilterUtils.PROXYUSER_PREFIX + ".admin.hosts");
+        testProxyUserImpersonationEnabled(true, false, proxyUserConfig);
+
+        final HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+        
EasyMock.expect(request.getParameter(AuthFilterUtils.QUERY_PARAMETER_DOAS)).andReturn(impersonatedUser).once();
+        final HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+        EasyMock.replay(request, response);
+
+        final FilterChainWrapper chain = new FilterChainWrapper((req, resp) -> 
{
+        });
+
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new PrimaryPrincipal("admin"));
+        Subject.doAs(subject, (PrivilegedExceptionAction<Object>) () -> {
+            filter.doFilter(request, response, chain);
+            return null;
+        });
+        assertTrue(filter.isImpersonationEnabled());
+        final Subject impersonatedSubject = chain.getSubject();
+        assertTrue(SubjectUtils.isImpersonating(impersonatedSubject));
+        assertEquals("admin", 
SubjectUtils.getPrimaryPrincipalName(impersonatedSubject));
+        assertEquals("bob", 
SubjectUtils.getEffectivePrincipalName(impersonatedSubject));
     }
-    final FilterConfig filterConfig = 
EasyMock.createNiceMock(FilterConfig.class);
-    final String impersonatedEnabledParam = impersonationEnabled ? 
Boolean.TRUE.toString() : Boolean.FALSE.toString();
-    
EasyMock.expect(filterConfig.getInitParameter(CommonIdentityAssertionFilter.IMPERSONATION_ENABLED_PARAM)).andReturn(impersonatedEnabledParam);
-    
EasyMock.expect(filterConfig.getInitParameter(CommonIdentityAssertionFilter.PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
-    
EasyMock.expect(filterConfig.getInitParameter(CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
-    
EasyMock.expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(filterConfigParameterNames)).atLeastOnce();
-    filterConfigParameterNames.forEach(filterConfigParameterName -> {
-      
EasyMock.expect(filterConfig.getInitParameter(filterConfigParameterName)).andReturn("*").anyTimes();
-    });
-    
EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes();
-    EasyMock.replay(servletContext, filterConfig);
-
-    if (configuredInHadoopAuth) {
-      AuthFilterUtils.refreshSuperUserGroupsConfiguration(filterConfig, 
Arrays.asList(), "topology1", "HadoopAuth");
-    } else {
-      AuthFilterUtils.removeProxyUserConfig("topology1", "HadoopAuth");
+
+    private static final class FilterChainWrapper implements FilterChain {
+        private final FilterChain wrappedFilterChain;
+        private Subject subject;
+
+        FilterChainWrapper(FilterChain wrappedFilterChain) {
+            this.wrappedFilterChain = wrappedFilterChain;
+        }
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response) 
throws IOException, ServletException {
+            this.subject = SubjectUtils.getCurrentSubject();
+            wrappedFilterChain.doFilter(request, response);
+        }
+
+        Subject getSubject() {
+            return subject;
+        }
     }
 
-    filter.init(filterConfig);
-    assertEquals(impersonationEnabled && !configuredInHadoopAuth, 
filter.isImpersonationEnabled());
-    EasyMock.verify(servletContext, filterConfig);
-  }
+    private void testProxyUserImpersonationEnabled(boolean 
impersonationEnabled) throws Exception {
+        testProxyUserImpersonationEnabled(impersonationEnabled, false, 
Arrays.asList());
+    }
+
+    private void testProxyUserImpersonationEnabled(boolean 
impersonationEnabled, boolean configuredInHadoopAuth, List<String> 
filterConfigParameterNames)
+            throws Exception {
+        ServletContext servletContext = createMock(ServletContext.class);
+        
EasyMock.expect(servletContext.getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE)).andReturn("topology1").anyTimes();
+        
EasyMock.expect(servletContext.getInitParameter(CommonIdentityAssertionFilter.PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
+        
EasyMock.expect(servletContext.getInitParameter(CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
+        
EasyMock.expect(servletContext.getInitParameter(CommonIdentityAssertionFilter.ADVANCED_PRINCIPAL_MAPPING)).andReturn("username").anyTimes();
+        
EasyMock.expect(servletContext.getInitParameterNames()).andReturn(Collections.enumeration(filterConfigParameterNames)).anyTimes();
+        
EasyMock.expect(servletContext.getInitParameter(IMPERSONATION_PARAMS)).andReturn("doAs").anyTimes();
+        if (!configuredInHadoopAuth) {
+            
servletContext.setAttribute(ContextAttributes.IMPERSONATION_ENABLED_ATTRIBUTE, 
Boolean.valueOf(impersonationEnabled));
+            EasyMock.expectLastCall();
+        }
+        final FilterConfig filterConfig = 
EasyMock.createNiceMock(FilterConfig.class);
+        final String impersonatedEnabledParam = impersonationEnabled ? 
Boolean.TRUE.toString() : Boolean.FALSE.toString();
+        
EasyMock.expect(filterConfig.getInitParameter(CommonIdentityAssertionFilter.IMPERSONATION_ENABLED_PARAM)).andReturn(impersonatedEnabledParam);
+        
EasyMock.expect(filterConfig.getInitParameter(CommonIdentityAssertionFilter.PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
+        
EasyMock.expect(filterConfig.getInitParameter(CommonIdentityAssertionFilter.GROUP_PRINCIPAL_MAPPING)).andReturn(null).anyTimes();
+        
EasyMock.expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(filterConfigParameterNames)).atLeastOnce();
+        filterConfigParameterNames.forEach(filterConfigParameterName -> {
+            
EasyMock.expect(filterConfig.getInitParameter(filterConfigParameterName)).andReturn("*").anyTimes();
+        });
+        
EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes();
+        EasyMock.replay(servletContext, filterConfig);
+
+        if (configuredInHadoopAuth) {
+            AuthFilterUtils.refreshSuperUserGroupsConfiguration(filterConfig, 
Arrays.asList(), "topology1", "HadoopAuth");
+        } else {
+            AuthFilterUtils.removeProxyUserConfig("topology1", "HadoopAuth");
+        }
+
+        filter.init(filterConfig);
+        assertEquals(impersonationEnabled && !configuredInHadoopAuth, 
filter.isImpersonationEnabled());
+        EasyMock.verify(servletContext, filterConfig);
+    }
 
 }
diff --git a/gateway-spi/pom.xml b/gateway-spi/pom.xml
index d09c39bf4..7796e8aab 100644
--- a/gateway-spi/pom.xml
+++ b/gateway-spi/pom.xml
@@ -216,5 +216,42 @@
             <artifactId>velocity-engine-core</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-auth</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.activation</groupId>
+                    <artifactId>javax.activation-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>dnsjava</groupId>
+                    <artifactId>dnsjava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-annotations</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.activation</groupId>
+                    <artifactId>javax.activation-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>dnsjava</groupId>
+                    <artifactId>dnsjava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
index e0559229e..721749970 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
@@ -85,6 +85,9 @@ public interface GatewaySpiMessages {
   @Message(level = MessageLevel.ERROR, text = "Failed to load truststore due 
to {0}")
   void failedToLoadTruststore(String message, @StackTrace(level = 
MessageLevel.DEBUG) Exception e);
 
+  @Message(level = MessageLevel.ERROR, text = "Impersonation failed, user {0} 
does not belong to configured proxy group")
+  void failedToImpersonateViaGroups(String user);
+
   @Message(level = MessageLevel.WARN, text = "Duplicated filter param key: 
{0}")
   void duplicatedFilterParamKey(String name);
 
@@ -93,4 +96,25 @@ public interface GatewaySpiMessages {
 
   @Message(level=MessageLevel.DEBUG, text="Ignoring cookie path scope filter 
for default topology")
   void ignoringCookiePathScopeForDefaultTopology();
+
+  @Message(level=MessageLevel.DEBUG, text="Loaded proxy groups ACLs: {0}")
+  void loadedProxyGroupsAcls(String acls);
+
+  @Message(level = MessageLevel.ERROR, text = "User impersonation failed for 
user {0}. Connections from remote address {1} are not authorized.")
+  void failedToImpersonateUserFromIP(String user, String address);
+
+  @Message(level = MessageLevel.ERROR, text = "User impersonation failed for 
user {0}, cause {1}. Trying group impersonation ...")
+  void failedToImpersonateUserTryingGroups(String user, String cause);
+
+  @Message(level = MessageLevel.INFO, text = "User {0} is allowed to 
impersonate user {1}")
+  void successfulImpersonation(String user, String iuser);
+
+  @Message(level = MessageLevel.ERROR, text = "User {0} with groups {1} is not 
allowed to impersonate {2}")
+  void failedToImpersonateGroups(String user, String groups, String iuser);
+
+  @Message(level = MessageLevel.ERROR, text = "User {0} with groups {1} is not 
allowed to impersonate {2} from address {3}")
+  void failedToImpersonateGroupsFromAddress(String user, String groups, String 
iuser, String address);
+
+  @Message(level=MessageLevel.ERROR, text="No proxy user or group 
configuration exists.")
+  void noProxyUserOrGroupConfigExists();
 }
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java 
b/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
index 676cf4b23..4d8c87b26 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
@@ -17,6 +17,8 @@
  */
 package org.apache.knox.gateway.util;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.security.Principal;
 import java.util.Collections;
 import java.util.List;
@@ -36,218 +38,243 @@ import javax.servlet.http.HttpServletRequestWrapper;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.UserGroupInformation;
-import org.apache.hadoop.security.authorize.DefaultImpersonationProvider;
 import org.apache.hadoop.security.authorize.ImpersonationProvider;
 import org.apache.knox.gateway.i18n.GatewaySpiMessages;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 
 public class AuthFilterUtils {
-  public static final String DEFAULT_AUTH_UNAUTHENTICATED_PATHS_PARAM = 
"/knoxtoken/api/v1/jwks.json";
-  public static final String PROXYUSER_PREFIX = "hadoop.proxyuser";
-  public static final String QUERY_PARAMETER_DOAS = "doAs";
-  public static final String REAL_USER_NAME_ATTRIBUTE = "real.user.name";
-  public static final String DO_GLOBAL_LOGOUT_ATTRIBUTE = "do.global.logout";
-
-  private static final GatewaySpiMessages LOG = 
MessagesFactory.get(GatewaySpiMessages.class);
-  private static final Map<String, Map<String, ImpersonationProvider>> 
TOPOLOGY_IMPERSONATION_PROVIDERS = new ConcurrentHashMap<>();
-  private static final Lock refreshSuperUserGroupsLock = new ReentrantLock();
-
-  /**
-   * A helper method that checks whether request contains
-   * unauthenticated path
-   * @param request
-   * @return
-   */
-  public static boolean doesRequestContainUnauthPath(
-      final Set<String> unAuthenticatedPaths, final ServletRequest request) {
-    /* make sure the path matches EXACTLY to prevent auth bypass */
-    return unAuthenticatedPaths.contains(((HttpServletRequest) 
request).getPathInfo());
-  }
-
-  /**
-   * A helper method that parses a string and adds to the
-   * provided unauthenticated set.
-   * @param unAuthenticatedPaths
-   * @param list
-   */
-  public static void parseStringThenAdd(final Set<String> 
unAuthenticatedPaths, final String list) {
-    final StringTokenizer tokenizer = new StringTokenizer(list, ";,");
-    while (tokenizer.hasMoreTokens()) {
-      unAuthenticatedPaths.add(tokenizer.nextToken());
-    }
-  }
-
-  /**
-   * A method that parses a string (delimiters = ;,) and adds them to the
-   * provided un-authenticated path set.
-   * @param unAuthenticatedPaths
-   * @param list
-   * @param defaultList
-   */
-  public static void addUnauthPaths(final Set<String> unAuthenticatedPaths, 
final String list, final String defaultList) {
-    /* add default unauthenticated paths list */
-    parseStringThenAdd(unAuthenticatedPaths, defaultList);
-    /* add provided unauthenticated paths list if specified */
-    if (!StringUtils.isBlank(list)) {
-      AuthFilterUtils.parseStringThenAdd(unAuthenticatedPaths, list);
-    }
-  }
-
-  public static void refreshSuperUserGroupsConfiguration(ServletContext 
context, List<String> initParameterNames, String topologyName, String role) {
-    if (context == null) {
-      throw new IllegalArgumentException("Cannot get proxyuser configuration 
from NULL context");
-    }
-    refreshSuperUserGroupsConfiguration(context, null, initParameterNames, 
topologyName, role);
-  }
-
-  public static void refreshSuperUserGroupsConfiguration(FilterConfig 
filterConfig, List<String> initParameterNames, String topologyName, String 
role) {
-    if (filterConfig == null) {
-      throw new IllegalArgumentException("Cannot get proxyuser configuration 
from NULL filter config");
-    }
-    refreshSuperUserGroupsConfiguration(null, filterConfig, 
initParameterNames, topologyName, role);
-  }
-
-  private static void refreshSuperUserGroupsConfiguration(ServletContext 
context, FilterConfig filterConfig, List<String> initParameterNames, String 
topologyName, String role) {
-    final Configuration conf = new Configuration(false);
-    if (initParameterNames != null) {
-      initParameterNames.stream().filter(name -> 
name.startsWith(PROXYUSER_PREFIX + ".")).forEach(name -> {
-        String value = context == null ? filterConfig.getInitParameter(name) : 
context.getInitParameter(name);
-        conf.set(name, value);
-      });
-    }
-
-    saveImpersonationProvider(topologyName, role, conf);
-  }
-
-  private static void saveImpersonationProvider(String topologyName, String 
role, final Configuration conf) {
-    refreshSuperUserGroupsLock.lock();
-    try {
-      final ImpersonationProvider impersonationProvider = new 
DefaultImpersonationProvider();
-      impersonationProvider.setConf(conf);
-      impersonationProvider.init(PROXYUSER_PREFIX);
-      LOG.createImpersonationProvider(topologyName, role, PROXYUSER_PREFIX, 
conf.getPropsWithPrefix(PROXYUSER_PREFIX + ".").toString());
-      TOPOLOGY_IMPERSONATION_PROVIDERS.putIfAbsent(topologyName, new 
ConcurrentHashMap<String, ImpersonationProvider>());
-      TOPOLOGY_IMPERSONATION_PROVIDERS.get(topologyName).put(role, 
impersonationProvider);
-    } finally {
-      refreshSuperUserGroupsLock.unlock();
-    }
-  }
-
-  public static HttpServletRequest getProxyRequest(HttpServletRequest request, 
String doAsUser, String topologyName, String role) throws 
AuthorizationException {
-    return getProxyRequest(request, request.getUserPrincipal().getName(), 
doAsUser, topologyName, role);
-  }
-
-  public static HttpServletRequest getProxyRequest(HttpServletRequest request, 
String remoteUser, String doAsUser, String topologyName, String role) throws 
AuthorizationException {
-    final UserGroupInformation remoteRequestUgi = 
getRemoteRequestUgi(remoteUser, doAsUser);
-    if (remoteRequestUgi != null) {
-      authorizeImpersonationRequest(request, remoteRequestUgi, topologyName, 
role);
-
-      return new HttpServletRequestWrapper(request) {
-        @Override
-        public String getRemoteUser() {
-          return remoteRequestUgi.getShortUserName();
+    public static final String DEFAULT_AUTH_UNAUTHENTICATED_PATHS_PARAM = 
"/knoxtoken/api/v1/jwks.json";
+    public static final String PROXYUSER_PREFIX = "hadoop.proxyuser";
+    public static final String QUERY_PARAMETER_DOAS = "doAs";
+    public static final String REAL_USER_NAME_ATTRIBUTE = "real.user.name";
+    public static final String DO_GLOBAL_LOGOUT_ATTRIBUTE = "do.global.logout";
+
+    private static final GatewaySpiMessages LOG = 
MessagesFactory.get(GatewaySpiMessages.class);
+    private static final Map<String, Map<String, ImpersonationProvider>> 
TOPOLOGY_IMPERSONATION_PROVIDERS = new ConcurrentHashMap<>();
+    private static final Lock refreshSuperUserGroupsLock = new ReentrantLock();
+
+    public static final String PROXYGROUP_PREFIX = "hadoop.proxygroup";
+    /**
+     * A helper method that checks whether request contains
+     * unauthenticated path
+     * @param request
+     * @return
+     */
+    public static boolean doesRequestContainUnauthPath(
+            final Set<String> unAuthenticatedPaths, final ServletRequest 
request) {
+        /* make sure the path matches EXACTLY to prevent auth bypass */
+        return unAuthenticatedPaths.contains(((HttpServletRequest) 
request).getPathInfo());
+    }
+
+    /**
+     * A helper method that parses a string and adds to the
+     * provided unauthenticated set.
+     * @param unAuthenticatedPaths
+     * @param list
+     */
+    public static void parseStringThenAdd(final Set<String> 
unAuthenticatedPaths, final String list) {
+        final StringTokenizer tokenizer = new StringTokenizer(list, ";,");
+        while (tokenizer.hasMoreTokens()) {
+            unAuthenticatedPaths.add(tokenizer.nextToken());
         }
+    }
 
-        @Override
-        public Principal getUserPrincipal() {
-          return remoteRequestUgi::getUserName;
+    /**
+     * A method that parses a string (delimiters = ;,) and adds them to the
+     * provided un-authenticated path set.
+     * @param unAuthenticatedPaths
+     * @param list
+     * @param defaultList
+     */
+    public static void addUnauthPaths(final Set<String> unAuthenticatedPaths, 
final String list, final String defaultList) {
+        /* add default unauthenticated paths list */
+        parseStringThenAdd(unAuthenticatedPaths, defaultList);
+        /* add provided unauthenticated paths list if specified */
+        if (!StringUtils.isBlank(list)) {
+            AuthFilterUtils.parseStringThenAdd(unAuthenticatedPaths, list);
         }
+    }
 
-        @Override
-        public Object getAttribute(String name) {
-          if (name != null && name.equals(REAL_USER_NAME_ATTRIBUTE)) {
-            return remoteRequestUgi.getRealUser().getShortUserName();
-          } else {
-            return super.getAttribute(name);
-          }
+    public static void refreshSuperUserGroupsConfiguration(ServletContext 
context, List<String> initParameterNames, String topologyName, String role) {
+        if (context == null) {
+            throw new IllegalArgumentException("Cannot get proxyuser 
configuration from NULL context");
         }
-      };
-
-    }
-    return null;
-  }
-
-  public static void authorizeImpersonationRequest(HttpServletRequest request, 
String remoteUser, String doAsUser, String topologyName, String role) throws 
AuthorizationException {
-    final UserGroupInformation remoteRequestUgi = 
getRemoteRequestUgi(remoteUser, doAsUser);
-    if (remoteRequestUgi != null) {
-      authorizeImpersonationRequest(request, remoteRequestUgi, topologyName, 
role);
-    }
-  }
-
-  private static void authorizeImpersonationRequest(HttpServletRequest 
request, UserGroupInformation remoteRequestUgi, String topologyName, String 
role)
-      throws AuthorizationException {
-
-    final ImpersonationProvider impersonationProvider = 
getImpersonationProvider(topologyName, role);
-
-    if (impersonationProvider != null) {
-      try {
-        impersonationProvider.authorize(remoteRequestUgi, 
request.getRemoteAddr());
-      } catch (org.apache.hadoop.security.authorize.AuthorizationException e) {
-        throw new AuthorizationException(e);
-      }
-    } else {
-      throw new AuthorizationException("ImpersonationProvider for " + 
topologyName + " / " + role + " not found!");
-    }
-  }
-
-  private static ImpersonationProvider getImpersonationProvider(String 
topologyName, String role) {
-    refreshSuperUserGroupsLock.lock();
-    final ImpersonationProvider impersonationProvider;
-    try {
-      impersonationProvider = 
(TOPOLOGY_IMPERSONATION_PROVIDERS.getOrDefault(topologyName, 
Collections.emptyMap())).get(role);
-    } finally {
-      refreshSuperUserGroupsLock.unlock();
-    }
-    return impersonationProvider;
-  }
-
-  private static UserGroupInformation getRemoteRequestUgi(String remoteUser, 
String doAsUser) {
-    if (remoteUser != null) {
-      final UserGroupInformation remoteUserUgi = 
UserGroupInformation.createRemoteUser(remoteUser);
-      return UserGroupInformation.createProxyUser(doAsUser, remoteUserUgi);
-    }
-    return null;
-  }
-
-  public static boolean hasProxyConfig(String topologyName, String role) {
-    return getImpersonationProvider(topologyName, role) != null;
-  }
-
-  public static void removeProxyUserConfig(String topologyName, String role) {
-    if (hasProxyConfig(topologyName, role)) {
-      refreshSuperUserGroupsLock.lock();
-      try {
-        TOPOLOGY_IMPERSONATION_PROVIDERS.get(topologyName).remove(role);
-      } finally {
-        refreshSuperUserGroupsLock.unlock();
-      }
-    }
-  }
-
-  /**
-   * FilterConfig.getInitParameters() returns an enumeration and the first 
time we
-   * iterate thru on its elements we can process the parameter names as desired
-   * (because hasMoreElements returns true). The subsequent calls, however, 
will not
-   * succeed because getInitParameters() returns the same object where the
-   * hasMoreElements returns false.
-   * <p>
-   * In classes where there are multiple iterations should be conducted, a
-   * collection should be used instead.
-   *
-   * @return the names of the filter's initialization parameters as a List of
-   *         String objects, or an empty List if the filter has no 
initialization
-   *         parameters.
-   */
-  public static List<String> getInitParameterNamesAsList(FilterConfig 
filterConfig) {
-    return filterConfig.getInitParameterNames() == null ? 
Collections.emptyList() : 
Collections.list(filterConfig.getInitParameterNames());
-  }
-
-  public static void markDoGlobalLogoutInRequest(HttpServletRequest request) {
-    request.setAttribute(DO_GLOBAL_LOGOUT_ATTRIBUTE, "true");
-  }
-
-  public static boolean shouldDoGlobalLogout(HttpServletRequest request) {
-    return request.getAttribute(DO_GLOBAL_LOGOUT_ATTRIBUTE) == null ? false : 
Boolean.parseBoolean((String) request.getAttribute(DO_GLOBAL_LOGOUT_ATTRIBUTE));
-  }
+        refreshSuperUserGroupsConfiguration(context, null, initParameterNames, 
topologyName, role);
+    }
+
+    public static void refreshSuperUserGroupsConfiguration(FilterConfig 
filterConfig, List<String> initParameterNames, String topologyName, String 
role) {
+        if (filterConfig == null) {
+            throw new IllegalArgumentException("Cannot get proxyuser 
configuration from NULL filter config");
+        }
+        refreshSuperUserGroupsConfiguration(null, filterConfig, 
initParameterNames, topologyName, role);
+    }
+
+    private static void refreshSuperUserGroupsConfiguration(ServletContext 
context, FilterConfig filterConfig, List<String> initParameterNames, String 
topologyName, String role) {
+        final Configuration conf = new Configuration(false);
+        boolean hasProxyGroupParams = false;
+
+        if (initParameterNames != null) {
+            initParameterNames.stream().filter(name -> 
name.startsWith(PROXYUSER_PREFIX + ".")).forEach(name -> {
+                String value = context == null ? 
filterConfig.getInitParameter(name) : context.getInitParameter(name);
+                conf.set(name, value);
+            });
+            /* For proxy groups */
+            hasProxyGroupParams = initParameterNames.stream().anyMatch(name -> 
name.startsWith(PROXYGROUP_PREFIX + "."));
+            if(hasProxyGroupParams) {
+                initParameterNames.stream().filter(name -> 
name.startsWith(PROXYGROUP_PREFIX + ".")).forEach(name -> {
+                    String value = context == null ? 
filterConfig.getInitParameter(name) : context.getInitParameter(name);
+                    conf.set(name, value);
+                });
+            }
+        }
+        saveImpersonationProvider(topologyName, role, conf, new 
KnoxImpersonationProvider(), PROXYUSER_PREFIX);
+    }
+
+    private static void saveImpersonationProvider(String topologyName, String 
role, final Configuration conf, final ImpersonationProvider 
impersonationProvider, final String prefix) {
+        refreshSuperUserGroupsLock.lock();
+        try {
+            impersonationProvider.setConf(conf);
+            impersonationProvider.init(prefix);
+            LOG.createImpersonationProvider(topologyName, role, prefix, 
conf.getPropsWithPrefix(prefix + ".").toString());
+            TOPOLOGY_IMPERSONATION_PROVIDERS.putIfAbsent(topologyName, new 
ConcurrentHashMap<String, ImpersonationProvider>());
+            TOPOLOGY_IMPERSONATION_PROVIDERS.get(topologyName).put(role, 
impersonationProvider);
+        } finally {
+            refreshSuperUserGroupsLock.unlock();
+        }
+    }
+
+    public static HttpServletRequest getProxyRequest(HttpServletRequest 
request, String doAsUser, String topologyName, String role) throws 
AuthorizationException {
+        return getProxyRequest(request, request.getUserPrincipal().getName(), 
doAsUser, topologyName, role);
+    }
+
+    public static HttpServletRequest getProxyRequest(HttpServletRequest 
request, String remoteUser, String doAsUser, String topologyName, String role) 
throws AuthorizationException {
+        final UserGroupInformation remoteRequestUgi = 
getRemoteRequestUgi(remoteUser, doAsUser);
+        if (remoteRequestUgi != null) {
+            authorizeImpersonationRequest(request, remoteRequestUgi, 
topologyName, role);
+
+            return new HttpServletRequestWrapper(request) {
+                @Override
+                public String getRemoteUser() {
+                    return remoteRequestUgi.getShortUserName();
+                }
+
+                @Override
+                public Principal getUserPrincipal() {
+                    return remoteRequestUgi::getUserName;
+                }
+
+                @Override
+                public Object getAttribute(String name) {
+                    if (name != null && name.equals(REAL_USER_NAME_ATTRIBUTE)) 
{
+                        return 
remoteRequestUgi.getRealUser().getShortUserName();
+                    } else {
+                        return super.getAttribute(name);
+                    }
+                }
+            };
+
+        }
+        return null;
+    }
+
+    private static void authorizeImpersonationRequest(HttpServletRequest 
request, UserGroupInformation remoteRequestUgi, String topologyName, String 
role)
+            throws AuthorizationException {
+        authorizeImpersonationRequest(request, remoteRequestUgi, topologyName, 
role, Collections.emptyList());
+    }
+
+    public static void authorizeImpersonationRequest(HttpServletRequest 
request, String remoteUser, String doAsUser, String topologyName, String role) 
throws AuthorizationException {
+        final UserGroupInformation remoteRequestUgi = 
getRemoteRequestUgi(remoteUser, doAsUser);
+        if (remoteRequestUgi != null) {
+            authorizeImpersonationRequest(request, remoteRequestUgi, 
topologyName, role, Collections.emptyList());
+        }
+    }
+
+    public static void authorizeImpersonationRequest(HttpServletRequest 
request, String remoteUser, String doAsUser, String topologyName, String role, 
List<String> groups) throws AuthorizationException {
+        final UserGroupInformation remoteRequestUgi = 
getRemoteRequestUgi(remoteUser, doAsUser);
+        if (remoteRequestUgi != null) {
+            authorizeImpersonationRequest(request, remoteRequestUgi, 
topologyName, role, groups);
+        }
+    }
+
+    private static void authorizeImpersonationRequest(HttpServletRequest 
request, UserGroupInformation remoteRequestUgi, String topologyName, String 
role, List<String> groups)
+            throws AuthorizationException {
+
+        final ImpersonationProvider impersonationProvider = 
getImpersonationProvider(topologyName, role);
+
+        if (impersonationProvider != null) {
+            try {
+                if (impersonationProvider instanceof 
KnoxImpersonationProvider) {
+                    ((KnoxImpersonationProvider) 
impersonationProvider).authorize(remoteRequestUgi, 
InetAddress.getByName(request.getRemoteAddr()), groups);
+                } else {
+                    impersonationProvider.authorize(remoteRequestUgi, 
request.getRemoteAddr());
+                }
+
+            } catch 
(org.apache.hadoop.security.authorize.AuthorizationException | 
UnknownHostException e) {
+                throw new AuthorizationException(e);
+            }
+        } else {
+            throw new AuthorizationException("ImpersonationProvider for " + 
topologyName + " / " + role + " not found!");
+        }
+    }
+
+    private static ImpersonationProvider getImpersonationProvider(String 
topologyName, String role) {
+        refreshSuperUserGroupsLock.lock();
+        final ImpersonationProvider impersonationProvider;
+        try {
+            impersonationProvider = 
(TOPOLOGY_IMPERSONATION_PROVIDERS.getOrDefault(topologyName, 
Collections.emptyMap())).get(role);
+        } finally {
+            refreshSuperUserGroupsLock.unlock();
+        }
+        return impersonationProvider;
+    }
+
+    private static UserGroupInformation getRemoteRequestUgi(String remoteUser, 
String doAsUser) {
+        if (remoteUser != null) {
+            final UserGroupInformation remoteUserUgi = 
UserGroupInformation.createRemoteUser(remoteUser);
+            return UserGroupInformation.createProxyUser(doAsUser, 
remoteUserUgi);
+        }
+        return null;
+    }
+
+    public static boolean hasProxyConfig(String topologyName, String role) {
+        return getImpersonationProvider(topologyName, role) != null;
+    }
+
+    public static void removeProxyUserConfig(String topologyName, String role) 
{
+        if (hasProxyConfig(topologyName, role)) {
+            refreshSuperUserGroupsLock.lock();
+            try {
+                
TOPOLOGY_IMPERSONATION_PROVIDERS.get(topologyName).remove(role);
+            } finally {
+                refreshSuperUserGroupsLock.unlock();
+            }
+        }
+    }
+
+    /**
+     * FilterConfig.getInitParameters() returns an enumeration and the first 
time we
+     * iterate thru on its elements we can process the parameter names as 
desired
+     * (because hasMoreElements returns true). The subsequent calls, however, 
will not
+     * succeed because getInitParameters() returns the same object where the
+     * hasMoreElements returns false.
+     * <p>
+     * In classes where there are multiple iterations should be conducted, a
+     * collection should be used instead.
+     *
+     * @return the names of the filter's initialization parameters as a List of
+     *         String objects, or an empty List if the filter has no 
initialization
+     *         parameters.
+     */
+    public static List<String> getInitParameterNamesAsList(FilterConfig 
filterConfig) {
+        return filterConfig.getInitParameterNames() == null ? 
Collections.emptyList() : 
Collections.list(filterConfig.getInitParameterNames());
+    }
+
+    public static void markDoGlobalLogoutInRequest(HttpServletRequest request) 
{
+        request.setAttribute(DO_GLOBAL_LOGOUT_ATTRIBUTE, "true");
+    }
+
+    public static boolean shouldDoGlobalLogout(HttpServletRequest request) {
+        return request.getAttribute(DO_GLOBAL_LOGOUT_ATTRIBUTE) == null ? 
false : Boolean.parseBoolean((String) 
request.getAttribute(DO_GLOBAL_LOGOUT_ATTRIBUTE));
+    }
 
 }
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/util/KnoxImpersonationProvider.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/util/KnoxImpersonationProvider.java
new file mode 100644
index 000000000..df1d9a181
--- /dev/null
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/util/KnoxImpersonationProvider.java
@@ -0,0 +1,295 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.knox.gateway.util;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authorize.AccessControlList;
+import org.apache.hadoop.security.authorize.AuthorizationException;
+import org.apache.hadoop.security.authorize.DefaultImpersonationProvider;
+import org.apache.hadoop.util.MachineList;
+import org.apache.knox.gateway.i18n.GatewaySpiMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static org.apache.knox.gateway.util.AuthFilterUtils.PROXYGROUP_PREFIX;
+import static org.apache.knox.gateway.util.AuthFilterUtils.PROXYUSER_PREFIX;
+
+/**
+ * An extension of Hadoop's DefaultImpersonationProvider that adds support for 
group-based impersonation.
+ * This provider allows users who belong to specific groups to impersonate 
other users.
+ */
+public class KnoxImpersonationProvider extends DefaultImpersonationProvider {
+    private static final GatewaySpiMessages LOG = 
MessagesFactory.get(GatewaySpiMessages.class);
+    private static final String CONF_HOSTS = ".hosts";
+    private static final String CONF_USERS = ".users";
+    private static final String CONF_GROUPS = ".groups";
+    private static final String PREFIX_REGEX_EXP = "\\.";
+    private static final String USERS_GROUPS_REGEX_EXP = "[\\S]*(" +
+            Pattern.quote(CONF_USERS) + "|" + Pattern.quote(CONF_GROUPS) + ")";
+    private static final String HOSTS_REGEX_EXP = "[\\S]*" + 
Pattern.quote(CONF_HOSTS);
+    private final Map<String, AccessControlList> proxyGroupsAcls = new 
HashMap<>();
+    private Map<String, MachineList> groupProxyHosts = new HashMap<>();
+    private String groupConfigPrefix;
+    private boolean doesProxyUserConfigExist = true;
+    private boolean doesProxyGroupConfigExist;
+    static final String IMPERSONATION_ENABLED_PARAM = ".impersonation.enabled";
+
+    public KnoxImpersonationProvider() {
+        super();
+    }
+
+    @Override
+    public Configuration getConf() {
+        return super.getConf();
+    }
+
+    @Override
+    public void setConf(Configuration conf) {
+        super.setConf(conf);
+    }
+
+    @Override
+    public void init(String configurationPrefix) {
+        super.init(configurationPrefix);
+
+        /* Check if user proxy configs are provided */
+        doesProxyUserConfigExist = 
!getFilteredProps(PROXYUSER_PREFIX).isEmpty();
+
+        /* Check if group proxy configs are provided */
+        doesProxyGroupConfigExist = 
!getFilteredProps(PROXYGROUP_PREFIX).isEmpty();
+
+        initGroupBasedProvider(PROXYGROUP_PREFIX);
+    }
+
+    /**
+     * Returns a filtered map of properties with the specified prefix,
+     * excluding the impersonation enabled parameter.
+     *
+     * @param prefix the prefix to filter properties by
+     * @return a map of filtered properties
+     */
+    private Map<String, String> getFilteredProps(String prefix) {
+        return Optional.ofNullable(getConf().getPropsWithPrefix(prefix))
+                .orElse(Collections.emptyMap())
+                .entrySet()
+                .stream()
+                .filter(entry -> 
!IMPERSONATION_ENABLED_PARAM.equals(entry.getKey()))
+                .collect(Collectors.toMap(Map.Entry::getKey, 
Map.Entry::getValue));
+    }
+
+    private void initGroupBasedProvider(final String proxyGroupPrefix) {
+        groupConfigPrefix = proxyGroupPrefix +
+                (proxyGroupPrefix.endsWith(".") ? "" : ".");
+
+        String prefixRegEx = groupConfigPrefix.replace(".", PREFIX_REGEX_EXP);
+        String usersGroupsRegEx = prefixRegEx + USERS_GROUPS_REGEX_EXP;
+        String hostsRegEx = prefixRegEx + HOSTS_REGEX_EXP;
+
+        // get list of users and groups per proxygroup
+        // Map of <hadoop.proxygroup.[VIRTUAL_GROUP].users|groups, 
group1,group2>
+        Map<String, String> allMatchKeys =
+                getConf().getValByRegex(usersGroupsRegEx);
+
+        for (Map.Entry<String, String> entry : allMatchKeys.entrySet()) {
+            //aclKey = hadoop.proxygroup.[VIRTUAL_GROUP]
+            String aclKey = getAclKey(entry.getKey());
+
+            if (!proxyGroupsAcls.containsKey(aclKey)) {
+                proxyGroupsAcls.put(aclKey, new AccessControlList(
+                        allMatchKeys.get(aclKey + CONF_USERS),
+                        allMatchKeys.get(aclKey + CONF_GROUPS)));
+            }
+        }
+
+        // get hosts per proxygroup
+        allMatchKeys = getConf().getValByRegex(hostsRegEx);
+        for (Map.Entry<String, String> entry : allMatchKeys.entrySet()) {
+            groupProxyHosts.put(entry.getKey(),
+                    new MachineList(entry.getValue()));
+        }
+    }
+
+    private String getAclKey(String key) {
+        int endIndex = key.lastIndexOf('.');
+        if (endIndex != -1) {
+            return key.substring(0, endIndex);
+        }
+        return key;
+    }
+
+    /**
+     * Authorization based on user and group impersonation policies.
+     *
+     * @param user the user information attempting the operation, which 
includes the real
+     *             user and the effective impersonated user.
+     * @param remoteAddress the remote address from which the user is 
connecting.
+     * @throws AuthorizationException if the user is not authorized based on 
the
+     *                                configured impersonation and group 
policies.
+     */
+    @Override
+    public void authorize(UserGroupInformation user, InetAddress 
remoteAddress) throws AuthorizationException {
+        authorize(user, remoteAddress, Collections.emptyList());
+    }
+
+    /**
+     * Authorization based on groups that are provided as a function argument
+     *
+     * @param user the user information attempting the operation, which 
includes the real
+     *             user and the effective impersonated user.
+     * @param groups the list of groups to check for authorization.
+     * @param remoteAddress the remote address from which the user is 
connecting.
+     * @throws AuthorizationException if the user is not authorized based on 
the
+     *                                configured impersonation and group 
policies.
+     */
+    public void authorize(UserGroupInformation user, InetAddress 
remoteAddress, List<String> groups) throws AuthorizationException {
+
+        /*
+         * There are few cases to consider here:
+         *  1. If proxy user config exists and there is impersonation failure:
+         *    1.1 if proxy group config exists, try to authorize using groups
+         *    1.2 if proxy group config does not exist, throw the original 
exception
+         *  2. If proxy user config exists and there is impersonation success:
+         *    2.2 Do not check for proxy group auth
+         *  3. If proxy user config does not exist:
+         *    3.1 Check for proxy group config and return success or failure 
based on the results.
+         */
+        if (doesProxyUserConfigExist) {
+            try{
+                /* check for proxy user authorization */
+                super.authorize(user, remoteAddress);
+            } catch (final AuthorizationException e) {
+                /*
+                 * Log and try group based impersonation.
+                 * Since this provider is for groups no need to check if
+                 * proxy group config exists, we know it does.
+                 */
+                LOG.failedToImpersonateUserTryingGroups(user.getUserName(), 
e.toString());
+                /* check for proxy group authorization */
+                if(doesProxyGroupConfigExist) {
+                    checkProxyGroupAuthorization(user, remoteAddress, groups);
+                } else {
+                    /* If proxy group config does not exist, throw the 
original exception */
+                    throw e;
+                }
+            }
+        } else if (doesProxyGroupConfigExist) {
+            /* check for proxy group authorization */
+            checkProxyGroupAuthorization(user, remoteAddress, groups);
+        } else {
+            /* If no proxy user or group config exists, throw an exception */
+            LOG.noProxyUserOrGroupConfigExists();
+            throw new AuthorizationException("User: " + user.getRealUser()
+                    + " is not allowed to impersonate " + user.getUserName());
+        }
+    }
+
+
+    /**
+     * Helper method to check if the group a given user belongs to is 
authorized to impersonate
+     * Returns true if the user is authorized, false otherwise.
+     *
+     * @param user
+     * @param remoteAddress
+     * @return
+     */
+    private void checkProxyGroupAuthorization(final UserGroupInformation user, 
final InetAddress remoteAddress, List<String> groups) throws 
AuthorizationException {
+        if (user == null) {
+            throw new IllegalArgumentException("user is null.");
+        }
+
+        final UserGroupInformation realUser = user.getRealUser();
+        if (realUser == null) {
+            return;
+        }
+
+        // Get the real user's groups (both real and virtual)
+        Set<String> realUserGroups = new HashSet<>();
+        /* Add provided groups */
+        if(groups != null && !groups.isEmpty()) {
+            realUserGroups.addAll(groups);
+        }
+        /* Add groups from subject */
+        if (user.getRealUser().getGroupNames() != null) {
+            Collections.addAll(realUserGroups, 
user.getRealUser().getGroupNames());
+        }
+
+        boolean proxyGroupFound = false;
+        // Check if any of the real user's groups have permission to 
impersonate the proxy user
+        proxyGroupFound = isProxyGroupFound(user, realUserGroups, 
proxyGroupFound);
+
+        if (!proxyGroupFound) {
+            LOG.failedToImpersonateGroups(realUser.getUserName(), 
realUserGroups.toString(), user.getUserName());
+            throw new AuthorizationException("User: " + realUser.getUserName()
+                    + " with groups " + realUserGroups.toString()
+                    + " is not allowed to impersonate " + user.getUserName());
+        }
+
+        boolean proxyGroupHostFound = false;
+        proxyGroupHostFound = isProxyGroupHostFound(remoteAddress, 
realUserGroups, proxyGroupHostFound);
+
+        if (!proxyGroupHostFound) {
+            LOG.failedToImpersonateGroupsFromAddress(realUser.getUserName(), 
realUserGroups.toString(), user.getUserName(), remoteAddress.toString());
+            throw new AuthorizationException("User: " + realUser.getUserName()
+                    + " with groups " + realUserGroups.toString()
+                    + " from address " + remoteAddress.toString()
+                    + " is not allowed to impersonate " + user.getUserName());
+        }
+
+        /* all checks pass */
+    }
+
+    private boolean isProxyGroupHostFound(InetAddress remoteAddress, 
Set<String> realUserGroups, boolean proxyGroupHostFound) {
+        for (final String group : realUserGroups) {
+            final MachineList machineList = 
groupProxyHosts.get(groupConfigPrefix + group + CONF_HOSTS);
+
+            if (machineList == null || !machineList.includes(remoteAddress)) {
+                continue;
+            } else {
+                proxyGroupHostFound = true;
+                break;
+            }
+        }
+        return proxyGroupHostFound;
+    }
+
+    private boolean isProxyGroupFound(UserGroupInformation user, Set<String> 
realUserGroups, boolean proxyGroupFound) {
+        for (String group : realUserGroups) {
+            final AccessControlList acl = 
proxyGroupsAcls.get(groupConfigPrefix +
+                    group);
+
+            if (acl == null || !acl.isUserAllowed(user)) {
+                continue;
+            } else {
+                proxyGroupFound = true;
+                break;
+            }
+        }
+        return proxyGroupFound;
+    }
+
+}
diff --git 
a/gateway-spi/src/test/java/org/apache/knox/gateway/util/KnoxImpersonationProviderTest.java
 
b/gateway-spi/src/test/java/org/apache/knox/gateway/util/KnoxImpersonationProviderTest.java
new file mode 100644
index 000000000..7d39443e2
--- /dev/null
+++ 
b/gateway-spi/src/test/java/org/apache/knox/gateway/util/KnoxImpersonationProviderTest.java
@@ -0,0 +1,725 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.knox.gateway.util;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.mockito.Mockito;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+
+
+import static org.apache.knox.gateway.util.AuthFilterUtils.PROXYUSER_PREFIX;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+public class KnoxImpersonationProviderTest {
+
+    @Rule
+    public Timeout globalTimeout = new Timeout(10000, TimeUnit.MILLISECONDS);
+    private KnoxImpersonationProvider provider;
+    private Configuration config;
+
+    @Before
+    public void setUp() {
+
+        provider = new KnoxImpersonationProvider();
+        config = new Configuration();
+
+        // Setup proxy user configuration
+        config.set("hadoop.proxyuser.testuser.users", "*");
+        config.set("hadoop.proxyuser.testuser.groups", "*");
+        config.set("hadoop.proxyuser.testuser.hosts", "*");
+
+        // Setup 3 proxy groups
+        config.set("hadoop.proxygroup.virtual_group_1.users", "*");
+        config.set("hadoop.proxygroup.virtual_group_1.groups", "*");
+        config.set("hadoop.proxygroup.virtual_group_1.hosts", "*");
+        config.set("hadoop.proxygroup.virtual.group_2.users", "*");
+        config.set("hadoop.proxygroup.virtual.group_2.groups", "*");
+        config.set("hadoop.proxygroup.virtual.group_2.hosts", "*");
+        config.set("hadoop.proxygroup.virtual group_3.users", "*");
+        config.set("hadoop.proxygroup.virtual group_3.groups", "*");
+        config.set("hadoop.proxygroup.virtual group_3.hosts", "*");
+
+        provider.setConf(config);
+        provider.init(PROXYUSER_PREFIX);
+    }
+
+    @Test
+    public void testGetConf() {
+        assertEquals(config, provider.getConf());
+    }
+
+    @Test
+    public void testSetConf() {
+        Configuration newConfig = new Configuration(false);
+        provider.setConf(newConfig);
+        assertEquals(newConfig, provider.getConf());
+    }
+
+    @Test
+    public void testInitWithEmptyConfig() {
+        provider.init("hadoop.proxygroup");
+        // No exception should be thrown
+    }
+
+    @Test
+    public void testInitWithValidConfig() {
+        // Set up configuration with valid proxy groups
+        config.set("hadoop.proxygroup.admin.users", "user1,user2");
+        config.set("hadoop.proxygroup.admin.groups", "group1,group2");
+        config.set("hadoop.proxygroup.admin.hosts", "host1,host2");
+
+        provider.init("hadoop.proxygroup");
+        // No exception should be thrown
+    }
+
+    @Test
+    public void testGetAclKey() throws Exception {
+        // Test the private getAclKey method using reflection
+        java.lang.reflect.Method method = 
KnoxImpersonationProvider.class.getDeclaredMethod("getAclKey", String.class);
+        method.setAccessible(true);
+
+        assertEquals("hadoop.proxygroup.admin", method.invoke(provider, 
"hadoop.proxygroup.admin.users"));
+        assertEquals("hadoop.proxygroup.admin", method.invoke(provider, 
"hadoop.proxygroup.admin.groups"));
+        assertEquals("key", method.invoke(provider, "key"));
+    }
+
+    @Test
+    public void testAuthorizationSuccess() throws Exception {
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+        String[] proxyGroups = {"virtual_group_1"};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Use reflection to call the checkProxyGroupAuthorization method 
directly
+        java.lang.reflect.Method method = 
KnoxImpersonationProvider.class.getDeclaredMethod(
+                "checkProxyGroupAuthorization",
+                UserGroupInformation.class,
+                InetAddress.class,
+                List.class);
+        method.setAccessible(true);
+        method.invoke(provider, userGroupInformation, 
InetAddress.getByName("2.2.2.2"), Collections.emptyList());
+
+        String[] proxyGroups2 = {"virtual.group_2"};
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups2);
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Use reflection to call the checkProxyGroupAuthorization method 
directly
+        method.invoke(provider, userGroupInformation, 
InetAddress.getByName("2.2.2.2"), Collections.emptyList());
+    }
+
+    /**
+     * Test the case where proxy user is disabled and only proxy groups is 
enabled.
+     * i.e.
+     * hadoop.proxyuser.impersonation.enabled = false
+     * hadoop.proxygroup.impersonation.enabled = true
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testAuthorizationSuccessWithOnlyProxyGroupsConfigured() throws 
Exception {
+        KnoxImpersonationProvider gProvider = new KnoxImpersonationProvider();
+        Configuration gConfig = new Configuration();
+
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+
+        // Setup proxy user configuration
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".users", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".hosts", "*");
+
+        // Setup 3 proxy groups
+        gConfig.set("hadoop.proxygroup.virtual_group_1.users", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.hosts", "*");
+        gConfig.set("hadoop.proxygroup.virtual.group_2.users", "*");
+        gConfig.set("hadoop.proxygroup.virtual.group_2.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual.group_2.hosts", "*");
+        gConfig.set("hadoop.proxygroup.virtual group_3.users", "*");
+        gConfig.set("hadoop.proxygroup.virtual group_3.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual group_3.hosts", "*");
+
+        gProvider.setConf(gConfig);
+        // Initialize with both prefixes to enable both proxy user and proxy 
group authorization
+        gProvider.init(PROXYUSER_PREFIX);
+
+        String[] proxyGroups = {"virtual_group_1"};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Use reflection to call the checkProxyGroupAuthorization method 
directly
+        java.lang.reflect.Method method = 
KnoxImpersonationProvider.class.getDeclaredMethod(
+                "checkProxyGroupAuthorization",
+                UserGroupInformation.class,
+                InetAddress.class,
+                List.class);
+        method.setAccessible(true);
+        method.invoke(gProvider, userGroupInformation, 
InetAddress.getByName("2.2.2.2"), Collections.emptyList());
+
+        String[] proxyGroups2 = {"virtual.group_2"};
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups2);
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Use reflection to call the checkProxyGroupAuthorization method 
directly
+        method.invoke(gProvider, userGroupInformation, 
InetAddress.getByName("2.2.2.2"), Collections.emptyList());
+    }
+
+    @Test(expected = 
org.apache.hadoop.security.authorize.AuthorizationException.class)
+    public void testAuthorizationFailure() throws Exception {
+        String proxyUser = "dummyUser";
+        String impersonatedUser = "impersonatedUser";
+        String[] proxyGroups = {"virtual group_3"};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+        provider.authorize(userGroupInformation, 
InetAddress.getByName("2.2.2.2"));
+    }
+
+    /**
+     * Test the case where both proxy user and proxy group are disabled.
+     * Authorization should succeed in this case.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testAuthorizationSuccessWithBothProxyMethodsDisabled() throws 
Exception {
+
+        KnoxImpersonationProvider gProvider = new KnoxImpersonationProvider();
+        Configuration gConfig = new Configuration();
+
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+
+        // Setup proxy user configuration
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".users", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".hosts", "*");
+
+        // Setup proxy group configuration
+        gConfig.set("hadoop.proxygroup.virtual_group_1.users", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.hosts", "*");
+
+        gProvider.setConf(gConfig);
+        gProvider.init(PROXYUSER_PREFIX);
+
+        String[] proxyGroups = {"virtual_group_1"};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Use reflection to call the checkProxyGroupAuthorization method 
directly
+        java.lang.reflect.Method method = 
KnoxImpersonationProvider.class.getDeclaredMethod(
+                "checkProxyGroupAuthorization",
+                UserGroupInformation.class,
+                InetAddress.class,
+                List.class);
+        method.setAccessible(true);
+        method.invoke(gProvider, userGroupInformation, 
InetAddress.getByName("2.2.2.2"), Collections.emptyList());
+    }
+
+    /**
+     * Test the case where only proxy user is enabled and proxy group is 
disabled.
+     * i.e.
+     * hadoop.proxyuser.impersonation.enabled = true
+     * hadoop.proxygroup.impersonation.enabled = false
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testAuthorizationSuccessWithOnlyProxyUserConfigured() throws 
Exception {
+        KnoxImpersonationProvider gProvider = new KnoxImpersonationProvider();
+        Configuration gConfig = new Configuration();
+
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+
+        // Setup proxy user configuration
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".users", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".groups", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".hosts", "*");
+
+        // Setup proxy group configuration for the group the user belongs to
+        gConfig.set("hadoop.proxygroup.somegroup.users", "*");
+        gConfig.set("hadoop.proxygroup.somegroup.groups", "*");
+        gConfig.set("hadoop.proxygroup.somegroup.hosts", "*");
+
+        gProvider.setConf(gConfig);
+        gProvider.init(PROXYUSER_PREFIX);
+
+        String[] proxyGroups = {"somegroup"};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Use reflection to call the checkProxyGroupAuthorization method 
directly
+        java.lang.reflect.Method method = 
KnoxImpersonationProvider.class.getDeclaredMethod(
+                "checkProxyGroupAuthorization",
+                UserGroupInformation.class,
+                InetAddress.class,
+                List.class);
+        method.setAccessible(true);
+        method.invoke(gProvider, userGroupInformation, 
InetAddress.getByName("2.2.2.2"), Collections.emptyList());
+    }
+
+    /**
+     * Test the case where both proxy user and proxy group are enabled with 
AND mode.
+     * This ensures that both authorization methods are actually being used.
+     * i.e.
+     * hadoop.proxyuser.impersonation.enabled = true
+     * hadoop.proxygroup.impersonation.enabled = true
+     * impersonation.mode = AND
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testAuthorizationSuccessWithBothProxyMethodsEnabledAndMode() 
throws Exception {
+        KnoxImpersonationProvider gProvider = new KnoxImpersonationProvider();
+        Configuration gConfig = new Configuration();
+
+        // Set impersonation mode to AND
+        gConfig.set("impersonation.mode", "AND");
+
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+
+        // Setup proxy user configuration
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".users", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".groups", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".hosts", "*");
+
+        // Setup proxy group configuration
+        gConfig.set("hadoop.proxygroup.virtual_group_1.users", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.hosts", "*");
+
+        gProvider.setConf(gConfig);
+        gProvider.init(PROXYUSER_PREFIX);
+
+        String[] proxyGroups = {"virtual_group_1"};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(proxyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Use reflection to call the checkProxyGroupAuthorization method 
directly
+        java.lang.reflect.Method method = 
KnoxImpersonationProvider.class.getDeclaredMethod(
+                "checkProxyGroupAuthorization",
+                UserGroupInformation.class,
+                InetAddress.class,
+                List.class);
+        method.setAccessible(true);
+        method.invoke(gProvider, userGroupInformation, 
InetAddress.getByName("2.2.2.2"), Collections.emptyList());
+    }
+
+    /**
+     * Test the case where authorization is successful using the new authorize 
method
+     * that accepts a list of groups as a parameter.
+     *
+     * @throws org.apache.hadoop.security.authorize.AuthorizationException
+     * @throws UnknownHostException
+     */
+    @Test
+    public void testAuthorizationSuccessWithProvidedGroups() throws 
org.apache.hadoop.security.authorize.AuthorizationException, 
UnknownHostException {
+        KnoxImpersonationProvider gProvider = new KnoxImpersonationProvider();
+        Configuration gConfig = new Configuration();
+
+        // Setup proxy group configuration
+        gConfig.set("hadoop.proxygroup.virtual_group_1.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.hosts", "*");
+
+        gProvider.setConf(gConfig);
+        gProvider.init(PROXYUSER_PREFIX);
+
+        String proxyUser = "testuser";
+        // User has no groups in their subject
+        String[] emptyGroups = {};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(emptyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        
when(userGroupInformation.getUserName()).thenReturn("impersonatedUser");
+
+        // Create a list of groups to provide to the authorize method
+        List<String> providedGroups = new ArrayList<>();
+        providedGroups.add("virtual_group_1");
+
+        // This should not throw an exception since the provided groups 
include virtual_group_1
+        // which is authorized to impersonate
+        gProvider.authorize(userGroupInformation, 
InetAddress.getByName("2.2.2.2"), providedGroups);
+    }
+
+    /**
+     * Test the case where authorization fails when the provided groups
+     * do not have permission to impersonate.
+     *
+     * @throws org.apache.hadoop.security.authorize.AuthorizationException
+     * @throws UnknownHostException
+     */
+    @Test(expected = 
org.apache.hadoop.security.authorize.AuthorizationException.class)
+    public void testAuthorizationFailureWithProvidedGroups() throws 
org.apache.hadoop.security.authorize.AuthorizationException, 
UnknownHostException {
+        KnoxImpersonationProvider gProvider = new KnoxImpersonationProvider();
+        Configuration gConfig = new Configuration();
+
+        // Setup proxy group configuration
+        gConfig.set("hadoop.proxygroup.virtual_group_1.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.hosts", "*");
+
+        gProvider.setConf(gConfig);
+        gProvider.init(PROXYUSER_PREFIX);
+
+        String proxyUser = "testuser";
+        // User has no groups in their subject
+        String[] emptyGroups = {};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(emptyGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        
when(userGroupInformation.getUserName()).thenReturn("impersonatedUser");
+
+        // Create a list of groups to provide to the authorize method
+        // These groups do not have permission to impersonate
+        List<String> providedGroups = new ArrayList<>();
+        providedGroups.add("unauthorized_group");
+
+        // This should throw an exception since the provided groups do not 
include any
+        // that are authorized to impersonate
+        gProvider.authorize(userGroupInformation, 
InetAddress.getByName("2.2.2.2"), providedGroups);
+    }
+
+    /**
+     * Test the case where authorization succeeds when the user's subject 
groups
+     * combined with the provided groups include one that has permission to 
impersonate.
+     *
+     * @throws org.apache.hadoop.security.authorize.AuthorizationException
+     * @throws UnknownHostException
+     */
+    @Test
+    public void testAuthorizationSuccessWithCombinedGroups() throws 
org.apache.hadoop.security.authorize.AuthorizationException, 
UnknownHostException {
+        KnoxImpersonationProvider gProvider = new KnoxImpersonationProvider();
+        Configuration gConfig = new Configuration();
+
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+
+        // Setup proxy user configuration
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".users", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".groups", "*");
+        gConfig.set("hadoop.proxyuser." + proxyUser + ".hosts", "*");
+
+        // Setup proxy group configuration
+        gConfig.set("hadoop.proxygroup.virtual_group_1.users", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_1.hosts", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_2.users", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_2.groups", "*");
+        gConfig.set("hadoop.proxygroup.virtual_group_2.hosts", "*");
+
+        gProvider.setConf(gConfig);
+        gProvider.init(PROXYUSER_PREFIX);
+
+        // User has virtual_group_1 in their subject
+        String[] subjectGroups = {"virtual_group_1"};
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(subjectGroups);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Create a list of groups to provide to the authorize method
+        // These include virtual_group_2 which is also authorized
+        List<String> providedGroups = new ArrayList<>();
+        providedGroups.add("virtual_group_2");
+
+        // This should not throw an exception since the combined groups include
+        // both virtual_group_1 (from subject) and virtual_group_2 (provided)
+        // which are both authorized to impersonate
+        gProvider.authorize(userGroupInformation, 
InetAddress.getByName("2.2.2.2"), providedGroups);
+    }
+    /**
+     * Test the isProxyGroupFound method directly.
+     * This method checks if any of the real user's groups have permission to 
impersonate the proxy user.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    @Test
+    public void testIsProxyGroupFound() throws Exception {
+        // Set up the provider with specific configuration
+        KnoxImpersonationProvider testProvider = new 
KnoxImpersonationProvider();
+        Configuration testConfig = new Configuration();
+
+        // Configure a specific proxy group
+        testConfig.set("hadoop.proxygroup.authorized_group.users", "*");
+        testConfig.set("hadoop.proxygroup.authorized_group.groups", "*");
+        testConfig.set("hadoop.proxygroup.authorized_group.hosts", "*");
+
+        testProvider.setConf(testConfig);
+        testProvider.init("hadoop.proxygroup");
+
+        // Create mocks for testing
+        UserGroupInformation userToImpersonate = 
Mockito.mock(UserGroupInformation.class);
+        when(userToImpersonate.getUserName()).thenReturn("impersonatedUser");
+
+        // Set up the method to test via reflection
+        java.lang.reflect.Method method = 
KnoxImpersonationProvider.class.getDeclaredMethod(
+                "isProxyGroupFound",
+                UserGroupInformation.class,
+                Set.class,
+                boolean.class);
+        method.setAccessible(true);
+
+        // Test case 1: User belongs to an authorized group
+        Set<String> authorizedGroups = new HashSet<>();
+        authorizedGroups.add("authorized_group");
+
+        boolean result1 = (boolean) method.invoke(testProvider, 
userToImpersonate, authorizedGroups, false);
+        assertTrue("User with authorized group should be allowed to 
impersonate", result1);
+
+        // Test case 2: User belongs to an unauthorized group
+        Set<String> unauthorizedGroups = new HashSet<>();
+        unauthorizedGroups.add("unauthorized_group");
+
+        boolean result2 = (boolean) method.invoke(testProvider, 
userToImpersonate, unauthorizedGroups, false);
+        assertFalse("User with unauthorized group should not be allowed to 
impersonate", result2);
+
+        // Test case 3: User belongs to multiple groups, including an 
authorized one
+        Set<String> mixedGroups = new HashSet<>();
+        mixedGroups.add("unauthorized_group");
+        mixedGroups.add("authorized_group");
+
+        boolean result3 = (boolean) method.invoke(testProvider, 
userToImpersonate, mixedGroups, false);
+        assertTrue("User with mixed groups including an authorized one should 
be allowed to impersonate", result3);
+
+        // Test case 4: Empty group set
+        Set<String> emptyGroups = new HashSet<>();
+
+        boolean result4 = (boolean) method.invoke(testProvider, 
userToImpersonate, emptyGroups, false);
+        assertFalse("User with no groups should not be allowed to 
impersonate", result4);
+
+        // Test case 5: Initial proxyGroupFound is true
+        boolean result5 = (boolean) method.invoke(testProvider, 
userToImpersonate, unauthorizedGroups, true);
+        assertTrue("Method should return true if initial proxyGroupFound is 
true", result5);
+    }
+    /**
+     * Test the authorize method when doesProxyUserConfigExist is false and 
groups exist.
+     * In this scenario, the method should directly call 
checkProxyGroupAuthorization.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    @Test
+    public void testAuthorizeWhenProxyUserConfigDoesNotExistAndGroupsExist() 
throws Exception {
+        // Set up a provider with only proxy group configuration (no proxy 
user configuration)
+        KnoxImpersonationProvider testProvider = new 
KnoxImpersonationProvider();
+        Configuration testConfig = new Configuration();
+
+        // Configure only proxy groups, not proxy users
+        testConfig.set("hadoop.proxygroup.authorized_group.users", "*");
+        testConfig.set("hadoop.proxygroup.authorized_group.groups", "*");
+        testConfig.set("hadoop.proxygroup.authorized_group.hosts", "*");
+
+        testProvider.setConf(testConfig);
+        testProvider.init("hadoop.proxygroup");
+
+        // Create mocks for testing
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getGroupNames()).thenReturn(new String[0]); // No 
groups in subject
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Create a list of groups to provide to the authorize method
+        List<String> providedGroups = new ArrayList<>();
+        providedGroups.add("authorized_group");
+
+        // This should not throw an exception since the provided groups 
include authorized_group
+        // which is authorized to impersonate
+        testProvider.authorize(userGroupInformation, 
InetAddress.getByName("2.2.2.2"), providedGroups);
+    }
+
+    /**
+     * Test the authorize method when doesProxyUserConfigExist is true and 
valid proxyuser configuration exists.
+     * In this scenario, the method should successfully authorize using the 
parent class's authorize method.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    @Test
+    public void testAuthorizeWhenProxyUserConfigExistsAndIsValid() throws 
Exception {
+        // Set up a provider with valid proxy user configuration
+        KnoxImpersonationProvider testProvider = new 
KnoxImpersonationProvider();
+        Configuration testConfig = new Configuration();
+
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+
+        // Setup valid proxy user configuration
+        testConfig.set("hadoop.proxyuser." + proxyUser + ".users", "*");
+        testConfig.set("hadoop.proxyuser." + proxyUser + ".groups", "*");
+        testConfig.set("hadoop.proxyuser." + proxyUser + ".hosts", "*");
+
+        testProvider.setConf(testConfig);
+        testProvider.init("hadoop.proxyuser");
+
+        // Create mocks for testing
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(proxyUser);
+        when(realUserUGI.getUserName()).thenReturn(proxyUser);
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // This should not throw an exception since the proxy user 
configuration is valid
+        testProvider.authorize(userGroupInformation, 
InetAddress.getByName("2.2.2.2"));
+    }
+
+    /**
+     * Test the authorize method when doesProxyUserConfigExist is true but the 
proxy user authorization fails,
+     * causing it to fall back to checkProxyGroupAuthorization.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    @Test
+    public void testAuthorizeWhenProxyUserConfigExistsButAuthorizationFails() 
throws Exception {
+        // Set up a provider with proxy user configuration that will fail 
authorization
+        // but with valid proxy group configuration
+        KnoxImpersonationProvider testProvider = new 
KnoxImpersonationProvider();
+        Configuration testConfig = new Configuration();
+
+        String proxyUser = "testuser";
+        String impersonatedUser = "impersonatedUser";
+        String unauthorizedUser = "unauthorizedUser"; // Different from the 
configured proxy user
+
+        // Setup proxy user configuration for a specific user
+        testConfig.set("hadoop.proxyuser." + proxyUser + ".users", 
"specificUser"); // Not wildcard
+        testConfig.set("hadoop.proxyuser." + proxyUser + ".groups", 
"specificGroup"); // Not wildcard
+        testConfig.set("hadoop.proxyuser." + proxyUser + ".hosts", "*");
+
+        // Setup proxy group configuration
+        testConfig.set("hadoop.proxygroup.authorized_group.users", "*");
+        testConfig.set("hadoop.proxygroup.authorized_group.groups", "*");
+        testConfig.set("hadoop.proxygroup.authorized_group.hosts", "*");
+
+        testProvider.setConf(testConfig);
+        testProvider.init("hadoop.proxyuser");
+
+        // Create mocks for testing
+        UserGroupInformation realUserUGI = 
Mockito.mock(UserGroupInformation.class);
+        UserGroupInformation userGroupInformation = 
Mockito.mock(UserGroupInformation.class);
+
+        when(realUserUGI.getShortUserName()).thenReturn(unauthorizedUser); // 
Use unauthorized user
+        when(realUserUGI.getUserName()).thenReturn(unauthorizedUser);
+        when(realUserUGI.getGroupNames()).thenReturn(new String[0]); // No 
groups in subject
+
+        when(userGroupInformation.getRealUser()).thenReturn(realUserUGI);
+        when(userGroupInformation.getUserName()).thenReturn(impersonatedUser);
+
+        // Create a list of groups to provide to the authorize method
+        List<String> providedGroups = new ArrayList<>();
+        providedGroups.add("authorized_group");
+
+        // This should not throw an exception because even though proxy user 
authorization fails,
+        // it falls back to group-based authorization which succeeds
+        testProvider.authorize(userGroupInformation, 
InetAddress.getByName("2.2.2.2"), providedGroups);
+    }
+}

Reply via email to