This is an automated email from the ASF dual-hosted git repository. liyang-kylin pushed a commit to branch kylin5 in repository https://gitbox.apache.org/repos/asf/kylin.git
commit 02be60a135261de2c2e827102a91d67bd4ad5ebf Author: Li Yang <[email protected]> AuthorDate: Fri Oct 2 15:10:53 2026 +0800 KYLIN-6109 improve table metadata authorization --- .../kylin/rest/service/SparkSourceService.java | 18 +++++++++--------- .../kylin/rest/service/SparkSourceServiceTest.java | 19 +++++++++++++++++++ .../engine/spark/source/NSparkMetadataExplorer.java | 11 +++++------ .../spark/source/NSparkMetadataExplorerTest.java | 21 +++++++++++++++++++++ 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/datasource-service/src/main/java/org/apache/kylin/rest/service/SparkSourceService.java b/src/datasource-service/src/main/java/org/apache/kylin/rest/service/SparkSourceService.java index d5287aa2cf..be842c89e4 100644 --- a/src/datasource-service/src/main/java/org/apache/kylin/rest/service/SparkSourceService.java +++ b/src/datasource-service/src/main/java/org/apache/kylin/rest/service/SparkSourceService.java @@ -44,6 +44,7 @@ import org.apache.kylin.rest.request.DDLRequest; import org.apache.kylin.rest.response.DDLResponse; import org.apache.kylin.rest.response.ExportTablesResponse; import org.apache.kylin.rest.response.TableNameResponse; +import org.apache.kylin.rest.source.DataSourceState; import org.apache.kylin.rest.util.AclEvaluate; import org.apache.kylin.source.ISourceMetadataExplorer; import org.apache.kylin.source.SourceFactory; @@ -55,7 +56,6 @@ import org.apache.spark.sql.SaveMode; import org.apache.spark.sql.SparderEnv; import org.apache.spark.sql.SparkSession; import org.apache.spark.sql.catalog.Database; -import org.apache.spark.sql.catalog.Table; import org.apache.spark.sql.catalyst.TableIdentifier; import org.apache.spark.sql.catalyst.catalog.CatalogTable; import org.apache.spark.sql.delta.DeltaTableUtils; @@ -146,15 +146,15 @@ public class SparkSourceService extends BasicService { } public List<TableNameResponse> listTables(String db, String project) throws Exception { - if (Strings.isNullOrEmpty(project)) { - SparkSession sparkSession = SparderEnv.getSparkSession(); - Dataset<Table> tableDataset = sparkSession.catalog().listTables(db); - List<Table> sparkTables = tableDataset.collectAsList(); - return sparkTables.stream() - .map(table -> new TableNameResponse(table.name().toUpperCase(Locale.ROOT), false)) - .collect(Collectors.toList()); + aclEvaluate.checkProjectReadPermission(project); + val projectInstance = getManager(NProjectManager.class).getProject(project); + List<String> hiveDatabases = DataSourceState.getInstance().getHiveFilterList(projectInstance); + if (!hiveDatabases.isEmpty() + && (Strings.isNullOrEmpty(db) || !hiveDatabases.contains(db.toUpperCase(Locale.ROOT)))) { + throw new KylinException(ServerErrorCode.PERMISSION_DENIED, + MsgPicker.getMsg().getAclPermissionRequired()); } - ISourceMetadataExplorer explr = SourceFactory.getSource(getManager(NProjectManager.class).getProject(project)) + ISourceMetadataExplorer explr = SourceFactory.getSource(projectInstance) .getSourceMetadataExplorer(); List<String> tables = explr.listTables(db).stream().map(str -> str.toUpperCase(Locale.ROOT)) .collect(Collectors.toList()); diff --git a/src/datasource-service/src/test/java/org/apache/kylin/rest/service/SparkSourceServiceTest.java b/src/datasource-service/src/test/java/org/apache/kylin/rest/service/SparkSourceServiceTest.java index e31dc52f17..1ebbed8c3b 100644 --- a/src/datasource-service/src/test/java/org/apache/kylin/rest/service/SparkSourceServiceTest.java +++ b/src/datasource-service/src/test/java/org/apache/kylin/rest/service/SparkSourceServiceTest.java @@ -195,6 +195,25 @@ public class SparkSourceServiceTest extends NLocalFileMetadataTestCase { @Test public void testListTables() throws Exception { Assert.assertEquals(11, sparkSourceService.listTables("DEFAULT", "default").size()); + Mockito.verify(aclEvaluate).checkProjectReadPermission("default"); + } + + @Test + public void testListTablesWithProjectReadPermissionDenied() { + Mockito.doThrow(new AccessDeniedException("Access is denied")).when(aclEvaluate) + .checkProjectReadPermission("default"); + Assert.assertThrows(AccessDeniedException.class, () -> sparkSourceService.listTables("DEFAULT", "default")); + } + + @Test + public void testListTablesOutsideProjectScope() { + ProjectInstance projectInstance = projectManager.getProject("default"); + LinkedHashMap<String, String> overrideKylinProps = projectInstance.getOverrideKylinProps(); + overrideKylinProps.put("kylin.source.hive.databases", "SSB"); + projectManager.updateProject(projectInstance, projectInstance.getName(), projectInstance.getDescription(), + overrideKylinProps); + + Assert.assertThrows(KylinException.class, () -> sparkSourceService.listTables("DEFAULT", "default")); } @Test diff --git a/src/spark-project/engine-spark/src/main/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorer.java b/src/spark-project/engine-spark/src/main/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorer.java index 25271a3a61..69207d6874 100644 --- a/src/spark-project/engine-spark/src/main/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorer.java +++ b/src/spark-project/engine-spark/src/main/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorer.java @@ -60,6 +60,7 @@ import org.apache.spark.sql.Row; import org.apache.spark.sql.SparderEnv; import org.apache.spark.sql.SparkSession; import org.apache.spark.sql.catalog.Database; +import org.apache.spark.sql.catalog.Table; import org.apache.spark.sql.catalyst.catalog.CatalogTableType; import org.apache.spark.sql.internal.SQLConf; import org.slf4j.Logger; @@ -131,12 +132,10 @@ public class NSparkMetadataExplorer implements ISourceMetadataExplorer, ISampleD List<String> tables = Lists.newArrayList(); try { - String sql = "show tables"; - if (StringUtils.isNotBlank(database)) { - sql = String.format(Locale.ROOT, sql + " in %s", database); - } - Dataset<Row> dataset = SparderEnv.getSparkSession().sql(sql).select("tableName"); - tables = dataset.collectAsList().stream().map(row -> row.getString(0)).collect(Collectors.toList()); + SparkSession sparkSession = SparderEnv.getSparkSession(); + Dataset<Table> dataset = StringUtils.isNotBlank(database) ? sparkSession.catalog().listTables(database) + : sparkSession.catalog().listTables(); + tables = dataset.collectAsList().stream().map(Table::name).collect(Collectors.toList()); if (config.getTableAccessFilterEnable() && config.getKerberosProjectLevelEnable() && UserGroupInformation.isSecurityEnabled()) { diff --git a/src/spark-project/engine-spark/src/test/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorerTest.java b/src/spark-project/engine-spark/src/test/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorerTest.java index f822324a6e..09d632bed7 100644 --- a/src/spark-project/engine-spark/src/test/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorerTest.java +++ b/src/spark-project/engine-spark/src/test/java/org/apache/kylin/engine/spark/source/NSparkMetadataExplorerTest.java @@ -17,6 +17,7 @@ */ package org.apache.kylin.engine.spark.source; +import java.util.Collections; import java.util.List; import org.apache.kylin.common.KylinConfig; @@ -26,9 +27,11 @@ import org.apache.kylin.engine.spark.NLocalWithSparkSessionTestBase; import org.apache.kylin.metadata.model.NTableMetadataManager; import org.apache.kylin.metadata.model.TableDesc; import org.apache.kylin.metadata.model.TableExtDesc; +import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SparderEnv; import org.apache.spark.sql.SparkSession; +import org.apache.spark.sql.catalog.Table; import org.apache.spark.sql.internal.SQLConf; import org.junit.Assert; import org.junit.Test; @@ -81,6 +84,24 @@ public class NSparkMetadataExplorerTest extends NLocalWithSparkSessionTestBase { Assert.assertEquals(tables.get(0), "part"); } + @Test + public void testListTablesUsesCatalogApi() throws Exception { + SparkSession sparkSession = Mockito.mock(SparkSession.class, Mockito.RETURNS_DEEP_STUBS); + Dataset<Table> dataset = Mockito.mock(Dataset.class); + Table table = Mockito.mock(Table.class); + Mockito.when(sparkSession.catalog().listTables("default")).thenReturn(dataset); + Mockito.when(dataset.collectAsList()).thenReturn(Collections.singletonList(table)); + Mockito.when(table.name()).thenReturn("safe_table"); + SparderEnv.setSparkSession(sparkSession); + try { + NSparkMetadataExplorer sparkMetadataExplorer = new NSparkMetadataExplorer(); + Assert.assertEquals(Collections.singletonList("safe_table"), sparkMetadataExplorer.listTables("default")); + Mockito.verify(sparkSession, Mockito.never()).sql(Mockito.anyString()); + } finally { + SparderEnv.setSparkSession(ss); + } + } + @Test public void testGetTableDesc() throws Exception { populateSSWithCSVData(getTestConfig(), "ssb", ss);
