diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/InputProtocolEnum.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/InputProtocolEnum.java index 4a192be7..158b24bc 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/InputProtocolEnum.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/InputProtocolEnum.java @@ -43,7 +43,7 @@ import javax.annotation.PostConstruct; public enum InputProtocolEnum { /** - * 连接终端 + * 请求连接 */ CONNECT("co", TerminalConnectHandler.class, @@ -74,7 +74,17 @@ public enum InputProtocolEnum { new String[]{"type", "width", "height"}, TerminalResizeRequest.class), - // ----------------------- SSH ---------------------- + // ----------------------- guacd ---------------------- + + /** + * guacd 指令 + */ + GUACD_INSTRUCTION("gi", + GuacdInstructionHandler.class, + new String[]{"type", "instruction"}, + GuacdInstructionRequest.class), + + // ----------------------- ssh ---------------------- /** * SSH 输入 @@ -84,7 +94,7 @@ public enum InputProtocolEnum { new String[]{"type", "command"}, SshInputRequest.class), - // ----------------------- SFTP ---------------------- + // ----------------------- sftp ---------------------- /** * SFTP 文件列表 @@ -182,15 +192,15 @@ public enum InputProtocolEnum { new String[]{"type", "path"}, SftpBaseRequest.class), - // ----------------------- guacd ---------------------- + // ----------------------- rdp ---------------------- /** - * guacd 指令 + * RDP 文件系统事件 */ - GUACD_INSTRUCTION("gi", - GuacdInstructionHandler.class, - new String[]{"type", "instruction"}, - GuacdInstructionRequest.class), + RDP_FILE_SYSTEM_EVENT("fse", + RdpFileSystemEventHandler.class, + new String[]{"type", "event"}, + RdpFileSystemEventRequest.class), ; diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/OutputProtocolEnum.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/OutputProtocolEnum.java index 99abd79b..1b469ad0 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/OutputProtocolEnum.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/enums/OutputProtocolEnum.java @@ -69,6 +69,13 @@ public enum OutputProtocolEnum { */ RESIZE("rs", "${type}|${width}|${height}"), + // ----------------------- guacd ---------------------- + + /** + * guacd 指令 + */ + GUACD_INSTRUCTION("gi", "${type}|${instruction}"), + // ----------------------- ssh ---------------------- /** @@ -143,13 +150,6 @@ public enum OutputProtocolEnum { */ SFTP_SET_CONTENT("sc", "${type}|${result}|${msg}|${token}"), - // ----------------------- guacd ---------------------- - - /** - * guacd 指令 - */ - GUACD_INSTRUCTION("gi", "${type}|${instruction}"), - ; private final String type; diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/RdpFileSystemEventHandler.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/RdpFileSystemEventHandler.java new file mode 100644 index 00000000..34f0b485 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/RdpFileSystemEventHandler.java @@ -0,0 +1,74 @@ +/* + * 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.terminal.handler.terminal.handler; + +import cn.orionsec.kit.lang.utils.collect.Maps; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs; +import org.dromara.visor.module.terminal.define.operator.TerminalOperatorType; +import org.dromara.visor.module.terminal.handler.terminal.model.TerminalChannelProps; +import org.dromara.visor.module.terminal.handler.terminal.model.request.RdpFileSystemEventRequest; +import org.dromara.visor.module.terminal.handler.terminal.model.transport.RdpFileSystemEvent; +import org.dromara.visor.module.terminal.handler.terminal.sender.IGuacdTerminalSender; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * rdp 文件系统事件 处理器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/2/19 11:13 + */ +@Slf4j +@Component +public class RdpFileSystemEventHandler extends AbstractTerminalHandler { + + @Override + public void handle(TerminalChannelProps props, IGuacdTerminalSender sender, RdpFileSystemEventRequest payload) { + long startTime = System.currentTimeMillis(); + String sessionId = props.getId(); + // 获取会话 + RdpFileSystemEvent fsEvent = JSON.parseObject(payload.getEvent(), RdpFileSystemEvent.class); + String event = fsEvent.getEvent(); + String path = fsEvent.getPath(); + log.info("RdpFileSystemEventHandler-handle start sessionId: {}, event: {}, path: {}", sessionId, event, path); + String operatorType; + if (TerminalOperatorType.RDP_UPLOAD.equals(event)) { + // 上传文件 + operatorType = TerminalOperatorType.RDP_UPLOAD; + } else if (TerminalOperatorType.RDP_DOWNLOAD.equals(event)) { + // 下载文件 + operatorType = TerminalOperatorType.RDP_DOWNLOAD; + } else { + return; + } + // 保存操作日志 + Map extra = Maps.newMap(); + extra.put(OperatorLogs.PATH, path); + this.saveOperatorLog(props, extra, operatorType, startTime, null); + } + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/model/request/RdpFileSystemEventRequest.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/model/request/RdpFileSystemEventRequest.java new file mode 100644 index 00000000..896a951e --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/model/request/RdpFileSystemEventRequest.java @@ -0,0 +1,51 @@ +/* + * 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.terminal.handler.terminal.model.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.dromara.visor.module.terminal.handler.terminal.model.TerminalBasePayload; + +/** + * rdp 文件系统事件请求 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/2/6 13:31 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class RdpFileSystemEventRequest extends TerminalBasePayload { + + /** + * 事件 + */ + private String event; + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/model/transport/RdpFileSystemEvent.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/model/transport/RdpFileSystemEvent.java new file mode 100644 index 00000000..c3c16116 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/model/transport/RdpFileSystemEvent.java @@ -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.terminal.handler.terminal.model.transport; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * RDP 文件系统事件 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2025/6/30 22:33 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RdpFileSystemEvent { + + /** + * 事件 + */ + private String event; + + /** + * 文件路径 + */ + private String path; + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/TransferSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/TransferSession.java index 3ab49dda..7f3e942b 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/TransferSession.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/session/TransferSession.java @@ -23,6 +23,7 @@ package org.dromara.visor.module.terminal.handler.transfer.session; import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException; +import cn.orionsec.kit.lang.utils.collect.Maps; import cn.orionsec.kit.lang.utils.io.Streams; import cn.orionsec.kit.net.host.SessionStore; import cn.orionsec.kit.net.host.sftp.SftpExecutor; @@ -33,14 +34,18 @@ import org.dromara.visor.common.constant.Const; import org.dromara.visor.common.constant.FieldConst; import org.dromara.visor.common.session.config.SshConnectConfig; import org.dromara.visor.framework.biz.operator.log.core.model.OperatorLogModel; +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.terminal.handler.terminal.model.TerminalChannelProps; import org.dromara.visor.module.terminal.handler.terminal.record.TerminalAsyncSaver; +import org.dromara.visor.module.terminal.handler.terminal.utils.TerminalUtils; import org.dromara.visor.module.terminal.handler.transfer.enums.TransferReceiver; import org.dromara.visor.module.terminal.handler.transfer.model.TransferOperatorRequest; import org.dromara.visor.module.terminal.handler.transfer.utils.TransferUtils; import org.springframework.web.socket.WebSocketSession; import java.util.List; +import java.util.Map; /** * 主机传输会话实现 @@ -149,10 +154,19 @@ public abstract class TransferSession implements ITransferSession { * @param paths paths */ protected void saveOperatorLog(Long logId, String type, List paths) { + TerminalChannelProps props = WebSockets.getAttr(channel, FieldConst.PROPS); String path = String.join(Const.LF, paths); int count = paths.size(); // 获取操作日志 - OperatorLogModel model = TransferUtils.getOperatorLogModel(type, path, count, connectConfig, WebSockets.getAttr(channel, FieldConst.PROPS)); + Map extra = Maps.newMap(); + extra.put(OperatorLogs.PATH, path); + extra.put(OperatorLogs.COUNT, count); + extra.put(OperatorLogs.HOST_ID, connectConfig.getHostId()); + extra.put(OperatorLogs.HOST_NAME, connectConfig.getHostName()); + extra.put(OperatorLogs.ADDRESS, connectConfig.getHostAddress()); + OperatorLogModel model = TerminalUtils.getOperatorLogModel(props, extra, type, System.currentTimeMillis(), null); + // 保存操作日志 + TerminalAsyncSaver.saveOperatorLog(model); // 保存操作日志 TerminalAsyncSaver.saveOperatorLog(model); } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java index 8dcf85d3..d4b0cf2a 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/transfer/utils/TransferUtils.java @@ -23,21 +23,14 @@ package org.dromara.visor.module.terminal.handler.transfer.utils; import cn.orionsec.kit.lang.utils.Strings; -import cn.orionsec.kit.lang.utils.collect.Maps; import com.alibaba.fastjson.JSON; import org.apache.catalina.connector.ClientAbortException; import org.dromara.visor.common.constant.ErrorMessage; -import org.dromara.visor.common.session.config.BaseConnectConfig; -import org.dromara.visor.framework.biz.operator.log.core.model.OperatorLogModel; -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.terminal.handler.terminal.model.TerminalChannelProps; -import org.dromara.visor.module.terminal.handler.terminal.utils.TerminalUtils; import org.dromara.visor.module.terminal.handler.transfer.enums.TransferReceiver; import org.dromara.visor.module.terminal.handler.transfer.model.TransferOperatorResponse; import org.springframework.web.socket.WebSocketSession; -import java.util.Map; import java.util.function.Consumer; /** @@ -52,30 +45,6 @@ public class TransferUtils { private TransferUtils() { } - /** - * 获取传输操作日志 - * - * @param type type - * @param path path - * @param count count - * @param config config - * @param props props - */ - public static OperatorLogModel getOperatorLogModel(String type, - String path, Integer count, - BaseConnectConfig config, - TerminalChannelProps props) { - // 设置参数 - Map extra = Maps.newMap(); - extra.put(OperatorLogs.PATH, path); - extra.put(OperatorLogs.COUNT, count); - extra.put(OperatorLogs.HOST_ID, config.getHostId()); - extra.put(OperatorLogs.HOST_NAME, config.getHostName()); - extra.put(OperatorLogs.ADDRESS, config.getHostAddress()); - // 获取操作日志 - return TerminalUtils.getOperatorLogModel(props, extra, type, System.currentTimeMillis(), null); - } - /** * 发送消息 * diff --git a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue index 1003f80e..631116ac 100644 --- a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue +++ b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue @@ -275,7 +275,11 @@ // 上传文件 const uploadFile = () => { - transferManager.rdp.addUpload(props.session, fileList.value[0].file as File); + const file = fileList.value[0].file as File; + // 记录事件 + props.session.onFileSystemEvent({ event: 'terminal:rdp-upload', path: file.name }); + // 上传文件 + transferManager.rdp.addUpload(props.session, file); fileList.value = []; }; diff --git a/orion-visor-ui/src/views/terminal/interfaces/session.ts b/orion-visor-ui/src/views/terminal/interfaces/session.ts index 63a79510..ee84b1a2 100644 --- a/orion-visor-ui/src/views/terminal/interfaces/session.ts +++ b/orion-visor-ui/src/views/terminal/interfaces/session.ts @@ -175,6 +175,8 @@ export interface IRdpSession extends IGuacdSession { // 初始化 init: (config: GuacdInitConfig) => Promise; + // 文件系统事件 + onFileSystemEvent: (event: Record) => void; // 发送键 sendKeys: (keys: Array) => void; // 粘贴 diff --git a/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts b/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts index eaafb149..7a40e47b 100644 --- a/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts +++ b/orion-visor-ui/src/views/terminal/service/session/rdp-session.ts @@ -11,6 +11,7 @@ import type { OutputPayload } from '@/views/terminal/types/protocol'; import { InputProtocol } from '@/views/terminal/types/protocol'; import { TerminalMessages, fitDisplayValue, TerminalCloseCode } from '@/views/terminal/types/const'; import { screenshot } from '@/views/terminal/types/utils'; +import { Message } from '@arco-design/web-vue'; import { useTerminalStore } from '@/store'; import Guacamole from 'guacamole-common-js'; import BaseSession from './base-session'; @@ -105,6 +106,13 @@ export default class RdpSession extends BaseSession { + if (!this.isWriteable()) { + Message.error('无写入权限'); + return; + } + // 记录事件 + this.onFileSystemEvent({ event: 'terminal:rdp-download', path: filename }); + // 下载文件 useTerminalStore().transferManager.rdp.addDownload(this, stream, mimetype, filename); }; } @@ -160,6 +168,11 @@ export default class RdpSession extends BaseSession): void { + this.channel.send(InputProtocol.RDP_FILE_SYSTEM_EVENT, { event: JSON.stringify(event) }); + } + // 发送键 sendKeys(keys: Array): void { if (!this.isWriteable()) { diff --git a/orion-visor-ui/src/views/terminal/types/protocol.ts b/orion-visor-ui/src/views/terminal/types/protocol.ts index 60a26e7b..e866f230 100644 --- a/orion-visor-ui/src/views/terminal/types/protocol.ts +++ b/orion-visor-ui/src/views/terminal/types/protocol.ts @@ -45,6 +45,11 @@ export const InputProtocol = { type: 'rs', template: ['type', 'width', 'height'] }, + // guacd 指令 + GUACD_INSTRUCTION: { + type: 'gi', + template: ['type', 'instruction'] + }, // SSH 输入 SSH_INPUT: { type: 'i', @@ -95,11 +100,11 @@ export const InputProtocol = { type: 'sc', template: ['type', 'path'] }, - // guacd 指令 - GUACD_INSTRUCTION: { - type: 'gi', - template: ['type', 'instruction'] - } + // RDP 文件系统事件 + RDP_FILE_SYSTEM_EVENT: { + type: 'fse', + template: ['type', 'event'] + }, }; // 输出协议 @@ -140,6 +145,12 @@ export const OutputProtocol = { template: ['type'], processMethod: 'processPong' }, + // guacd 指令 + GUACD_INSTRUCTION: { + type: 'gi', + template: ['type', 'instruction'], + processMethod: 'processInstruction' + }, // SSH 输出 SSH_OUTPUT: { type: 'o', @@ -200,12 +211,6 @@ export const OutputProtocol = { template: ['type', 'result', 'msg', 'token'], processMethod: 'processSftpSetContent' }, - // guacd 指令 - GUACD_INSTRUCTION: { - type: 'gi', - template: ['type', 'instruction'], - processMethod: 'processInstruction' - } }; // 解析参数