[ https://issues.apache.org/jira/browse/KNOX-3048?focusedWorklogId=970005&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-970005 ]
ASF GitHub Bot logged work on KNOX-3048: ---------------------------------------- Author: ASF GitHub Bot Created on: 20/May/25 13:15 Start Date: 20/May/25 13:15 Worklog Time Spent: 10m Work Description: moresandeep commented on code in PR #1043: URL: https://github.com/apache/knox/pull/1043#discussion_r2097948923 ########## gateway-provider-identity-assertion-common/src/main/java/org/apache/knox/gateway/identityasserter/common/filter/CommonIdentityAssertionFilter.java: ########## @@ -62,286 +35,318 @@ import org.apache.knox.gateway.util.AuthorizationException; import org.apache.knox.gateway.util.HttpExceptionUtils; +import javax.security.auth.Subject; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.stream.Collectors; + +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; + 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); + 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 IdentityAsserterMessages LOG = MessagesFactory.get(IdentityAsserterMessages.class); + private static final String PRINCIPAL_PARAM = "user.name"; + private static final String DOAS_PRINCIPAL_PARAM = "doAs"; + /* List of all default and configured impersonation params */ + protected final List<String> impersonationParamsList = new ArrayList<>(); + private final Parser parser = new Parser(); + protected boolean impersonationEnabled; + private SimplePrincipalMapper mapper = new SimplePrincipalMapper(); + private VirtualGroupMapper virtualGroupMapper; + private AbstractSyntaxTree expressionPrincipalMapping; + private String topologyName; + + 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()); } - 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); - } + + private static String[] unique(String[] groups) { + return new HashSet<>(Arrays.asList(groups)).toArray(new String[0]); } - expressionPrincipalMapping = parseAdvancedPrincipalMapping(filterConfig); - final List<String> initParameterNames = AuthFilterUtils.getInitParameterNamesAsList(filterConfig); + @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); - virtualGroupMapper = new VirtualGroupMapper(loadVirtualGroups(filterConfig, initParameterNames)); + final List<String> initParameterNames = AuthFilterUtils.getInitParameterNamesAsList(filterConfig); - initImpersonationParamsList(filterConfig); - initProxyUserConfiguration(filterConfig, initParameterNames); - } + virtualGroupMapper = new VirtualGroupMapper(loadVirtualGroups(filterConfig, initParameterNames)); - private AbstractSyntaxTree parseAdvancedPrincipalMapping(FilterConfig filterConfig) { - String expression = filterConfig.getInitParameter(ADVANCED_PRINCIPAL_MAPPING); - if (StringUtils.isBlank(expression)) { - expression = filterConfig.getServletContext().getInitParameter(ADVANCED_PRINCIPAL_MAPPING); + initImpersonationParamsList(filterConfig); + initProxyUserConfiguration(filterConfig, initParameterNames); } - 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 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); } - /* 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); + /* + * 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); + } + + /* 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 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 initProxyUserConfiguration(FilterConfig filterConfig, List<String> initParameterNames) { + impersonationEnabled = shouldAddImpersonationProvider(filterConfig); + 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); + } } - } - boolean isImpersonationEnabled() { - return impersonationEnabled; - } + /** + * Check if the impersonation provider should be added to the filter chain based on + * user and group impersonation settings. + * + * @param filterConfig + * @return + */ + boolean shouldAddImpersonationProvider(final FilterConfig filterConfig) { Review Comment: @zeroflag yup! Issue Time Tracking ------------------- Worklog Id: (was: 970005) Time Spent: 1h 40m (was: 1.5h) > 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: 1h 40m > 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)