[ 
https://issues.apache.org/jira/browse/KNOX-3048?focusedWorklogId=972305&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-972305
 ]

ASF GitHub Bot logged work on KNOX-3048:
----------------------------------------

                Author: ASF GitHub Bot
            Created on: 09/Jun/25 20:43
            Start Date: 09/Jun/25 20:43
    Worklog Time Spent: 10m 
      Work Description: pzampino commented on code in PR #1043:
URL: https://github.com/apache/knox/pull/1043#discussion_r2136472233


##########
gateway-spi/src/main/java/org/apache/knox/gateway/util/GroupBasedImpersonationProvider.java:
##########
@@ -0,0 +1,257 @@
+/*
+ * 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 GroupBasedImpersonationProvider 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 final Map<String, AccessControlList> proxyGroupsAcls = new 
HashMap<>();
+    private Map<String, MachineList> groupProxyHosts = new HashMap<>();
+    private String groupConfigPrefix;
+    private boolean doesProxyUserConfigExist = true;
+    static final String IMPERSONATION_ENABLED_PARAM = "impersonation.enabled";
+
+    public GroupBasedImpersonationProvider() {
+        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 */
+        final Map<String, String> filteredProps = 
Optional.ofNullable(getConf().getPropsWithPrefix(PROXYUSER_PREFIX + "."))
+                .orElse(Collections.emptyMap())  // handle null map defensively
+                .entrySet()
+                .stream()
+                .filter(entry -> 
!IMPERSONATION_ENABLED_PARAM.equals(entry.getKey())) // avoid NPE by reversing 
equals
+                .collect(Collectors.toMap(Map.Entry::getKey, 
Map.Entry::getValue));
+
+        doesProxyUserConfigExist = !filteredProps.isEmpty();
+
+        initGroupBasedProvider(PROXYGROUP_PREFIX);
+    }
+
+    private void initGroupBasedProvider(String proxyGroupPrefix) {
+        groupConfigPrefix = proxyGroupPrefix +
+                (proxyGroupPrefix.endsWith(".") ? "" : ".");
+
+        // constructing regex to match the following patterns:
+        //   $configPrefix.[ANY].users
+        //   $configPrefix.[ANY].groups
+        //   $configPrefix.[ANY].hosts
+        //
+        String prefixRegEx = groupConfigPrefix.replace(".", "\\.");
+        String usersGroupsRegEx = prefixRegEx + "[\\S]*(" +
+                Pattern.quote(CONF_USERS) + "|" + Pattern.quote(CONF_GROUPS) + 
")";
+        String hostsRegEx = prefixRegEx + "[\\S]*" + Pattern.quote(CONF_HOSTS);
+
+        // 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 {
+
+        /**
+         *  check for proxy user authorization only if PROXYUSER_PREFIX 
properties exist.
+         *  If proxy is configured then use those properties instead of 
group-based properties
+         *  given user based configs are more finegrained.
+         */
+        if (doesProxyUserConfigExist) {
+            super.authorize(user, remoteAddress);
+        } else{

Review Comment:
   Probably means catching AuthorizationException from the super.authorize() 
call, and continuing if there is group-level config. Perhaps, something like
   ```
   if(doesProxyUserConfigExist && doesProxyUserConfigExist(user)) {
        try {
           super.authorize(user, remoteAddress);
        } catch (AuthorizationException e) {
            if (!doesProxyGroupConfigExist) {
               throw e;
            }
        }
   }
   
   if (doesProxyGroupConfigExist) {
       checkProxyGroupAuthorization(user, remoteAddress, groups);
   }
   ```





Issue Time Tracking
-------------------

    Worklog Id:     (was: 972305)
    Time Spent: 4.5h  (was: 4h 20m)

> 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: 4.5h
>  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)

Reply via email to