🐛 SSH 配置未启用还可以连接.

This commit is contained in:
lijiahang
2024-03-06 17:58:32 +08:00
parent ba338c15de
commit 201956855f
29 changed files with 177 additions and 57 deletions

View File

@@ -14,6 +14,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@@ -40,8 +41,8 @@ public class AssetAuthorizedDataServiceController {
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/current-host")
@Operation(summary = "查询当前用户已授权的主机")
public AuthorizedHostWrapperVO getCurrentAuthorizedHostGroup() {
return assetAuthorizedDataService.getUserAuthorizedHostGroup(SecurityUtils.getLoginUserId());
public AuthorizedHostWrapperVO getCurrentAuthorizedHost(@RequestParam("type") String type) {
return assetAuthorizedDataService.getUserAuthorizedHost(SecurityUtils.getLoginUserId(), type);
}
@IgnoreLog(IgnoreLogMode.RET)

View File

@@ -43,7 +43,7 @@ public enum HostConfigTypeEnum implements GenericsDataDefinition {
return null;
}
for (HostConfigTypeEnum value : values()) {
if (value.type.equals(type)) {
if (value.type.equalsIgnoreCase(type)) {
return value;
}
}

View File

@@ -26,7 +26,7 @@ public enum HostConnectTypeEnum {
return null;
}
for (HostConnectTypeEnum value : values()) {
if (value.name().equals(type)) {
if (value.name().equalsIgnoreCase(type)) {
return value;
}
}

View File

@@ -9,14 +9,18 @@ package com.orion.ops.module.asset.handler.host.terminal.constant;
*/
public interface TerminalMessage {
String CLOSED_CONNECTION = "closed connection...";
String CONFIG_DISABLED = "SSH configuration has been disabled.";
String AUTHENTICATION_FAILURE = "authentication failure...";
String AUTHENTICATION_FAILURE = "authentication failed. please check the configuration.";
String UNREACHABLE = "remote server unreachable...";
String SERVER_UNREACHABLE = "remote server unreachable. please check the configuration.";
String CONNECTION_TIMEOUT = "connection timeout...";
String CONNECTION_FAILED = "connection failed.";
String FORCED_OFFLINE = "forced offline...";
String CONNECTION_TIMEOUT = "connection timeout.";
String CONNECTION_CLOSED = "connection closed.";
String FORCED_OFFLINE = "forced offline.";
}

View File

@@ -1,5 +1,6 @@
package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.lang.exception.DisabledException;
import com.orion.lang.exception.argument.InvalidArgumentException;
import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.collect.Maps;
@@ -17,6 +18,7 @@ import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
import com.orion.ops.module.asset.entity.request.host.HostConnectLogCreateRequest;
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalCheckRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCheckResponse;
@@ -82,9 +84,12 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
log.info("TerminalCheckHandler-handle success userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
} catch (InvalidArgumentException e) {
ex = e;
log.error("TerminalCheckHandler-handle error userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
log.error("TerminalCheckHandler-handle start error userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
} catch (DisabledException e) {
ex = Exceptions.runtime(TerminalMessage.CONFIG_DISABLED);
log.error("TerminalCheckHandler-handle disabled error userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
} catch (Exception e) {
ex = Exceptions.runtime(ErrorMessage.CONNECT_ERROR);
ex = Exceptions.runtime(TerminalMessage.CONNECTION_FAILED);
log.error("TerminalCheckHandler-handle exception userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
}
// 记录主机日志

View File

@@ -1,7 +1,6 @@
package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.lang.exception.AuthenticationException;
import com.orion.lang.exception.ConnectionRuntimeException;
import com.orion.lang.exception.TimeoutException;
import com.orion.lang.exception.argument.InvalidArgumentException;
import com.orion.lang.utils.Exceptions;
@@ -151,9 +150,6 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
if (Exceptions.isCausedBy(e, TimeoutException.class)) {
// 连接超时
return TerminalMessage.CONNECTION_TIMEOUT;
} else if (Exceptions.isCausedBy(e, ConnectionRuntimeException.class)) {
// 无法连接
return TerminalMessage.UNREACHABLE;
} else if (Exceptions.isCausedBy(e, AuthenticationException.class)) {
// 认证失败
return TerminalMessage.AUTHENTICATION_FAILURE;
@@ -162,7 +158,7 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
return e.getMessage();
} else {
// 其他错误
return TerminalMessage.UNREACHABLE;
return TerminalMessage.SERVER_UNREACHABLE;
}
}

View File

@@ -56,7 +56,7 @@ public abstract class TerminalSession implements ITerminalSession {
.type(OutputTypeEnum.CLOSE.getType())
.sessionId(this.sessionId)
.forceClose(BooleanBit.of(this.forceOffline).getValue())
.msg(this.forceOffline ? TerminalMessage.FORCED_OFFLINE : TerminalMessage.CLOSED_CONNECTION)
.msg(this.forceOffline ? TerminalMessage.FORCED_OFFLINE : TerminalMessage.CONNECTION_CLOSED)
.build();
WebSockets.sendText(channel, OutputTypeEnum.CLOSE.format(resp));
}

View File

@@ -30,9 +30,10 @@ public interface AssetAuthorizedDataService {
* 查询用户已授权的主机主机
*
* @param userId userId
* @param type type
* @return group
*/
AuthorizedHostWrapperVO getUserAuthorizedHostGroup(Long userId);
AuthorizedHostWrapperVO getUserAuthorizedHost(Long userId, String type);
/**
* 获取用户已授权的主机id 不查询角色

View File

@@ -66,4 +66,13 @@ public interface HostConfigService {
*/
void initHostConfig(Long hostId);
/**
* 获取启用配置的 hostId
*
* @param type type
* @param hostIdList hostIdList
* @return hostId
*/
List<Long> getEnabledConfigHostId(String type, List<Long> hostIdList);
}

View File

@@ -55,6 +55,9 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
@Resource
private HostService hostService;
@Resource
private HostConfigService hostConfigService;
@Resource
private HostKeyService hostKeyService;
@@ -88,10 +91,10 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
}
@Override
public AuthorizedHostWrapperVO getUserAuthorizedHostGroup(Long userId) {
public AuthorizedHostWrapperVO getUserAuthorizedHost(Long userId, String type) {
if (systemUserApi.isAdminUser(userId)) {
// 管理员查询所有
return this.buildUserAuthorizedHostGroup(userId, null);
return this.buildUserAuthorizedHost(userId, null, type);
} else {
// 其他用户 查询授权的数据
List<Long> authorizedIdList = dataPermissionApi.getUserAuthorizedRelIdList(DataPermissionTypeEnum.HOST_GROUP, userId);
@@ -102,7 +105,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
.hostList(Lists.empty())
.build();
}
return this.buildUserAuthorizedHostGroup(userId, authorizedIdList);
return this.buildUserAuthorizedHost(userId, authorizedIdList, type);
}
}
@@ -173,24 +176,27 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
*
* @param userId userId
* @param authorizedGroupIdList authorizedGroupIdList
* @param type type
* @return tree
*/
@SneakyThrows
private AuthorizedHostWrapperVO buildUserAuthorizedHostGroup(Long userId, List<Long> authorizedGroupIdList) {
private AuthorizedHostWrapperVO buildUserAuthorizedHost(Long userId, List<Long> authorizedGroupIdList, String type) {
final boolean allData = Lists.isEmpty(authorizedGroupIdList);
AuthorizedHostWrapperVO wrapper = new AuthorizedHostWrapperVO();
// 查询我的收藏
Future<List<Long>> favoriteResult = favoriteApi.getFavoriteRelIdListAsync(FavoriteTypeEnum.HOST, userId);
// 查询最近连接的主机
Future<List<Long>> latestConnectHostIdList = hostConnectLogService.getLatestConnectHostIdAsync(HostConnectTypeEnum.SSH, userId);
// 查询别名
Future<Map<Long, String>> dataAliasResult = dataExtraApi.getExtraItemValuesByCacheAsync(userId, DataExtraTypeEnum.HOST, DataExtraItems.ALIAS);
// 查询颜色
Future<Map<Long, String>> dataColorResult = dataExtraApi.getExtraItemValuesByCacheAsync(userId, DataExtraTypeEnum.HOST, DataExtraItems.COLOR);
Future<List<Long>> latestConnectHostIdList = hostConnectLogService.getLatestConnectHostIdAsync(HostConnectTypeEnum.of(type), userId);
// 查询主机拓展信息
Future<List<Map<Long, String>>> hostExtraResult = dataExtraApi.getExtraItemsValuesByCacheAsync(userId,
DataExtraTypeEnum.HOST,
Lists.of(DataExtraItems.ALIAS, DataExtraItems.COLOR));
// 查询分组
List<DataGroupDTO> dataGroup = dataGroupApi.getDataGroupList(DataGroupTypeEnum.HOST);
// 查询分组引用
Map<Long, Set<Long>> dataGroupRel = dataGroupRelApi.getGroupRelList(DataGroupTypeEnum.HOST);
// 查询配置启用的主机
List<Long> enabledConfigHostId = this.getEnabledConfigHostId(allData, dataGroupRel, type);
// 过滤已经授权的分组
if (!allData) {
// 构建已授权的分组
@@ -209,17 +215,47 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
wrapper.setHostList(this.getAuthorizedHostList(allData,
dataGroup,
dataGroupRel,
authorizedGroupIdList));
authorizedGroupIdList,
enabledConfigHostId));
// 设置主机拓展信息
this.getAuthorizedHostExtra(wrapper.getHostList(),
favoriteResult.get(),
dataAliasResult.get(),
dataColorResult.get());
hostExtraResult.get());
// 设置最近连接的主机
wrapper.setLatestHosts(new LinkedHashSet<>(latestConnectHostIdList.get()));
return wrapper;
}
/**
* 获取已启用配置的 hostId
*
* @param allData allData
* @param dataGroupRel dataGroupRel
* @param type type
* @return enabledHostIdList
*/
private List<Long> getEnabledConfigHostId(boolean allData,
Map<Long, Set<Long>> dataGroupRel,
String type) {
List<Long> hostIdList = null;
if (!allData) {
// 非全部数据从分组映射中获取
hostIdList = dataGroupRel.values()
.stream()
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
if (hostIdList.isEmpty()) {
return Lists.empty();
}
}
// 查询启用配置的主机
List<Long> enabledConfigHostId = hostConfigService.getEnabledConfigHostId(type, hostIdList);
// 从分组引用中移除
dataGroupRel.forEach((k, v) -> v.removeIf(s -> !enabledConfigHostId.contains(s)));
return enabledConfigHostId;
}
/**
* 构建主机分组树
*
@@ -265,14 +301,19 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
* @param dataGroup dataGroup
* @param dataGroupRel dataGroupRel
* @param authorizedGroupIdList authorizedGroupIdList
* @param enabledConfigHostId enabledConfigHostId
* @return hosts
*/
private List<HostVO> getAuthorizedHostList(boolean allData,
List<DataGroupDTO> dataGroup,
Map<Long, Set<Long>> dataGroupRel,
List<Long> authorizedGroupIdList) {
List<Long> authorizedGroupIdList,
List<Long> enabledConfigHostId) {
// 查询主机列表
List<HostVO> hosts = hostService.getHostListByCache();
List<HostVO> hosts = hostService.getHostListByCache()
.stream()
.filter(s -> enabledConfigHostId.contains(s.getId()))
.collect(Collectors.toList());
// 全部数据直接返回
if (allData) {
return hosts;
@@ -296,15 +337,13 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
/**
* 设置授权主机的额外参数
*
* @param hosts hosts
* @param favorite favorite
* @param aliasMap aliasMap
* @param colorMap colorMap
* @param hosts hosts
* @param favorite favorite
* @param extraList extraList
*/
private void getAuthorizedHostExtra(List<HostVO> hosts,
List<Long> favorite,
Map<Long, String> aliasMap,
Map<Long, String> colorMap) {
List<Map<Long, String>> extraList) {
if (Lists.isEmpty(hosts)) {
return;
}
@@ -321,6 +360,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
hosts.get(i).setTags(tags.get(i));
}
// 设置主机别名
Map<Long, String> aliasMap = extraList.get(0);
if (!Maps.isEmpty(aliasMap)) {
hosts.forEach(s -> {
String alias = aliasMap.get(s.getId());
@@ -330,6 +370,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
});
}
// 设置主机颜色
Map<Long, String> colorMap = extraList.get(1);
if (!Maps.isEmpty(colorMap)) {
hosts.forEach(s -> {
HostColorExtraModel color = JSON.parseObject(colorMap.get(s.getId()), HostColorExtraModel.class);

View File

@@ -1,8 +1,10 @@
package com.orion.ops.module.asset.service.impl;
import com.orion.lang.utils.Exceptions;
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.enums.BooleanBit;
import com.orion.ops.framework.common.enums.EnableStatus;
import com.orion.ops.framework.common.handler.data.model.GenericsDataModel;
import com.orion.ops.framework.common.handler.data.strategy.MapDataStrategy;
@@ -21,10 +23,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -66,6 +65,10 @@ public class HostConfigServiceImpl implements HostConfigService {
if (config == null) {
return null;
}
// 检查配置状态
if (!BooleanBit.toBoolean(config.getStatus())) {
throw Exceptions.disabled();
}
return type.parse(config.getConfig());
}
@@ -145,6 +148,7 @@ public class HostConfigServiceImpl implements HostConfigService {
update.setId(config.getId());
update.setStatus(status);
update.setVersion(version);
update.setUpdateTime(new Date());
int effect = hostConfigDAO.updateById(update);
Valid.version(effect);
return update.getVersion();
@@ -167,6 +171,20 @@ public class HostConfigServiceImpl implements HostConfigService {
hostConfigDAO.insertBatch(configs);
}
@Override
public List<Long> getEnabledConfigHostId(String type, List<Long> hostIdList) {
return hostConfigDAO.of()
.createValidateWrapper()
.select(HostConfigDO::getHostId)
.eq(HostConfigDO::getType, type)
.eq(HostConfigDO::getStatus, BooleanBit.TRUE.getValue())
.in(HostConfigDO::getHostId, hostIdList)
.then()
.stream()
.map(HostConfigDO::getHostId)
.collect(Collectors.toList());
}
/**
* 通过类型获取配置
*