Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80e7c28ef1 | ||
|
|
154f8d56ac | ||
|
|
156e63bef9 | ||
|
|
34d65c0bc5 | ||
|
|
decbce5410 | ||
|
|
8b795e889a | ||
|
|
b75446f405 | ||
|
|
2b43270896 | ||
|
|
9d4c2aaeb4 | ||
|
|
712b175179 | ||
|
|
10178d998e | ||
|
|
c603c57ad8 | ||
|
|
f69093de66 | ||
|
|
cec7e21d5a | ||
|
|
f1ade56e13 | ||
|
|
b182452f30 | ||
|
|
5ed513f472 | ||
|
|
aa8b380289 | ||
|
|
6149010bf4 | ||
|
|
5183b7ccb4 | ||
|
|
f78958532b | ||
|
|
603dd89be4 | ||
|
|
1a58e40607 | ||
|
|
84721f2e17 | ||
|
|
d72ccb1df6 |
@@ -1,5 +1,7 @@
|
||||
VOLUME_BASE=/data/orion-visor-space/docker-volumes
|
||||
|
||||
DEMO_MODE=false
|
||||
|
||||
SERVICE_PORT=1081
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
SECRET_KEY=uQeacXV8b3isvKLK
|
||||
@@ -14,4 +16,6 @@ MYSQL_ROOT_PASSWORD=Data@123456
|
||||
REDIS_HOST=redis
|
||||
REDIS_PASSWORD=Data@123456
|
||||
|
||||
DEMO_MODE=false
|
||||
GUACD_HOST=guacd
|
||||
GUACD_PORT=4822
|
||||
GUACD_DRIVE_PATH=/drive
|
||||
|
||||
48
README.md
48
README.md
@@ -1,4 +1,4 @@
|
||||
<div align="center"><img src="https://bjuimg.obs.cn-north-4.myhuaweicloud.com/images/2024/9/11/11e7e78e-2af0-4c68-9811-db8a4c4400f4.png" alt="logo" width="520" /></div>
|
||||
<div align="center"><img src="https://oss.orionsec.cn/orion-visor/logo_horizontal.png" alt="logo" width="520" /></div>
|
||||
<p style="margin-top: 12px" align="center"><b>【Dromara】 一款高颜值、现代化的自动化运维&轻量堡垒机平台。</b></p>
|
||||
<p align="center">
|
||||
<a target="_blank"
|
||||
@@ -48,7 +48,7 @@
|
||||
**`orion-visor`** 提供一站式自动化运维解决方案。
|
||||
|
||||
* **资产管理**:支持对资产进行分组,实现对主机、密钥和身份的统一管理和授权。
|
||||
* **在线终端**:提供在线终端 SSH 服务,支持快捷命令、自定义快捷键和主题风格。
|
||||
* **在线终端**:提供在线终端 SSH/RDP 等多种协议,支持快捷命令、自定义快捷键和主题风格。
|
||||
* **文件管理**:支持远程主机 SFTP 大文件的批量上传、下载和在线编辑等操作。
|
||||
* **批量操作**:支持批量执行主机命令、多主机文件分发等功能。
|
||||
* **计划任务**:支持配置 cron 表达式,定时执行主机命令。
|
||||
@@ -64,7 +64,7 @@
|
||||
* 🎭 演示环境部分功能不可用, 完整功能请本地部署!
|
||||
* 📛 演示环境请不要随便删除数据!
|
||||
* 📧 如果演示环境不可用请联系我!
|
||||
* 📨 **作者已被毕(cai)业(yuan) 寻java高级内推 望京/5号/10号线 有坑位的联系我哦** 微信: `ljh1553488`
|
||||
* 📨 **作者已被毕(cai)业(yuan) 寻java高级内推 望京/5号/10号线 有坑位的联系我哦(随缘)** 微信: `ljh1553488`
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -87,32 +87,42 @@ docker compose up -d
|
||||
|
||||
## 技术栈
|
||||
|
||||
* SpringBoot 2.7+
|
||||
* Mysql 8+
|
||||
* Redis 6+
|
||||
* Vue3 3+
|
||||
* Arco Design 2+
|
||||
* SpringBoot 2.7.17
|
||||
* Mysql 8.0+
|
||||
* Redis 6.0+
|
||||
* Vue3 3.5+
|
||||
* Arco Design 2.56+
|
||||
|
||||
## 主要功能预览
|
||||
|
||||
#### 工作台
|
||||
|
||||

|
||||
|
||||
#### 主机终端
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### 主机列表
|
||||
|
||||

|
||||
|
||||
#### 批量执行
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
#### 批量上传
|
||||
|
||||

|
||||

|
||||
|
||||
#### 计划任务
|
||||
|
||||

|
||||

|
||||
|
||||
## Star History
|
||||
|
||||
@@ -124,7 +134,7 @@ docker compose up -d
|
||||
|
||||
## 联系我
|
||||
|
||||
<img src="https://bjuimg.obs.cn-north-4.myhuaweicloud.com/images/2024/10/11/cf72c603-3951-4171-95b4-06271dda1c80.jpg" alt="wx" width="628px"/>
|
||||
<img src="https://oss.orionsec.cn/orion-visor/vx.jpg" alt="vx" width="628px"/>
|
||||
|
||||
微信: ljh1553488
|
||||
QQ群: 755242157
|
||||
@@ -134,7 +144,7 @@ QQ群: 755242157
|
||||
|
||||
## 支持一下
|
||||
|
||||
<img src="https://bjuimg.obs.cn-north-4.myhuaweicloud.com/images/2024/5/17/a5351e59-294c-4bec-b767-1a44c362fcbc.jpg" alt="收款码" width="540px"/>
|
||||
<img src="https://oss.orionsec.cn/orion-visor/pay.jpg" alt="收款码" width="540px"/>
|
||||
|
||||
🎁 为了项目能健康持续的发展, 我期望获得相应的资金支持, 你们的支持是我不断更新前进的动力!
|
||||
|
||||
@@ -148,8 +158,8 @@ QQ群: 755242157
|
||||
|
||||
## Gitee 最有价值的开源项目 GVP
|
||||
|
||||

|
||||

|
||||
|
||||
## GitCode 最有影响力的开源项目 G-Star
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -7,16 +7,16 @@ services:
|
||||
ports:
|
||||
- 9200:9200
|
||||
environment:
|
||||
- SPRING_PROFILES_ACTIVE=prod
|
||||
- MYSQL_HOST=mysql
|
||||
- MYSQL_PORT=3306
|
||||
- MYSQL_DATABASE=orion_visor
|
||||
- MYSQL_USER=root
|
||||
- MYSQL_PASSWORD=Data@123456
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PASSWORD=Data@123456
|
||||
- SECRET_KEY=uQeacXV8b3isvKLK
|
||||
- DEMO_MODE=false
|
||||
SPRING_PROFILES_ACTIVE: prod
|
||||
MYSQL_HOST: mysql
|
||||
MYSQL_PORT: 3306
|
||||
MYSQL_DATABASE: orion_visor
|
||||
MYSQL_USER: root
|
||||
MYSQL_PASSWORD: Data@123456
|
||||
REDIS_HOST: redis
|
||||
REDIS_PASSWORD: Data@123456
|
||||
SECRET_KEY: uQeacXV8b3isvKLK
|
||||
DEMO_MODE: false
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/service/root-orion:/root/orion
|
||||
healthcheck:
|
||||
@@ -39,10 +39,10 @@ services:
|
||||
ports:
|
||||
- 3307:3306
|
||||
environment:
|
||||
- MYSQL_DATABASE=orion_visor
|
||||
- MYSQL_USER=orion
|
||||
- MYSQL_PASSWORD=Data@123456
|
||||
- MYSQL_ROOT_PASSWORD=Data@123456
|
||||
MYSQL_DATABASE: orion_visor
|
||||
MYSQL_USER: orion
|
||||
MYSQL_PASSWORD: Data@123456
|
||||
MYSQL_ROOT_PASSWORD: Data@123456
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql:/var/lib/mysql
|
||||
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql-files:/var/lib/mysql-files
|
||||
@@ -61,7 +61,7 @@ services:
|
||||
ports:
|
||||
- 6380:6379
|
||||
environment:
|
||||
- REDIS_PASSWORD=Data@123456
|
||||
REDIS_PASSWORD: Data@123456
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/redis/data:/data
|
||||
command: sh -c "redis-server /usr/local/redis.conf --requirepass $${REDIS_PASSWORD}"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
version: '3.3'
|
||||
|
||||
# latest = 2.4.0
|
||||
services:
|
||||
ui:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:latest
|
||||
@@ -18,16 +19,19 @@ services:
|
||||
ports:
|
||||
- 9200:9200
|
||||
environment:
|
||||
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-prod}
|
||||
- MYSQL_HOST=${MYSQL_HOST:-mysql}
|
||||
- MYSQL_PORT=${MYSQL_PORT:-3306}
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE:-orion_visor}
|
||||
- MYSQL_USER=${MYSQL_USER:-root}
|
||||
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-Data@123456}
|
||||
- REDIS_HOST=${REDIS_HOST:-redis}
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD:-Data@123456}
|
||||
- SECRET_KEY=${SECRET_KEY:-uQeacXV8b3isvKLK}
|
||||
- DEMO_MODE=${DEMO_MODE:-false}
|
||||
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
|
||||
MYSQL_HOST: ${MYSQL_HOST:-mysql}
|
||||
MYSQL_PORT: ${MYSQL_PORT:-3306}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-orion_visor}
|
||||
MYSQL_USER: ${MYSQL_USER:-root}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-Data@123456}
|
||||
REDIS_HOST: ${REDIS_HOST:-redis}
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-Data@123456}
|
||||
GUACD_HOST: ${GUACD_HOST:-guacd}
|
||||
GUACD_PORT: ${GUACD_PORT:-4822}
|
||||
GUACD_DRIVE_PATH: ${GUACD_DRIVE_PATH:-/drive}
|
||||
SECRET_KEY: ${SECRET_KEY:-uQeacXV8b3isvKLK}
|
||||
DEMO_MODE: ${DEMO_MODE:-false}
|
||||
volumes:
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/service/root-orion:/root/orion
|
||||
restart: unless-stopped
|
||||
@@ -51,10 +55,10 @@ services:
|
||||
ports:
|
||||
- 3307:3306
|
||||
environment:
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE:-orion_visor}
|
||||
- MYSQL_USER=${MYSQL_USER:-orion}
|
||||
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-Data@123456}
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-Data@123456}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-orion_visor}
|
||||
MYSQL_USER: ${MYSQL_USER:-orion}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-Data@123456}
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-Data@123456}
|
||||
volumes:
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/mysql/var-lib-mysql:/var/lib/mysql
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/mysql/var-lib-mysql-files:/var/lib/mysql-files
|
||||
@@ -74,7 +78,7 @@ services:
|
||||
ports:
|
||||
- 6380:6379
|
||||
environment:
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD:-Data@123456}
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-Data@123456}
|
||||
volumes:
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/redis/data:/data
|
||||
command: sh -c "redis-server /usr/local/redis.conf --requirepass $${REDIS_PASSWORD}"
|
||||
@@ -88,12 +92,34 @@ services:
|
||||
networks:
|
||||
- orion-visor-net
|
||||
|
||||
guacd:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:latest
|
||||
ports:
|
||||
- 4822:4822
|
||||
environment:
|
||||
GUACD_LOG_LEVEL: info
|
||||
GUACD_LOG_FILE: /var/log/guacd.log
|
||||
volumes:
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/guacd/drive:${GUACD_DRIVE_PATH:-/drive}
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/guacd/var-logs:/var/log
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/guacd/local-guacamole-lib:/usr/local/guacamole/lib
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/guacd/local-guacamole-extensions:/usr/local/guacamole/extensions
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: [ "CMD", "nc", "-vz", "localhost", "4822" ]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
networks:
|
||||
- orion-visor-net
|
||||
|
||||
adminer:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:latest
|
||||
ports:
|
||||
- 8081:8080
|
||||
environment:
|
||||
- ADMINER_DEFAULT_SERVER=${MYSQL_HOST:-mysql}
|
||||
ADMINER_DEFAULT_SERVER: ${MYSQL_HOST:-mysql}
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
#/bin/bash
|
||||
docker compose down
|
||||
# demo 启动
|
||||
#!/bin/bash
|
||||
|
||||
# 停止并移除现有容器
|
||||
docker compose down --remove-orphans
|
||||
|
||||
if [ "$1" == "demo" ]; then
|
||||
sed -i 's/\${DEMO_MODE:-false}/true/g' docker-compose.yml
|
||||
# 设置 DEMO_MODE 环境变量为 true
|
||||
export DEMO_MODE=true
|
||||
echo "Starting services for demo mode..."
|
||||
# 启动指定的服务
|
||||
docker compose up -d --remove-orphans mysql redis ui service adminer
|
||||
else
|
||||
echo "Starting all services..."
|
||||
# 正常启动所有服务
|
||||
docker compose up -d --remove-orphans
|
||||
fi
|
||||
docker compose up -d --remove-orphans
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
version=2.3.9
|
||||
version=2.4.0
|
||||
docker build -t orion-visor-adminer:${version} .
|
||||
docker tag orion-visor-adminer:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:${version}
|
||||
docker tag orion-visor-adminer:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:latest
|
||||
|
||||
10
docker/guacd/Dockerfile
Normal file
10
docker/guacd/Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM guacamole/guacd:1.5.5
|
||||
USER root
|
||||
# 系统时区
|
||||
ARG TZ=Asia/Shanghai
|
||||
# 设置时区
|
||||
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
||||
echo '${TZ}' > /etc/timezone
|
||||
|
||||
# 创建所需目录
|
||||
RUN mkdir -p /home/guacd/drive /usr/share/guacd/drive
|
||||
6
docker/guacd/build.sh
Normal file
6
docker/guacd/build.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
version=2.4.0
|
||||
docker build -t orion-visor-guacd:${version} .
|
||||
docker tag orion-visor-guacd:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:${version}
|
||||
docker tag orion-visor-guacd:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:latest
|
||||
@@ -1,6 +1,6 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
version=2.3.9
|
||||
version=2.4.0
|
||||
cp -r ../../sql ./sql
|
||||
docker build -t orion-visor-mysql:${version} .
|
||||
rm -rf ./sql
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
version=2.3.9
|
||||
version=2.4.0
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:latest
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:latest
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
version=2.3.9
|
||||
version=2.4.0
|
||||
docker build -t orion-visor-redis:${version} .
|
||||
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:${version}
|
||||
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:latest
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
version=2.3.9
|
||||
version=2.4.0
|
||||
mv ../../orion-visor-launch/target/orion-visor-launch.jar ./orion-visor-launch.jar
|
||||
docker build -t orion-visor-service:${version} .
|
||||
rm -rf ./orion-visor-launch.jar
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#/bin/bash
|
||||
set -e
|
||||
version=2.3.9
|
||||
version=2.4.0
|
||||
mv ../../orion-visor-ui/dist ./dist
|
||||
docker build -t orion-visor-ui:${version} .
|
||||
rm -rf ./orion-visor-launch.jar
|
||||
|
||||
@@ -36,7 +36,7 @@ public interface AppConst extends OrionConst {
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "2.3.9";
|
||||
String VERSION = "2.4.0";
|
||||
|
||||
/**
|
||||
* 同 ${spring.application.name}
|
||||
|
||||
@@ -51,6 +51,11 @@ public interface Const extends cn.orionsec.kit.lang.constant.Const, FieldConst,
|
||||
|
||||
String SYSTEM_USERNAME = "system";
|
||||
|
||||
// FIXME KIT
|
||||
String ADMINISTRATOR = "Administrator";
|
||||
|
||||
Long ALL_HOST_ID = -1L;
|
||||
|
||||
int BATCH_COUNT = 500;
|
||||
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public enum ErrorCode implements CodeInfo {
|
||||
|
||||
EXCEL_PASSWORD_ERROR(905, "文档密码错误"),
|
||||
|
||||
PASER_FAILED(906, "解析失败"),
|
||||
PASER_FAILED(906, "表达式解析失败"),
|
||||
|
||||
ENCRYPT_ERROR(907, "数据加密异常"),
|
||||
|
||||
@@ -134,9 +134,10 @@ public enum ErrorCode implements CodeInfo {
|
||||
* 获取 wapper
|
||||
*
|
||||
* @param data data
|
||||
* @param <T> T
|
||||
* @return HttpWrapper
|
||||
*/
|
||||
public HttpWrapper<?> wrapper() {
|
||||
public <T> HttpWrapper<T> wrapper() {
|
||||
return HttpWrapper.of(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@ package org.dromara.visor.common.constant;
|
||||
|
||||
import cn.orionsec.kit.lang.exception.ApplicationException;
|
||||
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
@@ -52,6 +60,12 @@ public interface ErrorMessage {
|
||||
|
||||
String IDENTITY_ABSENT = "主机身份不存在";
|
||||
|
||||
String CHECK_IDENTITY_PASSWORD = "请选择类型为[密码]的主机身份";
|
||||
|
||||
String KEY_ABSENT_WITH = "主机密钥不存在 {}";
|
||||
|
||||
String IDENTITY_ABSENT_WITH = "主机身份不存在 {}";
|
||||
|
||||
String CONFIG_ABSENT = "配置不存在";
|
||||
|
||||
String CONFIG_PRESENT = "配置已存在";
|
||||
@@ -96,24 +110,36 @@ public interface ErrorMessage {
|
||||
|
||||
String UNSUPPORTED_CHARSET = "不支持的编码 [{}]";
|
||||
|
||||
String DECRYPT_ERROR = "数据解密失败";
|
||||
|
||||
String PASSWORD_MISSING = "请输入密码";
|
||||
|
||||
String BEFORE_PASSWORD_ERROR = "原密码错误";
|
||||
|
||||
String DATA_NO_PERMISSION = "数据无权限";
|
||||
|
||||
String EXPRESSION_ERROR = "表达式错误";
|
||||
|
||||
String ANY_NO_PERMISSION = "{}无权限";
|
||||
|
||||
String OPT_NO_PERMISSION = "无操作权限";
|
||||
|
||||
String SESSION_PRESENT = "会话已存在";
|
||||
|
||||
String SESSION_ABSENT = "会话不存在";
|
||||
|
||||
String SESSION_CLOSED = "会话已关闭";
|
||||
|
||||
String USER_UNSUPPORTED_OPT = "用户不支持此操作";
|
||||
|
||||
String CURRENT_USER_UNSUPPORTED_OPT = "当前" + USER_UNSUPPORTED_OPT;
|
||||
|
||||
String PATH_NOT_NORMALIZE = "路径不合法";
|
||||
|
||||
String OPERATE_ERROR = "操作失败";
|
||||
|
||||
String ENCRYPT_KEY_UNSET = "加密密钥未配置";
|
||||
|
||||
String DECRYPT_ERROR = "数据解密失败";
|
||||
|
||||
String UNKNOWN_TYPE = "未知类型";
|
||||
|
||||
String ERROR_TYPE = "错误的类型";
|
||||
@@ -148,8 +174,26 @@ public interface ErrorMessage {
|
||||
|
||||
String CLIENT_ABORT = "手动中断";
|
||||
|
||||
String COMMAND_EXEC_ERROR = "命令执行失败 [{}]";
|
||||
|
||||
String COMPRESS_ERROR = "压缩失败";
|
||||
|
||||
String DECOMPRESS_ERROR = "解压失败";
|
||||
|
||||
String COMPRESS_FILE_ABSENT = "压缩文件不存在";
|
||||
|
||||
String UNABLE_DOWNLOAD_FOLDER = "无法下载文件夹";
|
||||
|
||||
String VALID_ERROR = "验证失败";
|
||||
|
||||
String CONVERT_ERROR = "转换失败";
|
||||
|
||||
String PRESENT_MODIFY = "{} 已存在, 请修改后重试";
|
||||
|
||||
String ILLEGAL_MODIFY = "{} 不正确, 请修改后重试";
|
||||
|
||||
String PLEASE_SELECT_SUFFIX_FILE = "请选择 {} 类型的文件";
|
||||
|
||||
/**
|
||||
* 是否为业务异常
|
||||
*
|
||||
@@ -187,4 +231,37 @@ public interface ErrorMessage {
|
||||
return defaultMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证错误消息
|
||||
*
|
||||
* @param ex ex
|
||||
* @param defaultMsg defaultMsg
|
||||
* @return message
|
||||
*/
|
||||
static String getValidErrorMessage(Exception ex, String defaultMsg) {
|
||||
if (ex == null) {
|
||||
return null;
|
||||
}
|
||||
// 参数不存在异常
|
||||
if (ex instanceof MissingServletRequestParameterException) {
|
||||
return Strings.format(ErrorMessage.MISSING, ((MissingServletRequestParameterException) ex).getParameterName());
|
||||
}
|
||||
// 参数绑定异常
|
||||
if (ex instanceof BindException) {
|
||||
return Optional.ofNullable(((BindException) ex)
|
||||
.getFieldError())
|
||||
.map(error -> error.getField() + Const.SPACE + error.getDefaultMessage())
|
||||
.orElse(defaultMsg);
|
||||
}
|
||||
// 参数验证异常
|
||||
if (ex instanceof ConstraintViolationException) {
|
||||
return Optional.ofNullable(((ConstraintViolationException) ex).getConstraintViolations())
|
||||
.map(Set::iterator)
|
||||
.map(Iterator::next)
|
||||
.map(s -> s.getPropertyPath().toString() + Const.SPACE + s.getMessage())
|
||||
.orElse(defaultMsg);
|
||||
}
|
||||
return getErrorMessage(ex, defaultMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public interface ExtraFieldConst extends FieldConst {
|
||||
|
||||
String GRANT_NAME = "grantName";
|
||||
|
||||
String CHANNEL_ID = "channelId";
|
||||
String CHANNEL = "channel";
|
||||
|
||||
String SESSION_ID = "sessionId";
|
||||
|
||||
|
||||
@@ -103,8 +103,20 @@ public interface FieldConst {
|
||||
|
||||
String FILTER = "filter";
|
||||
|
||||
String LICENSE = "license";
|
||||
|
||||
String SESSION = "session";
|
||||
|
||||
String CONNECT = "connect";
|
||||
|
||||
String ALL = "all";
|
||||
|
||||
String PROPS = "props";
|
||||
|
||||
String SENDER = "sender";
|
||||
|
||||
String RESULT = "result";
|
||||
|
||||
String CONFIG = "config";
|
||||
|
||||
}
|
||||
|
||||
@@ -52,14 +52,26 @@ public enum EnableStatus {
|
||||
|
||||
public static EnableStatus of(Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
return DISABLED;
|
||||
}
|
||||
for (EnableStatus e : values()) {
|
||||
if (e.value.equals(value)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return DISABLED;
|
||||
}
|
||||
|
||||
public static EnableStatus of(String value) {
|
||||
if (value == null) {
|
||||
return DISABLED;
|
||||
}
|
||||
for (EnableStatus e : values()) {
|
||||
if (e.name().equals(value)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return DISABLED;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.enums;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import cn.orionsec.kit.lang.utils.time.DateStream;
|
||||
import cn.orionsec.kit.lang.utils.time.Dates;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 统计区间枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/12/23 14:02
|
||||
*/
|
||||
public enum StatisticsRange {
|
||||
|
||||
/**
|
||||
* 当天
|
||||
*/
|
||||
TODAY {
|
||||
@Override
|
||||
public Date getRangeEndTime(Date startTime) {
|
||||
return DateStream.of(startTime)
|
||||
.dayEnd()
|
||||
.date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDateRanges(Date startTime) {
|
||||
return Lists.singleton(Dates.format(startTime, Dates.YMD));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 日视图
|
||||
*/
|
||||
DAY {
|
||||
@Override
|
||||
public Date getRangeEndTime(Date startTime) {
|
||||
return DateStream.of(startTime)
|
||||
.dayEnd()
|
||||
.date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDateRanges(Date startTime) {
|
||||
return Lists.singleton(Dates.format(startTime, Dates.YMD));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 周视图
|
||||
*/
|
||||
WEEK {
|
||||
@Override
|
||||
public Date getRangeEndTime(Date startTime) {
|
||||
return DateStream.of(startTime)
|
||||
.addDay(7)
|
||||
.dayEnd()
|
||||
.date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDateRanges(Date startTime) {
|
||||
return Arrays.stream(Dates.getIncrementDayDates(startTime, 1, 7))
|
||||
.map(s -> Dates.format(s, Dates.YMD))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 月视图
|
||||
*/
|
||||
MONTH {
|
||||
@Override
|
||||
public Date getRangeEndTime(Date startTime) {
|
||||
return DateStream.of(startTime)
|
||||
.addMonth(1)
|
||||
.dayEnd()
|
||||
.date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDateRanges(Date startTime) {
|
||||
int monthLastDay = Dates.getMonthLastDay(startTime);
|
||||
return Arrays.stream(Dates.getIncrementDayDates(startTime, 1, monthLastDay - 1))
|
||||
.map(s -> Dates.format(s, Dates.YMD))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
},
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* 获取区间结束时间
|
||||
*
|
||||
* @param startTime startTime
|
||||
* @return end
|
||||
*/
|
||||
public abstract Date getRangeEndTime(Date startTime);
|
||||
|
||||
/**
|
||||
* 获取时间区间
|
||||
*
|
||||
* @param startTime startTime
|
||||
* @return ranges
|
||||
*/
|
||||
public abstract List<String> getDateRanges(Date startTime);
|
||||
|
||||
public static StatisticsRange of(String type) {
|
||||
if (type == null) {
|
||||
return TODAY;
|
||||
}
|
||||
for (StatisticsRange value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return TODAY;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,7 +44,6 @@ public interface GenericsDataModel {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 转为 map
|
||||
*
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.session;
|
||||
|
||||
import cn.orionsec.kit.lang.exception.AuthenticationException;
|
||||
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
|
||||
/**
|
||||
* 连接消息
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/7/11 16:30
|
||||
*/
|
||||
public interface SessionMessage {
|
||||
|
||||
String AUTHENTICATION_FAILURE = "身份认证失败. {}";
|
||||
|
||||
String SERVER_UNREACHABLE = "无法连接至服务器. {}";
|
||||
|
||||
String CONNECTION_TIMEOUT = "连接服务器超时. {}";
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param address address
|
||||
* @param e e
|
||||
* @return errorMessage
|
||||
*/
|
||||
static String getErrorMessage(String address, Exception e) {
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
String message = e.getMessage();
|
||||
if (Strings.contains(message, Const.TIMEOUT)) {
|
||||
// 连接超时
|
||||
return Strings.format(SessionMessage.CONNECTION_TIMEOUT, address);
|
||||
} else if (Exceptions.isCausedBy(e, AuthenticationException.class)) {
|
||||
// 认证失败
|
||||
return Strings.format(SessionMessage.AUTHENTICATION_FAILURE, address);
|
||||
} else if (Exceptions.isCausedBy(e, InvalidArgumentException.class)) {
|
||||
// 参数错误
|
||||
if (Strings.isBlank(message)) {
|
||||
return Strings.format(SessionMessage.SERVER_UNREACHABLE, address);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
} else {
|
||||
// 其他错误
|
||||
return Strings.format(SessionMessage.SERVER_UNREACHABLE, address);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.session.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 基础连接配置实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/4/1 16:59
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "BaseConnectConfig", description = "基础连接参数")
|
||||
public class BaseConnectConfig implements IBaseConnectConfig {
|
||||
|
||||
@Schema(description = "系统类型")
|
||||
private String osType;
|
||||
|
||||
@Schema(description = "系统架构")
|
||||
private String archType;
|
||||
|
||||
@Schema(description = "hostId")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "hostName")
|
||||
private String hostName;
|
||||
|
||||
@Schema(description = "主机编码")
|
||||
private String hostCode;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String hostAddress;
|
||||
|
||||
@Schema(description = "主机端口")
|
||||
private Integer hostPort;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.session.config;
|
||||
|
||||
/**
|
||||
* 基础连接配置定义
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/6/24 17:11
|
||||
*/
|
||||
public interface IBaseConnectConfig {
|
||||
|
||||
// -------------------- getter/setter --------------------
|
||||
|
||||
String getOsType();
|
||||
|
||||
void setOsType(String osType);
|
||||
|
||||
String getArchType();
|
||||
|
||||
void setArchType(String archType);
|
||||
|
||||
Long getHostId();
|
||||
|
||||
void setHostId(Long hostId);
|
||||
|
||||
String getHostName();
|
||||
|
||||
void setHostName(String hostName);
|
||||
|
||||
String getHostCode();
|
||||
|
||||
void setHostCode(String hostCode);
|
||||
|
||||
String getHostAddress();
|
||||
|
||||
void setHostAddress(String hostAddress);
|
||||
|
||||
Integer getHostPort();
|
||||
|
||||
void setHostPort(Integer hostPort);
|
||||
|
||||
String getUsername();
|
||||
|
||||
void setUsername(String username);
|
||||
|
||||
String getPassword();
|
||||
|
||||
void setPassword(String password);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.session.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* RDP 连接参数
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/4/1 16:57
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "RdpConnectConfig", description = "RDP 连接参数")
|
||||
public class RdpConnectConfig extends BaseConnectConfig {
|
||||
|
||||
@Schema(description = "低带宽模式")
|
||||
private Boolean lowBandwidthMode;
|
||||
|
||||
@Schema(description = "RDP 版本是否大于8.1")
|
||||
private Boolean versionGt81;
|
||||
|
||||
@Schema(description = "时区")
|
||||
private String timezone;
|
||||
|
||||
@Schema(description = "键盘布局")
|
||||
private String keyboardLayout;
|
||||
|
||||
@Schema(description = "剪切板规范")
|
||||
private String clipboardNormalize;
|
||||
|
||||
@Schema(description = "域")
|
||||
private String domain;
|
||||
|
||||
@Schema(description = "预连接id")
|
||||
private String preConnectionId;
|
||||
|
||||
@Schema(description = "预连接数据")
|
||||
private String preConnectionBlob;
|
||||
|
||||
@Schema(description = "远程应用")
|
||||
private String remoteApp;
|
||||
|
||||
@Schema(description = "远程应用路径")
|
||||
private String remoteAppDir;
|
||||
|
||||
@Schema(description = "远程应用参数")
|
||||
private String remoteAppArgs;
|
||||
|
||||
}
|
||||
@@ -20,57 +20,29 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.entity.dto;
|
||||
package org.dromara.visor.common.session.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.visor.framework.desensitize.core.annotation.Desensitize;
|
||||
import org.dromara.visor.framework.desensitize.core.annotation.DesensitizeObject;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 终端连接参数
|
||||
* SSH 连接参数
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 15:47
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@DesensitizeObject
|
||||
@Schema(name = "TerminalConnectDTO", description = "终端连接参数")
|
||||
public class TerminalConnectDTO {
|
||||
|
||||
@Schema(description = "logId")
|
||||
private Long logId;
|
||||
|
||||
@Schema(description = "连接类型")
|
||||
private String connectType;
|
||||
|
||||
@Schema(description = "hostId")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "hostName")
|
||||
private String hostName;
|
||||
|
||||
@Schema(description = "主机编码")
|
||||
private String hostCode;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String hostAddress;
|
||||
|
||||
@Schema(description = "主机端口")
|
||||
private Integer hostPort;
|
||||
|
||||
@Schema(description = "系统类型")
|
||||
private String osType;
|
||||
|
||||
@Schema(description = "系统架构")
|
||||
private String archType;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "SshConnectConfig", description = "SSH 连接参数")
|
||||
public class SshConnectConfig extends BaseConnectConfig {
|
||||
|
||||
@Schema(description = "超时时间")
|
||||
private Integer timeout;
|
||||
@@ -84,25 +56,15 @@ public class TerminalConnectDTO {
|
||||
@Schema(description = "文件内容编码")
|
||||
private String fileContentCharset;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Desensitize(toEmpty = true)
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "密钥id")
|
||||
private Long keyId;
|
||||
|
||||
@Desensitize(toEmpty = true)
|
||||
@Schema(description = "公钥文本")
|
||||
private String publicKey;
|
||||
|
||||
@Desensitize(toEmpty = true)
|
||||
@Schema(description = "私钥文本")
|
||||
private String privateKey;
|
||||
|
||||
@Desensitize(toEmpty = true)
|
||||
@Schema(description = "私钥密码")
|
||||
private String privateKeyPassword;
|
||||
|
||||
@@ -20,10 +20,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.jsch;
|
||||
package org.dromara.visor.common.session.ssh;
|
||||
|
||||
import cn.orionsec.kit.lang.exception.AuthenticationException;
|
||||
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.net.host.SessionHolder;
|
||||
@@ -31,9 +29,9 @@ import cn.orionsec.kit.net.host.SessionLogger;
|
||||
import cn.orionsec.kit.net.host.SessionStore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.AppConst;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.session.SessionMessage;
|
||||
import org.dromara.visor.common.session.config.SshConnectConfig;
|
||||
import org.dromara.visor.common.utils.AesEncryptUtils;
|
||||
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -47,25 +45,22 @@ import java.util.Optional;
|
||||
@Slf4j
|
||||
public class SessionStores {
|
||||
|
||||
protected static final ThreadLocal<String> CURRENT_ADDRESS = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 打开 sessionStore
|
||||
*
|
||||
* @param conn conn
|
||||
* @param config config
|
||||
* @return sessionStore
|
||||
*/
|
||||
public static SessionStore openSessionStore(TerminalConnectDTO conn) {
|
||||
Long hostId = conn.getHostId();
|
||||
String address = conn.getHostAddress();
|
||||
String username = conn.getUsername();
|
||||
public static SessionStore openSessionStore(SshConnectConfig config) {
|
||||
Long hostId = config.getHostId();
|
||||
String address = config.getHostAddress();
|
||||
String username = config.getUsername();
|
||||
log.info("SessionStores-open-start hostId: {}, address: {}, username: {}", hostId, address, username);
|
||||
try {
|
||||
CURRENT_ADDRESS.set(address);
|
||||
// 创建会话
|
||||
SessionHolder sessionHolder = SessionHolder.create();
|
||||
sessionHolder.setLogger(SessionLogger.INFO);
|
||||
SessionStore session = createSessionStore(conn, sessionHolder);
|
||||
SessionStore session = createSessionStore(config, sessionHolder);
|
||||
// 设置版本
|
||||
session.getSession().setClientVersion("SSH-2.0-ORION_VISOR_V" + AppConst.VERSION);
|
||||
// 连接
|
||||
@@ -75,81 +70,48 @@ public class SessionStores {
|
||||
} catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
log.error("SessionStores-open-error hostId: {}, address: {}, username: {}, message: {}", hostId, address, username, message, e);
|
||||
throw Exceptions.app(getErrorMessage(e), e);
|
||||
} finally {
|
||||
CURRENT_ADDRESS.remove();
|
||||
throw Exceptions.app(SessionMessage.getErrorMessage(address, e), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 sessionStore
|
||||
*
|
||||
* @param conn conn
|
||||
* @param config config
|
||||
* @param sessionHolder sessionHolder
|
||||
* @return sessionStore
|
||||
*/
|
||||
private static SessionStore createSessionStore(TerminalConnectDTO conn, SessionHolder sessionHolder) {
|
||||
final boolean useKey = conn.getKeyId() != null;
|
||||
private static SessionStore createSessionStore(SshConnectConfig config, SessionHolder sessionHolder) {
|
||||
final boolean useKey = config.getKeyId() != null;
|
||||
// 使用密钥认证
|
||||
if (useKey) {
|
||||
// 加载密钥
|
||||
String publicKey = Optional.ofNullable(conn.getPublicKey())
|
||||
String publicKey = Optional.ofNullable(config.getPublicKey())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
String privateKey = Optional.ofNullable(conn.getPrivateKey())
|
||||
String privateKey = Optional.ofNullable(config.getPrivateKey())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
String password = Optional.ofNullable(conn.getPrivateKeyPassword())
|
||||
String password = Optional.ofNullable(config.getPrivateKeyPassword())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
sessionHolder.addIdentityValue(String.valueOf(conn.getKeyId()),
|
||||
sessionHolder.addIdentityValue(String.valueOf(config.getKeyId()),
|
||||
privateKey,
|
||||
publicKey,
|
||||
password);
|
||||
}
|
||||
// 获取会话
|
||||
SessionStore session = sessionHolder.getSession(conn.getHostAddress(), conn.getHostPort(), conn.getUsername());
|
||||
SessionStore session = sessionHolder.getSession(config.getHostAddress(), config.getHostPort(), config.getUsername());
|
||||
// 使用密码认证
|
||||
if (!useKey) {
|
||||
String password = conn.getPassword();
|
||||
String password = config.getPassword();
|
||||
if (!Strings.isEmpty(password)) {
|
||||
session.password(AesEncryptUtils.decryptAsString(password));
|
||||
}
|
||||
}
|
||||
// 超时时间
|
||||
session.timeout(conn.getTimeout());
|
||||
session.timeout(config.getTimeout());
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param e e
|
||||
* @return errorMessage
|
||||
*/
|
||||
private static String getErrorMessage(Exception e) {
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
String host = CURRENT_ADDRESS.get();
|
||||
String message = e.getMessage();
|
||||
if (Strings.contains(message, Const.TIMEOUT)) {
|
||||
// 连接超时
|
||||
return Strings.format(SessionMessage.CONNECTION_TIMEOUT, host);
|
||||
} else if (Exceptions.isCausedBy(e, AuthenticationException.class)) {
|
||||
// 认证失败
|
||||
return Strings.format(SessionMessage.AUTHENTICATION_FAILURE, host);
|
||||
} else if (Exceptions.isCausedBy(e, InvalidArgumentException.class)) {
|
||||
// 参数错误
|
||||
if (Strings.isBlank(message)) {
|
||||
return Strings.format(SessionMessage.SERVER_UNREACHABLE, host);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
} else {
|
||||
// 其他错误
|
||||
return Strings.format(SessionMessage.SERVER_UNREACHABLE, host);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
*/
|
||||
package org.dromara.visor.common.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,6 +43,9 @@ public class SqlUtils {
|
||||
* @return limit
|
||||
*/
|
||||
public static String limit(Number limit) {
|
||||
if (limit == null) {
|
||||
return Const.EMPTY;
|
||||
}
|
||||
return Const.LIMIT + Const.SPACE + limit;
|
||||
}
|
||||
|
||||
@@ -54,6 +57,9 @@ public class SqlUtils {
|
||||
* @return limit
|
||||
*/
|
||||
public static String limit(Number offset, Number limit) {
|
||||
if (offset == null) {
|
||||
return limit(limit);
|
||||
}
|
||||
return Const.LIMIT + Const.SPACE + offset + Const.COMMA + limit;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ package org.dromara.visor.common.utils;
|
||||
import cn.orionsec.kit.lang.utils.Arrays1;
|
||||
import cn.orionsec.kit.lang.utils.io.Files1;
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
@@ -159,4 +160,17 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
return Files1.getPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查后缀
|
||||
*
|
||||
* @param file file
|
||||
* @param suffix suffix
|
||||
* @return file
|
||||
*/
|
||||
public static String checkSuffix(String file, String suffix) {
|
||||
Valid.notBlank(file);
|
||||
Valid.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.3.9</revision>
|
||||
<revision>2.4.0</revision>
|
||||
<spring.boot.version>2.7.17</spring.boot.version>
|
||||
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
|
||||
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
|
||||
@@ -34,6 +34,7 @@
|
||||
<mockito.inline.version>4.11.0</mockito.inline.version>
|
||||
<jedis.mock.version>1.0.7</jedis.mock.version>
|
||||
<podam.version>7.2.11.RELEASE</podam.version>
|
||||
<guacd.version>1.5.5</guacd.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -312,6 +313,13 @@
|
||||
<artifactId>podam</artifactId>
|
||||
<version>${podam.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- guacd -->
|
||||
<dependency>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-common</artifactId>
|
||||
<version>${guacd.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -122,4 +122,9 @@ public class OperatorLogModel implements RequestIdentity {
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.function.Supplier;
|
||||
@@ -68,12 +68,10 @@ public class OrionNoRedisAutoConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
public JedisConnectionFactory jedisConnectionFactory(RedisServer redisServer) {
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
factory.setHostName(redisServer.getHost());
|
||||
factory.setPort(redisServer.getBindPort());
|
||||
factory.setUsePool(true);
|
||||
factory.setPoolConfig(new JedisPoolConfig());
|
||||
return factory;
|
||||
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
|
||||
config.setHostName(redisServer.getHost());
|
||||
config.setPort(redisServer.getBindPort());
|
||||
return new JedisConnectionFactory(config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,8 @@ public class CacheBarriers {
|
||||
|
||||
public static final GenericsBarrier<Map<?, ?>> MAP = GenericsAnonymousMapBarrier.create(Const.NONE_ID, Const.NONE_ID);
|
||||
|
||||
public static final GenericsBarrier<Map<?, ?>> STRING_MAP = GenericsAnonymousMapBarrier.create(Const.NONE_ID.toString(), Const.NONE_ID);
|
||||
|
||||
/**
|
||||
* 创建屏障对象 防止穿透
|
||||
*
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.websocket.core.constant;
|
||||
|
||||
/**
|
||||
* ws 服务端关闭 code
|
||||
* <p>
|
||||
* > 2999 && < 5000
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2021/6/16 15:18
|
||||
*/
|
||||
public interface CloseCode {
|
||||
|
||||
/**
|
||||
* code
|
||||
*
|
||||
* @return code
|
||||
*/
|
||||
int getCode();
|
||||
|
||||
/**
|
||||
* reason
|
||||
*
|
||||
* @return reason
|
||||
*/
|
||||
String getReason();
|
||||
|
||||
}
|
||||
@@ -22,27 +22,36 @@
|
||||
*/
|
||||
package org.dromara.visor.framework.websocket.core.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* ws 服务端关闭 code
|
||||
* ws 关闭码
|
||||
* <p>
|
||||
* > 2999 && < 5000
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2021/6/16 15:18
|
||||
* @since 2024/7/31 17:41
|
||||
*/
|
||||
public interface WsCloseCode {
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum WsCloseCode implements CloseCode {
|
||||
|
||||
/**
|
||||
* code
|
||||
*
|
||||
* @return code
|
||||
* 初始化失败
|
||||
*/
|
||||
int getCode();
|
||||
INIT_ERROR(3000, "init error"),
|
||||
|
||||
/**
|
||||
* reason
|
||||
*
|
||||
* @return reason
|
||||
* 会话已关闭
|
||||
*/
|
||||
String getReason();
|
||||
SESSION_CLOSED(3100, "session closed"),
|
||||
|
||||
;
|
||||
|
||||
private final int code;
|
||||
|
||||
private final String reason;
|
||||
|
||||
}
|
||||
|
||||
@@ -22,18 +22,23 @@
|
||||
*/
|
||||
package org.dromara.visor.framework.websocket.core.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.constant.StandardHttpHeader;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Threads;
|
||||
import cn.orionsec.kit.lang.utils.io.Streams;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.framework.websocket.core.constant.WsCloseCode;
|
||||
import org.dromara.visor.framework.websocket.core.constant.CloseCode;
|
||||
import org.dromara.visor.framework.websocket.core.session.WebSocketSyncSession;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* websocket 工具类
|
||||
@@ -132,18 +137,54 @@ public class WebSockets {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置子协议
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
*/
|
||||
public static void setSubProtocols(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
List<String> subProtocols = request.getHeaders().get(StandardHttpHeader.SEC_WEBSOCKET_PROTOCOL);
|
||||
if (subProtocols != null) {
|
||||
response.getHeaders().put(StandardHttpHeader.SEC_WEBSOCKET_PROTOCOL, subProtocols);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭会话
|
||||
*
|
||||
* @param session session
|
||||
*/
|
||||
public static void close(WebSocketSession session) {
|
||||
if (!session.isOpen()) {
|
||||
return;
|
||||
}
|
||||
Streams.close(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭会话
|
||||
*
|
||||
* @param session session
|
||||
* @param code code
|
||||
*/
|
||||
public static void close(WebSocketSession session, WsCloseCode code) {
|
||||
public static void close(WebSocketSession session, CloseCode code) {
|
||||
close(session, code.getCode(), code.getReason());
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭会话
|
||||
*
|
||||
* @param session session
|
||||
* @param code code
|
||||
* @param reason reason
|
||||
*/
|
||||
public static void close(WebSocketSession session, int code, String reason) {
|
||||
if (!session.isOpen()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
session.close(new CloseStatus(code.getCode(), code.getReason()));
|
||||
session.close(new CloseStatus(code, reason));
|
||||
} catch (Exception e) {
|
||||
log.error("websocket close failure", e);
|
||||
}
|
||||
|
||||
@@ -23,13 +23,26 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- starter -->
|
||||
<!-- spring boot starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- modules -->
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- module common -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-module-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- module service -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-module-infra-service</artifactId>
|
||||
@@ -40,6 +53,16 @@
|
||||
<artifactId>orion-visor-module-asset-service</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-module-exec-service</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-module-terminal-service</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- framework starter -->
|
||||
<dependency>
|
||||
|
||||
@@ -24,6 +24,11 @@ spring:
|
||||
server:
|
||||
enabled: false
|
||||
|
||||
guacd:
|
||||
host: ${GUACD_HOST:127.0.0.1}
|
||||
port: ${GUACD_PORT:4822}
|
||||
drive-path: ${GUACD_DRIVE_PATH:/home/guacd/drive}
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
@@ -33,7 +38,7 @@ management:
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
# sql 日志打印
|
||||
# 日志打印
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
no:
|
||||
|
||||
@@ -41,6 +41,11 @@ spring:
|
||||
server:
|
||||
enabled: true
|
||||
|
||||
guacd:
|
||||
host: ${GUACD_HOST:127.0.0.1}
|
||||
port: ${GUACD_PORT:4822}
|
||||
drive-path: ${GUACD_DRIVE_PATH:/usr/share/guacd/drive}
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: true
|
||||
|
||||
@@ -196,6 +196,12 @@ orion:
|
||||
asset:
|
||||
group: "asset - 资产模块"
|
||||
path: "asset"
|
||||
exec:
|
||||
group: "exec - 执行模块"
|
||||
path: "exec"
|
||||
terminal:
|
||||
group: "terminal - 终端模块"
|
||||
path: "terminal"
|
||||
logging:
|
||||
# 全局日志打印
|
||||
printer:
|
||||
@@ -208,6 +214,7 @@ orion:
|
||||
field:
|
||||
ignore:
|
||||
- password,beforePassword,newPassword,useNewPassword,publicKey,privateKey,privateKeyPassword
|
||||
- accessKey,secretKey
|
||||
- metrics
|
||||
desensitize:
|
||||
storage:
|
||||
|
||||
@@ -39,9 +39,9 @@ import java.util.function.Function;
|
||||
*/
|
||||
public class ReplaceVersion {
|
||||
|
||||
private static final String TARGET_VERSION = "2.3.8";
|
||||
private static final String TARGET_VERSION = "2.3.9";
|
||||
|
||||
private static final String REPLACE_VERSION = "2.3.9";
|
||||
private static final String REPLACE_VERSION = "2.4.0";
|
||||
|
||||
private static final String PATH = new File("").getAbsolutePath();
|
||||
|
||||
@@ -50,6 +50,7 @@ public class ReplaceVersion {
|
||||
"docker/adminer/build.sh",
|
||||
"docker/mysql/build.sh",
|
||||
"docker/redis/build.sh",
|
||||
"docker/guacd/build.sh",
|
||||
"docker/service/build.sh",
|
||||
"docker/ui/build.sh",
|
||||
"docker-compose.yml",
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.api;
|
||||
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
|
||||
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 资产模块 授权数据对外服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/12 16:13
|
||||
*/
|
||||
public interface AssetAuthorizedDataApi {
|
||||
|
||||
/**
|
||||
* 获取用户已授权&配置已启用的主机id 查询角色
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @return hostId
|
||||
*/
|
||||
List<Long> getUserAuthorizedEnabledHostId(Long userId, HostTypeEnum type);
|
||||
|
||||
/**
|
||||
* 查询用户已授权并且启用的主机
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @return group
|
||||
*/
|
||||
List<HostDTO> getUserAuthorizedHostList(Long userId, HostTypeEnum type);
|
||||
|
||||
}
|
||||
@@ -20,26 +20,35 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.terminal.handler;
|
||||
package org.dromara.visor.module.asset.api;
|
||||
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 终端消息处理器
|
||||
* 主机 对外服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 18:53
|
||||
* @since 2024/10/12 16:14
|
||||
*/
|
||||
public interface ITerminalHandler<T extends TerminalBasePayload> {
|
||||
public interface HostApi {
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
* 通过 id 查询
|
||||
*
|
||||
* @param channel channel
|
||||
* @param payload payload
|
||||
* @param id id
|
||||
* @return row
|
||||
*/
|
||||
void handle(WebSocketSession channel, T payload);
|
||||
HostDTO selectById(Long id);
|
||||
|
||||
/**
|
||||
* 通过 id 查询
|
||||
*
|
||||
* @param idList idList
|
||||
* @return rows
|
||||
*/
|
||||
List<HostDTO> selectByIdList(List<Long> idList);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.api;
|
||||
|
||||
import org.dromara.visor.common.session.config.RdpConnectConfig;
|
||||
import org.dromara.visor.common.session.config.SshConnectConfig;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
|
||||
|
||||
/**
|
||||
* 主机连接 对外服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/12 23:53
|
||||
*/
|
||||
public interface HostConnectApi {
|
||||
|
||||
/**
|
||||
* 获取 SSH 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @return session
|
||||
*/
|
||||
SshConnectConfig getSshConnectConfig(Long hostId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 SSH 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
SshConnectConfig getSshConnectConfig(Long hostId, Long userId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 SSH 连接配置
|
||||
*
|
||||
* @param host host
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
SshConnectConfig getSshConnectConfig(HostDTO host, Long userId);
|
||||
|
||||
/**
|
||||
* 获取 RDP 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @return session
|
||||
*/
|
||||
RdpConnectConfig getRdpConnectConfig(Long hostId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 RDP 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
RdpConnectConfig getRdpConnectConfig(Long hostId, Long userId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 RDP 连接配置
|
||||
*
|
||||
* @param host host
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
RdpConnectConfig getRdpConnectConfig(HostDTO host, Long userId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.entity.dto.host;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 主机基本信息 业务响应对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-9-11 14:16
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "HostBaseDTO", description = "主机基本信息 业务响应对象")
|
||||
public class HostBaseDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "主机类型")
|
||||
private String types;
|
||||
|
||||
@Schema(description = "系统类型")
|
||||
private String osType;
|
||||
|
||||
@Schema(description = "系统架构")
|
||||
private String archType;
|
||||
|
||||
@Schema(description = "主机名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "主机编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "主机端口")
|
||||
private Integer port;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.entity.dto.host;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 主机 业务对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-9-11 14:16
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "HostDTO", description = "主机 业务对象")
|
||||
public class HostDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "主机类型")
|
||||
private String types;
|
||||
|
||||
@Schema(description = "系统类型")
|
||||
private String osType;
|
||||
|
||||
@Schema(description = "系统架构")
|
||||
private String archType;
|
||||
|
||||
@Schema(description = "主机名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "主机编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "主机端口")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "主机状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
@Schema(description = "修改时间")
|
||||
private Date updateTime;
|
||||
|
||||
@Schema(description = "创建人")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "修改人")
|
||||
private String updater;
|
||||
|
||||
@Schema(description = "是否收藏")
|
||||
private Boolean favorite;
|
||||
|
||||
@Schema(description = "分组 id")
|
||||
private Set<Long> groupIdList;
|
||||
|
||||
@Schema(description = "别名")
|
||||
private String alias;
|
||||
|
||||
@Schema(description = "颜色")
|
||||
private String color;
|
||||
|
||||
/**
|
||||
* 转为 base
|
||||
*
|
||||
* @return base
|
||||
*/
|
||||
public HostBaseDTO toBase() {
|
||||
return HostBaseDTO.builder()
|
||||
.id(this.id)
|
||||
.types(this.types)
|
||||
.name(this.name)
|
||||
.code(this.code)
|
||||
.address(this.address)
|
||||
.port(this.port)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.entity.dto.host;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
import org.dromara.visor.common.security.UpdatePasswordAction;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 主机 RDP 配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/13 16:18
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "HostRdpConfigDTO", description = "主机 RDP 配置业务对象")
|
||||
public class HostRdpConfigDTO implements GenericsDataModel, UpdatePasswordAction {
|
||||
|
||||
@NotNull
|
||||
@Min(value = 1)
|
||||
@Max(value = 65535)
|
||||
@Schema(description = "主机端口")
|
||||
private Integer port;
|
||||
|
||||
@Size(max = 128)
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
@Schema(description = "认证方式")
|
||||
private String authType;
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "身份id")
|
||||
private Long identityId;
|
||||
|
||||
@Schema(description = "RDP 版本是否大于8.1")
|
||||
private Boolean versionGt81;
|
||||
|
||||
@Schema(description = "时区")
|
||||
private String timezone;
|
||||
|
||||
@Schema(description = "键盘布局")
|
||||
private String keyboardLayout;
|
||||
|
||||
@Schema(description = "剪切板规范")
|
||||
private String clipboardNormalize;
|
||||
|
||||
@Schema(description = "域")
|
||||
private String domain;
|
||||
|
||||
@Schema(description = "预连接id")
|
||||
private String preConnectionId;
|
||||
|
||||
@Schema(description = "预连接数据")
|
||||
private String preConnectionBlob;
|
||||
|
||||
@Schema(description = "远程应用")
|
||||
private String remoteApp;
|
||||
|
||||
@Schema(description = "远程应用路径")
|
||||
private String remoteAppDir;
|
||||
|
||||
@Schema(description = "远程应用参数")
|
||||
private String remoteAppArgs;
|
||||
|
||||
@Schema(description = "是否使用新密码 仅参数")
|
||||
private Boolean useNewPassword;
|
||||
|
||||
@Schema(description = "是否已设置密码 仅返回")
|
||||
private Boolean hasPassword;
|
||||
|
||||
}
|
||||
@@ -20,8 +20,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.config.model;
|
||||
package org.dromara.visor.module.asset.entity.dto.host;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -42,81 +43,58 @@ import javax.validation.constraints.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HostSshConfigModel implements GenericsDataModel, UpdatePasswordAction {
|
||||
@Schema(name = "HostSshConfigDTO", description = "主机 SSH 配置业务对象")
|
||||
public class HostSshConfigDTO implements GenericsDataModel, UpdatePasswordAction {
|
||||
|
||||
/**
|
||||
* 主机端口
|
||||
*/
|
||||
@NotNull
|
||||
@Min(value = 1)
|
||||
@Max(value = 65535)
|
||||
@Schema(description = "主机端口")
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@Size(max = 128)
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 认证方式
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
@Schema(description = "认证方式")
|
||||
private String authType;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 身份id
|
||||
*/
|
||||
@Schema(description = "身份id")
|
||||
private Long identityId;
|
||||
|
||||
/**
|
||||
* 密钥id
|
||||
*/
|
||||
@Schema(description = "密钥id")
|
||||
private Long keyId;
|
||||
|
||||
/**
|
||||
* 连接超时时间
|
||||
*/
|
||||
@NotNull
|
||||
@Min(value = 1)
|
||||
@Max(value = 100000)
|
||||
@Schema(description = "连接超时时间")
|
||||
private Integer connectTimeout;
|
||||
|
||||
/**
|
||||
* SSH输出编码
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
@Schema(description = "SSH输出编码")
|
||||
private String charset;
|
||||
|
||||
/**
|
||||
* 文件名称编码
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
@Schema(description = "文件名称编码")
|
||||
private String fileNameCharset;
|
||||
|
||||
/**
|
||||
* 文件内容编码
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
@Schema(description = "文件内容编码")
|
||||
private String fileContentCharset;
|
||||
|
||||
/**
|
||||
* 是否使用新密码 仅参数
|
||||
*/
|
||||
@Schema(description = "是否使用新密码 仅参数")
|
||||
private Boolean useNewPassword;
|
||||
|
||||
/**
|
||||
* 是否已设置密码 仅返回
|
||||
*/
|
||||
@Schema(description = "是否已设置密码 仅返回")
|
||||
private Boolean hasPassword;
|
||||
|
||||
}
|
||||
@@ -22,13 +22,11 @@
|
||||
*/
|
||||
package org.dromara.visor.module.asset.enums;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.handler.data.GenericsStrategyDefinition;
|
||||
import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
import org.dromara.visor.common.handler.data.strategy.GenericsDataStrategy;
|
||||
import org.dromara.visor.module.asset.handler.host.config.strategy.HostSshConfigStrategy;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostRdpConfigDTO;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostSshConfigDTO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -37,24 +35,28 @@ import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 主机配置类型枚举
|
||||
* 主机类型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/11 14:37
|
||||
* @since 2024/10/12 18:12
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum HostTypeEnum implements GenericsStrategyDefinition {
|
||||
public enum HostTypeEnum {
|
||||
|
||||
/**
|
||||
* SSH
|
||||
*/
|
||||
SSH(HostSshConfigStrategy.class),
|
||||
SSH(HostSshConfigDTO.class),
|
||||
|
||||
/**
|
||||
* RDP
|
||||
*/
|
||||
RDP(HostRdpConfigDTO.class),
|
||||
|
||||
;
|
||||
|
||||
private final Class<? extends GenericsDataStrategy<? extends GenericsDataModel>> strategyClass;
|
||||
private final Class<?> clazz;
|
||||
|
||||
public static HostTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
@@ -79,4 +81,9 @@ public enum HostTypeEnum implements GenericsStrategyDefinition {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T parse(String config) {
|
||||
return (T) JSON.parseObject(config, this.clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,11 @@
|
||||
<artifactId>orion-visor-module-asset-provider</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-module-exec-provider</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- framework starter -->
|
||||
<dependency>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.api.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.module.asset.api.AssetAuthorizedDataApi;
|
||||
import org.dromara.visor.module.asset.convert.HostProviderConvert;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
|
||||
import org.dromara.visor.module.asset.entity.vo.AuthorizedHostWrapperVO;
|
||||
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
||||
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 资产模块 授权数据对外服务实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/12 18:14
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AssetAuthorizedDataApiImpl implements AssetAuthorizedDataApi {
|
||||
|
||||
@Resource
|
||||
private AssetAuthorizedDataService assetAuthorizedDataService;
|
||||
|
||||
@Override
|
||||
public List<Long> getUserAuthorizedEnabledHostId(Long userId, HostTypeEnum type) {
|
||||
return assetAuthorizedDataService.getUserAuthorizedEnabledHostId(userId, type.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HostDTO> getUserAuthorizedHostList(Long userId, HostTypeEnum type) {
|
||||
AuthorizedHostWrapperVO wrapper = assetAuthorizedDataService.getUserAuthorizedHost(userId, type.name());
|
||||
return wrapper.getHostList()
|
||||
.stream()
|
||||
.map(HostProviderConvert.MAPPER::to)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.api.impl;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.module.asset.api.HostApi;
|
||||
import org.dromara.visor.module.asset.convert.HostProviderConvert;
|
||||
import org.dromara.visor.module.asset.dao.HostDAO;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 主机 对外服务实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/12 18:27
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class HostApiImpl implements HostApi {
|
||||
|
||||
@Resource
|
||||
private HostDAO hostDAO;
|
||||
|
||||
@Override
|
||||
public HostDTO selectById(Long id) {
|
||||
return HostProviderConvert.MAPPER.to(hostDAO.selectById(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HostDTO> selectByIdList(List<Long> idList) {
|
||||
if (Lists.isEmpty(idList)) {
|
||||
return Lists.empty();
|
||||
}
|
||||
return hostDAO.selectBatchIds(idList)
|
||||
.stream()
|
||||
.map(HostProviderConvert.MAPPER::to)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.api.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.session.config.RdpConnectConfig;
|
||||
import org.dromara.visor.common.session.config.SshConnectConfig;
|
||||
import org.dromara.visor.module.asset.api.HostConnectApi;
|
||||
import org.dromara.visor.module.asset.convert.HostProviderConvert;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
|
||||
import org.dromara.visor.module.asset.service.HostConnectService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 主机连接 对外服务实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/13 0:03
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class HostConnectApiImpl implements HostConnectApi {
|
||||
|
||||
@Resource
|
||||
private HostConnectService hostConnectService;
|
||||
|
||||
@Override
|
||||
public SshConnectConfig getSshConnectConfig(Long hostId) {
|
||||
return hostConnectService.getSshConnectConfig(hostId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshConnectConfig getSshConnectConfig(Long hostId, Long userId) {
|
||||
return hostConnectService.getSshConnectConfig(hostId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshConnectConfig getSshConnectConfig(HostDTO host, Long userId) {
|
||||
return hostConnectService.getSshConnectConfig(HostProviderConvert.MAPPER.to(host), userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RdpConnectConfig getRdpConnectConfig(Long hostId) {
|
||||
return hostConnectService.getRdpConnectConfig(hostId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RdpConnectConfig getRdpConnectConfig(Long hostId, Long userId) {
|
||||
return hostConnectService.getRdpConnectConfig(hostId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RdpConnectConfig getRdpConnectConfig(HostDTO host, Long userId) {
|
||||
return hostConnectService.getRdpConnectConfig(HostProviderConvert.MAPPER.to(host), userId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -143,7 +143,7 @@ public class HostController {
|
||||
@PostMapping("/count")
|
||||
@Operation(summary = "查询主机数量")
|
||||
@PreAuthorize("@ss.hasPermission('asset:host:query')")
|
||||
public Long getHostExportCount(@Validated @RequestBody HostQueryRequest request) {
|
||||
public Long getHostCount(@Validated @RequestBody HostQueryRequest request) {
|
||||
return hostService.getHostCount(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.dromara.visor.framework.log.core.annotation.IgnoreLog;
|
||||
import org.dromara.visor.framework.log.core.enums.IgnoreLogMode;
|
||||
import org.dromara.visor.framework.web.core.annotation.RestWrapper;
|
||||
import org.dromara.visor.module.asset.entity.request.host.HostExtraUpdateRequest;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.service.HostExtraService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.convert;
|
||||
|
||||
import org.dromara.visor.common.mapstruct.StringConversion;
|
||||
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostBaseDTO;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
|
||||
import org.dromara.visor.module.asset.entity.vo.HostVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* 主机 对外对象转换器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-9-11 14:16
|
||||
*/
|
||||
@Mapper(uses = StringConversion.class)
|
||||
public interface HostProviderConvert {
|
||||
|
||||
HostProviderConvert MAPPER = Mappers.getMapper(HostProviderConvert.class);
|
||||
|
||||
HostDO to(HostDTO host);
|
||||
|
||||
HostDTO to(HostDO domain);
|
||||
|
||||
HostDTO to(HostVO vo);
|
||||
|
||||
HostBaseDTO toBase(HostDO domain);
|
||||
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class HostOperatorType extends InitializingOperatorTypes {
|
||||
new OperatorType(L, UPDATE, "修改主机 <sb>${name}</sb>"),
|
||||
new OperatorType(H, DELETE, "删除主机 <sb>${count}</sb> 条"),
|
||||
new OperatorType(M, UPDATE_STATUS, "修改主机状态 <sb>${name}</sb> - <sb>${status}</sb>"),
|
||||
new OperatorType(M, UPDATE_CONFIG, "修改主机配置 <sb>${name}</sb>"),
|
||||
new OperatorType(M, UPDATE_CONFIG, "修改主机配置 <sb>${name}</sb> - <sb>${type}</sb>"),
|
||||
new OperatorType(M, UPDATE_SPEC, "修改主机规格信息 <sb>${name}</sb>"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,10 +61,16 @@ public class HostIdentityCacheDTO implements LongCacheIdModel, Serializable {
|
||||
@Schema(description = "密钥id")
|
||||
private Long keyId;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "创建时间 资产页面展示")
|
||||
/**
|
||||
* 资产页面展示
|
||||
*/
|
||||
@Schema(description = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 资产页面展示
|
||||
*/
|
||||
@Schema(description = "修改时间")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@
|
||||
package org.dromara.visor.module.asset.enums;
|
||||
|
||||
/**
|
||||
* 主机认证类型 - ssh
|
||||
* 主机认证类型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/21 19:01
|
||||
*/
|
||||
public enum HostSshAuthTypeEnum {
|
||||
public enum HostAuthTypeEnum {
|
||||
|
||||
/**
|
||||
* 密码认证
|
||||
@@ -48,11 +48,11 @@ public enum HostSshAuthTypeEnum {
|
||||
|
||||
;
|
||||
|
||||
public static HostSshAuthTypeEnum of(String type) {
|
||||
public static HostAuthTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return PASSWORD;
|
||||
}
|
||||
for (HostSshAuthTypeEnum value : values()) {
|
||||
for (HostAuthTypeEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ package org.dromara.visor.module.asset.enums;
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 21:41
|
||||
*/
|
||||
public enum HostExtraSshAuthTypeEnum {
|
||||
public enum HostExtraAuthTypeEnum {
|
||||
|
||||
/**
|
||||
* 默认认证方式
|
||||
@@ -48,11 +48,11 @@ public enum HostExtraSshAuthTypeEnum {
|
||||
|
||||
;
|
||||
|
||||
public static HostExtraSshAuthTypeEnum of(String type) {
|
||||
public static HostExtraAuthTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return DEFAULT;
|
||||
}
|
||||
for (HostExtraSshAuthTypeEnum value : values()) {
|
||||
for (HostExtraAuthTypeEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.config;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Booleans;
|
||||
import cn.orionsec.kit.lang.utils.Charsets;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
import org.dromara.visor.common.handler.data.strategy.AbstractGenericsDataStrategy;
|
||||
import org.dromara.visor.common.security.UpdatePasswordAction;
|
||||
import org.dromara.visor.common.utils.AesEncryptUtils;
|
||||
import org.dromara.visor.common.utils.RsaParamDecryptUtils;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.module.asset.enums.HostAuthTypeEnum;
|
||||
|
||||
/**
|
||||
* 主机配置策略基类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/3/31 19:44
|
||||
*/
|
||||
public abstract class AbstractHostConfigStrategy<T extends GenericsDataModel> extends AbstractGenericsDataStrategy<T> {
|
||||
|
||||
public AbstractHostConfigStrategy(Class<T> modelClass) {
|
||||
super(modelClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查加密密码
|
||||
*
|
||||
* @param before before
|
||||
* @param after after
|
||||
*/
|
||||
protected void checkEncryptPassword(String authType, UpdatePasswordAction before, UpdatePasswordAction after) {
|
||||
// 非密码认证/使用原始密码则直接赋值
|
||||
if (!HostAuthTypeEnum.PASSWORD.name().equals(authType) || !Booleans.isTrue(after.getUseNewPassword())) {
|
||||
if (before != null) {
|
||||
after.setPassword(before.getPassword());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 检查新密码
|
||||
String newPassword = Valid.notBlank(after.getPassword(), ErrorMessage.PASSWORD_MISSING);
|
||||
// 解密密码
|
||||
newPassword = RsaParamDecryptUtils.decrypt(newPassword);
|
||||
Valid.notBlank(newPassword, ErrorMessage.DECRYPT_ERROR);
|
||||
// 设置密码
|
||||
after.setPassword(AesEncryptUtils.encryptAsString(newPassword));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查编码格式
|
||||
*
|
||||
* @param charset charset
|
||||
*/
|
||||
protected void validCharset(String charset) {
|
||||
Valid.isTrue(Charsets.isSupported(charset), ErrorMessage.UNSUPPORTED_CHARSET, charset);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.visor.common.handler.data.GenericsStrategyDefinition;
|
||||
import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
import org.dromara.visor.common.handler.data.strategy.GenericsDataStrategy;
|
||||
|
||||
/**
|
||||
* 主机配置类型策略枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/11 14:37
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum HostConfigStrategyEnum implements GenericsStrategyDefinition {
|
||||
|
||||
/**
|
||||
* SSH
|
||||
*/
|
||||
SSH(HostSshConfigStrategy.class),
|
||||
|
||||
/**
|
||||
* RDP
|
||||
*/
|
||||
RDP(HostRdpConfigStrategy.class),
|
||||
|
||||
;
|
||||
|
||||
private final Class<? extends GenericsDataStrategy<? extends GenericsDataModel>> strategyClass;
|
||||
|
||||
public static HostConfigStrategyEnum of(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (HostConfigStrategyEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.config;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.module.asset.dao.HostIdentityDAO;
|
||||
import org.dromara.visor.module.asset.entity.domain.HostIdentityDO;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostRdpConfigDTO;
|
||||
import org.dromara.visor.module.asset.enums.HostAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostIdentityTypeEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 主机 RDP 配置策略
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/19 14:26
|
||||
*/
|
||||
@Component
|
||||
public class HostRdpConfigStrategy extends AbstractHostConfigStrategy<HostRdpConfigDTO> {
|
||||
|
||||
@Resource
|
||||
private HostIdentityDAO hostIdentityDAO;
|
||||
|
||||
public HostRdpConfigStrategy() {
|
||||
super(HostRdpConfigDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostRdpConfigDTO getDefault() {
|
||||
return HostRdpConfigDTO.builder()
|
||||
.port(3389)
|
||||
.username(Const.ADMINISTRATOR)
|
||||
.authType(HostAuthTypeEnum.PASSWORD.name())
|
||||
.versionGt81(true)
|
||||
.timezone("Asia/Shanghai")
|
||||
.keyboardLayout("en-us-qwerty")
|
||||
.clipboardNormalize("preserve")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preValid(HostRdpConfigDTO model) {
|
||||
// 检查主机身份是否存在
|
||||
Long identityId = model.getIdentityId();
|
||||
if (identityId != null) {
|
||||
HostIdentityDO identity = Valid.notNull(hostIdentityDAO.selectById(identityId), ErrorMessage.IDENTITY_ABSENT);
|
||||
Valid.eq(HostIdentityTypeEnum.PASSWORD.name(), identity.getType(), ErrorMessage.CHECK_IDENTITY_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void valid(HostRdpConfigDTO model) {
|
||||
// 验证填充后的参数
|
||||
Valid.valid(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFill(HostRdpConfigDTO beforeModel, HostRdpConfigDTO afterModel) {
|
||||
// 加密密码
|
||||
this.checkEncryptPassword(afterModel.getAuthType(), beforeModel, afterModel);
|
||||
afterModel.setHasPassword(null);
|
||||
afterModel.setUseNewPassword(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostRdpConfigDTO parse(String serialModel) {
|
||||
return HostTypeEnum.RDP.parse(serialModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toView(HostRdpConfigDTO model) {
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
model.setHasPassword(Strings.isNotBlank(model.getPassword()));
|
||||
model.setPassword(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,21 +20,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.config.strategy;
|
||||
package org.dromara.visor.module.asset.handler.host.config;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Booleans;
|
||||
import cn.orionsec.kit.lang.utils.Charsets;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.handler.data.strategy.AbstractGenericsDataStrategy;
|
||||
import org.dromara.visor.common.utils.AesEncryptUtils;
|
||||
import org.dromara.visor.common.utils.RsaParamDecryptUtils;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.module.asset.dao.HostIdentityDAO;
|
||||
import org.dromara.visor.module.asset.dao.HostKeyDAO;
|
||||
import org.dromara.visor.module.asset.enums.HostSshAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.config.model.HostSshConfigModel;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostSshConfigDTO;
|
||||
import org.dromara.visor.module.asset.enums.HostAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -47,7 +43,7 @@ import javax.annotation.Resource;
|
||||
* @since 2023/9/19 14:26
|
||||
*/
|
||||
@Component
|
||||
public class HostSshConfigStrategy extends AbstractGenericsDataStrategy<HostSshConfigModel> {
|
||||
public class HostSshConfigStrategy extends AbstractHostConfigStrategy<HostSshConfigDTO> {
|
||||
|
||||
@Resource
|
||||
private HostKeyDAO hostKeyDAO;
|
||||
@@ -55,18 +51,16 @@ public class HostSshConfigStrategy extends AbstractGenericsDataStrategy<HostSshC
|
||||
@Resource
|
||||
private HostIdentityDAO hostIdentityDAO;
|
||||
|
||||
private static final String USERNAME = "root";
|
||||
|
||||
public HostSshConfigStrategy() {
|
||||
super(HostSshConfigModel.class);
|
||||
super(HostSshConfigDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostSshConfigModel getDefault() {
|
||||
return HostSshConfigModel.builder()
|
||||
public HostSshConfigDTO getDefault() {
|
||||
return HostSshConfigDTO.builder()
|
||||
.port(22)
|
||||
.username(USERNAME)
|
||||
.authType(HostSshAuthTypeEnum.PASSWORD.name())
|
||||
.username(Const.ROOT)
|
||||
.authType(HostAuthTypeEnum.PASSWORD.name())
|
||||
.connectTimeout(Const.MS_S_10)
|
||||
.charset(Const.UTF_8)
|
||||
.fileNameCharset(Const.UTF_8)
|
||||
@@ -75,7 +69,7 @@ public class HostSshConfigStrategy extends AbstractGenericsDataStrategy<HostSshC
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preValid(HostSshConfigModel model) {
|
||||
protected void preValid(HostSshConfigDTO model) {
|
||||
// 验证编码格式
|
||||
this.validCharset(model.getCharset());
|
||||
this.validCharset(model.getFileNameCharset());
|
||||
@@ -93,21 +87,26 @@ public class HostSshConfigStrategy extends AbstractGenericsDataStrategy<HostSshC
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void valid(HostSshConfigModel model) {
|
||||
protected void valid(HostSshConfigDTO model) {
|
||||
// 验证填充后的参数
|
||||
Valid.valid(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFill(HostSshConfigModel beforeModel, HostSshConfigModel afterModel) {
|
||||
protected void updateFill(HostSshConfigDTO beforeModel, HostSshConfigDTO afterModel) {
|
||||
// 加密密码
|
||||
this.checkEncryptPassword(beforeModel, afterModel);
|
||||
this.checkEncryptPassword(afterModel.getAuthType(), beforeModel, afterModel);
|
||||
afterModel.setHasPassword(null);
|
||||
afterModel.setUseNewPassword(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toView(HostSshConfigModel model) {
|
||||
public HostSshConfigDTO parse(String serialModel) {
|
||||
return HostTypeEnum.SSH.parse(serialModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toView(HostSshConfigDTO model) {
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
@@ -115,37 +114,4 @@ public class HostSshConfigStrategy extends AbstractGenericsDataStrategy<HostSshC
|
||||
model.setPassword(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查加密密码
|
||||
*
|
||||
* @param before before
|
||||
* @param after after
|
||||
*/
|
||||
private void checkEncryptPassword(HostSshConfigModel before, HostSshConfigModel after) {
|
||||
// 非密码认证/使用原始密码则直接赋值
|
||||
if (!HostSshAuthTypeEnum.PASSWORD.name().equals(after.getAuthType())
|
||||
|| !Booleans.isTrue(after.getUseNewPassword())) {
|
||||
if (before != null) {
|
||||
after.setPassword(before.getPassword());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 检查新密码
|
||||
String newPassword = Valid.notBlank(after.getPassword(), ErrorMessage.PASSWORD_MISSING);
|
||||
// 解密密码
|
||||
newPassword = RsaParamDecryptUtils.decrypt(newPassword);
|
||||
Valid.notBlank(newPassword, ErrorMessage.DECRYPT_ERROR);
|
||||
// 设置密码
|
||||
after.setPassword(AesEncryptUtils.encryptAsString(newPassword));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查编码格式
|
||||
*
|
||||
* @param charset charset
|
||||
*/
|
||||
private void validCharset(String charset) {
|
||||
Valid.isTrue(Charsets.isSupported(charset), ErrorMessage.UNSUPPORTED_CHARSET, charset);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.enums;
|
||||
package org.dromara.visor.module.asset.handler.host.extra;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -28,11 +28,12 @@ import org.dromara.visor.common.handler.data.GenericsStrategyDefinition;
|
||||
import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
import org.dromara.visor.common.handler.data.strategy.GenericsDataStrategy;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.strategy.HostLabelExtraStrategy;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.strategy.HostRdpExtraStrategy;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.strategy.HostSpecExtraStrategy;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.strategy.HostSshExtraStrategy;
|
||||
|
||||
/**
|
||||
* 主机额外配置项枚举
|
||||
* 主机额外配置项策略枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
@@ -42,15 +43,20 @@ import org.dromara.visor.module.asset.handler.host.extra.strategy.HostSshExtraSt
|
||||
@AllArgsConstructor
|
||||
public enum HostExtraItemEnum implements GenericsStrategyDefinition {
|
||||
|
||||
/**
|
||||
* 标签额外配置
|
||||
*/
|
||||
LABEL(HostLabelExtraStrategy.class, true),
|
||||
|
||||
/**
|
||||
* SSH 额外配置
|
||||
*/
|
||||
SSH(HostSshExtraStrategy.class, true),
|
||||
|
||||
/**
|
||||
* 标签额外配置
|
||||
* RDP 额外配置
|
||||
*/
|
||||
LABEL(HostLabelExtraStrategy.class, true),
|
||||
RDP(HostRdpExtraStrategy.class, true),
|
||||
|
||||
/**
|
||||
* 规格信息配置
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.extra.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - rdp 模型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 21:36
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HostRdpExtraModel implements GenericsDataModel {
|
||||
|
||||
/**
|
||||
* 认证方式
|
||||
*/
|
||||
private String authType;
|
||||
|
||||
/**
|
||||
* 主机身份
|
||||
*/
|
||||
private Long identityId;
|
||||
|
||||
/**
|
||||
* 低带宽模式
|
||||
*/
|
||||
private Boolean lowBandwidthMode;
|
||||
|
||||
}
|
||||
@@ -53,12 +53,14 @@ public class HostLabelExtraStrategy extends AbstractGenericsDataStrategy<HostLab
|
||||
|
||||
@Override
|
||||
public void updateFill(HostLabelExtraModel beforeModel, HostLabelExtraModel afterModel) {
|
||||
// 为空则覆盖
|
||||
if (afterModel.getAlias() == null) {
|
||||
afterModel.setAlias(beforeModel.getAlias());
|
||||
}
|
||||
if (afterModel.getColor() == null) {
|
||||
afterModel.setColor(beforeModel.getColor());
|
||||
if (beforeModel != null) {
|
||||
// 为空则覆盖
|
||||
if (afterModel.getAlias() == null) {
|
||||
afterModel.setAlias(beforeModel.getAlias());
|
||||
}
|
||||
if (afterModel.getColor() == null) {
|
||||
afterModel.setColor(beforeModel.getColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.extra.strategy;
|
||||
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.handler.data.strategy.AbstractGenericsDataStrategy;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.framework.security.core.utils.SecurityUtils;
|
||||
import org.dromara.visor.module.asset.dao.HostIdentityDAO;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostRdpExtraModel;
|
||||
import org.dromara.visor.module.infra.api.DataPermissionApi;
|
||||
import org.dromara.visor.module.infra.enums.DataPermissionTypeEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - rdp 模型处理策略
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 22:17
|
||||
*/
|
||||
@Component
|
||||
public class HostRdpExtraStrategy extends AbstractGenericsDataStrategy<HostRdpExtraModel> {
|
||||
|
||||
@Resource
|
||||
private HostIdentityDAO hostIdentityDAO;
|
||||
|
||||
@Resource
|
||||
private DataPermissionApi dataPermissionApi;
|
||||
|
||||
public HostRdpExtraStrategy() {
|
||||
super(HostRdpExtraModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostRdpExtraModel getDefault() {
|
||||
return HostRdpExtraModel.builder()
|
||||
.authType(HostExtraAuthTypeEnum.DEFAULT.name())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preValid(HostRdpExtraModel model) {
|
||||
HostExtraAuthTypeEnum authType = Valid.valid(HostExtraAuthTypeEnum::of, model.getAuthType());
|
||||
model.setAuthType(authType.name());
|
||||
Long identityId = model.getIdentityId();
|
||||
Long userId = SecurityUtils.getLoginUserId();
|
||||
// 必填验证
|
||||
if (HostExtraAuthTypeEnum.CUSTOM_IDENTITY.equals(authType)) {
|
||||
Valid.notNull(identityId);
|
||||
// 验证主机身份是否存在
|
||||
Valid.notNull(hostIdentityDAO.selectById(identityId), ErrorMessage.IDENTITY_ABSENT);
|
||||
// 验证主机身份是否有权限
|
||||
Valid.isTrue(dataPermissionApi.hasPermission(DataPermissionTypeEnum.HOST_IDENTITY, userId, identityId),
|
||||
ErrorMessage.ANY_NO_PERMISSION,
|
||||
DataPermissionTypeEnum.HOST_IDENTITY.getPermissionName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.framework.security.core.utils.SecurityUtils;
|
||||
import org.dromara.visor.module.asset.dao.HostIdentityDAO;
|
||||
import org.dromara.visor.module.asset.dao.HostKeyDAO;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraSshAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostSshExtraModel;
|
||||
import org.dromara.visor.module.infra.api.DataPermissionApi;
|
||||
import org.dromara.visor.module.infra.enums.DataPermissionTypeEnum;
|
||||
@@ -62,20 +62,20 @@ public class HostSshExtraStrategy extends AbstractGenericsDataStrategy<HostSshEx
|
||||
@Override
|
||||
public HostSshExtraModel getDefault() {
|
||||
return HostSshExtraModel.builder()
|
||||
.authType(HostExtraSshAuthTypeEnum.DEFAULT.name())
|
||||
.authType(HostExtraAuthTypeEnum.DEFAULT.name())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preValid(HostSshExtraModel model) {
|
||||
HostExtraSshAuthTypeEnum authType = Valid.valid(HostExtraSshAuthTypeEnum::of, model.getAuthType());
|
||||
HostExtraAuthTypeEnum authType = Valid.valid(HostExtraAuthTypeEnum::of, model.getAuthType());
|
||||
model.setAuthType(authType.name());
|
||||
Long keyId = model.getKeyId();
|
||||
Long identityId = model.getIdentityId();
|
||||
// 必填验证
|
||||
if (HostExtraSshAuthTypeEnum.CUSTOM_KEY.equals(authType)) {
|
||||
if (HostExtraAuthTypeEnum.CUSTOM_KEY.equals(authType)) {
|
||||
Valid.notNull(keyId);
|
||||
} else if (HostExtraSshAuthTypeEnum.CUSTOM_IDENTITY.equals(authType)) {
|
||||
} else if (HostExtraAuthTypeEnum.CUSTOM_IDENTITY.equals(authType)) {
|
||||
Valid.notNull(identityId);
|
||||
}
|
||||
// 验证主机密钥是否存在
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.terminal;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.module.asset.define.AssetThreadPools;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.enums.InputTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.manager.TerminalManager;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 终端处理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/28 14:33
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TerminalMessageDispatcher extends AbstractWebSocketHandler {
|
||||
|
||||
@Resource
|
||||
private TerminalManager terminalManager;
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
String payload = message.getPayload();
|
||||
try {
|
||||
// 解析类型
|
||||
InputTypeEnum type = InputTypeEnum.of(payload);
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
// 解析并处理消息
|
||||
if (type.isAsyncExec()) {
|
||||
// 异步执行
|
||||
AssetThreadPools.TERMINAL_OPERATOR.execute(() -> {
|
||||
type.getHandler().handle(session, type.parse(payload));
|
||||
});
|
||||
} else {
|
||||
// 同步执行
|
||||
type.getHandler().handle(session, type.parse(payload));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("TerminalDispatchHandler-handleMessage-error id: {}, msg: {}", session.getId(), payload, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
log.info("TerminalMessageDispatcher-afterConnectionEstablished id: {}", session.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession session, Throwable exception) {
|
||||
log.error("TerminalMessageDispatcher-handleTransportError id: {}", session.getId(), exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
String id = session.getId();
|
||||
log.info("TerminalMessageDispatcher-afterConnectionClosed id: {}, code: {}, reason: {}", id, status.getCode(), status.getReason());
|
||||
// 关闭会话
|
||||
terminalManager.closeSession(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.terminal.handler;
|
||||
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.model.OperatorLogModel;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.service.OperatorLogFrameworkService;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import org.dromara.visor.framework.websocket.core.utils.WebSockets;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.manager.TerminalManager;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.session.ITerminalSession;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.utils.TerminalUtils;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 终端消息处理器 基类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 18:59
|
||||
*/
|
||||
public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> implements ITerminalHandler<T> {
|
||||
|
||||
@Resource
|
||||
protected TerminalManager terminalManager;
|
||||
|
||||
@Resource
|
||||
private OperatorLogFrameworkService operatorLogFrameworkService;
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param type type
|
||||
* @param body body
|
||||
* @param <E> E
|
||||
*/
|
||||
public <E extends TerminalBasePayload> void send(WebSocketSession channel, OutputTypeEnum type, E body) {
|
||||
body.setType(type.getType());
|
||||
// 发送消息
|
||||
this.send(channel, type.format(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param message message
|
||||
*/
|
||||
protected void send(WebSocketSession channel, String message) {
|
||||
WebSockets.sendText(channel, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存操作日志
|
||||
*
|
||||
* @param payload payload
|
||||
* @param channel channel
|
||||
* @param extra extra
|
||||
* @param type type
|
||||
* @param startTime startTime
|
||||
* @param ex ex
|
||||
*/
|
||||
protected void saveOperatorLog(T payload,
|
||||
WebSocketSession channel,
|
||||
Map<String, Object> extra,
|
||||
String type,
|
||||
long startTime,
|
||||
Exception ex) {
|
||||
String channelId = channel.getId();
|
||||
String sessionId = payload.getSessionId();
|
||||
// 获取会话并且设置参数
|
||||
ITerminalSession session = terminalManager.getSession(channelId, sessionId);
|
||||
if (session != null) {
|
||||
TerminalConfig config = session.getConfig();
|
||||
extra.put(OperatorLogs.HOST_ID, config.getHostId());
|
||||
extra.put(OperatorLogs.HOST_NAME, config.getHostName());
|
||||
extra.put(OperatorLogs.ADDRESS, config.getAddress());
|
||||
}
|
||||
extra.put(OperatorLogs.CHANNEL_ID, channelId);
|
||||
extra.put(OperatorLogs.SESSION_ID, sessionId);
|
||||
// 获取日志
|
||||
OperatorLogModel model = TerminalUtils.getOperatorLogModel(channel, extra, type, startTime, ex);
|
||||
// 保存
|
||||
operatorLogFrameworkService.insert(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param ex ex
|
||||
* @return msg
|
||||
*/
|
||||
protected String getErrorMessage(Exception ex) {
|
||||
// 获取错误信息
|
||||
return ErrorMessage.getErrorMessage(ex, ErrorMessage.OPERATE_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.terminal.handler;
|
||||
|
||||
import cn.orionsec.kit.lang.exception.DisabledException;
|
||||
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.constant.ExtraFieldConst;
|
||||
import org.dromara.visor.common.enums.BooleanBit;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.model.OperatorLogModel;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.service.OperatorLogFrameworkService;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import org.dromara.visor.framework.websocket.core.utils.WebSockets;
|
||||
import org.dromara.visor.module.asset.dao.HostDAO;
|
||||
import org.dromara.visor.module.asset.define.operator.TerminalOperatorType;
|
||||
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
||||
import org.dromara.visor.module.asset.entity.domain.TerminalConnectLogDO;
|
||||
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
|
||||
import org.dromara.visor.module.asset.entity.request.host.TerminalConnectLogCreateRequest;
|
||||
import org.dromara.visor.module.asset.enums.TerminalConnectStatusEnum;
|
||||
import org.dromara.visor.module.asset.enums.TerminalConnectTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.request.TerminalCheckRequest;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.response.TerminalCheckResponse;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.session.ITerminalSession;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.utils.TerminalUtils;
|
||||
import org.dromara.visor.module.asset.service.HostConnectService;
|
||||
import org.dromara.visor.module.asset.service.TerminalConnectLogService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 终端连接检查
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 15:32
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckRequest> {
|
||||
|
||||
@Resource
|
||||
private HostDAO hostDAO;
|
||||
|
||||
@Resource
|
||||
private HostConnectService hostConnectService;
|
||||
|
||||
@Resource
|
||||
private TerminalConnectLogService terminalConnectLogService;
|
||||
|
||||
@Resource
|
||||
private OperatorLogFrameworkService operatorLogFrameworkService;
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, TerminalCheckRequest payload) {
|
||||
Long hostId = payload.getHostId();
|
||||
Long userId = WebSockets.getAttr(channel, ExtraFieldConst.USER_ID);
|
||||
long startTime = System.currentTimeMillis();
|
||||
TerminalConnectTypeEnum connectType = TerminalConnectTypeEnum.of(payload.getConnectType());
|
||||
String sessionId = payload.getSessionId();
|
||||
log.info("TerminalCheckHandler-handle start userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
|
||||
// 检查 session 是否存在
|
||||
if (this.checkSession(channel, payload)) {
|
||||
log.info("TerminalCheckHandler-handle present session userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
|
||||
return;
|
||||
}
|
||||
// 获取主机信息
|
||||
HostDO host = this.checkHost(channel, payload, hostId);
|
||||
if (host == null) {
|
||||
log.info("TerminalCheckHandler-handle unknown host userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
|
||||
return;
|
||||
}
|
||||
TerminalConnectDTO connect = null;
|
||||
Exception ex = null;
|
||||
try {
|
||||
// 获取连接信息
|
||||
connect = hostConnectService.getSshConnectInfo(host, userId);
|
||||
connect.setConnectType(connectType.name());
|
||||
// 设置到缓存中
|
||||
channel.getAttributes().put(sessionId, connect);
|
||||
log.info("TerminalCheckHandler-handle success userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
|
||||
} catch (InvalidArgumentException e) {
|
||||
ex = 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(TerminalMessage.CONNECTION_FAILED);
|
||||
log.error("TerminalCheckHandler-handle exception userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
|
||||
}
|
||||
// 记录主机日志
|
||||
TerminalConnectLogDO connectLog = this.saveHostLog(channel, userId, host, startTime, ex, sessionId, connectType);
|
||||
if (connect != null) {
|
||||
connect.setLogId(connectLog.getId());
|
||||
}
|
||||
// 响应检查结果
|
||||
this.send(channel,
|
||||
OutputTypeEnum.CHECK,
|
||||
TerminalCheckResponse.builder()
|
||||
.sessionId(payload.getSessionId())
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(ex == null ? null : ex.getMessage())
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查会话是否存在
|
||||
*
|
||||
* @param channel channel
|
||||
* @param payload payload
|
||||
* @return 是否存在
|
||||
*/
|
||||
private boolean checkSession(WebSocketSession channel, TerminalCheckRequest payload) {
|
||||
ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
if (session != null) {
|
||||
this.sendCheckFailedMessage(channel, payload, ErrorMessage.SESSION_PRESENT);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机信息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param payload payload
|
||||
* @param hostId hostId
|
||||
* @return host
|
||||
*/
|
||||
private HostDO checkHost(WebSocketSession channel, TerminalCheckRequest payload, Long hostId) {
|
||||
// 查询主机信息
|
||||
HostDO host = hostDAO.selectById(hostId);
|
||||
// 不存在返回错误信息
|
||||
if (host == null) {
|
||||
this.sendCheckFailedMessage(channel, payload, ErrorMessage.HOST_ABSENT);
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送检查失败消息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param payload payload
|
||||
* @param msg msg
|
||||
*/
|
||||
private void sendCheckFailedMessage(WebSocketSession channel, TerminalCheckRequest payload, String msg) {
|
||||
TerminalCheckResponse resp = TerminalCheckResponse.builder()
|
||||
.sessionId(payload.getSessionId())
|
||||
.result(BooleanBit.FALSE.getValue())
|
||||
.msg(msg)
|
||||
.build();
|
||||
// 发送
|
||||
this.send(channel, OutputTypeEnum.CHECK, resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录主机日志
|
||||
*
|
||||
* @param channel channel
|
||||
* @param userId userId
|
||||
* @param host host
|
||||
* @param startTime startTime
|
||||
* @param ex ex
|
||||
* @param sessionId sessionId
|
||||
* @param connectType connectType
|
||||
* @return connectLog
|
||||
*/
|
||||
private TerminalConnectLogDO saveHostLog(WebSocketSession channel,
|
||||
Long userId,
|
||||
HostDO host,
|
||||
long startTime,
|
||||
Exception ex,
|
||||
String sessionId,
|
||||
TerminalConnectTypeEnum connectType) {
|
||||
Long hostId = host.getId();
|
||||
String hostName = host.getName();
|
||||
String username = WebSockets.getAttr(channel, ExtraFieldConst.USERNAME);
|
||||
// 额外参数
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.HOST_ID, hostId);
|
||||
extra.put(OperatorLogs.HOST_NAME, hostName);
|
||||
extra.put(OperatorLogs.CONNECT_TYPE, connectType.name());
|
||||
extra.put(OperatorLogs.CHANNEL_ID, channel.getId());
|
||||
extra.put(OperatorLogs.SESSION_ID, sessionId);
|
||||
// 日志参数
|
||||
OperatorLogModel logModel = TerminalUtils.getOperatorLogModel(channel, extra,
|
||||
TerminalOperatorType.CONNECT, startTime, ex);
|
||||
// 记录操作日志
|
||||
operatorLogFrameworkService.insert(logModel);
|
||||
// 记录连接日志
|
||||
TerminalConnectLogCreateRequest connectLog = TerminalConnectLogCreateRequest.builder()
|
||||
.userId(userId)
|
||||
.username(username)
|
||||
.hostId(hostId)
|
||||
.hostName(hostName)
|
||||
.hostAddress(host.getAddress())
|
||||
.status(ex == null ? TerminalConnectStatusEnum.CONNECTING.name() : TerminalConnectStatusEnum.FAILED.name())
|
||||
.sessionId(sessionId)
|
||||
.extra(extra)
|
||||
.build();
|
||||
// 填充其他信息
|
||||
extra.put(OperatorLogs.TRACE_ID, logModel.getTraceId());
|
||||
extra.put(OperatorLogs.ADDRESS, logModel.getAddress());
|
||||
extra.put(OperatorLogs.LOCATION, logModel.getLocation());
|
||||
extra.put(OperatorLogs.USER_AGENT, logModel.getUserAgent());
|
||||
extra.put(OperatorLogs.ERROR_MESSAGE, logModel.getErrorMessage());
|
||||
// 记录连接日志
|
||||
return terminalConnectLogService.create(connectType, connectLog);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.terminal.handler;
|
||||
|
||||
import cn.orionsec.kit.lang.exception.AuthenticationException;
|
||||
import cn.orionsec.kit.lang.exception.TimeoutException;
|
||||
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import cn.orionsec.kit.lang.utils.io.Streams;
|
||||
import cn.orionsec.kit.net.host.SessionStore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.constant.ExtraFieldConst;
|
||||
import org.dromara.visor.common.enums.BooleanBit;
|
||||
import org.dromara.visor.framework.websocket.core.utils.WebSockets;
|
||||
import org.dromara.visor.module.asset.define.config.AppSftpConfig;
|
||||
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
|
||||
import org.dromara.visor.module.asset.enums.TerminalConnectStatusEnum;
|
||||
import org.dromara.visor.module.asset.enums.TerminalConnectTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.jsch.SessionStores;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.request.TerminalConnectRequest;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.response.TerminalConnectResponse;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.session.ITerminalSession;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.session.SftpSession;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.session.SshSession;
|
||||
import org.dromara.visor.module.asset.service.TerminalConnectLogService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 连接主机处理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 15:32
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConnectRequest> {
|
||||
|
||||
@Resource
|
||||
private AppSftpConfig appSftpConfig;
|
||||
|
||||
@Resource
|
||||
private TerminalConnectLogService terminalConnectLogService;
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, TerminalConnectRequest payload) {
|
||||
String sessionId = payload.getSessionId();
|
||||
log.info("TerminalConnectHandler-handle start sessionId: {}", sessionId);
|
||||
// 获取终端连接信息
|
||||
TerminalConnectDTO connect = WebSockets.getAttr(channel, sessionId);
|
||||
if (connect == null) {
|
||||
log.info("TerminalConnectHandler-handle unknown sessionId: {}", sessionId);
|
||||
this.send(channel,
|
||||
OutputTypeEnum.CONNECT,
|
||||
TerminalConnectResponse.builder()
|
||||
.sessionId(payload.getSessionId())
|
||||
.result(BooleanBit.FALSE.getValue())
|
||||
.msg(ErrorMessage.SESSION_ABSENT)
|
||||
.build());
|
||||
return;
|
||||
}
|
||||
// 移除会话连接信息
|
||||
channel.getAttributes().remove(sessionId);
|
||||
Exception ex = null;
|
||||
ITerminalSession session = null;
|
||||
try {
|
||||
// 连接主机
|
||||
session = this.connect(sessionId, connect, channel, payload);
|
||||
// 添加会话到 manager
|
||||
terminalManager.addSession(session);
|
||||
} catch (Exception e) {
|
||||
ex = e;
|
||||
Streams.close(session);
|
||||
// 修改连接状态为失败
|
||||
Map<String, Object> extra = Maps.newMap(4);
|
||||
extra.put(ExtraFieldConst.ERROR_MESSAGE, this.getConnectErrorMessage(e));
|
||||
terminalConnectLogService.updateStatusById(connect.getLogId(), TerminalConnectStatusEnum.FAILED, extra);
|
||||
}
|
||||
// 返回连接状态
|
||||
this.send(channel,
|
||||
OutputTypeEnum.CONNECT,
|
||||
TerminalConnectResponse.builder()
|
||||
.sessionId(payload.getSessionId())
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getConnectErrorMessage(ex))
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接主机
|
||||
*
|
||||
* @param sessionId sessionId
|
||||
* @param connect connect
|
||||
* @param channel channel
|
||||
* @param body body
|
||||
* @return channel
|
||||
*/
|
||||
private ITerminalSession connect(String sessionId,
|
||||
TerminalConnectDTO connect,
|
||||
WebSocketSession channel,
|
||||
TerminalConnectRequest body) {
|
||||
String connectType = connect.getConnectType();
|
||||
ITerminalSession session = null;
|
||||
try {
|
||||
// 连接配置
|
||||
TerminalConfig config = TerminalConfig.builder()
|
||||
.logId(connect.getLogId())
|
||||
.hostId(connect.getHostId())
|
||||
.hostName(connect.getHostName())
|
||||
.address(connect.getHostAddress())
|
||||
.charset(connect.getCharset())
|
||||
.fileNameCharset(connect.getFileNameCharset())
|
||||
.fileContentCharset(connect.getFileContentCharset())
|
||||
.filePreviewSize(appSftpConfig.getPreviewSize())
|
||||
.build();
|
||||
// 建立连接
|
||||
SessionStore sessionStore = SessionStores.openSessionStore(connect);
|
||||
if (TerminalConnectTypeEnum.SSH.name().equals(connectType)) {
|
||||
// 打开 ssh 会话
|
||||
SshSession sshSession = new SshSession(sessionId, channel, sessionStore, config);
|
||||
sshSession.connect(body.getTerminalType(), body.getCols(), body.getRows());
|
||||
session = sshSession;
|
||||
} else if (TerminalConnectTypeEnum.SFTP.name().equals(connectType)) {
|
||||
// 打开 sftp 会话
|
||||
SftpSession sftpSession = new SftpSession(sessionId, channel, sessionStore, config);
|
||||
sftpSession.connect();
|
||||
session = sftpSession;
|
||||
}
|
||||
log.info("TerminalConnectHandler-handle success sessionId: {}", sessionId);
|
||||
return session;
|
||||
} catch (Exception e) {
|
||||
Streams.close(session);
|
||||
log.error("TerminalConnectHandler-handle error sessionId: {}", sessionId, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建立连接错误信息
|
||||
*
|
||||
* @param e e
|
||||
* @return errorMessage
|
||||
*/
|
||||
private String getConnectErrorMessage(Exception e) {
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
if (Exceptions.isCausedBy(e, TimeoutException.class)) {
|
||||
// 连接超时
|
||||
return TerminalMessage.CONNECTION_TIMEOUT;
|
||||
} else if (Exceptions.isCausedBy(e, AuthenticationException.class)) {
|
||||
// 认证失败
|
||||
return TerminalMessage.AUTHENTICATION_FAILURE;
|
||||
} else if (Exceptions.isCausedBy(e, InvalidArgumentException.class)) {
|
||||
// 参数错误
|
||||
return e.getMessage();
|
||||
} else {
|
||||
// 其他错误
|
||||
return TerminalMessage.SERVER_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.terminal.manager;
|
||||
|
||||
import cn.orionsec.kit.lang.define.collect.MultiConcurrentHashMap;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import cn.orionsec.kit.lang.utils.io.Streams;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.session.ITerminalSession;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 终端管理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/1/3 11:35
|
||||
*/
|
||||
@Component
|
||||
public class TerminalManager {
|
||||
|
||||
/**
|
||||
* 会话存储器
|
||||
*/
|
||||
private final MultiConcurrentHashMap<String, String, ITerminalSession> channelSessions = MultiConcurrentHashMap.create();
|
||||
|
||||
/**
|
||||
* 添加会话
|
||||
*
|
||||
* @param session session
|
||||
*/
|
||||
public void addSession(ITerminalSession session) {
|
||||
channelSessions.put(session.getChannelId(), session.getSessionId(), session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 channel 关闭会话
|
||||
*
|
||||
* @param channelId channelId
|
||||
*/
|
||||
public void closeSession(String channelId) {
|
||||
// 获取并移除
|
||||
ConcurrentHashMap<String, ITerminalSession> session = channelSessions.remove(channelId);
|
||||
if (!Maps.isEmpty(session)) {
|
||||
session.values().forEach(Streams::close);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 channel + sessionId 关闭会话
|
||||
*
|
||||
* @param channelId channelId
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
public void closeSession(String channelId, String sessionId) {
|
||||
// 获取并移除
|
||||
ITerminalSession session = channelSessions.removeElement(channelId, sessionId);
|
||||
if (session != null) {
|
||||
Streams.close(session);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话
|
||||
*
|
||||
* @param channelId channelId
|
||||
* @param sessionId sessionId
|
||||
* @param <T> T
|
||||
* @return session
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends ITerminalSession> T getSession(String channelId, String sessionId) {
|
||||
return (T) channelSessions.get(channelId, sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话
|
||||
*
|
||||
* @param channelId channelId
|
||||
* @return session
|
||||
*/
|
||||
public Map<String, ITerminalSession> getSession(String channelId) {
|
||||
return channelSessions.get(channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部会话
|
||||
*
|
||||
* @return session
|
||||
*/
|
||||
public MultiConcurrentHashMap<String, String, ITerminalSession> getChannelSessions() {
|
||||
return channelSessions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.terminal.session;
|
||||
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.enums.BooleanBit;
|
||||
import org.dromara.visor.framework.websocket.core.utils.WebSockets;
|
||||
import org.dromara.visor.module.asset.enums.TerminalConnectStatusEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import org.dromara.visor.module.asset.handler.host.terminal.model.response.TerminalCloseResponse;
|
||||
import org.dromara.visor.module.asset.service.TerminalConnectLogService;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
/**
|
||||
* 终端会话基类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/4 16:51
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class TerminalSession implements ITerminalSession {
|
||||
|
||||
@Getter
|
||||
protected final String sessionId;
|
||||
|
||||
protected final WebSocketSession channel;
|
||||
|
||||
@Getter
|
||||
protected final TerminalConfig config;
|
||||
|
||||
@Getter
|
||||
protected volatile boolean closed;
|
||||
|
||||
protected volatile boolean forceOffline;
|
||||
|
||||
public TerminalSession(String sessionId, WebSocketSession channel, TerminalConfig config) {
|
||||
this.sessionId = sessionId;
|
||||
this.channel = channel;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
*/
|
||||
protected abstract void releaseResource();
|
||||
|
||||
/**
|
||||
* 发送关闭消息
|
||||
*/
|
||||
protected void sendCloseMessage() {
|
||||
log.info("TerminalSession close {}, forClose: {}, forceOffline: {}", sessionId, this.closed, this.forceOffline);
|
||||
// 发送关闭信息
|
||||
TerminalCloseResponse resp = TerminalCloseResponse.builder()
|
||||
.type(OutputTypeEnum.CLOSE.getType())
|
||||
.sessionId(this.sessionId)
|
||||
.forceClose(BooleanBit.of(this.forceOffline).getValue())
|
||||
.msg(this.forceOffline ? TerminalMessage.FORCED_OFFLINE : TerminalMessage.CONNECTION_CLOSED)
|
||||
.build();
|
||||
WebSockets.sendText(channel, OutputTypeEnum.CLOSE.format(resp));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
log.info("terminal close {}", sessionId);
|
||||
// 检查并且关闭
|
||||
if (this.checkAndClose()) {
|
||||
// 修改状态
|
||||
SpringHolder.getBean(TerminalConnectLogService.class)
|
||||
.updateStatusById(config.getLogId(), TerminalConnectStatusEnum.COMPLETE, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceOffline() {
|
||||
log.info("terminal forceOffline {}", sessionId);
|
||||
this.forceOffline = true;
|
||||
// 关闭
|
||||
this.checkAndClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并且关闭会话
|
||||
*
|
||||
* @return close
|
||||
*/
|
||||
private boolean checkAndClose() {
|
||||
if (closed) {
|
||||
return false;
|
||||
}
|
||||
this.closed = true;
|
||||
// 释放资源
|
||||
try {
|
||||
this.releaseResource();
|
||||
} catch (Exception e) {
|
||||
log.error("terminal release error {}", sessionId, e);
|
||||
}
|
||||
// 发送关闭信息
|
||||
try {
|
||||
this.sendCloseMessage();
|
||||
} catch (Exception e) {
|
||||
log.error("terminal send close error {}", sessionId, e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelId() {
|
||||
return channel.getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,8 +22,9 @@
|
||||
*/
|
||||
package org.dromara.visor.module.asset.service;
|
||||
|
||||
import org.dromara.visor.common.session.config.RdpConnectConfig;
|
||||
import org.dromara.visor.common.session.config.SshConnectConfig;
|
||||
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
||||
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
|
||||
import org.dromara.visor.module.asset.entity.request.host.HostTestConnectRequest;
|
||||
|
||||
/**
|
||||
@@ -43,29 +44,55 @@ public interface HostConnectService {
|
||||
void testHostConnect(HostTestConnectRequest request);
|
||||
|
||||
/**
|
||||
* 获取 SSH 连接信息
|
||||
* 获取 SSH 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @return session
|
||||
*/
|
||||
TerminalConnectDTO getSshConnectInfo(Long hostId);
|
||||
SshConnectConfig getSshConnectConfig(Long hostId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 SSH 连接信息
|
||||
* 使用用户配置获取 SSH 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
TerminalConnectDTO getSshConnectInfo(Long hostId, Long userId);
|
||||
SshConnectConfig getSshConnectConfig(Long hostId, Long userId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 SSH 连接信息
|
||||
* 使用用户配置获取 SSH 连接配置
|
||||
*
|
||||
* @param host host
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
TerminalConnectDTO getSshConnectInfo(HostDO host, Long userId);
|
||||
SshConnectConfig getSshConnectConfig(HostDO host, Long userId);
|
||||
|
||||
/**
|
||||
* 获取 RDP 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @return session
|
||||
*/
|
||||
RdpConnectConfig getRdpConnectConfig(Long hostId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 RDP 连接配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
RdpConnectConfig getRdpConnectConfig(Long hostId, Long userId);
|
||||
|
||||
/**
|
||||
* 使用用户配置获取 RDP 连接配置
|
||||
*
|
||||
* @param host host
|
||||
* @param userId userId
|
||||
* @return session
|
||||
*/
|
||||
RdpConnectConfig getRdpConnectConfig(HostDO host, Long userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ package org.dromara.visor.module.asset.service;
|
||||
|
||||
import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
import org.dromara.visor.module.asset.entity.request.host.HostExtraUpdateRequest;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostSpecExtraModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,8 +34,8 @@ import org.dromara.visor.module.asset.convert.HostGroupConvert;
|
||||
import org.dromara.visor.module.asset.dao.HostDAO;
|
||||
import org.dromara.visor.module.asset.entity.request.asset.AssetAuthorizedDataQueryRequest;
|
||||
import org.dromara.visor.module.asset.entity.vo.*;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostStatusEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostLabelExtraModel;
|
||||
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
|
||||
import org.dromara.visor.module.asset.service.HostIdentityService;
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.dromara.visor.module.asset.entity.request.host.HostConfigQueryRequest
|
||||
import org.dromara.visor.module.asset.entity.request.host.HostConfigUpdateRequest;
|
||||
import org.dromara.visor.module.asset.enums.HostStatusEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.config.HostConfigStrategyEnum;
|
||||
import org.dromara.visor.module.asset.service.HostConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -78,7 +79,7 @@ public class HostConfigServiceImpl implements HostConfigService {
|
||||
Valid.notNull(host, ErrorMessage.HOST_ABSENT);
|
||||
OperatorLogs.add(OperatorLogs.NAME, host.getName());
|
||||
// 获取处理策略
|
||||
HostTypeEnum strategy = HostTypeEnum.of(type);
|
||||
HostConfigStrategyEnum strategy = HostConfigStrategyEnum.of(type);
|
||||
GenericsDataModel newConfig = strategy.parse(request.getConfig());
|
||||
// 查询配置
|
||||
HostConfigDO record = hostConfigDAO.selectByHostIdType(hostId, type);
|
||||
@@ -123,7 +124,7 @@ public class HostConfigServiceImpl implements HostConfigService {
|
||||
String configValue = originHostConfigMap.get(type);
|
||||
if (Strings.isBlank(configValue)) {
|
||||
// 获取默认值
|
||||
configValue = HostTypeEnum.of(type).getDefault().serial();
|
||||
configValue = HostConfigStrategyEnum.of(type).getDefault().serial();
|
||||
}
|
||||
HostConfigDO newConfig = HostConfigDO.builder()
|
||||
.hostId(newId)
|
||||
@@ -152,7 +153,7 @@ public class HostConfigServiceImpl implements HostConfigService {
|
||||
@Override
|
||||
public <T extends GenericsDataModel> T getHostConfigView(HostConfigQueryRequest request) {
|
||||
String type = request.getType();
|
||||
HostTypeEnum strategy = HostTypeEnum.of(type);
|
||||
HostConfigStrategyEnum strategy = HostConfigStrategyEnum.of(type);
|
||||
// 查询配置
|
||||
HostConfigDO record = hostConfigDAO.selectByHostIdType(request.getHostId(), type);
|
||||
if (record == null) {
|
||||
|
||||
@@ -27,6 +27,10 @@ import cn.orionsec.kit.lang.utils.io.Streams;
|
||||
import cn.orionsec.kit.net.host.SessionStore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.session.config.BaseConnectConfig;
|
||||
import org.dromara.visor.common.session.config.RdpConnectConfig;
|
||||
import org.dromara.visor.common.session.config.SshConnectConfig;
|
||||
import org.dromara.visor.common.session.ssh.SessionStores;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.module.asset.dao.HostDAO;
|
||||
import org.dromara.visor.module.asset.dao.HostIdentityDAO;
|
||||
@@ -34,12 +38,16 @@ import org.dromara.visor.module.asset.dao.HostKeyDAO;
|
||||
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
||||
import org.dromara.visor.module.asset.entity.domain.HostIdentityDO;
|
||||
import org.dromara.visor.module.asset.entity.domain.HostKeyDO;
|
||||
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostRdpConfigDTO;
|
||||
import org.dromara.visor.module.asset.entity.dto.host.HostSshConfigDTO;
|
||||
import org.dromara.visor.module.asset.entity.request.host.HostTestConnectRequest;
|
||||
import org.dromara.visor.module.asset.enums.*;
|
||||
import org.dromara.visor.module.asset.handler.host.config.model.HostSshConfigModel;
|
||||
import org.dromara.visor.module.asset.enums.HostAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraAuthTypeEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostIdentityTypeEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostRdpExtraModel;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostSshExtraModel;
|
||||
import org.dromara.visor.module.asset.handler.host.jsch.SessionStores;
|
||||
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
|
||||
import org.dromara.visor.module.asset.service.HostConfigService;
|
||||
import org.dromara.visor.module.asset.service.HostConnectService;
|
||||
@@ -92,68 +100,93 @@ public class HostConnectServiceImpl implements HostConnectService {
|
||||
// SSH 连接测试
|
||||
SessionStore sessionStore = null;
|
||||
try {
|
||||
TerminalConnectDTO info = this.getSshConnectInfo(id);
|
||||
sessionStore = SessionStores.openSessionStore(info);
|
||||
SshConnectConfig config = this.getSshConnectConfig(id);
|
||||
sessionStore = SessionStores.openSessionStore(config);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.app(e.getMessage(), e);
|
||||
} finally {
|
||||
Streams.close(sessionStore);
|
||||
}
|
||||
}
|
||||
// TODO: 其他连接方式
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalConnectDTO getSshConnectInfo(Long hostId) {
|
||||
log.info("HostConnectService.getSshConnectInfo-withHost hostId: {}", hostId);
|
||||
public SshConnectConfig getSshConnectConfig(Long hostId) {
|
||||
log.info("HostConnectService.getSshConnectConfig-withHost hostId: {}", hostId);
|
||||
// 查询主机
|
||||
HostDO host = hostDAO.selectById(hostId);
|
||||
// 查询主机配置
|
||||
HostSshConfigModel config = hostConfigService.getHostConfig(hostId, HostTypeEnum.SSH.name());
|
||||
HostSshConfigDTO config = hostConfigService.getHostConfig(hostId, HostTypeEnum.SSH.name());
|
||||
// 获取配置
|
||||
return this.getHostConnectInfo(host, config, null);
|
||||
return this.getSshConnectConfig(host, config, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalConnectDTO getSshConnectInfo(Long hostId, Long userId) {
|
||||
public SshConnectConfig getSshConnectConfig(Long hostId, Long userId) {
|
||||
// 查询主机
|
||||
HostDO host = hostDAO.selectById(hostId);
|
||||
Valid.notNull(host, ErrorMessage.HOST_ABSENT);
|
||||
// 获取配置
|
||||
return this.getSshConnectInfo(host, userId);
|
||||
return this.getSshConnectConfig(host, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalConnectDTO getSshConnectInfo(HostDO host, Long userId) {
|
||||
public SshConnectConfig getSshConnectConfig(HostDO host, Long userId) {
|
||||
Long hostId = host.getId();
|
||||
log.info("HostConnectService.getSshConnectInfo hostId: {}, userId: {}", hostId, userId);
|
||||
// 验证主机是否有权限
|
||||
List<Long> hostIdList = assetAuthorizedDataService.getUserAuthorizedHostId(userId);
|
||||
Valid.isTrue(hostIdList.contains(hostId),
|
||||
ErrorMessage.ANY_NO_PERMISSION,
|
||||
DataPermissionTypeEnum.HOST_GROUP.getPermissionName());
|
||||
log.info("HostConnectService.getSshConnectConfig hostId: {}, userId: {}", hostId, userId);
|
||||
// 验证权限
|
||||
this.validHostAuthorized(userId, hostId);
|
||||
// 获取主机配置
|
||||
HostSshConfigModel config = hostConfigService.getHostConfig(hostId, HostTypeEnum.SSH.name());
|
||||
HostSshConfigDTO config = hostConfigService.getHostConfig(hostId, HostTypeEnum.SSH.name());
|
||||
Valid.notNull(config, ErrorMessage.CONFIG_ABSENT);
|
||||
// 查询主机额外配置
|
||||
HostSshExtraModel extra = hostExtraService.getHostExtra(userId, hostId, HostExtraItemEnum.SSH);
|
||||
if (extra != null) {
|
||||
HostExtraSshAuthTypeEnum extraAuthType = HostExtraSshAuthTypeEnum.of(extra.getAuthType());
|
||||
if (HostExtraSshAuthTypeEnum.CUSTOM_KEY.equals(extraAuthType)) {
|
||||
// 验证主机密钥是否有权限
|
||||
Valid.notNull(extra.getKeyId(), ErrorMessage.KEY_ABSENT);
|
||||
Valid.isTrue(dataPermissionApi.hasPermission(DataPermissionTypeEnum.HOST_KEY, userId, extra.getKeyId()),
|
||||
ErrorMessage.ANY_NO_PERMISSION,
|
||||
DataPermissionTypeEnum.HOST_KEY.getPermissionName());
|
||||
} else if (HostExtraSshAuthTypeEnum.CUSTOM_IDENTITY.equals(extraAuthType)) {
|
||||
// 验证主机身份是否有权限
|
||||
Valid.notNull(extra.getIdentityId(), ErrorMessage.IDENTITY_ABSENT);
|
||||
Valid.isTrue(dataPermissionApi.hasPermission(DataPermissionTypeEnum.HOST_IDENTITY, userId, extra.getIdentityId()),
|
||||
ErrorMessage.ANY_NO_PERMISSION,
|
||||
DataPermissionTypeEnum.HOST_IDENTITY.getPermissionName());
|
||||
}
|
||||
// 验证额外认证方式
|
||||
this.validExtraAuthentication(userId, extra.getAuthType(), extra.getKeyId(), extra.getIdentityId());
|
||||
}
|
||||
// 获取连接配置
|
||||
return this.getHostConnectInfo(host, config, extra);
|
||||
return this.getSshConnectConfig(host, config, extra);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RdpConnectConfig getRdpConnectConfig(Long hostId) {
|
||||
log.info("HostConnectService.getRdpConnectConfig-withHost hostId: {}", hostId);
|
||||
// 查询主机
|
||||
HostDO host = hostDAO.selectById(hostId);
|
||||
// 查询主机配置
|
||||
HostRdpConfigDTO config = hostConfigService.getHostConfig(hostId, HostTypeEnum.RDP.name());
|
||||
// 获取配置
|
||||
return this.getRdpConnectConfig(host, config, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RdpConnectConfig getRdpConnectConfig(Long hostId, Long userId) {
|
||||
// 查询主机
|
||||
HostDO host = hostDAO.selectById(hostId);
|
||||
Valid.notNull(host, ErrorMessage.HOST_ABSENT);
|
||||
// 获取配置
|
||||
return this.getRdpConnectConfig(host, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RdpConnectConfig getRdpConnectConfig(HostDO host, Long userId) {
|
||||
Long hostId = host.getId();
|
||||
log.info("HostConnectService.getRdpConnectConfig hostId: {}, userId: {}", hostId, userId);
|
||||
// 验证权限
|
||||
this.validHostAuthorized(userId, hostId);
|
||||
// 获取主机配置
|
||||
HostRdpConfigDTO config = hostConfigService.getHostConfig(hostId, HostTypeEnum.RDP.name());
|
||||
Valid.notNull(config, ErrorMessage.CONFIG_ABSENT);
|
||||
// 查询主机额外配置
|
||||
HostRdpExtraModel extra = hostExtraService.getHostExtra(userId, hostId, HostExtraItemEnum.RDP);
|
||||
if (extra != null) {
|
||||
// 验证额外认证方式
|
||||
this.validExtraAuthentication(userId, extra.getAuthType(), null, extra.getIdentityId());
|
||||
}
|
||||
// 获取连接配置
|
||||
return this.getRdpConnectConfig(host, config, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,46 +195,41 @@ public class HostConnectServiceImpl implements HostConnectService {
|
||||
* @param host host
|
||||
* @param config config
|
||||
* @param extra extra
|
||||
* @return session
|
||||
* @return info
|
||||
*/
|
||||
private TerminalConnectDTO getHostConnectInfo(HostDO host,
|
||||
HostSshConfigModel config,
|
||||
HostSshExtraModel extra) {
|
||||
// 填充认证信息
|
||||
TerminalConnectDTO conn = new TerminalConnectDTO();
|
||||
conn.setOsType(host.getOsType());
|
||||
conn.setArchType(host.getArchType());
|
||||
conn.setHostId(host.getId());
|
||||
conn.setHostName(host.getName());
|
||||
conn.setHostCode(host.getCode());
|
||||
conn.setHostAddress(host.getAddress());
|
||||
conn.setHostPort(config.getPort());
|
||||
conn.setTimeout(config.getConnectTimeout());
|
||||
conn.setCharset(config.getCharset());
|
||||
conn.setFileNameCharset(config.getFileNameCharset());
|
||||
conn.setFileContentCharset(config.getFileContentCharset());
|
||||
|
||||
private SshConnectConfig getSshConnectConfig(HostDO host,
|
||||
HostSshConfigDTO config,
|
||||
HostSshExtraModel extra) {
|
||||
SshConnectConfig connectConfig = SshConnectConfig.builder()
|
||||
.hostPort(config.getPort())
|
||||
.timeout(config.getConnectTimeout())
|
||||
.charset(config.getCharset())
|
||||
.fileNameCharset(config.getFileNameCharset())
|
||||
.fileContentCharset(config.getFileContentCharset())
|
||||
.build();
|
||||
// 填充基础主机信息
|
||||
this.setBaseConnectConfig(connectConfig, host);
|
||||
// 获取自定义认证方式
|
||||
HostExtraSshAuthTypeEnum extraAuthType = Optional.ofNullable(extra)
|
||||
HostExtraAuthTypeEnum extraAuthType = Optional.ofNullable(extra)
|
||||
.map(HostSshExtraModel::getAuthType)
|
||||
.map(HostExtraSshAuthTypeEnum::of)
|
||||
.map(HostExtraAuthTypeEnum::of)
|
||||
.orElse(null);
|
||||
if (HostExtraSshAuthTypeEnum.CUSTOM_KEY.equals(extraAuthType)) {
|
||||
if (HostExtraAuthTypeEnum.CUSTOM_KEY.equals(extraAuthType)) {
|
||||
// 自定义密钥
|
||||
config.setAuthType(HostSshAuthTypeEnum.KEY.name());
|
||||
config.setAuthType(HostAuthTypeEnum.KEY.name());
|
||||
config.setKeyId(extra.getKeyId());
|
||||
if (extra.getUsername() != null) {
|
||||
config.setUsername(extra.getUsername());
|
||||
}
|
||||
} else if (HostExtraSshAuthTypeEnum.CUSTOM_IDENTITY.equals(extraAuthType)) {
|
||||
} else if (HostExtraAuthTypeEnum.CUSTOM_IDENTITY.equals(extraAuthType)) {
|
||||
// 自定义身份
|
||||
config.setAuthType(HostSshAuthTypeEnum.IDENTITY.name());
|
||||
config.setAuthType(HostAuthTypeEnum.IDENTITY.name());
|
||||
config.setIdentityId(extra.getIdentityId());
|
||||
}
|
||||
|
||||
// 身份认证
|
||||
HostSshAuthTypeEnum authType = HostSshAuthTypeEnum.of(config.getAuthType());
|
||||
if (HostSshAuthTypeEnum.IDENTITY.equals(authType)) {
|
||||
HostAuthTypeEnum authType = HostAuthTypeEnum.of(config.getAuthType());
|
||||
if (HostAuthTypeEnum.IDENTITY.equals(authType)) {
|
||||
// 身份认证
|
||||
Valid.notNull(config.getIdentityId(), ErrorMessage.IDENTITY_ABSENT);
|
||||
HostIdentityDO identity = hostIdentityDAO.selectById(config.getIdentityId());
|
||||
@@ -210,32 +238,146 @@ public class HostConnectServiceImpl implements HostConnectService {
|
||||
HostIdentityTypeEnum identityType = HostIdentityTypeEnum.of(identity.getType());
|
||||
if (HostIdentityTypeEnum.PASSWORD.equals(identityType)) {
|
||||
// 密码类型
|
||||
authType = HostSshAuthTypeEnum.PASSWORD;
|
||||
authType = HostAuthTypeEnum.PASSWORD;
|
||||
config.setPassword(identity.getPassword());
|
||||
} else if (HostIdentityTypeEnum.KEY.equals(identityType)) {
|
||||
// 密钥类型
|
||||
authType = HostSshAuthTypeEnum.KEY;
|
||||
authType = HostAuthTypeEnum.KEY;
|
||||
config.setKeyId(identity.getKeyId());
|
||||
}
|
||||
}
|
||||
|
||||
// 填充认证信息
|
||||
conn.setUsername(config.getUsername());
|
||||
if (HostSshAuthTypeEnum.PASSWORD.equals(authType)) {
|
||||
connectConfig.setUsername(config.getUsername());
|
||||
if (HostAuthTypeEnum.PASSWORD.equals(authType)) {
|
||||
// 密码认证
|
||||
conn.setPassword(config.getPassword());
|
||||
} else if (HostSshAuthTypeEnum.KEY.equals(authType)) {
|
||||
connectConfig.setPassword(config.getPassword());
|
||||
} else if (HostAuthTypeEnum.KEY.equals(authType)) {
|
||||
// 密钥认证
|
||||
Long keyId = config.getKeyId();
|
||||
Valid.notNull(keyId, ErrorMessage.KEY_ABSENT);
|
||||
HostKeyDO key = hostKeyDAO.selectById(keyId);
|
||||
Valid.notNull(key, ErrorMessage.KEY_ABSENT);
|
||||
conn.setKeyId(keyId);
|
||||
conn.setPublicKey(key.getPublicKey());
|
||||
conn.setPrivateKey(key.getPrivateKey());
|
||||
conn.setPrivateKeyPassword(key.getPassword());
|
||||
connectConfig.setKeyId(keyId);
|
||||
connectConfig.setPublicKey(key.getPublicKey());
|
||||
connectConfig.setPrivateKey(key.getPrivateKey());
|
||||
connectConfig.setPrivateKeyPassword(key.getPassword());
|
||||
}
|
||||
return conn;
|
||||
return connectConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 RDP 连接信息
|
||||
*
|
||||
* @param host host
|
||||
* @param config config
|
||||
* @return info
|
||||
*/
|
||||
private RdpConnectConfig getRdpConnectConfig(HostDO host,
|
||||
HostRdpConfigDTO config,
|
||||
HostRdpExtraModel extra) {
|
||||
// 填充认证信息
|
||||
RdpConnectConfig connectConfig = RdpConnectConfig.builder()
|
||||
.hostPort(config.getPort())
|
||||
.versionGt81(config.getVersionGt81())
|
||||
.timezone(config.getTimezone())
|
||||
.keyboardLayout(config.getKeyboardLayout())
|
||||
.clipboardNormalize(config.getClipboardNormalize())
|
||||
.domain(config.getDomain())
|
||||
.preConnectionId(config.getPreConnectionId())
|
||||
.preConnectionBlob(config.getPreConnectionBlob())
|
||||
.remoteApp(config.getRemoteApp())
|
||||
.remoteAppDir(config.getRemoteAppDir())
|
||||
.remoteAppArgs(config.getRemoteAppArgs())
|
||||
.build();
|
||||
// 填充基础主机信息
|
||||
this.setBaseConnectConfig(connectConfig, host);
|
||||
if (extra != null) {
|
||||
// 设置低带宽模式
|
||||
connectConfig.setLowBandwidthMode(extra.getLowBandwidthMode());
|
||||
// 获取自定义认证方式
|
||||
HostExtraAuthTypeEnum extraAuthType = HostExtraAuthTypeEnum.of(extra.getAuthType());
|
||||
if (HostExtraAuthTypeEnum.CUSTOM_IDENTITY.equals(extraAuthType)) {
|
||||
// 自定义身份
|
||||
config.setAuthType(HostAuthTypeEnum.IDENTITY.name());
|
||||
config.setIdentityId(extra.getIdentityId());
|
||||
}
|
||||
}
|
||||
|
||||
// 身份认证
|
||||
HostAuthTypeEnum authType = HostAuthTypeEnum.of(config.getAuthType());
|
||||
if (HostAuthTypeEnum.IDENTITY.equals(authType)) {
|
||||
// 身份认证 - 仅密码
|
||||
authType = HostAuthTypeEnum.PASSWORD;
|
||||
Valid.notNull(config.getIdentityId(), ErrorMessage.IDENTITY_ABSENT);
|
||||
HostIdentityDO identity = hostIdentityDAO.selectById(config.getIdentityId());
|
||||
Valid.notNull(identity, ErrorMessage.IDENTITY_ABSENT);
|
||||
// 设置身份信息
|
||||
config.setUsername(identity.getUsername());
|
||||
config.setPassword(identity.getPassword());
|
||||
}
|
||||
|
||||
// 填充认证信息
|
||||
connectConfig.setUsername(config.getUsername());
|
||||
if (HostAuthTypeEnum.PASSWORD.equals(authType)) {
|
||||
// 密码认证
|
||||
connectConfig.setPassword(config.getPassword());
|
||||
}
|
||||
return connectConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证主机权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param hostId hostId
|
||||
*/
|
||||
private void validHostAuthorized(Long userId, Long hostId) {
|
||||
// 验证主机是否有权限
|
||||
List<Long> hostIdList = assetAuthorizedDataService.getUserAuthorizedHostId(userId);
|
||||
Valid.isTrue(hostIdList.contains(hostId),
|
||||
ErrorMessage.ANY_NO_PERMISSION,
|
||||
DataPermissionTypeEnum.HOST_GROUP.getPermissionName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证额外认证方式
|
||||
*
|
||||
* @param userId userId
|
||||
* @param authType authType
|
||||
* @param keyId keyId
|
||||
* @param identityId identityId
|
||||
*/
|
||||
private void validExtraAuthentication(Long userId, String authType, Long keyId, Long identityId) {
|
||||
HostExtraAuthTypeEnum extraAuthType = HostExtraAuthTypeEnum.of(authType);
|
||||
if (HostExtraAuthTypeEnum.CUSTOM_KEY.equals(extraAuthType)) {
|
||||
// 验证主机密钥是否有权限
|
||||
Valid.notNull(keyId, ErrorMessage.KEY_ABSENT);
|
||||
Valid.isTrue(dataPermissionApi.hasPermission(DataPermissionTypeEnum.HOST_KEY, userId, keyId),
|
||||
ErrorMessage.ANY_NO_PERMISSION,
|
||||
DataPermissionTypeEnum.HOST_KEY.getPermissionName());
|
||||
} else if (HostExtraAuthTypeEnum.CUSTOM_IDENTITY.equals(extraAuthType)) {
|
||||
// 验证主机身份是否有权限
|
||||
Valid.notNull(identityId, ErrorMessage.IDENTITY_ABSENT);
|
||||
Valid.isTrue(dataPermissionApi.hasPermission(DataPermissionTypeEnum.HOST_IDENTITY, userId, identityId),
|
||||
ErrorMessage.ANY_NO_PERMISSION,
|
||||
DataPermissionTypeEnum.HOST_IDENTITY.getPermissionName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置基础主机信息
|
||||
*
|
||||
* @param config config
|
||||
* @param host host
|
||||
*/
|
||||
private void setBaseConnectConfig(BaseConnectConfig config, HostDO host) {
|
||||
config.setOsType(host.getOsType());
|
||||
config.setArchType(host.getArchType());
|
||||
config.setHostId(host.getId());
|
||||
config.setHostName(host.getName());
|
||||
config.setHostCode(host.getCode());
|
||||
config.setHostAddress(host.getAddress());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.dromara.visor.common.handler.data.model.GenericsDataModel;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.framework.security.core.utils.SecurityUtils;
|
||||
import org.dromara.visor.module.asset.entity.request.host.HostExtraUpdateRequest;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostSpecExtraModel;
|
||||
import org.dromara.visor.module.asset.service.HostExtraService;
|
||||
import org.dromara.visor.module.infra.api.DataExtraApi;
|
||||
|
||||
@@ -298,8 +298,8 @@ public class HostIdentityServiceImpl implements HostIdentityService {
|
||||
return hostIdentityDAO.wrapper()
|
||||
.eq(HostIdentityDO::getId, request.getId())
|
||||
.eq(HostIdentityDO::getType, request.getType())
|
||||
.eq(HostIdentityDO::getKeyId, request.getKeyId())
|
||||
.like(HostIdentityDO::getName, request.getName())
|
||||
.eq(HostIdentityDO::getKeyId, request.getKeyId())
|
||||
.like(HostIdentityDO::getUsername, request.getUsername())
|
||||
.like(HostIdentityDO::getDescription, request.getDescription())
|
||||
.and(Strings.isNotEmpty(searchValue), c -> c
|
||||
|
||||
@@ -214,7 +214,7 @@ public class HostKeyServiceImpl implements HostKeyService {
|
||||
OperatorLogs.add(OperatorLogs.NAME, name);
|
||||
// 删除数据库
|
||||
int effect = hostKeyDAO.deleteBatchIds(idList);
|
||||
// 删除关联
|
||||
// 删除身份关联
|
||||
hostIdentityDAO.setKeyWithNull(idList);
|
||||
// 删除主机配置
|
||||
hostConfigDAO.setKeyIdWithNull(idList);
|
||||
|
||||
@@ -46,10 +46,14 @@ import org.dromara.visor.module.asset.entity.domain.HostDO;
|
||||
import org.dromara.visor.module.asset.entity.dto.HostCacheDTO;
|
||||
import org.dromara.visor.module.asset.entity.request.host.*;
|
||||
import org.dromara.visor.module.asset.entity.vo.HostVO;
|
||||
import org.dromara.visor.module.asset.enums.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.enums.HostStatusEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.HostExtraItemEnum;
|
||||
import org.dromara.visor.module.asset.handler.host.extra.model.HostSpecExtraModel;
|
||||
import org.dromara.visor.module.asset.service.*;
|
||||
import org.dromara.visor.module.asset.service.HostConfigService;
|
||||
import org.dromara.visor.module.asset.service.HostExtraService;
|
||||
import org.dromara.visor.module.asset.service.HostService;
|
||||
import org.dromara.visor.module.exec.api.ExecJobApi;
|
||||
import org.dromara.visor.module.exec.api.ExecTemplateApi;
|
||||
import org.dromara.visor.module.infra.api.DataExtraApi;
|
||||
import org.dromara.visor.module.infra.api.DataGroupRelApi;
|
||||
import org.dromara.visor.module.infra.api.FavoriteApi;
|
||||
@@ -95,10 +99,10 @@ public class HostServiceImpl implements HostService {
|
||||
private HostExtraService hostExtraService;
|
||||
|
||||
@Resource
|
||||
private ExecJobHostService execJobHostService;
|
||||
private ExecJobApi execJobApi;
|
||||
|
||||
@Resource
|
||||
private ExecTemplateHostService execTemplateHostService;
|
||||
private ExecTemplateApi execTemplateApi;
|
||||
|
||||
@Resource
|
||||
private TagRelApi tagRelApi;
|
||||
@@ -324,9 +328,9 @@ public class HostServiceImpl implements HostService {
|
||||
// 删除主机配置
|
||||
hostConfigDAO.deleteByHostIdList(idList);
|
||||
// 删除计划任务主机
|
||||
execJobHostService.deleteByHostIdList(idList);
|
||||
execJobApi.deleteByHostIdList(idList);
|
||||
// 删除执行模板主机
|
||||
execTemplateHostService.deleteByHostIdList(idList);
|
||||
execTemplateApi.deleteByHostIdList(idList);
|
||||
// 删除分组
|
||||
dataGroupRelApi.deleteByRelIdList(DataGroupTypeEnum.HOST, idList);
|
||||
// 删除 tag 引用
|
||||
|
||||
26
orion-visor-modules/orion-visor-module-common/pom.xml
Normal file
26
orion-visor-modules/orion-visor-module-common/pom.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-modules</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>orion-visor-module-common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>项目公共模块</description>
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.define.config;
|
||||
package org.dromara.visor.module.common.config;
|
||||
|
||||
import org.dromara.visor.common.config.ConfigRef;
|
||||
import org.dromara.visor.common.config.ConfigStore;
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.define.config;
|
||||
package org.dromara.visor.module.common.config;
|
||||
|
||||
import org.dromara.visor.common.config.ConfigRef;
|
||||
import org.dromara.visor.common.config.ConfigStore;
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.infra.define.config;
|
||||
package org.dromara.visor.module.common.config;
|
||||
|
||||
import org.dromara.visor.common.config.ConfigRef;
|
||||
import org.dromara.visor.common.config.ConfigStore;
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.define.config;
|
||||
package org.dromara.visor.module.common.config;
|
||||
|
||||
import org.dromara.visor.common.config.ConfigRef;
|
||||
import org.dromara.visor.common.config.ConfigStore;
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.common.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* guacd 配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/6/9 19:41
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "guacd")
|
||||
public class GuacdConfig {
|
||||
|
||||
/**
|
||||
* guacd 主机
|
||||
*/
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* guacd 端口
|
||||
*/
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* guacd 驱动路径
|
||||
*/
|
||||
private String drivePath;
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.handler.host.transfer.model;
|
||||
package org.dromara.visor.module.common.entity.dto;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.time.Dates;
|
||||
import lombok.Data;
|
||||
@@ -35,7 +35,7 @@ import java.util.Date;
|
||||
* @since 2024/4/15 23:13
|
||||
*/
|
||||
@Data
|
||||
public class SftpFileBackupParams {
|
||||
public class SftpFileBackupDTO {
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
@@ -52,9 +52,9 @@ public class SftpFileBackupParams {
|
||||
*/
|
||||
private String time;
|
||||
|
||||
public SftpFileBackupParams(String fileName) {
|
||||
this.fileName = fileName;
|
||||
public SftpFileBackupDTO(String fileName) {
|
||||
Date date = new Date();
|
||||
this.fileName = fileName;
|
||||
this.timestamp = date.getTime();
|
||||
this.time = Dates.format(date, Dates.YMD_HMS2);
|
||||
}
|
||||
@@ -20,16 +20,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.asset.utils;
|
||||
package org.dromara.visor.module.common.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.constant.Letters;
|
||||
import cn.orionsec.kit.lang.utils.Booleans;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.io.Files1;
|
||||
import cn.orionsec.kit.net.host.sftp.SftpExecutor;
|
||||
import cn.orionsec.kit.net.host.sftp.SftpFile;
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.dromara.visor.module.asset.define.config.AppSftpConfig;
|
||||
import org.dromara.visor.module.asset.handler.host.transfer.model.SftpFileBackupParams;
|
||||
import org.dromara.visor.module.common.config.AppSftpConfig;
|
||||
import org.dromara.visor.module.common.entity.dto.SftpFileBackupDTO;
|
||||
|
||||
/**
|
||||
* sftp 工具类
|
||||
@@ -60,11 +62,28 @@ public class SftpUtils {
|
||||
SftpFile file = executor.getFile(path);
|
||||
if (file != null) {
|
||||
// 文件存在则备份
|
||||
SftpFileBackupParams backupParams = new SftpFileBackupParams(file.getName());
|
||||
SftpFileBackupDTO backupParams = new SftpFileBackupDTO(file.getName());
|
||||
String target = Strings.format(appSftpConfig.getUploadBackupFileName(), JSON.parseObject(JSON.toJSONString(backupParams)));
|
||||
// 移动
|
||||
executor.move(path, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取移动目标路径
|
||||
*
|
||||
* @param source source
|
||||
* @param target target
|
||||
* @return absolute target
|
||||
*/
|
||||
public static String getAbsoluteTargetPath(String source, String target) {
|
||||
if (target.charAt(0) == Letters.SLASH) {
|
||||
// 绝对路径
|
||||
return Files1.getPath(Files1.normalize(target));
|
||||
} else {
|
||||
// 相对路径
|
||||
return Files1.getPath(Files1.normalize(Files1.getPath(source + "/../" + target)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "guacd",
|
||||
"type": "org.dromara.visor.module.common.config.GuacdConfig",
|
||||
"sourceType": "org.dromara.visor.module.common.config.GuacdConfig"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "guacd.host",
|
||||
"type": "java.lang.String",
|
||||
"description": "guacd 主机."
|
||||
},
|
||||
{
|
||||
"name": "guacd.port",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "guacd 端口."
|
||||
},
|
||||
{
|
||||
"name": "guacd.drive-path",
|
||||
"type": "java.lang.String",
|
||||
"description": "guacd 驱动路径."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-module-exec</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>orion-visor-module-exec-provider</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>项目执行模块</description>
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.exec.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 执行任务对外服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/14 13:10
|
||||
*/
|
||||
public interface ExecJobApi {
|
||||
|
||||
/**
|
||||
* 通过 hostId 删除任务主机
|
||||
*
|
||||
* @param hostIdList hostIdList
|
||||
* @return effect
|
||||
*/
|
||||
Integer deleteByHostIdList(List<Long> hostIdList);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user