fix(dameng): list all schemas in object browse for normal users

Closes #7699
This commit is contained in:
t8y2
2026-09-06 14:37:01 +08:00
parent 260c6710d8
commit b9ea54350d
2 changed files with 94 additions and 1 deletions
@@ -461,7 +461,21 @@ public final class DamengAgent extends AbstractJdbcAgent {
if (legacyJdbcMetadata) {
return unchecked(() -> listJdbcSchemas().stream().map(DatabaseInfo::new).toList());
}
return unchecked(() -> listVisibleUsers().stream().map(DatabaseInfo::new).toList());
// DM8 ALL_USERS is privilege-filtered: a normal user only sees itself, so prefer the
// full SYS.SYSOBJECTS catalog (mirroring listSchemas; newer/hardened builds require
// the SOI role) and fall back to ALL_USERS when the catalog is not readable.
return unchecked(() -> {
try {
return listVisibleSchemas().stream().map(DatabaseInfo::new).toList();
} catch (SQLException catalogError) {
try {
return listVisibleUsers().stream().map(DatabaseInfo::new).toList();
} catch (Exception fallbackError) {
catalogError.addSuppressed(fallbackError);
throw catalogError;
}
}
});
}
@Override
@@ -52,6 +52,48 @@ class DamengAgentMetadataTest {
Assertions.assertTrue(metadataCalls.contains("getTables:DBX\\_TEST"), metadataCalls.toString());
}
@Test
void listDatabasesPrefersFullSchemaCatalogOverPrivilegeFilteredAllUsers() {
DamengAgent agent = new DamengAgent();
List<String> sqls = new ArrayList<>();
TestSupport.setPrivateConnection(agent, databaseListConnection(sqls, null, null));
List<DatabaseInfo> databases = agent.listDatabases();
Assertions.assertEquals(List.of("APP", "E2E_NORMAL", "SYSDBA"), databases.stream().map(DatabaseInfo::getName).toList());
Assertions.assertEquals(1, sqls.size(), String.join("\n", sqls));
Assertions.assertTrue(sqls.get(0).contains("SYS.SYSOBJECTS"), sqls.get(0));
Assertions.assertTrue(sqls.stream().noneMatch(sql -> sql.contains("ALL_USERS")), String.join("\n", sqls));
}
@Test
void listDatabasesFallsBackToAllUsersWithoutSysObjectsPrivilege() {
DamengAgent agent = new DamengAgent();
List<String> sqls = new ArrayList<>();
TestSupport.setPrivateConnection(agent, databaseListConnection(sqls, "no SYS.SYSOBJECTS privilege", null));
List<DatabaseInfo> databases = agent.listDatabases();
// 无 SOI 角色的普通用户:ALL_USERS 受自主访问控制过滤,只能看到自己,按权限返回属正确行为。
Assertions.assertEquals(List.of("E2E_NORMAL"), databases.stream().map(DatabaseInfo::getName).toList());
Assertions.assertEquals(2, sqls.size(), String.join("\n", sqls));
Assertions.assertTrue(sqls.get(0).contains("SYS.SYSOBJECTS"), sqls.get(0));
Assertions.assertTrue(sqls.get(1).contains("ALL_USERS"), sqls.get(1));
}
@Test
void listDatabasesPreservesCatalogErrorWhenAllUsersFails() {
DamengAgent agent = new DamengAgent();
SQLException usersError = new SQLException("ALL_USERS query failed");
TestSupport.setPrivateConnection(agent, databaseListConnection(new ArrayList<>(), "no SYS.SYSOBJECTS privilege", usersError));
RuntimeException error = Assertions.assertThrows(RuntimeException.class, agent::listDatabases);
Assertions.assertEquals("no SYS.SYSOBJECTS privilege", error.getCause().getMessage());
Assertions.assertEquals(1, error.getCause().getSuppressed().length);
Assertions.assertSame(usersError, error.getCause().getSuppressed()[0]);
}
@Test
void usesColumnCommentsMetadataQuery() {
DamengAgent agent = new DamengAgent();
@@ -1890,6 +1932,43 @@ class DamengAgentMetadataTest {
});
}
private static Connection databaseListConnection(
List<String> sqls,
String catalogError,
SQLException usersError
) {
return proxy(Connection.class, (method, args) -> {
String name = method.getName();
if ("prepareStatement".equals(name)) {
String sql = (String) args[0];
sqls.add(sql);
if (sql.contains("SYS.SYSOBJECTS")) {
if (catalogError != null) {
return failingMetadataStatement(catalogError);
}
return metadataStatement(List.of(List.of("APP"), List.of("E2E_NORMAL"), List.of("SYSDBA")));
}
if (sql.contains("ALL_USERS")) {
if (usersError != null) {
return failingMetadataStatement(usersError);
}
return metadataStatement(List.of(List.of("E2E_NORMAL")));
}
throw new AssertionError("Unexpected SQL: " + sql);
}
if ("getMetaData".equals(name)) {
throw new AssertionError("listDatabases must not fall back to JDBC metadata getSchemas");
}
if ("close".equals(name)) {
return null;
}
if ("isClosed".equals(name)) {
return false;
}
return defaultValue(method.getReturnType());
});
}
private static Connection schemaConnection(List<String> params) {
return proxy(Connection.class, (method, args) -> {
String name = method.getName();