[ https://issues.apache.org/jira/browse/KNOX-3048?focusedWorklogId=970850&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-970850 ]
ASF GitHub Bot logged work on KNOX-3048: ---------------------------------------- Author: ASF GitHub Bot Created on: 27/May/25 20:11 Start Date: 27/May/25 20:11 Worklog Time Spent: 10m Work Description: pzampino commented on code in PR #1043: URL: https://github.com/apache/knox/pull/1043#discussion_r2110104035 ########## gateway-spi/src/main/java/org/apache/knox/gateway/util/GroupBasedImpersonationProvider.java: ########## @@ -0,0 +1,228 @@ +/* + * 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.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * 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 GroupBasedImpersonationProvider extends DefaultImpersonationProvider { Review Comment: Related to my previous comment, could this extension augment the DefaultImpersonationProvider implementation such that ONLY this provider ever needs to be used by Knox to support the default user-level impersonation plus the group-level support being added here? ########## gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java: ########## @@ -42,212 +45,307 @@ 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(); - } - - @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; - } - - 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)); - } + 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"; + + public static final String PROXYGROUP_PREFIX = "hadoop.proxygroup"; + public static final String IMPERSONATION_MODE = "hadoop.impersonation.mode"; + public static final String DEFAULT_IMPERSONATION_MODE = "OR"; + public static final String IMPERSONATION_ENABLED_PARAM = AuthFilterUtils.PROXYUSER_PREFIX + ".impersonation.enabled"; + public static final String GROUP_IMPERSONATION_ENABLED_PARAM = AuthFilterUtils.PROXYGROUP_PREFIX + ".impersonation.enabled"; + + 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(); + + /** + * Represents the modes of impersonation that can be configured and used + * within the authentication process. + * + * USER_IMPERSONATION: + * Indicates that the impersonation process is based on a user identity. + * This is typically used when one user needs to act on behalf of another. + * + * GROUP_IMPERSONATION: + * Represents group-based impersonation where actions can be performed + * based on group roles or permissions. + */ + public enum ImpersonationFlags { + USER_IMPERSONATION, GROUP_IMPERSONATION + } + + + /** + * 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, new DefaultImpersonationProvider(), PROXYUSER_PREFIX); + } + + /* For proxy groups */ + public static void refreshProxyGroupsConfiguration(FilterConfig filterConfig, List<String> initParameterNames, String topologyName, String role) { + if (filterConfig == null) { + throw new IllegalArgumentException("Cannot get proxyuser configuration from NULL filter config"); + } + refreshProxyGroupsConfiguration(null, filterConfig, initParameterNames, topologyName, role); + } + + private static void refreshProxyGroupsConfiguration(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(PROXYGROUP_PREFIX + ".")).forEach(name -> { + String value = context == null ? filterConfig.getInitParameter(name) : context.getInitParameter(name); + conf.set(name, value); + }); + initParameterNames.stream().filter(name -> name.startsWith(IMPERSONATION_MODE + ".")).forEach(name -> { + String value = context == null ? filterConfig.getInitParameter(name) : context.getInitParameter(name); + conf.set(name, value); + }); + } + /* For proxy group use GroupBasedImpersonationProvider */ + saveImpersonationProvider(topologyName, role, conf, new GroupBasedImpersonationProvider(getImpersonationEnabledFlags(filterConfig)), PROXYGROUP_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; + } + + public static void authorizeGroupImpersonationRequest(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); + } + } + + 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 { + authorizeImpersonationRequest(request, remoteRequestUgi, topologyName, role, Collections.emptyList()); + } + + 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 GroupBasedImpersonationProvider) { + ((GroupBasedImpersonationProvider) 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)); + } + + /** + * Check if user or group impersonation is enabled based on filter configuration. + * + * @param filterConfig The filter configuration + * @return A boolean array where the first element indicates if user impersonation is enabled + * and the second element indicates if group impersonation is enabled + */ + public static EnumSet<ImpersonationFlags> getImpersonationEnabledFlags(final FilterConfig filterConfig) { + boolean userImpersonationEnabledValue = false; + boolean groupImpersonationEnabledValue = false; + final EnumSet <ImpersonationFlags> impersonationFlags = EnumSet.noneOf(ImpersonationFlags.class); + /* Check if user or group impersonation is enabled */ + if (filterConfig.getInitParameter(IMPERSONATION_ENABLED_PARAM) != null) { + String userImpersonationEnabledString = filterConfig.getInitParameter(IMPERSONATION_ENABLED_PARAM); + userImpersonationEnabledValue = userImpersonationEnabledString == null ? Boolean.FALSE : Boolean.parseBoolean(userImpersonationEnabledString); + if(userImpersonationEnabledValue) { + impersonationFlags.add(ImpersonationFlags.USER_IMPERSONATION); + } + } + + if (filterConfig.getInitParameter(GROUP_IMPERSONATION_ENABLED_PARAM) != null) { Review Comment: Why does group-based impersonation need to be enabled separately? I would expect to enable impersonation, and have the provider check the user, then fall back to groups if any such mappings are configured. Issue Time Tracking ------------------- Worklog Id: (was: 970850) Time Spent: 3h (was: 2h 50m) > Surrogate proxy user configuration for user groups > -------------------------------------------------- > > Key: KNOX-3048 > URL: https://issues.apache.org/jira/browse/KNOX-3048 > Project: Apache Knox > Issue Type: Improvement > Components: Server > Affects Versions: 2.0.0 > Reporter: Philip Zampino > Assignee: Sandeep More > Priority: Major > Fix For: 2.1.0 > > Time Spent: 3h > Remaining Estimate: 0h > > *Problem Statement:* > Currently Knox has the ability for specific users (say for e.g. {{sp_user}}) > to impersonate other users (say for e.g.{{ot_user}}). This is driven by > configs defined in a topology. Currently these configs are needed for each > user that impersonates other users (i.e. {{sp_user}}), this can get out of > hand quickly and can be difficult to maintain. > To solve this problem the proposed solution uses a group level impersonation > configuration. This configuration will be based on the virtual groups feature > that is already available in Knox. With this new configuration we can have > specific users who belong to a virtual group/s (based on conditions such as > {{(match groups 'analyst|scientist') }}) impersonate other users. This will > significantly cut down on the config properties and keep them readable and > maintainable. -- This message was sent by Atlassian Jira (v8.20.10#820010)