Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d62edf6
[DSIP-93][API] Project add owner permissions
Dec 1, 2025
d3659b2
[DSIP-93][API] Project add owner permissions
Dec 2, 2025
6609dde
[DSIP-93][API] Project add owner permissions
Dec 2, 2025
89d98eb
[DSIP-93][API] Project add owner permissions
Dec 2, 2025
86734f5
[DSIP-93][API] Project add owner permissions
Dec 3, 2025
aa92066
[DSIP-93][API] Project add owner permissions
Dec 3, 2025
5148f9b
[DSIP-93][API] Project add owner permissions
Dec 3, 2025
f081a6e
[DSIP-93][API] Project add owner permissions
Dec 3, 2025
07e9b34
[DSIP-93][API] Project add owner permissions
Dec 3, 2025
206409f
[DSIP-93][API] Project add owner permissions
Dec 3, 2025
a56a15e
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 4, 2025
6a74bdc
[DSIP-93][API] Project add owner permissions
Dec 4, 2025
a2e16b6
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 4, 2025
5b9f5ed
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 4, 2025
9fbd23b
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 5, 2025
fb8ed65
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 8, 2025
c7e00c8
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 15, 2025
b158de1
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 21, 2025
582e674
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 22, 2025
e3a5d3a
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 22, 2025
ef61df9
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Dec 23, 2025
9933128
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 5, 2026
f543387
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 6, 2026
d7af310
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 12, 2026
4080775
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 15, 2026
8a90cde
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 19, 2026
6c0254f
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 23, 2026
c570966
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 28, 2026
00c7ede
Merge branch 'dev' into DSIP-93-project-add-owner-permission
niumy0701 Jan 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,29 @@ public Result grantProjectWithReadPerm(@Parameter(hidden = true) @RequestAttribu
return returnDataList(result);
}

/**
* grant project with owner permission
*
* @param loginUser login user
* @param userId user id
* @param projectIds project id array
* @return grant result code
*/
@Operation(summary = "grantProjectWithOwnerPerm", description = "GRANT_PROJECT_WITH_OWNER_PERM_NOTES")
@Parameters({
@Parameter(name = "userId", description = "USER_ID", required = true, schema = @Schema(implementation = int.class, example = "100")),
@Parameter(name = "projectIds", description = "PROJECT_IDS", required = true, schema = @Schema(implementation = String.class))
})
@PostMapping(value = "/grant-project-with-owner-perm")
@ResponseStatus(HttpStatus.OK)
@ApiException(GRANT_PROJECT_ERROR)
public Result grantProjectWithOwnerPerm(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "userId") int userId,
@RequestParam(value = "projectIds") String projectIds) {
Map<String, Object> result = usersService.grantProjectWithOwnerPerm(loginUser, userId, projectIds);
return returnDataList(result);
}

/**
* grant project
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ User updateUser(User loginUser,
*/
Map<String, Object> grantProject(User loginUser, int userId, String projectIds);

/**
* grant project with read permission
*
* @param loginUser login user
* @param userId user id
* @param projectIds project id array
* @return grant result code
*/
Map<String, Object> grantProjectWithOwnerPerm(User loginUser, int userId, String projectIds);

/**
* grant project with read permission
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.dao.entity.DataSource;
import org.apache.dolphinscheduler.dao.entity.DatasourceUser;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.DataSourceMapper;
import org.apache.dolphinscheduler.dao.mapper.DataSourceUserMapper;
Expand Down Expand Up @@ -123,6 +124,13 @@ public DataSource createDataSource(User loginUser, BaseDataSourceParamDTO dataso
dataSource.setUpdateTime(now);
try {
dataSourceMapper.insert(dataSource);
DatasourceUser datasourceUser = new DatasourceUser();
datasourceUser.setUserId(loginUser.getId());
datasourceUser.setDatasourceId(dataSource.getId());
datasourceUser.setPerm(Constants.AUTHORIZE_WRITABLE_PERM);
datasourceUser.setCreateTime(now);
datasourceUser.setUpdateTime(now);
datasourceUserMapper.insert(datasourceUser);
return dataSource;
} catch (DuplicateKeyException ex) {
throw new ServiceException(Status.DATASOURCE_EXIST);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -129,9 +128,18 @@ public Result createProject(User loginUser, String name, String desc) {
.createTime(now)
.updateTime(now)
.build();

if (projectMapper.insert(project) > 0) {
log.info("Project is created and id is :{}", project.getId());

// project creator default has owner permission
ProjectUser projectUser = new ProjectUser();
projectUser.setUserId(loginUser.getId());
projectUser.setProjectId(project.getId());
projectUser.setPerm(Constants.OWNER_PERMISSION);
projectUser.setCreateTime(now);
projectUser.setUpdateTime(now);
projectUserMapper.insert(projectUser);

result.setData(project);
putMsg(result, Status.SUCCESS);
} else {
Expand Down Expand Up @@ -268,13 +276,9 @@ public boolean hasProjectAndWritePerm(User loginUser, Project project, Result re
if (loginUser.getUserType() == UserType.ADMIN_USER) {
return true;
}
// case 2: user is project owner
if (project.getUserId().equals(loginUser.getId())) {
return true;
}
// case 3: check user permission level
// case 2: check user permission level
ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), loginUser.getId());
if (projectUser == null || projectUser.getPerm() != Constants.DEFAULT_ADMIN_PERMISSION) {
if (projectUser == null || projectUser.getPerm() < Constants.WRITE_PERMISSION) {
putMsg(result, Status.USER_NO_WRITE_PROJECT_PERM, loginUser.getUserName(), project.getCode());
checkResult = false;
} else {
Expand All @@ -295,13 +299,9 @@ public boolean hasProjectAndWritePerm(User loginUser, Project project, Map<Strin
if (loginUser.getUserType() == UserType.ADMIN_USER) {
return true;
}
// case 2: user is project owner
if (project.getUserId().equals(loginUser.getId())) {
return true;
}
// case 3: check user permission level
// case 2: check user permission level
ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), loginUser.getId());
if (projectUser == null || projectUser.getPerm() != Constants.DEFAULT_ADMIN_PERMISSION) {
if (projectUser == null || projectUser.getPerm() < Constants.WRITE_PERMISSION) {
putMsg(result, Status.USER_NO_WRITE_PROJECT_PERM, loginUser.getUserName(), project.getCode());
checkResult = false;
} else {
Expand All @@ -326,13 +326,9 @@ public void checkHasProjectWritePermissionThrowException(User loginUser, Project
if (loginUser.getUserType() == UserType.ADMIN_USER) {
return;
}
// case 2: user is project owner
if (project.getUserId().equals(loginUser.getId())) {
return;
}
// case 3: check user permission level
// case 2: check user permission level
ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), loginUser.getId());
if (projectUser == null || projectUser.getPerm() != Constants.DEFAULT_ADMIN_PERMISSION) {
if (projectUser == null || projectUser.getPerm() < Constants.WRITE_PERMISSION) {
throw new ServiceException(Status.USER_NO_WRITE_PROJECT_PERM, loginUser.getUserName(), project.getCode());
}
}
Expand Down Expand Up @@ -381,7 +377,10 @@ public Result queryProjectListPaging(User loginUser, Integer pageSize, Integer p
List<Project> projectList = projectIPage.getRecords();
if (loginUser.getUserType() != UserType.ADMIN_USER) {
for (Project project : projectList) {
project.setPerm(Constants.DEFAULT_ADMIN_PERMISSION);
ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), loginUser.getId());
if (projectUser != null) {
project.setPerm(projectUser.getPerm());
}
}
}
if (CollectionUtils.isEmpty(projectList)) {
Expand Down Expand Up @@ -442,10 +441,7 @@ public Result queryProjectWithAuthorizedLevelListPaging(Integer userId, User log
for (Project project : projectList) {
if (userProjectIds.contains(project.getId())) {
ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), userId);
if (projectUser == null) {
// in this case, the user is the project owner, maybe it's better to set it to ALL_PERMISSION.
project.setPerm(Constants.DEFAULT_ADMIN_PERMISSION);
} else {
if (projectUser != null) {
project.setPerm(projectUser.getPerm());
}
} else {
Expand Down Expand Up @@ -779,10 +775,6 @@ private int queryPermission(User user, Project project) {
return Constants.READ_PERMISSION;
}

if (Objects.equals(project.getUserId(), user.getId())) {
return Constants.ALL_PERMISSIONS;
}

ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), user.getId());

if (projectUser == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,18 +431,14 @@ private boolean hasProjectPerm(User loginUser, long projectCode, Map<String, Obj
return true;
}

if (project.getUserId().equals(loginUser.getId())) {
return true;
}

ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), loginUser.getId());
if (projectUser == null) {
log.warn("User {} does not have operation permission for project {}", loginUser.getUserName(),
project.getCode());
putMsg(result, Status.USER_NO_OPERATION_PROJECT_PERM, loginUser.getUserName(), project.getCode());
return false;
}
if (writePermission && projectUser.getPerm() != Constants.DEFAULT_ADMIN_PERMISSION) {
if (writePermission && projectUser.getPerm() < Constants.WRITE_PERMISSION) {
log.warn("User {} does not have write permission for project {}", loginUser.getUserName(),
project.getCode());
putMsg(result, Status.USER_NO_WRITE_PROJECT_PERM, loginUser.getUserName(), project.getCode());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import lombok.extern.slf4j.Slf4j;

Expand Down Expand Up @@ -462,15 +461,6 @@ public Map<String, Object> deleteUserById(User loginUser, int id) throws IOExcep
putMsg(result, Status.USER_NOT_EXIST, id);
return result;
}
// check if is a project owner
List<Project> projects = projectMapper.queryProjectCreatedByUser(id);
if (CollectionUtils.isNotEmpty(projects)) {
String projectNames = projects.stream().map(Project::getName).collect(Collectors.joining(","));
putMsg(result, Status.TRANSFORM_PROJECT_OWNERSHIP, projectNames);
log.warn("Please transfer the project ownership before deleting the user, userId:{}, projects:{}.", id,
projectNames);
return result;
}
// delete user
userMapper.queryTenantCodeByUserId(id);

Expand Down Expand Up @@ -578,6 +568,61 @@ public Map<String, Object> grantProjectWithReadPerm(User loginUser, int userId,
return result;
}

/**
* grant project
*
* @param loginUser login user
* @param userId user id
* @param projectIds project id array
* @return grant result code
*/
@Override
@Transactional
public Map<String, Object> grantProjectWithOwnerPerm(User loginUser, int userId, String projectIds) {
Map<String, Object> result = new HashMap<>();
result.put(Constants.STATUS, false);

if (resourcePermissionCheckService.functionDisabled()) {
putMsg(result, Status.FUNCTION_DISABLED);
return result;
}

// check exist
User tempUser = userMapper.selectById(userId);
if (tempUser == null) {
log.error("User does not exist, userId:{}.", userId);
putMsg(result, Status.USER_NOT_EXIST, userId);
return result;
}

if (!isAdmin(loginUser)) {
putMsg(result, Status.NO_CURRENT_OPERATING_PERMISSION);
return result;
}

if (check(result, StringUtils.isEmpty(projectIds), Status.SUCCESS)) {
log.warn("Parameter projectIds is empty.");
return result;
}
Arrays.stream(projectIds.split(",")).distinct().forEach(projectId -> {
ProjectUser projectUserOld = projectUserMapper.queryProjectRelation(Integer.parseInt(projectId), userId);
if (projectUserOld != null) {
projectUserMapper.deleteProjectRelation(Integer.parseInt(projectId), userId);
}
Date now = new Date();
ProjectUser projectUser = new ProjectUser();
projectUser.setUserId(userId);
projectUser.setProjectId(Integer.parseInt(projectId));
projectUser.setPerm(Constants.OWNER_PERMISSION);
projectUser.setCreateTime(now);
projectUser.setUpdateTime(now);
projectUserMapper.insert(projectUser);
});
putMsg(result, Status.SUCCESS);

return result;
}

/**
* grant project
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,42 @@ public void testGrantProjectByCode() throws Exception {
logger.info(mvcResult.getResponse().getContentAsString());
}

@Test
public void testGrantProjectWithReadPerm() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("userId", "32");
paramsMap.add("projectIds", "3");

MvcResult mvcResult = mockMvc.perform(post("/users/grant-project-with-read-perm")
.header(SESSION_ID, sessionId)
.params(paramsMap))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();

Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
Assertions.assertEquals(Status.USER_NOT_EXIST.getCode(), result.getCode().intValue());
logger.info(mvcResult.getResponse().getContentAsString());
}

@Test
public void testGrantProjectWithOwnerPerm() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("userId", "32");
paramsMap.add("projectIds", "3");

MvcResult mvcResult = mockMvc.perform(post("/users/grant-project-with-owner-perm")
.header(SESSION_ID, sessionId)
.params(paramsMap))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();

Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
Assertions.assertEquals(Status.USER_NOT_EXIST.getCode(), result.getCode().intValue());
logger.info(mvcResult.getResponse().getContentAsString());
}

@Test
public void testRevokeProject() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
Expand All @@ -145,6 +181,24 @@ public void testRevokeProject() throws Exception {
logger.info(mvcResult.getResponse().getContentAsString());
}

@Test
public void testRevokeProjectById() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("userId", "32");
paramsMap.add("projectIds", "3");

MvcResult mvcResult = this.mockMvc.perform(post("/users/revoke-project-by-id")
.header(SESSION_ID, this.sessionId)
.params(paramsMap))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();

Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
Assertions.assertEquals(Status.USER_NOT_EXIST.getCode(), result.getCode().intValue());
logger.info(mvcResult.getResponse().getContentAsString());
}

@Test
public void testGrantDataSource() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
Expand All @@ -163,6 +217,24 @@ public void testGrantDataSource() throws Exception {
logger.info(mvcResult.getResponse().getContentAsString());
}

@Test
public void testGrantNamespace() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("userId", "32");
paramsMap.add("namespaceIds", "5");

MvcResult mvcResult = mockMvc.perform(post("/users/grant-namespace")
.header(SESSION_ID, sessionId)
.params(paramsMap))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();

Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
Assertions.assertEquals(Status.USER_NOT_EXIST.getCode(), result.getCode().intValue());
logger.info(mvcResult.getResponse().getContentAsString());
}

@Test
public void testGetUserInfo() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/users/get-user-info")
Expand Down
Loading