Skip to content

Commit e6a2844

Browse files
authored
[Improvement-18072][Api] Add user permission validation logic to the connectionTest, getDatabases, getTables, and getTableColumns methods in DataSourceController (#18073)
1 parent b090fee commit e6a2844

7 files changed

Lines changed: 128 additions & 54 deletions

File tree

dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/DataSourceController.java

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public Result<Boolean> connectDataSource(@Parameter(hidden = true) @RequestAttri
225225
*
226226
* @param loginUser login user
227227
* @param id data source id
228-
* @return connect result code
228+
* @return A Result wrapping {@code true} if the connection is successful; otherwise, throws an exception.
229229
*/
230230
@Operation(summary = "connectionTest", description = "CONNECT_DATA_SOURCE_TEST_NOTES")
231231
@Parameters({
@@ -236,7 +236,7 @@ public Result<Boolean> connectDataSource(@Parameter(hidden = true) @RequestAttri
236236
@ApiException(CONNECTION_TEST_FAILURE)
237237
public Result<Boolean> connectionTest(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
238238
@PathVariable("id") int id) {
239-
dataSourceService.connectionTest(id);
239+
dataSourceService.connectionTest(loginUser, id);
240240
return Result.success(true);
241241
}
242242

@@ -337,6 +337,14 @@ public Result<Object> getKerberosStartupState(@Parameter(hidden = true) @Request
337337
return success(Status.SUCCESS.getMsg(), CommonUtils.getKerberosStartupState());
338338
}
339339

340+
/**
341+
* Retrieves the list of tables within a specific database of a data source.
342+
*
343+
* @param loginUser the current logged-in user (injected from session)
344+
* @param datasourceId the unique identifier of the data source
345+
* @param database the name of the database to query
346+
* @return a list of table names/options accessible to the user
347+
*/
340348
@Operation(summary = "tables", description = "GET_DATASOURCE_TABLES_NOTES")
341349
@Parameters({
342350
@Parameter(name = "datasourceId", description = "DATA_SOURCE_ID", required = true, schema = @Schema(implementation = int.class, example = "1")),
@@ -345,37 +353,56 @@ public Result<Object> getKerberosStartupState(@Parameter(hidden = true) @Request
345353
@GetMapping(value = "/tables")
346354
@ResponseStatus(HttpStatus.OK)
347355
@ApiException(GET_DATASOURCE_TABLES_ERROR)
348-
public Result<Object> getTables(@RequestParam("datasourceId") Integer datasourceId,
349-
@RequestParam(value = "database") String database) {
350-
List<ParamsOptions> options = dataSourceService.getTables(datasourceId, database);
356+
public Result<Object> getTables(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
357+
@RequestParam("datasourceId") Integer datasourceId,
358+
@RequestParam("database") String database) {
359+
List<ParamsOptions> options = dataSourceService.getTables(loginUser, datasourceId, database);
351360
return Result.success(options);
352361
}
353362

363+
/**
364+
* Retrieves the column details (schema) for a specific table.
365+
*
366+
* @param loginUser the current logged-in user (injected from session)
367+
* @param datasourceId the unique identifier of the data source
368+
* @param database the name of the database containing the table
369+
* @param tableName the name of the table to query columns for
370+
* @return a list of column definitions (name, type, etc.) for the specified table
371+
*/
354372
@Operation(summary = "tableColumns", description = "GET_DATASOURCE_TABLE_COLUMNS_NOTES")
355373
@Parameters({
356374
@Parameter(name = "datasourceId", description = "DATA_SOURCE_ID", required = true, schema = @Schema(implementation = int.class, example = "1")),
357-
@Parameter(name = "tableName", description = "TABLE_NAME", required = true, schema = @Schema(implementation = String.class, example = "test")),
358-
@Parameter(name = "database", description = "DATABASE", required = true, schema = @Schema(implementation = String.class, example = "test"))
375+
@Parameter(name = "database", description = "DATABASE", required = true, schema = @Schema(implementation = String.class, example = "test")),
376+
@Parameter(name = "tableName", description = "TABLE_NAME", required = true, schema = @Schema(implementation = String.class, example = "test"))
359377
})
360378
@GetMapping(value = "/tableColumns")
361379
@ResponseStatus(HttpStatus.OK)
362380
@ApiException(GET_DATASOURCE_TABLE_COLUMNS_ERROR)
363-
public Result<Object> getTableColumns(@RequestParam("datasourceId") Integer datasourceId,
364-
@RequestParam("tableName") String tableName,
365-
@RequestParam(value = "database") String database) {
366-
List<ParamsOptions> options = dataSourceService.getTableColumns(datasourceId, database, tableName);
381+
public Result<Object> getTableColumns(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
382+
@RequestParam("datasourceId") Integer datasourceId,
383+
@RequestParam("database") String database,
384+
@RequestParam("tableName") String tableName) {
385+
List<ParamsOptions> options = dataSourceService.getTableColumns(loginUser, datasourceId, database, tableName);
367386
return Result.success(options);
368387
}
369388

389+
/**
390+
* Retrieves the list of databases available in a specific data source.
391+
*
392+
* @param loginUser the current logged-in user (injected from session)
393+
* @param datasourceId the unique identifier of the data source
394+
* @return a list of database names/options accessible to the user
395+
*/
370396
@Operation(summary = "databases", description = "GET_DATASOURCE_DATABASE_NOTES")
371397
@Parameters({
372398
@Parameter(name = "datasourceId", description = "DATA_SOURCE_ID", required = true, schema = @Schema(implementation = int.class, example = "1"))
373399
})
374400
@GetMapping(value = "/databases")
375401
@ResponseStatus(HttpStatus.OK)
376402
@ApiException(GET_DATASOURCE_DATABASES_ERROR)
377-
public Result<Object> getDatabases(@RequestParam("datasourceId") Integer datasourceId) {
378-
List<ParamsOptions> options = dataSourceService.getDatabases(datasourceId);
403+
public Result<Object> getDatabases(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
404+
@RequestParam("datasourceId") Integer datasourceId) {
405+
List<ParamsOptions> options = dataSourceService.getDatabases(loginUser, datasourceId);
379406
return Result.success(options);
380407
}
381408
}

dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/DataSourceService.java

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.dolphinscheduler.api.service;
1919

20+
import org.apache.dolphinscheduler.api.exceptions.ServiceException;
2021
import org.apache.dolphinscheduler.api.utils.PageInfo;
2122
import org.apache.dolphinscheduler.dao.entity.DataSource;
2223
import org.apache.dolphinscheduler.dao.entity.User;
@@ -96,12 +97,13 @@ public interface DataSourceService {
9697
void checkConnection(DbType type, ConnectionParam parameter);
9798

9899
/**
99-
* test connection
100+
* Tests the connectivity of a specific data source.
100101
*
101-
* @param id datasource id
102-
* @return connect result code
102+
* @param loginUser the current logged-in user (required for permission check)
103+
* @param id the unique identifier of the data source to test
104+
* @throws ServiceException if the resource doesn't exist, permission is denied, or connection fails
103105
*/
104-
void connectionTest(int id);
106+
void connectionTest(User loginUser, int id);
105107

106108
/**
107109
* delete datasource
@@ -131,26 +133,36 @@ public interface DataSourceService {
131133
List<DataSource> authedDatasource(User loginUser, Integer userId);
132134

133135
/**
134-
* get tables
135-
* @param datasourceId
136-
* @param database
137-
* @return
136+
* Retrieves the list of tables from a specific database within a data source.
137+
*
138+
* @param loginUser the current logged-in user (required for permission check)
139+
* @param datasourceId the unique identifier of the data source
140+
* @param database the specific database/schema name to query (nullable for some DB types like SQLite)
141+
* @return a list of {@link ParamsOptions} containing table names and optional metadata (e.g., comments)
142+
* @throws ServiceException if permission denied, resource not found, or connection fails
138143
*/
139-
List<ParamsOptions> getTables(Integer datasourceId, String database);
144+
List<ParamsOptions> getTables(User loginUser, Integer datasourceId, String database);
140145

141146
/**
142-
* get table columns
143-
* @param datasourceId
144-
* @param database
145-
* @param tableName
146-
* @return
147+
* Retrieves the list of columns for a specific table in a data source.
148+
*
149+
* @param loginUser current logged-in user
150+
* @param datasourceId ID of the data source
151+
* @param database database/schema name
152+
* @param tableName table name to query
153+
* @return list of {@link ParamsOptions} representing column names and types
154+
* @throws ServiceException if permission denied, resource not found, or connection fails
147155
*/
148-
List<ParamsOptions> getTableColumns(Integer datasourceId, String database, String tableName);
156+
List<ParamsOptions> getTableColumns(User loginUser, Integer datasourceId, String database, String tableName);
149157

150158
/**
151-
* get databases
152-
* @param datasourceId
153-
* @return
159+
* Retrieves the list of databases (or schemas) available in a specific data source.
160+
*
161+
* @param loginUser current logged-in user
162+
* @param datasourceId ID of the data source
163+
* @return list of {@link ParamsOptions} representing database/schema names
164+
* @throws ServiceException if permission denied, resource not found, or connection fails
154165
*/
155-
List<ParamsOptions> getDatabases(Integer datasourceId);
166+
List<ParamsOptions> getDatabases(User loginUser, Integer datasourceId);
167+
156168
}

dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/DataSourceServiceImpl.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -330,18 +330,19 @@ public void checkConnection(DbType type, ConnectionParam connectionParam) {
330330
throw new ServiceException(Status.CONNECTION_TEST_FAILURE);
331331
}
332332

333-
/**
334-
* test connection
335-
*
336-
* @param id datasource id
337-
* @return connect result code
338-
*/
339333
@Override
340-
public void connectionTest(int id) {
334+
public void connectionTest(User loginUser, int id) {
341335
DataSource dataSource = dataSourceMapper.selectById(id);
336+
342337
if (dataSource == null) {
343338
throw new ServiceException(Status.RESOURCE_NOT_EXIST);
344339
}
340+
341+
if (!canOperatorPermissions(loginUser, new Object[]{id}, AuthorizationType.DATASOURCE,
342+
ApiFuncIdentificationConstant.DATASOURCE)) {
343+
throw new ServiceException(Status.USER_NO_OPERATION_PERM);
344+
}
345+
345346
checkConnection(dataSource.getType(),
346347
DataSourceUtils.buildConnectionParams(dataSource.getType(), dataSource.getConnectionParams()));
347348
}
@@ -417,9 +418,18 @@ public List<DataSource> authedDatasource(User loginUser, Integer userId) {
417418
}
418419

419420
@Override
420-
public List<ParamsOptions> getTables(Integer datasourceId, String database) {
421+
public List<ParamsOptions> getTables(User loginUser, Integer datasourceId, String database) {
421422
DataSource dataSource = dataSourceMapper.selectById(datasourceId);
422423

424+
if (dataSource == null) {
425+
throw new ServiceException(Status.QUERY_DATASOURCE_ERROR);
426+
}
427+
428+
if (!canOperatorPermissions(loginUser, new Object[]{datasourceId}, AuthorizationType.DATASOURCE,
429+
ApiFuncIdentificationConstant.DATASOURCE)) {
430+
throw new ServiceException(Status.USER_NO_OPERATION_PERM);
431+
}
432+
423433
List<String> tableList;
424434
BaseConnectionParam connectionParam =
425435
(BaseConnectionParam) DataSourceUtils.buildConnectionParams(
@@ -477,8 +487,19 @@ public List<ParamsOptions> getTables(Integer datasourceId, String database) {
477487
}
478488

479489
@Override
480-
public List<ParamsOptions> getTableColumns(Integer datasourceId, String database, String tableName) {
490+
public List<ParamsOptions> getTableColumns(User loginUser, Integer datasourceId, String database,
491+
String tableName) {
481492
DataSource dataSource = dataSourceMapper.selectById(datasourceId);
493+
494+
if (dataSource == null) {
495+
throw new ServiceException(Status.QUERY_DATASOURCE_ERROR);
496+
}
497+
498+
if (!canOperatorPermissions(loginUser, new Object[]{datasourceId}, AuthorizationType.DATASOURCE,
499+
ApiFuncIdentificationConstant.DATASOURCE)) {
500+
throw new ServiceException(Status.USER_NO_OPERATION_PERM);
501+
}
502+
482503
BaseConnectionParam connectionParam =
483504
(BaseConnectionParam) DataSourceUtils.buildConnectionParams(
484505
dataSource.getType(),
@@ -523,14 +544,19 @@ public List<ParamsOptions> getTableColumns(Integer datasourceId, String database
523544
}
524545

525546
@Override
526-
public List<ParamsOptions> getDatabases(Integer datasourceId) {
547+
public List<ParamsOptions> getDatabases(User loginUser, Integer datasourceId) {
527548

528549
DataSource dataSource = dataSourceMapper.selectById(datasourceId);
529550

530551
if (dataSource == null) {
531552
throw new ServiceException(Status.QUERY_DATASOURCE_ERROR);
532553
}
533554

555+
if (!canOperatorPermissions(loginUser, new Object[]{datasourceId}, AuthorizationType.DATASOURCE,
556+
ApiFuncIdentificationConstant.DATASOURCE)) {
557+
throw new ServiceException(Status.USER_NO_OPERATION_PERM);
558+
}
559+
534560
List<String> tableList;
535561
BaseConnectionParam connectionParam =
536562
(BaseConnectionParam) DataSourceUtils.buildConnectionParams(

dolphinscheduler-api/src/main/resources/i18n/messages.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ DELETE_DATA_SOURCE_NOTES=delete data source
224224
VERIFY_DATA_SOURCE_NOTES=verify data source
225225
UNAUTHORIZED_DATA_SOURCE_NOTES=unauthorized data source
226226
AUTHORIZED_DATA_SOURCE_NOTES=authorized data source
227+
GET_DATASOURCE_DATABASE_NOTES=get datasource databases
228+
GET_DATASOURCE_TABLES_NOTES=get datasource tables
229+
GET_DATASOURCE_TABLE_COLUMNS_NOTES=get datasource table columns
227230
DELETE_SCHEDULE_NOTES=delete schedule by id
228231
QUERY_ALERT_GROUP_LIST_PAGING_NOTES=query alert group list paging
229232
QUERY_AUTHORIZED_AND_USER_CREATED_PROJECT_NOTES= query authorized and user created project
@@ -234,8 +237,6 @@ QUERY_WORKFLOW_DEFINITION_VERSIONS_NOTES=query workflow definition versions
234237
SWITCH_WORKFLOW_DEFINITION_VERSION_NOTES=switch workflow definition version
235238
VERSION=version
236239
STATE=state
237-
GET_DATASOURCE_TABLES_NOTES=get datasource table
238-
GET_DATASOURCE_TABLE_COLUMNS_NOTES=get datasource table columns
239240
TABLE_NAME=table name
240241
AUDIT_LOG_TAG=audit log related operation
241242
TASK_DEFINITION_TAG=task definition related operation

dolphinscheduler-api/src/main/resources/i18n/messages_en_US.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ DELETE_DATA_SOURCE_NOTES=delete data source
257257
VERIFY_DATA_SOURCE_NOTES=verify data source
258258
UNAUTHORIZED_DATA_SOURCE_NOTES=unauthorized data source
259259
AUTHORIZED_DATA_SOURCE_NOTES=authorized data source
260+
GET_DATASOURCE_DATABASE_NOTES=get datasource databases
261+
GET_DATASOURCE_TABLES_NOTES=get datasource tables
262+
GET_DATASOURCE_TABLE_COLUMNS_NOTES=get datasource table columns
260263
DELETE_SCHEDULE_NOTES=delete schedule by id
261264
QUERY_ALERT_GROUP_LIST_PAGING_NOTES=query alert group list paging
262265
QUERY_AUTHORIZED_AND_USER_CREATED_PROJECT_NOTES=query authorized and user created project
@@ -267,8 +270,6 @@ QUERY_WORKFLOW_DEFINITION_VERSIONS_NOTES=query process definition versions
267270
SWITCH_WORKFLOW_DEFINITION_VERSION_NOTES=switch process definition version
268271
VERSION=version
269272
TASK_GROUP_QUEUE_PRIORITY=task group queue priority
270-
GET_DATASOURCE_TABLES_NOTES=get datasource table
271-
GET_DATASOURCE_TABLE_COLUMNS_NOTES=get datasource table columns
272273
TABLE_NAME=table name
273274
QUERY_AUDIT_LOG=query audit log
274275
AUDIT_LOG_TAG=audit log related operation

dolphinscheduler-api/src/main/resources/i18n/messages_zh_CN.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ DELETE_DATA_SOURCE_NOTES=\u5220\u9664\u6570\u636E\u6E90
256256
VERIFY_DATA_SOURCE_NOTES=\u9A8C\u8BC1\u6570\u636E\u6E90
257257
UNAUTHORIZED_DATA_SOURCE_NOTES=\u672A\u6388\u6743\u7684\u6570\u636E\u6E90
258258
AUTHORIZED_DATA_SOURCE_NOTES=\u6388\u6743\u7684\u6570\u636E\u6E90
259+
GET_DATASOURCE_DATABASE_NOTES=\u83B7\u53D6\u6570\u636E\u6E90\u5E93\u5217\u8868
260+
GET_DATASOURCE_TABLES_NOTES=\u83B7\u53D6\u6570\u636E\u6E90\u8868\u5217\u8868
261+
GET_DATASOURCE_TABLE_COLUMNS_NOTES=\u83B7\u53D6\u6570\u636E\u6E90\u8868\u5217\u540D
259262
DELETE_SCHEDULE_NOTES=\u6839\u636E\u5B9A\u65F6id\u5220\u9664\u5B9A\u65F6\u6570\u636E
260263
QUERY_ALERT_GROUP_LIST_PAGING_NOTES=\u5206\u9875\u67E5\u8BE2\u544A\u8B66\u7EC4\u5217\u8868
261264
QUERY_AUTHORIZED_AND_USER_CREATED_PROJECT_NOTES=\u67E5\u8BE2\u6388\u6743\u548C\u7528\u6237\u521B\u5EFA\u7684\u9879\u76EE
@@ -266,8 +269,6 @@ QUERY_WORKFLOW_DEFINITION_VERSIONS_NOTES=\u67E5\u8BE2\u6D41\u7A0B\u5386\u53F2\u7
266269
SWITCH_WORKFLOW_DEFINITION_VERSION_NOTES=\u5207\u6362\u6D41\u7A0B\u7248\u672C
267270
VERSION=\u7248\u672C\u53F7
268271
TASK_GROUP_QUEUE_PRIORITY=\u4EFB\u52A1\u961F\u5217\u4F18\u5148\u7EA7
269-
GET_DATASOURCE_TABLES_NOTES=\u83B7\u53D6\u6570\u636E\u6E90\u8868\u5217\u8868
270-
GET_DATASOURCE_TABLE_COLUMNS_NOTES=\u83B7\u53D6\u6570\u636E\u6E90\u8868\u5217\u540D
271272
TABLE_NAME=\u8868\u540D
272273
QUERY_AUDIT_LOG=\u67E5\u8BE2\u5BA1\u8BA1\u65E5\u5FD7
273274
AUDIT_LOG_TAG=\u5BA1\u8BA1\u65E5\u5FD7\u6267\u884C\u76F8\u5173\u64CD\u4F5C

dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/DataSourceServiceTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,11 @@ public void testQueryDataSourceListPaging() {
268268

269269
@Test
270270
public void testConnectionTest() {
271+
User loginUser = getAdminUser();
271272
int dataSourceId = -1;
272273
when(dataSourceMapper.selectById(dataSourceId)).thenReturn(null);
273-
assertThrowsServiceException(Status.RESOURCE_NOT_EXIST, () -> dataSourceService.connectionTest(dataSourceId));
274+
assertThrowsServiceException(Status.RESOURCE_NOT_EXIST,
275+
() -> dataSourceService.connectionTest(loginUser, dataSourceId));
274276

275277
try (
276278
MockedStatic<DataSourceUtils> ignored =
@@ -281,11 +283,12 @@ public void testConnectionTest() {
281283

282284
when(DataSourceUtils.getDatasourceProcessor(Mockito.any())).thenReturn(dataSourceProcessor);
283285
when(dataSourceProcessor.checkDataSourceConnectivity(Mockito.any())).thenReturn(true);
284-
assertDoesNotThrow(() -> dataSourceService.connectionTest(dataSource.getId()));
286+
passResourcePermissionCheckService();
287+
assertDoesNotThrow(() -> dataSourceService.connectionTest(loginUser, dataSource.getId()));
285288

286289
when(dataSourceProcessor.checkDataSourceConnectivity(Mockito.any())).thenReturn(false);
287290
assertThrowsServiceException(Status.CONNECTION_TEST_FAILURE,
288-
() -> dataSourceService.connectionTest(dataSource.getId()));
291+
() -> dataSourceService.connectionTest(loginUser, dataSource.getId()));
289292
}
290293

291294
}
@@ -605,13 +608,15 @@ public void testCheckConnection() throws Exception {
605608

606609
@Test
607610
public void testGetDatabases() throws SQLException {
611+
User loginUser = getAdminUser();
612+
608613
DataSource dataSource = getOracleDataSource();
609614
int datasourceId = 1;
610615
dataSource.setId(datasourceId);
611616
when(dataSourceMapper.selectById(datasourceId)).thenReturn(null);
612617

613618
try {
614-
dataSourceService.getDatabases(datasourceId);
619+
dataSourceService.getDatabases(loginUser, datasourceId);
615620
} catch (Exception e) {
616621
Assertions.assertTrue(e.getMessage().contains(Status.QUERY_DATASOURCE_ERROR.getMsg()));
617622
}
@@ -623,9 +628,10 @@ public void testGetDatabases() throws SQLException {
623628
dataSourceUtils.when(() -> DataSourceUtils.getConnection(Mockito.any(), Mockito.any())).thenReturn(connection);
624629
dataSourceUtils.when(() -> DataSourceUtils.buildConnectionParams(Mockito.any(), Mockito.any()))
625630
.thenReturn(connectionParam);
631+
passResourcePermissionCheckService();
626632

627633
try {
628-
dataSourceService.getDatabases(datasourceId);
634+
dataSourceService.getDatabases(loginUser, datasourceId);
629635
} catch (Exception e) {
630636
Assertions.assertTrue(e.getMessage().contains(Status.GET_DATASOURCE_TABLES_ERROR.getMsg()));
631637
}
@@ -634,7 +640,7 @@ public void testGetDatabases() throws SQLException {
634640
.thenReturn(null);
635641

636642
try {
637-
dataSourceService.getDatabases(datasourceId);
643+
dataSourceService.getDatabases(loginUser, datasourceId);
638644
} catch (Exception e) {
639645
Assertions.assertTrue(e.getMessage().contains(Status.DATASOURCE_CONNECT_FAILED.getMsg()));
640646
}

0 commit comments

Comments
 (0)