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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7f5a8ae568 Provide user access to all tables except tableX (#14839)
7f5a8ae568 is described below

commit 7f5a8ae568b5127b42abaed4c8bd2cbe13766fd9
Author: Daniil Roman <[email protected]>
AuthorDate: Thu Feb 20 18:38:03 2025 +0100

    Provide user access to all tables except tableX (#14839)
---
 .../utils/config/AccessControlUserConfigUtils.java |  7 +++-
 .../helix/core/PinotHelixResourceManager.java      | 10 +++--
 .../apache/pinot/core/auth/BasicAuthPrincipal.java | 13 ++++++-
 .../org/apache/pinot/core/auth/BasicAuthUtils.java |  9 ++++-
 .../pinot/core/auth/ZkBasicAuthPrincipal.java      |  4 +-
 .../org/apache/pinot/core/auth/BasicAuthTest.java  | 43 +++++++++++++++-------
 .../apache/pinot/core/auth/ZkBasicAuthTest.java    | 29 ++++++++++-----
 .../apache/pinot/spi/config/user/UserConfig.java   | 11 ++++++
 .../pinot/spi/utils/builder/UserConfigBuilder.java |  8 +++-
 9 files changed, 99 insertions(+), 35 deletions(-)

diff --git 
a/pinot-common/src/main/java/org/apache/pinot/common/utils/config/AccessControlUserConfigUtils.java
 
b/pinot-common/src/main/java/org/apache/pinot/common/utils/config/AccessControlUserConfigUtils.java
index a31439cb2c..b910ca6187 100644
--- 
a/pinot-common/src/main/java/org/apache/pinot/common/utils/config/AccessControlUserConfigUtils.java
+++ 
b/pinot-common/src/main/java/org/apache/pinot/common/utils/config/AccessControlUserConfigUtils.java
@@ -46,6 +46,7 @@ public class AccessControlUserConfigUtils {
         String role = simpleFields.get(UserConfig.ROLE_KEY);
 
         List<String> tableList = znRecord.getListField(UserConfig.TABLES_KEY);
+        List<String> excludeTableList = 
znRecord.getListField(UserConfig.EXCLUDE_TABLES_KEY);
 
         List<String> permissionListFromZNRecord = 
znRecord.getListField(UserConfig.PERMISSIONS_KEY);
         List<AccessType> permissionList = null;
@@ -53,7 +54,7 @@ public class AccessControlUserConfigUtils {
             permissionList = permissionListFromZNRecord.stream()
                 .map(x -> AccessType.valueOf(x)).collect(Collectors.toList());
         }
-        return new UserConfig(username, password, component, role, tableList, 
permissionList);
+        return new UserConfig(username, password, component, role, tableList, 
excludeTableList, permissionList);
     }
 
     public static ZNRecord toZNRecord(UserConfig userConfig)
@@ -73,6 +74,10 @@ public class AccessControlUserConfigUtils {
         if (tableList != null) {
             listFields.put(UserConfig.TABLES_KEY, userConfig.getTables());
         }
+        List<String> excludeTableList = userConfig.getExcludeTables();
+        if (excludeTableList != null) {
+            listFields.put(UserConfig.EXCLUDE_TABLES_KEY, 
userConfig.getExcludeTables());
+        }
 
         List<AccessType> permissionList = userConfig.getPermissios();
         if (permissionList != null) {
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
index fd36bf44ea..5351d48063 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
@@ -1670,12 +1670,14 @@ public class PinotHelixResourceManager {
     if 
(CollectionUtils.isEmpty(ZKMetadataProvider.getAllUserName(_propertyStore))) {
       String initUsername = controllerConf.getInitAccessControlUsername();
       String initPassword = controllerConf.getInitAccessControlPassword();
-      addUser(new UserConfig(initUsername, initPassword, 
ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), null,
-          null));
+      addUser(new UserConfig(initUsername, initPassword, 
ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(),
+          null, null, null));
       addUser(
-          new UserConfig(initUsername, initPassword, 
ComponentType.BROKER.name(), RoleType.ADMIN.name(), null, null));
+          new UserConfig(initUsername, initPassword, 
ComponentType.BROKER.name(), RoleType.ADMIN.name(),
+              null, null, null));
       addUser(
-          new UserConfig(initUsername, initPassword, 
ComponentType.SERVER.name(), RoleType.ADMIN.name(), null, null));
+          new UserConfig(initUsername, initPassword, 
ComponentType.SERVER.name(), RoleType.ADMIN.name(),
+              null, null, null));
     }
   }
 
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthPrincipal.java 
b/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthPrincipal.java
index 20831a0257..f53fd8c495 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthPrincipal.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthPrincipal.java
@@ -29,12 +29,15 @@ public class BasicAuthPrincipal {
   private final String _name;
   private final String _token;
   private final Set<String> _tables;
+  private final Set<String> _excludeTables;
   private final Set<String> _permissions;
 
-  public BasicAuthPrincipal(String name, String token, Set<String> tables, 
Set<String> permissions) {
+  public BasicAuthPrincipal(String name, String token, Set<String> tables, 
Set<String> excludeTables,
+      Set<String> permissions) {
     _name = name;
     _token = token;
     _tables = tables;
+    _excludeTables = excludeTables;
     _permissions = permissions.stream().map(s -> 
s.toLowerCase()).collect(Collectors.toSet());
   }
 
@@ -47,9 +50,17 @@ public class BasicAuthPrincipal {
   }
 
   public boolean hasTable(String tableName) {
+    return isTableIncluded(tableName) && isTableNotExcluded(tableName);
+  }
+
+  private boolean isTableIncluded(String tableName) {
     return _tables.isEmpty() || _tables.contains(tableName);
   }
 
+  private boolean isTableNotExcluded(String tableName) {
+    return !_excludeTables.contains(tableName);
+  }
+
   public boolean hasPermission(String permission) {
     return _permissions.isEmpty() || 
_permissions.contains(permission.toLowerCase());
   }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthUtils.java 
b/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthUtils.java
index ab4caa91b3..94f6b6bd95 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthUtils.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/auth/BasicAuthUtils.java
@@ -37,6 +37,7 @@ public final class BasicAuthUtils {
   private static final String PASSWORD = "password";
   private static final String PERMISSIONS = "permissions";
   private static final String TABLES = "tables";
+  private static final String EXCLUDE_TABLES = "excludeTables";
   private static final String ALL = "*";
 
   private BasicAuthUtils() {
@@ -72,10 +73,11 @@ public final class BasicAuthUtils {
       Preconditions.checkArgument(StringUtils.isNotBlank(password), "must 
provide a password for %s", name);
 
       Set<String> tables = extractSet(configuration, prefix + "." + name + "." 
+ TABLES);
+      Set<String> excludeTables = extractSet(configuration, prefix + "." + 
name + "." + EXCLUDE_TABLES);
       Set<String> permissions = extractSet(configuration, prefix + "." + name 
+ "." + PERMISSIONS);
 
       return new BasicAuthPrincipal(name, 
org.apache.pinot.common.auth.BasicAuthUtils.toBasicAuthToken(name, password),
-          tables, permissions);
+          tables, excludeTables, permissions);
     }).collect(Collectors.toList());
   }
 
@@ -92,13 +94,16 @@ public final class BasicAuthUtils {
           Set<String> tables = Optional.ofNullable(user.getTables())
               .orElseGet(() -> Collections.emptyList())
               .stream().collect(Collectors.toSet());
+          Set<String> excludeTables = 
Optional.ofNullable(user.getExcludeTables())
+              .orElseGet(() -> Collections.emptyList())
+              .stream().collect(Collectors.toSet());
           Set<String> permissions = Optional.ofNullable(user.getPermissios())
               .orElseGet(() -> Collections.emptyList())
               .stream().map(x -> x.toString())
               .collect(Collectors.toSet());
           return new ZkBasicAuthPrincipal(name,
               
org.apache.pinot.common.auth.BasicAuthUtils.toBasicAuthToken(name, password), 
password,
-              component, role, tables, permissions);
+              component, role, tables, excludeTables, permissions);
         }).collect(Collectors.toList());
   }
 
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/auth/ZkBasicAuthPrincipal.java 
b/pinot-core/src/main/java/org/apache/pinot/core/auth/ZkBasicAuthPrincipal.java
index 93f97943ce..a4ee23e035 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/auth/ZkBasicAuthPrincipal.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/auth/ZkBasicAuthPrincipal.java
@@ -31,8 +31,8 @@ public class ZkBasicAuthPrincipal extends BasicAuthPrincipal {
     private final String _role;
 
     public ZkBasicAuthPrincipal(String name, String token, String password, 
String component, String role,
-        Set<String> tables, Set<String> permissions) {
-        super(name, token, tables, permissions);
+        Set<String> tables, Set<String> excludeTables, Set<String> 
permissions) {
+        super(name, token, tables, excludeTables, permissions);
         _component = component;
         _role = role;
         _password = password;
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/auth/BasicAuthTest.java 
b/pinot-core/src/test/java/org/apache/pinot/core/auth/BasicAuthTest.java
index bcadc8b886..20897c8ee7 100644
--- a/pinot-core/src/test/java/org/apache/pinot/core/auth/BasicAuthTest.java
+++ b/pinot-core/src/test/java/org/apache/pinot/core/auth/BasicAuthTest.java
@@ -19,6 +19,7 @@
 package org.apache.pinot.core.auth;
 
 import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -26,26 +27,40 @@ import org.testng.annotations.Test;
 public class BasicAuthTest {
 
   @Test
-  public void testBasicAuthPrincipal()
-      throws Exception {
-    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("READ"))
+  public void testBasicAuthPrincipal() {
+    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), Collections.emptySet(),
+        ImmutableSet.of("READ"))
         .hasTable("myTable"));
-    Assert.assertTrue(
-        new BasicAuthPrincipal("name", "token", ImmutableSet.of("myTable", 
"myTable1"), ImmutableSet.of("Read"))
-            .hasTable("myTable1"));
-    Assert.assertFalse(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("read"))
+    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable", "myTable1"),
+        Collections.emptySet(), ImmutableSet.of("Read"))
         .hasTable("myTable1"));
-    Assert.assertFalse(
-        new BasicAuthPrincipal("name", "token", ImmutableSet.of("myTable", 
"myTable1"), ImmutableSet.of("read"))
-            .hasTable("myTable2"));
+    Assert.assertFalse(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), Collections.emptySet(),
+        ImmutableSet.of("read"))
+        .hasTable("myTable1"));
+    Assert.assertFalse(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable", "myTable1"),
+        Collections.emptySet(), ImmutableSet.of("read"))
+        .hasTable("myTable2"));
+    Assert.assertFalse(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("myTable"),
+        ImmutableSet.of("read"))
+        .hasTable("myTable"));
+    Assert.assertFalse(new BasicAuthPrincipal("name", "token", 
Collections.emptySet(), ImmutableSet.of("myTable"),
+        ImmutableSet.of("read"))
+        .hasTable("myTable"));
+    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("myTable1"),
+        ImmutableSet.of("read"))
+        .hasTable("myTable"));
 
-    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("READ"))
+    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), Collections.emptySet(),
+        ImmutableSet.of("READ"))
         .hasPermission("read"));
-    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("Read"))
+    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), Collections.emptySet(),
+        ImmutableSet.of("Read"))
         .hasPermission("READ"));
-    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("read"))
+    Assert.assertTrue(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), Collections.emptySet(),
+        ImmutableSet.of("read"))
         .hasPermission("Read"));
-    Assert.assertFalse(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), ImmutableSet.of("read"))
+    Assert.assertFalse(new BasicAuthPrincipal("name", "token", 
ImmutableSet.of("myTable"), Collections.emptySet(),
+        ImmutableSet.of("read"))
         .hasPermission("write"));
   }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/auth/ZkBasicAuthTest.java 
b/pinot-core/src/test/java/org/apache/pinot/core/auth/ZkBasicAuthTest.java
index 274505a543..8ddecec764 100644
--- a/pinot-core/src/test/java/org/apache/pinot/core/auth/ZkBasicAuthTest.java
+++ b/pinot-core/src/test/java/org/apache/pinot/core/auth/ZkBasicAuthTest.java
@@ -19,6 +19,7 @@
 package org.apache.pinot.core.auth;
 
 import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
 import org.apache.pinot.spi.config.user.ComponentType;
 import org.apache.pinot.spi.config.user.RoleType;
 import org.testng.Assert;
@@ -28,32 +29,40 @@ import org.testng.annotations.Test;
 public class ZkBasicAuthTest {
 
     @Test
-    public void testBasicAuthPrincipal()
-        throws Exception {
+    public void testBasicAuthPrincipal() {
         Assert.assertTrue(new ZkBasicAuthPrincipal("name", "token", "password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable"),
-          ImmutableSet.of("READ")).hasTable("myTable"));
+            Collections.emptySet(), 
ImmutableSet.of("READ")).hasTable("myTable"));
         Assert.assertTrue(new ZkBasicAuthPrincipal("name", "token", "password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable", "myTable1"),
-          ImmutableSet.of("Read")).hasTable("myTable1"));
+            Collections.emptySet(), 
ImmutableSet.of("Read")).hasTable("myTable1"));
         Assert.assertFalse(new ZkBasicAuthPrincipal("name", "token", 
"password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable"),
-          ImmutableSet.of("read")).hasTable("myTable1"));
+            Collections.emptySet(), 
ImmutableSet.of("read")).hasTable("myTable1"));
         Assert.assertFalse(new ZkBasicAuthPrincipal("name", "token", 
"password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable", "myTable1"),
-          ImmutableSet.of("read")).hasTable("myTable2"));
+            Collections.emptySet(), 
ImmutableSet.of("read")).hasTable("myTable2"));
+        Assert.assertFalse(new ZkBasicAuthPrincipal("name", "token", 
"password",
+            ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable", "myTable1"),
+            ImmutableSet.of("myTable3"), 
ImmutableSet.of("Read")).hasTable("myTable3"));
+        Assert.assertTrue(new ZkBasicAuthPrincipal("name", "token", "password",
+            ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable", "myTable1"),
+            ImmutableSet.of("myTable"), 
ImmutableSet.of("read")).hasTable("myTable1"));
+        Assert.assertFalse(new ZkBasicAuthPrincipal("name", "token", 
"password",
+            ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
Collections.emptySet(),
+            ImmutableSet.of("myTable"), 
ImmutableSet.of("read")).hasTable("myTable"));
 
         Assert.assertTrue(new ZkBasicAuthPrincipal("name", "token", "password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable"),
-          ImmutableSet.of("READ")).hasPermission("read"));
+            Collections.emptySet(), 
ImmutableSet.of("READ")).hasPermission("read"));
         Assert.assertTrue(new ZkBasicAuthPrincipal("name", "token", "password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable"),
-          ImmutableSet.of("Read")).hasPermission("READ"));
+            Collections.emptySet(), 
ImmutableSet.of("Read")).hasPermission("READ"));
         Assert.assertTrue(new ZkBasicAuthPrincipal("name", "token", "password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable"),
-          ImmutableSet.of("read")).hasPermission("Read"));
+            Collections.emptySet(), 
ImmutableSet.of("read")).hasPermission("Read"));
         Assert.assertFalse(new ZkBasicAuthPrincipal("name", "token", 
"password",
           ComponentType.CONTROLLER.name(), RoleType.ADMIN.name(), 
ImmutableSet.of("myTable"),
-          ImmutableSet.of("read")).hasPermission("write"));
+            Collections.emptySet(), 
ImmutableSet.of("read")).hasPermission("write"));
     }
 }
diff --git 
a/pinot-spi/src/main/java/org/apache/pinot/spi/config/user/UserConfig.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/config/user/UserConfig.java
index bfe9d0a8c9..cd05cd7d83 100644
--- a/pinot-spi/src/main/java/org/apache/pinot/spi/config/user/UserConfig.java
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/config/user/UserConfig.java
@@ -35,6 +35,7 @@ public class UserConfig extends BaseJsonConfig {
     public static final String ROLE_KEY = "role";
     public static final String AUTH_TOKEN_KEY = "authToken";
     public static final String TABLES_KEY = "tables";
+    public static final String EXCLUDE_TABLES_KEY = "excludeTables";
     public static final String PERMISSIONS_KEY = "permissions";
 
     @JsonPropertyDescription("The name of User")
@@ -52,6 +53,9 @@ public class UserConfig extends BaseJsonConfig {
     @JsonPropertyDescription("The tables owned of User")
     private List<String> _tables;
 
+    @JsonPropertyDescription("The tables excluded for User")
+    private List<String> _excludeTables;
+
     @JsonPropertyDescription("The table permission of User")
     private List<AccessType> _permissions;
 
@@ -61,6 +65,7 @@ public class UserConfig extends BaseJsonConfig {
         @JsonProperty(value = COMPONET_KEY, required = true) String component,
         @JsonProperty(value = ROLE_KEY, required = true) String role,
         @JsonProperty(value = TABLES_KEY) @Nullable List<String> tableList,
+        @JsonProperty(value = EXCLUDE_TABLES_KEY) @Nullable List<String> 
excludeTableList,
         @JsonProperty(value = PERMISSIONS_KEY) @Nullable List<AccessType> 
permissionList
     ) {
         Preconditions.checkArgument(username != null, "'username' must be 
configured");
@@ -72,6 +77,7 @@ public class UserConfig extends BaseJsonConfig {
         _componentType = ComponentType.valueOf(component.toUpperCase());
         _roleType = RoleType.valueOf(role.toUpperCase());
         _tables = tableList;
+        _excludeTables = excludeTableList;
         _permissions = permissionList;
     }
 
@@ -98,6 +104,11 @@ public class UserConfig extends BaseJsonConfig {
         return _tables;
     }
 
+    @JsonProperty(EXCLUDE_TABLES_KEY)
+    public List<String> getExcludeTables() {
+        return _excludeTables;
+    }
+
     @JsonProperty(PERMISSIONS_KEY)
     public List<AccessType> getPermissios() {
         return _permissions;
diff --git 
a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/builder/UserConfigBuilder.java
 
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/builder/UserConfigBuilder.java
index 5afbf639ac..c2c70c9f75 100644
--- 
a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/builder/UserConfigBuilder.java
+++ 
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/builder/UserConfigBuilder.java
@@ -31,6 +31,7 @@ public class UserConfigBuilder {
     private String _password;
     private RoleType _roleType;
     private List<String> _tableList;
+    private List<String> _excludeTableList;
     private List<AccessType> _permissionList;
 
     public UserConfigBuilder setComponentType(ComponentType componentType) {
@@ -58,6 +59,11 @@ public class UserConfigBuilder {
         return this;
     }
 
+    public UserConfigBuilder setExcludeTableList(List<String> 
excludeTableList) {
+        _excludeTableList = excludeTableList;
+        return this;
+    }
+
     public UserConfigBuilder setPermissionList(List<AccessType> 
permissionList) {
         _permissionList = permissionList;
         return this;
@@ -65,6 +71,6 @@ public class UserConfigBuilder {
 
     public UserConfig build() {
         return new UserConfig(_username, _password, _componentType.toString(), 
_roleType.toString(), _tableList,
-            _permissionList);
+            _excludeTableList, _permissionList);
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to