支持单位授权与客户端限制
This commit is contained in:
@@ -25,6 +25,9 @@ public class SandboxConfigDto {
|
||||
@Schema(description = "用户ID(scope为user时必填)")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户组/单位ID")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "配置名称")
|
||||
private String name;
|
||||
|
||||
@@ -81,4 +84,4 @@ public class SandboxConfigDto {
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private Date modified;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import com.xspaceagi.sandbox.spec.enums.SandboxScopeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 沙盒配置查询 DTO
|
||||
*/
|
||||
@@ -17,6 +19,12 @@ public class SandboxConfigQueryDto {
|
||||
@Schema(description = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户组/单位ID")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "用户组/单位ID列表")
|
||||
private List<Long> groupIds;
|
||||
|
||||
@Schema(description = "配置名称(模糊查询)")
|
||||
private String name;
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.xspaceagi.sandbox.infra.dao.vo.SandboxServerInfo;
|
||||
import com.xspaceagi.sandbox.infra.network.ReverseServerContainer;
|
||||
import com.xspaceagi.sandbox.spec.enums.SandboxScopeEnum;
|
||||
import com.xspaceagi.system.application.dto.TenantConfigDto;
|
||||
import com.xspaceagi.system.application.service.SysGroupApplicationService;
|
||||
import com.xspaceagi.system.infra.dao.entity.SysGroup;
|
||||
import com.xspaceagi.system.spec.common.RequestContext;
|
||||
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
||||
import com.xspaceagi.system.spec.exception.BizException;
|
||||
@@ -27,6 +29,7 @@ import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
||||
import com.xspaceagi.system.spec.utils.RedisUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -41,6 +44,7 @@ import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -65,6 +69,9 @@ public class SandboxConfigApplicationServiceImpl implements SandboxConfigApplica
|
||||
@Resource
|
||||
private IAgentRpcService iAgentRpcService;
|
||||
|
||||
@Resource
|
||||
private SysGroupApplicationService sysGroupApplicationService;
|
||||
|
||||
static {
|
||||
// disable keep alive,暂不使用连接池
|
||||
System.setProperty("jdk.httpclient.keepalive.timeout", "0");
|
||||
@@ -201,6 +208,7 @@ public class SandboxConfigApplicationServiceImpl implements SandboxConfigApplica
|
||||
if (entity.getScope() == SandboxScopeEnum.USER && entity.getUserId() == null) {
|
||||
entity.setUserId(RequestContext.get().getUserId());
|
||||
}
|
||||
bindAuthorizedGroupForUserConfig(entity);
|
||||
sandboxConfigService.save(entity);
|
||||
|
||||
// 创建用户沙盒代理
|
||||
@@ -240,6 +248,14 @@ public class SandboxConfigApplicationServiceImpl implements SandboxConfigApplica
|
||||
SandboxConfig entity = convertToEntity(dto);
|
||||
entity.setId(dto.getId());
|
||||
entity.setModified(new Date());
|
||||
entity.setScope(existingEntity.getScope());
|
||||
entity.setUserId(existingEntity.getUserId());
|
||||
entity.setGroupId(existingEntity.getGroupId());
|
||||
if (existingEntity.getGroupId() == null) {
|
||||
bindAuthorizedGroupForUserConfig(entity);
|
||||
} else {
|
||||
validateExistingGroupForUserConfig(entity);
|
||||
}
|
||||
|
||||
sandboxConfigService.updateById(entity);
|
||||
|
||||
@@ -337,6 +353,14 @@ public class SandboxConfigApplicationServiceImpl implements SandboxConfigApplica
|
||||
queryWrapper.eq(SandboxConfig::getUserId, queryDto.getUserId());
|
||||
}
|
||||
|
||||
if (queryDto.getGroupId() != null) {
|
||||
queryWrapper.eq(SandboxConfig::getGroupId, queryDto.getGroupId());
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(queryDto.getGroupIds())) {
|
||||
queryWrapper.in(SandboxConfig::getGroupId, queryDto.getGroupIds());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(queryDto.getName())) {
|
||||
queryWrapper.like(SandboxConfig::getName, queryDto.getName());
|
||||
}
|
||||
@@ -350,6 +374,44 @@ public class SandboxConfigApplicationServiceImpl implements SandboxConfigApplica
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
private void bindAuthorizedGroupForUserConfig(SandboxConfig entity) {
|
||||
if (entity.getScope() != SandboxScopeEnum.USER || entity.getUserId() == null) {
|
||||
return;
|
||||
}
|
||||
List<SysGroup> groups = sysGroupApplicationService.getEffectiveGroupListByUserId(entity.getUserId());
|
||||
if (groups.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, Long> clientCountByGroupId = groups.stream()
|
||||
.filter(group -> group.getId() != null)
|
||||
.collect(Collectors.toMap(
|
||||
SysGroup::getId,
|
||||
group -> countUserClientsByGroupId(group.getId()),
|
||||
(a, b) -> a));
|
||||
SysGroup group = sysGroupApplicationService.selectAuthorizedGroupForClient(entity.getUserId(), clientCountByGroupId);
|
||||
if (group == null) {
|
||||
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.systemGroupClientLimitExceeded);
|
||||
}
|
||||
entity.setGroupId(group.getId());
|
||||
}
|
||||
|
||||
private long countUserClientsByGroupId(Long groupId) {
|
||||
return sandboxConfigService.count(new LambdaQueryWrapper<SandboxConfig>()
|
||||
.eq(SandboxConfig::getScope, SandboxScopeEnum.USER)
|
||||
.eq(SandboxConfig::getGroupId, groupId));
|
||||
}
|
||||
|
||||
private void validateExistingGroupForUserConfig(SandboxConfig entity) {
|
||||
if (entity.getScope() != SandboxScopeEnum.USER || entity.getUserId() == null || entity.getGroupId() == null) {
|
||||
return;
|
||||
}
|
||||
boolean authorized = sysGroupApplicationService.getAuthorizedGroupListForLogin(entity.getUserId()).stream()
|
||||
.anyMatch(group -> entity.getGroupId().equals(group.getId()));
|
||||
if (!authorized) {
|
||||
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.systemGroupAuthorizationUnavailable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证配置键唯一性
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,9 @@ public class SandboxConfig implements Serializable {
|
||||
@TableField(value = "user_id")
|
||||
private Long userId;
|
||||
|
||||
@TableField(value = "group_id")
|
||||
private Long groupId;
|
||||
|
||||
@TableField(value = "name")
|
||||
private String name;
|
||||
|
||||
@@ -75,4 +78,4 @@ public class SandboxConfig implements Serializable {
|
||||
|
||||
@TableField(value = "modified")
|
||||
private Date modified;
|
||||
}
|
||||
}
|
||||
@@ -199,15 +199,16 @@ public class SandboxConfigController {
|
||||
RequestContext.get().setLangMap(user.getLangMap());
|
||||
}
|
||||
SandboxConfigDto dto = new SandboxConfigDto();
|
||||
String clientName = StringUtils.trimToNull(sandboxRegDto.getName());
|
||||
dto.setUserId(user.getId());
|
||||
dto.setScope(SandboxScopeEnum.USER);
|
||||
dto.setConfigKey(UUID.randomUUID().toString().replace("-", ""));
|
||||
dto.setName(I18nUtil.systemMessage("Backend.Sandbox.MyComputer"));
|
||||
dto.setName(clientName != null ? clientName : I18nUtil.systemMessage("Backend.Sandbox.MyComputer"));
|
||||
dto.setConfigValue(sandboxRegDto.getSandboxConfigValue());
|
||||
dto.setDescription("");
|
||||
dto.setIsActive(true);
|
||||
dto.setOnline(false);
|
||||
sandboxConfigApplicationService.create(dto, false);
|
||||
sandboxConfigApplicationService.create(dto, clientName != null);
|
||||
String token = authService.createToken(user, StringUtils.isNotBlank(sandboxRegDto.getDeviceId()) ? sandboxRegDto.getDeviceId() : UUID.randomUUID().toString().replace("-", ""));
|
||||
dto.setToken(token);
|
||||
return ReqResult.success(sandboxConfigApplicationService.getByKey(dto.getConfigKey()));
|
||||
|
||||
@@ -5,9 +5,16 @@ import com.xspaceagi.sandbox.application.dto.SandboxConfigDto;
|
||||
import com.xspaceagi.sandbox.application.dto.SandboxConfigQueryDto;
|
||||
import com.xspaceagi.sandbox.application.dto.SandboxGlobalConfigDto;
|
||||
import com.xspaceagi.sandbox.application.service.SandboxConfigApplicationService;
|
||||
import com.xspaceagi.system.application.dto.UserDto;
|
||||
import com.xspaceagi.system.application.service.SysGroupApplicationService;
|
||||
import com.xspaceagi.system.infra.dao.entity.SysGroup;
|
||||
import com.xspaceagi.system.infra.dao.entity.User;
|
||||
import com.xspaceagi.system.spec.annotation.RequireResource;
|
||||
import com.xspaceagi.system.spec.common.RequestContext;
|
||||
import com.xspaceagi.system.spec.dto.ReqResult;
|
||||
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
||||
import com.xspaceagi.system.spec.exception.BizException;
|
||||
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -15,6 +22,8 @@ import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.xspaceagi.system.spec.enums.ResourceEnum.*;
|
||||
|
||||
@@ -29,10 +38,15 @@ public class SandboxConfigManageController {
|
||||
@Resource
|
||||
private SandboxConfigApplicationService sandboxConfigApplicationService;
|
||||
|
||||
@Resource
|
||||
private SysGroupApplicationService sysGroupApplicationService;
|
||||
|
||||
@RequireResource(SANDBOX_CONFIG_QUERY)
|
||||
@Operation(summary = "分页查询配置列表")
|
||||
@PostMapping("/page")
|
||||
public ReqResult<Page<SandboxConfigDto>> pageQuery(@RequestBody SandboxConfigQueryDto queryDto) {
|
||||
queryDto = queryDto == null ? new SandboxConfigQueryDto() : queryDto;
|
||||
applySandboxConfigManageScope(queryDto);
|
||||
return ReqResult.success(sandboxConfigApplicationService.pageQuery(queryDto));
|
||||
}
|
||||
|
||||
@@ -41,13 +55,16 @@ public class SandboxConfigManageController {
|
||||
@GetMapping("/get/{id}")
|
||||
public ReqResult<SandboxConfigDto> getById(
|
||||
@Parameter(description = "配置ID") @PathVariable Long id) {
|
||||
return ReqResult.success(sandboxConfigApplicationService.getById(id));
|
||||
SandboxConfigDto dto = sandboxConfigApplicationService.getById(id);
|
||||
checkSandboxConfigManageScope(dto);
|
||||
return ReqResult.success(dto);
|
||||
}
|
||||
|
||||
@Operation(summary = "沙箱连通性测试")
|
||||
@GetMapping("/test/{id}")
|
||||
public ReqResult<Void> testConnection(@Parameter(description = "配置ID") @PathVariable Long id) {
|
||||
try {
|
||||
checkSandboxConfigManageScope(sandboxConfigApplicationService.getById(id));
|
||||
sandboxConfigApplicationService.testConnection(id);
|
||||
} catch (Exception e) {
|
||||
return ReqResult.error("0007", e.getMessage());
|
||||
@@ -60,7 +77,14 @@ public class SandboxConfigManageController {
|
||||
@GetMapping("/user/list")
|
||||
public ReqResult<List<SandboxConfigDto>> listUserConfigsByType(
|
||||
@Parameter(description = "用户ID(为空时查询当前用户)") @RequestParam(required = false) Long userId) {
|
||||
return ReqResult.success(sandboxConfigApplicationService.listUserConfigsByType(userId));
|
||||
List<SandboxConfigDto> configs = sandboxConfigApplicationService.listUserConfigsByType(userId);
|
||||
if (!isPlatformAdmin()) {
|
||||
Set<Long> allowedGroupIds = currentUserGroupIds();
|
||||
configs = configs.stream()
|
||||
.filter(config -> config.getGroupId() != null && allowedGroupIds.contains(config.getGroupId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return ReqResult.success(configs);
|
||||
}
|
||||
|
||||
@RequireResource(SANDBOX_CONFIG_QUERY)
|
||||
@@ -74,6 +98,7 @@ public class SandboxConfigManageController {
|
||||
@Operation(summary = "创建配置")
|
||||
@PostMapping("/create")
|
||||
public ReqResult<Void> create(@RequestBody SandboxConfigDto dto) {
|
||||
checkSandboxConfigManageScope(dto);
|
||||
sandboxConfigApplicationService.create(dto, false);
|
||||
return ReqResult.success();
|
||||
}
|
||||
@@ -82,6 +107,7 @@ public class SandboxConfigManageController {
|
||||
@Operation(summary = "更新配置")
|
||||
@PostMapping("/update")
|
||||
public ReqResult<Void> update(@RequestBody SandboxConfigDto dto) {
|
||||
checkSandboxConfigManageScope(sandboxConfigApplicationService.getById(dto.getId()));
|
||||
sandboxConfigApplicationService.update(dto);
|
||||
return ReqResult.success();
|
||||
}
|
||||
@@ -91,6 +117,7 @@ public class SandboxConfigManageController {
|
||||
@PostMapping("/delete/{id}")
|
||||
public ReqResult<Void> delete(
|
||||
@Parameter(description = "配置ID") @PathVariable Long id) {
|
||||
checkSandboxConfigManageScope(sandboxConfigApplicationService.getById(id));
|
||||
sandboxConfigApplicationService.delete(id);
|
||||
return ReqResult.success();
|
||||
}
|
||||
@@ -100,6 +127,7 @@ public class SandboxConfigManageController {
|
||||
@PostMapping("/toggle/{id}")
|
||||
public ReqResult<Void> toggle(
|
||||
@Parameter(description = "配置ID") @PathVariable Long id) {
|
||||
checkSandboxConfigManageScope(sandboxConfigApplicationService.getById(id));
|
||||
sandboxConfigApplicationService.toggle(id);
|
||||
return ReqResult.success();
|
||||
}
|
||||
@@ -119,4 +147,45 @@ public class SandboxConfigManageController {
|
||||
SandboxGlobalConfigDto globalConfig = sandboxConfigApplicationService.getGlobalConfig(RequestContext.get().getTenantId());
|
||||
return ReqResult.success(globalConfig);
|
||||
}
|
||||
|
||||
private void applySandboxConfigManageScope(SandboxConfigQueryDto queryDto) {
|
||||
if (queryDto == null || isPlatformAdmin()) {
|
||||
return;
|
||||
}
|
||||
Set<Long> allowedGroupIds = currentUserGroupIds();
|
||||
if (queryDto.getGroupId() != null) {
|
||||
if (!allowedGroupIds.contains(queryDto.getGroupId())) {
|
||||
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
||||
}
|
||||
return;
|
||||
}
|
||||
queryDto.setGroupIds(allowedGroupIds.isEmpty() ? List.of(-1L) : allowedGroupIds.stream().toList());
|
||||
}
|
||||
|
||||
private void checkSandboxConfigManageScope(SandboxConfigDto dto) {
|
||||
if (dto == null || isPlatformAdmin()) {
|
||||
return;
|
||||
}
|
||||
if (dto.getGroupId() == null || !currentUserGroupIds().contains(dto.getGroupId())) {
|
||||
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Long> currentUserGroupIds() {
|
||||
return sysGroupApplicationService.getEffectiveGroupListByUserId(getCurrentUserDto().getId()).stream()
|
||||
.map(SysGroup::getId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private boolean isPlatformAdmin() {
|
||||
return getCurrentUserDto().getRole() == User.Role.Admin;
|
||||
}
|
||||
|
||||
private UserDto getCurrentUserDto() {
|
||||
UserDto userDto = (UserDto) RequestContext.get().getUser();
|
||||
if (userDto == null) {
|
||||
throw BizException.of(ErrorCodeEnum.UNAUTHORIZED, BizExceptionCodeEnum.systemUserNotLoggedInWeb);
|
||||
}
|
||||
return userDto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ public class SandboxRegDto {
|
||||
@Schema(description = "设备ID")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "客户端注册名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "终端配置信息")
|
||||
private SandboxConfigValue sandboxConfigValue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user